cpu usage limit

This commit is contained in:
weihuoya
2021-03-02 12:51:03 +08:00
parent 760a964902
commit 0936ced20f
53 changed files with 1041 additions and 429 deletions

7
.gitmodules vendored
View File

@@ -44,5 +44,8 @@
path = externals/teakra
url = https://github.com/wwylele/teakra.git
[submodule "externals/oboe"]
path = externals/oboe
url = https://github.com/google/oboe.git
path = externals/oboe
url = https://github.com/google/oboe.git
[submodule "libyuv"]
path = externals/libyuv
url = https://github.com/lemenkov/libyuv.git

View File

@@ -34,9 +34,11 @@ endif()
# Glad
add_subdirectory(glad)
if (ANDROID)
add_subdirectory(oboe)
else()
# libyuv
add_subdirectory(libyuv)
target_include_directories(yuv INTERFACE ./libyuv/include)
if (NOT ANDROID)
# inih
add_subdirectory(inih)
endif()
@@ -52,6 +54,9 @@ target_include_directories(nihstro-headers INTERFACE ./nihstro/include)
# Open Source Archives
add_subdirectory(open_source_archives)
# lzo compress library
add_subdirectory(minilzo)
# SoundTouch
add_subdirectory(soundtouch)
# The SoundTouch target doesn't export the necessary include paths as properties by default

View File

@@ -7,6 +7,7 @@
<uses-feature android:glEsVersion="0x00030001" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

View File

@@ -1,3 +1,20 @@
0004000000190B00 = 沙漠老鼠团
0004000000120800 = 海岛之日
00040000001ACD00 = 勇气地牢
0004000000154700 = 乐高都市 卧底风云 追捕
00040000000AD600 = 乐高都市 卧底风云 追捕
00040000000AD500 = 乐高都市 卧底风云 追捕
000400000007C700 = 马里奥网球公开赛
000400000007C800 = 马里奥网球公开赛
0004000000064D00 = 马里奥网球公开赛
00040000000B9100 = 马里奥网球公开赛
00040000000DCD00 = 马里奥高尔夫 世界巡回赛
00040000000A5300 = 马里奥高尔夫 世界巡回赛
00040000000DCE00 = 马里奥高尔夫 世界巡回赛
000400000017E200 = 马里奥&索尼克 里约2016奥运会
000400000F700100 = 异度之刃
000400000F700200 = 异度之刃
000400000F700000 = 异度之刃
0004000000187500 = 极度恐惧
0004000000115400 = 第七龙神3 代号VFD
0004000000053700 = 终极兵团

View File

@@ -171,7 +171,7 @@ public final class NativeLibrary {
public static boolean isValidFile(String filename) {
String name = filename.toLowerCase();
return (name.endsWith(".cia") || name.endsWith(".cci") || name.endsWith(".3ds") ||
return (name.endsWith(".cia") || name.endsWith(".cci") || name.endsWith(".3ds") || name.endsWith(".elf") ||
name.endsWith(".cxi") || name.endsWith(".app") || name.endsWith(".3dsx"));
}

View File

@@ -285,6 +285,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
mActivity.setSettingChanged();
StringSetting setting = scSetting.setSelectedValue(value);
if (scSetting.getSetting().getKey().equals(SettingsFile.KEY_CAMERA_TYPE) &&
"camera".equals(value)) {
PermissionsHandler.checkCameraPermission(mActivity);
}
if (setting != null) {
mActivity.putSetting(setting);
}

View File

@@ -28,6 +28,7 @@ public final class SettingsFile {
public static final String KEY_SYSTEM_REGION = "region_value";
public static final String KEY_SYSTEM_LANGUAGE = "language";
public static final String KEY_USE_PRESENT_THREAD = "use_present_thread";
public static final String KEY_CPU_USAGE_LIMIT = "cpu_usage_limit";
// Renderer
public static final String KEY_USE_GLES = "use_gles";
public static final String KEY_SHOW_FPS = "show_fps";

View File

@@ -130,6 +130,7 @@ public final class SettingsFragment extends Fragment {
SettingSection debugSection = mSettings.getSection(Settings.SECTION_INI_DEBUG);
Setting shaderType = debugSection.getSetting(SettingsFile.KEY_SHADER_TYPE);
Setting presentThread = debugSection.getSetting(SettingsFile.KEY_USE_PRESENT_THREAD);
Setting cpuLimit = debugSection.getSetting(SettingsFile.KEY_CPU_USAGE_LIMIT);
Setting ocrKey = debugSection.getSetting(SettingsFile.KEY_BAIDU_OCR_KEY);
Setting ocrSecret = debugSection.getSetting(SettingsFile.KEY_BAIDU_OCR_SECRET);
@@ -165,8 +166,8 @@ public final class SettingsFragment extends Fragment {
R.string.setting_custom_textures, 0, false, customTex));
sl.add(new CheckBoxSetting(SettingsFile.KEY_PRELOAD_TEXTURES, Settings.SECTION_INI_RENDERER,
R.string.setting_preload_textures, 0, false, preloadTex));
sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_FRAME_LIMIT, Settings.SECTION_INI_RENDERER,
R.string.frame_limit_enable, R.string.frame_limit_enable_description, true, useFrameLimit));
sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_USAGE_LIMIT, Settings.SECTION_INI_DEBUG,
R.string.cpu_usage_limit, R.string.cpu_usage_limit_description, false, cpuLimit));
sl.add(new SliderSetting(SettingsFile.KEY_FRAME_LIMIT, Settings.SECTION_INI_RENDERER,
R.string.frame_limit_slider, R.string.frame_limit_slider_description, 200, "",
100, frameLimit));

View File

@@ -156,6 +156,8 @@
<string name="post_processing_shader">后处理效果</string>
<string name="setting_use_present_thread">启用多核心</string>
<string name="setting_use_present_thread_desc">大部分游戏启用多核心可以提升性能。</string>
<string name="cpu_usage_limit">CPU占用率限制</string>
<string name="cpu_usage_limit_description">启用后模拟CPU使用将被限制在较小的时间片内。</string>
<string name="frame_limit_enable">启用速度限制</string>
<string name="frame_limit_enable_description">启用时速度将被限制为正常速度的指定百分比。</string>
<string name="frame_limit_slider">限制百分比</string>
@@ -197,6 +199,7 @@
<string name="camera_blank">空白</string>
<string name="camera_still_image">静止图片</string>
<string name="camera_from_device">使用相机</string>
<string name="shader_type_normal">普通着色器</string>
<string name="shader_type_normal_with_cache">普通着色器+缓存</string>
<string name="shader_type_separate">分离着色器(不稳定)</string>

View File

@@ -97,10 +97,12 @@
<string-array name="cameraEntries">
<item>@string/camera_blank</item>
<item>@string/camera_still_image</item>
<item>@string/camera_from_device</item>
</string-array>
<string-array name="cameraValues">
<item>blank</item>
<item>image</item>
<item>camera</item>
</string-array>
<!-- Mic Input Preference -->

View File

@@ -156,6 +156,8 @@
<string name="post_processing_shader">Post-Processing Effect</string>
<string name="setting_use_present_thread">Use Dual Core</string>
<string name="setting_use_present_thread_desc">Most games can improve performance by using dual core.</string>
<string name="cpu_usage_limit">Enable CPU Usage Limit</string>
<string name="cpu_usage_limit_description">When enabled, emulation cpu usage will be limited to a smaller time slice.</string>
<string name="frame_limit_enable">Enable Speed Limit</string>
<string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed.</string>
<string name="frame_limit_slider">Speed Limit Percent</string>
@@ -197,6 +199,7 @@
<string name="camera_blank">Blank</string>
<string name="camera_still_image">Still Image</string>
<string name="camera_from_device">Use Camera</string>
<string name="shader_type_normal">Normal Shader</string>
<string name="shader_type_normal_with_cache">Normal Shader with Cache</string>
<string name="shader_type_separate">Separate Shader (Unstable)</string>

View File

@@ -22,10 +22,10 @@ add_library(main SHARED
ndk_motion.h
png_handler.h
png_handler.cpp
camera/camera_base.cpp
camera/camera_base.h
camera/camera_util.cpp
camera/camera_util.h
camera/ndk_camera.cpp
camera/ndk_camera.h
camera/still_image_camera.cpp
camera/still_image_camera.h
config/config.cpp
@@ -44,5 +44,5 @@ add_library(main SHARED
config/config_loader.h
)
target_link_libraries(main android EGL log core input_common network)
target_link_libraries(main android camera2ndk EGL log core input_common network yuv)
target_include_directories(main PRIVATE "./" "../../../externals/glad/include/")

View File

@@ -1,64 +0,0 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <android/log.h>
#include "camera/camera_base.h"
#include "camera/camera_util.h"
namespace Camera {
CameraBase::CameraBase(const Service::CAM::Flip& flip) {
using namespace Service::CAM;
flip_horizontal = basic_flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse);
flip_vertical = basic_flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse);
}
void CameraBase::SetFormat(Service::CAM::OutputFormat output_format) {
output_rgb = output_format == Service::CAM::OutputFormat::RGB565;
__android_log_print(ANDROID_LOG_INFO, "citra", "CameraBase::SetFormat output_rgb: %d", output_rgb);
}
void CameraBase::SetResolution(const Service::CAM::Resolution& resolution) {
width = resolution.width;
height = resolution.height;
__android_log_print(ANDROID_LOG_INFO, "citra", "CameraBase::SetResolution width: %d, height: %d", width, height);
}
void CameraBase::SetFlip(Service::CAM::Flip flip) {
using namespace Service::CAM;
flip_horizontal = basic_flip_horizontal ^ (flip == Flip::Horizontal || flip == Flip::Reverse);
flip_vertical = basic_flip_vertical ^ (flip == Flip::Vertical || flip == Flip::Reverse);
__android_log_print(ANDROID_LOG_INFO, "citra", "CameraBase::SetFlip flip_horizontal: %d, flip_vertical: %d", flip_horizontal, flip_vertical);
}
void CameraBase::SetEffect(Service::CAM::Effect effect) {
if (effect != Service::CAM::Effect::None) {
LOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast<int>(effect));
}
__android_log_print(ANDROID_LOG_INFO, "citra", "CameraBase::SetEffect effect: %d", effect);
}
void CameraBase::SetFrameRate(Service::CAM::FrameRate frame_rate) {
__android_log_print(ANDROID_LOG_INFO, "citra", "CameraBase::SetFrameRate frame_rate: %d", frame_rate);
}
std::vector<u16> CameraBase::ReceiveFrame() {
return CameraUtil::ProcessImage(DoReceiveFrame(), width, height, output_rgb, flip_horizontal,
flip_vertical);
}
std::unique_ptr<CameraInterface> BaseCameraFactory::CreatePreview(const std::string& config,
int width, int height,
const Service::CAM::Flip& flip) {
std::unique_ptr<CameraInterface> camera = Create(config, flip);
if (camera->IsPreviewAvailable()) {
return camera;
}
LOG_ERROR(Service_CAM, "Couldn't load the camera: {}", config);
return nullptr;
}
} // namespace Camera

View File

@@ -1,38 +0,0 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "core/frontend/camera/factory.h"
namespace Camera {
// Base class for camera interfaces of citra
class CameraBase : public CameraInterface {
public:
CameraBase(const Service::CAM::Flip& flip);
void SetResolution(const Service::CAM::Resolution&) override;
void SetFlip(Service::CAM::Flip) override;
void SetEffect(Service::CAM::Effect) override;
void SetFormat(Service::CAM::OutputFormat) override;
void SetFrameRate(Service::CAM::FrameRate frame_rate) override;
std::vector<u16> ReceiveFrame() override;
virtual std::vector<u32>& DoReceiveFrame() = 0;
protected:
int width = 0;
int height = 0;
bool output_rgb = false;
bool flip_horizontal, flip_vertical;
bool basic_flip_horizontal, basic_flip_vertical;
};
// Base class for camera factories of citra_qt
class BaseCameraFactory : public CameraFactory {
std::unique_ptr<CameraInterface> CreatePreview(const std::string& config, int width, int height,
const Service::CAM::Flip& flip) override;
};
} // namespace Camera

View File

@@ -0,0 +1,598 @@
// Copyright 2020 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
#include <array>
#include <mutex>
#include <android/log.h>
#include <media/NdkImageReader.h>
#include <camera/NdkCameraCaptureSession.h>
#include <camera/NdkCameraDevice.h>
#include <camera/NdkCameraManager.h>
#include <camera/NdkCameraMetadata.h>
#include <camera/NdkCaptureRequest.h>
#include <libyuv.h>
#include "jni_common.h"
#include "camera/ndk_camera.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
/// YUVImage
struct YUVImage {
int width;
int height;
std::vector<u8> y;
std::vector<u8> u;
std::vector<u8> v;
YUVImage() : width(0), height(0) {}
explicit YUVImage(int w, int h) : width(w), height(h), y(w * h), u(w * h / 4), v(w * h / 4) {}
void SetDimension(int w, int h) {
width = w;
height = h;
y.resize(w *h);
u.resize(w * h / 4);
v.resize(w * h / 4);
}
void Swap(YUVImage& other) {
y.swap(other.y);
u.swap(other.u);
v.swap(other.v);
std::swap(width, other.width);
std::swap(height, other.height);
}
void Clear() {
y.clear();
u.clear();
v.clear();
width = height = 0;
}
};
#define YUV(image) image.y.data(), image.width, image.u.data(), image.width / 2, image.v.data(), image.width / 2
////////////////////////////////////////////////////////////////////////////////////////////////////
/// CaptureSession
struct CaptureSession {
bool Create(ACameraManager* manager) {
// init image first
yuv_image.SetDimension(width, height);
out_image.SetDimension(width, height);
ACameraDevice_StateCallbacks callback{
this,
&CaptureSession::OnCameraDisconnected,
&CaptureSession::OnCameraError
};
camera_status_t camera_status = ACameraManager_openCamera(manager, camera_id.c_str(), &callback, &camera_device);
if (camera_status != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to create camera: %d", camera_status);
return false;
}
media_status_t media_status = AImageReader_new(width, height, format, 4, &image_reader);
if (media_status != AMEDIA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to create image reader: %d", media_status);
Release();
return false;
}
AImageReader_ImageListener image_listener{
this,
&CaptureSession::OnImageAvailable
};
media_status = AImageReader_setImageListener(image_reader, &image_listener);
if (media_status != AMEDIA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to set image listener: %d", media_status);
Release();
return false;
}
media_status = AImageReader_getWindow(image_reader, &native_window);
if (media_status != AMEDIA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to get window: %d", media_status);
Release();
return false;
}
camera_status = ACaptureSessionOutput_create(native_window, &session_output);
if (camera_status != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to create output session: %d", camera_status);
Release();
return false;
}
camera_status = ACaptureSessionOutputContainer_create(&output_container);
if (camera_status != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to create output container: %d", camera_status);
Release();
return false;
}
camera_status = ACaptureSessionOutputContainer_add(output_container, session_output);
if (camera_status != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to create output container: %d", camera_status);
Release();
return false;
}
ACameraCaptureSession_stateCallbacks state_callbacks{
nullptr,
&CaptureSession::OnSessionClosed,
&CaptureSession::OnSessionReady,
&CaptureSession::OnSessionActive
};
camera_status = ACameraDevice_createCaptureSession(camera_device, output_container, &state_callbacks, &capture_session);
if (camera_status != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to create capture session: %d", camera_status);
Release();
return false;
}
camera_status = ACameraDevice_createCaptureRequest(camera_device, TEMPLATE_PREVIEW, &capture_request);
if (camera_status != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to create capture request: %d", camera_status);
Release();
return false;
}
camera_status = ACameraOutputTarget_create(native_window, &output_target);
if (camera_status != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to create output target: %d", camera_status);
Release();
return false;
}
camera_status = ACaptureRequest_addTarget(capture_request, output_target);
if (camera_status != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to add output target: %d", camera_status);
Release();
return false;
}
std::array<ACaptureRequest*, 1> requests = {capture_request};
ACameraCaptureSession_captureCallbacks capture_callbacks{
this,
&CaptureSession::OnCaptureStarted,
&CaptureSession::OnCaptureProgressed,
&CaptureSession::OnCaptureCompleted,
&CaptureSession::OnCaptureFailed,
&CaptureSession::OnCaptureSequenceCompleted,
&CaptureSession::OnCaptureSequenceAborted,
&CaptureSession::OnCaptureBufferLost
};
camera_status = ACameraCaptureSession_setRepeatingRequest(capture_session, &capture_callbacks, requests.size(), requests.data(), nullptr);
if (camera_status != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to set repeating request: %d", camera_status);
Release();
return false;
}
switch (GetDisplayRotation()) {
case 0:
rotation_mode = libyuv::RotationMode::kRotate90;
break;
case 1:
rotation_mode = libyuv::RotationMode::kRotate0;
break;
case 2:
rotation_mode = libyuv::RotationMode::kRotate270;
break;
case 3:
rotation_mode = libyuv::RotationMode::kRotate180;
break;
}
is_session_start = true;
return true;
}
void Release() {
// release session
if (capture_session) {
if (is_session_start) {
ACameraCaptureSession_stopRepeating(capture_session);
is_session_start = false;
}
ACameraCaptureSession_close(capture_session);
capture_session = nullptr;
}
if (capture_request) {
ACaptureRequest_free(capture_request);
capture_request = nullptr;
}
if (output_container) {
ACaptureSessionOutputContainer_free(output_container);
output_container = nullptr;
}
if (output_target) {
ACameraOutputTarget_free(output_target);
output_target = nullptr;
}
// release image
if (image_reader) {
AImageReader_delete(image_reader);
image_reader = nullptr;
}
// release device
if (camera_device) {
ACameraDevice_close(camera_device);
camera_device = nullptr;
}
}
std::vector<u16> GetOutput(const Service::CAM::Resolution& resolution, bool mirror, bool invert, bool rgb565) {
{
std::lock_guard lock{image_mutex};
out_image.Swap(yuv_image);
}
int rotated_width = width;
int rotated_height = height;
if (rotation_mode == libyuv::RotationMode::kRotate90 || rotation_mode == libyuv::RotationMode::kRotate270) {
std::swap(rotated_width, rotated_height);
}
// Rotate the image to get it in upright position
YUVImage rotated(rotated_width, rotated_height);
libyuv::I420Rotate(YUV(out_image), YUV(rotated), out_image.width, out_image.height, rotation_mode);
// Calculate crop coordinates
int crop_width, crop_height;
if (resolution.width * rotated.height > resolution.height * rotated.width) {
crop_width = rotated.width;
crop_height = rotated.width * resolution.height / resolution.width;
} else {
crop_height = rotated.height;
crop_width = rotated.height * resolution.width / resolution.height;
}
const int crop_x = (rotated.width - crop_width) / 2;
const int crop_y = (rotated.height - crop_height) / 2;
const int y_offset = crop_y * rotated.width + crop_x;
const int uv_offset = crop_y / 2 * rotated.width / 2 + crop_x / 2;
YUVImage scaled(resolution.width, resolution.height);
// Crop and scale
libyuv::I420Scale(rotated.y.data() + y_offset, rotated.width, rotated.u.data() + uv_offset, rotated.width / 2,
rotated.v.data() + uv_offset, rotated.width / 2, crop_width, crop_height, YUV(scaled),
resolution.width, resolution.height, libyuv::kFilterBilinear);
if (mirror) {
YUVImage mirrored(scaled.width, scaled.height);
libyuv::I420Mirror(YUV(scaled), YUV(mirrored), resolution.width, resolution.height);
scaled.Swap(mirrored);
}
std::vector<u16> output(resolution.width * resolution.height);
if (rgb565) {
libyuv::I420ToRGB565(YUV(scaled), reinterpret_cast<u8*>(output.data()),
resolution.width * 2, resolution.width,
invert ? -resolution.height : resolution.height);
} else {
libyuv::I420ToYUY2(YUV(scaled), reinterpret_cast<u8*>(output.data()), resolution.width * 2,
resolution.width, invert ? -resolution.height : resolution.height);
}
return output;
}
static void OnCameraDisconnected(void* context, ACameraDevice* device) {
auto* that = reinterpret_cast<CaptureSession*>(context);
__android_log_print(ANDROID_LOG_INFO, "citra", "OnCameraDisconnected");
}
static void OnCameraError(void* context, ACameraDevice* device, int error) {
auto* that = reinterpret_cast<CaptureSession*>(context);
__android_log_print(ANDROID_LOG_INFO, "citra", "OnCameraError");
}
static void OnImageAvailable(void* context, AImageReader* reader) {
auto* that = reinterpret_cast<CaptureSession*>(context);
//__android_log_print(ANDROID_LOG_INFO, "citra", "OnImageAvailable");
AImage* image = nullptr;
media_status_t media_status = AImageReader_acquireLatestImage(reader, &image);
// Y
uint8_t* src_y;
int src_size_y;
int src_stride_y;
AImage_getPlaneData(image, 0, &src_y, &src_size_y);
AImage_getPlaneRowStride(image, 0, &src_stride_y);
// U
uint8_t* src_u;
int src_size_u;
int src_stride_u;
AImage_getPlaneData(image, 1, &src_u, &src_size_u);
AImage_getPlaneRowStride(image, 1, &src_stride_u);
// V
uint8_t* src_v;
int src_size_v;
int src_stride_v;
AImage_getPlaneData(image, 2, &src_v, &src_size_v);
AImage_getPlaneRowStride(image, 2, &src_stride_v);
// stride
int src_pixel_stride_uv;
AImage_getPlanePixelStride(image, 1, &src_pixel_stride_uv);
{
int width = that->width;
int height = that->height;
auto& yuv_image = that->yuv_image;
std::lock_guard lock{that->image_mutex};
libyuv::Android420ToI420(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, src_pixel_stride_uv, YUV(yuv_image), width, height);
}
AImage_delete(image);
}
static void OnSessionClosed(void* context, ACameraCaptureSession *session) {
auto* that = reinterpret_cast<CaptureSession*>(context);
__android_log_print(ANDROID_LOG_INFO, "citra", "OnSessionClosed");
}
static void OnSessionReady(void* context, ACameraCaptureSession* session) {
auto* that = reinterpret_cast<CaptureSession*>(context);
__android_log_print(ANDROID_LOG_INFO, "citra", "OnSessionReady");
}
static void OnSessionActive(void* context, ACameraCaptureSession* session) {
auto* that = reinterpret_cast<CaptureSession*>(context);
__android_log_print(ANDROID_LOG_INFO, "citra", "OnSessionActive");
}
static void OnCaptureStarted(void* context, ACameraCaptureSession* session, const ACaptureRequest* request, int64_t timestamp) {
// ignore
}
static void OnCaptureProgressed(void* context, ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result) {
// ignore
}
static void OnCaptureCompleted(void* context, ACameraCaptureSession* session, ACaptureRequest* request, const ACameraMetadata* result) {
// ignore
}
static void OnCaptureFailed(void* context, ACameraCaptureSession* session, ACaptureRequest* request, ACameraCaptureFailure* failure) {
auto* that = reinterpret_cast<CaptureSession*>(context);
__android_log_print(ANDROID_LOG_INFO, "citra", "OnCaptureFailed");
}
static void OnCaptureSequenceCompleted(void* context, ACameraCaptureSession* session, int sequenceId, int64_t frameNumber) {
auto* that = reinterpret_cast<CaptureSession*>(context);
__android_log_print(ANDROID_LOG_INFO, "citra", "OnCaptureSequenceCompleted");
}
static void OnCaptureSequenceAborted(void* context, ACameraCaptureSession* session, int sequenceId) {
auto* that = reinterpret_cast<CaptureSession*>(context);
__android_log_print(ANDROID_LOG_INFO, "citra", "OnCaptureSequenceAborted");
}
static void OnCaptureBufferLost(void* context, ACameraCaptureSession* session, ACaptureRequest* request, ACameraWindowType* window, int64_t frameNumber) {
auto* that = reinterpret_cast<CaptureSession*>(context);
__android_log_print(ANDROID_LOG_INFO, "citra", "OnCaptureBufferLost");
}
std::mutex image_mutex;
YUVImage yuv_image;
YUVImage out_image;
//
std::string camera_id;
int format = 0;
int width = 0;
int height = 0;
bool is_session_start = false;
libyuv::RotationMode rotation_mode;
//
ACameraDevice* camera_device = nullptr;
AImageReader* image_reader = nullptr;
// managed by image reader, Do NOT call ANativeWindow_release on it
ANativeWindow* native_window = nullptr;
ACaptureSessionOutputContainer* output_container = nullptr;
ACaptureSessionOutput* session_output = nullptr;
ACameraOutputTarget* output_target = nullptr;
ACaptureRequest* capture_request = nullptr;
ACameraCaptureSession* capture_session = nullptr;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// NDKCameraInterface Impl
class NDKCameraInterface::Impl {
public:
Impl(const std::string &config, const Service::CAM::Flip& flip) {
manager = ACameraManager_create();
mirror = base_mirror =
flip == Service::CAM::Flip::Horizontal || flip == Service::CAM::Flip::Reverse;
invert = base_invert =
flip == Service::CAM::Flip::Vertical || flip == Service::CAM::Flip::Reverse;
}
~Impl() {
if (manager) {
capture_sessions[0].Release();
capture_sessions[1].Release();
ACameraManager_delete(manager);
manager = nullptr;
}
}
void Initialize() {
ACameraIdList* id_list = nullptr;
camera_status_t ret = ACameraManager_getCameraIdList(manager, &id_list);
if (ret != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to get camera id list: %d", ret);
ACameraManager_delete(manager);
return;
}
if (id_list->numCameras <= 0) {
__android_log_print(ANDROID_LOG_INFO, "citra", "no camera devices found");
ACameraManager_deleteCameraIdList(id_list);
ACameraManager_delete(manager);
return;
}
for (int i = 0; i < id_list->numCameras; ++i) {
ACameraMetadata* metadata = nullptr;
ret = ACameraManager_getCameraCharacteristics(manager, id_list->cameraIds[i], &metadata);
if (ret != ACAMERA_OK) {
__android_log_print(ANDROID_LOG_INFO, "citra", "failed to get camera characteristics: %d", ret);
continue;
}
int is_front_camera = 0;
ACameraMetadata_const_entry entry;
ACameraMetadata_getConstEntry(metadata, ACAMERA_LENS_FACING, &entry);
if (entry.data.i32[0] == ACAMERA_LENS_FACING_FRONT) {
is_front_camera = 1;
} else if (entry.data.i32[0] == ACAMERA_LENS_FACING_BACK) {
is_front_camera = 0;
}
auto& session = capture_sessions[is_front_camera];
session.camera_id = id_list->cameraIds[i];
ACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry);
session.format = 0;
session.width = std::numeric_limits<int>::max();
session.height = std::numeric_limits<int>::max();
for (int j = 0; j < entry.count; j += 4) {
int format = entry.data.i32[j + 0];
int width = entry.data.i32[j + 1];
int height = entry.data.i32[j + 2];
int stream = entry.data.i32[j + 3];
if (stream & ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) {
// This is an input stream
continue;
}
if (format == AIMAGE_FORMAT_YUV_420_888) {
session.format = AIMAGE_FORMAT_YUV_420_888;
if (width > 640 && height > 640) {
if (session.width > width || session.height > height) {
session.width = width;
session.height = height;
}
}
}
}
ACameraMetadata_free(metadata);
}
ACameraManager_deleteCameraIdList(id_list);
}
CaptureSession& GetCurrentSession() {
return capture_sessions[current];
}
void StartCapture() {
GetCurrentSession().Create(manager);
}
void StopCapture() {
GetCurrentSession().Release();
}
void SetResolution(const Service::CAM::Resolution& r) {
resolution = r;
}
void SetFlip(Service::CAM::Flip flip) {
mirror = base_mirror ^
(flip == Service::CAM::Flip::Horizontal || flip == Service::CAM::Flip::Reverse);
invert =
base_invert ^ (flip == Service::CAM::Flip::Vertical || flip == Service::CAM::Flip::Reverse);
}
void SetFormat(Service::CAM::OutputFormat f) {
rgb565 = f == Service::CAM::OutputFormat::RGB565;
}
std::vector<u16> ReceiveFrame() {
return GetCurrentSession().GetOutput(resolution, mirror, invert, rgb565);
}
bool IsPreviewAvailable() {
return true;
}
private:
ACameraManager* manager = nullptr;
u32 current = 0;
// 0 - back camera, 1 - front camera
std::array<CaptureSession, 2> capture_sessions;
// config
Service::CAM::Resolution resolution;
bool base_mirror;
bool base_invert;
bool mirror;
bool invert;
bool rgb565;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// NDKCameraInterface
NDKCameraInterface::NDKCameraInterface(const std::string &config, const Service::CAM::Flip& flip)
: impl(std::make_shared<Impl>(config, flip)) {
impl->Initialize();
}
void NDKCameraInterface::StartCapture() {
impl->StartCapture();
}
void NDKCameraInterface::StopCapture() {
impl->StopCapture();
}
NDKCameraInterface::~NDKCameraInterface() = default;
void NDKCameraInterface::SetResolution(const Service::CAM::Resolution& r) {
impl->SetResolution(r);
}
void NDKCameraInterface::SetFlip(Service::CAM::Flip flip) {
impl->SetFlip(flip);
}
void NDKCameraInterface::SetEffect(Service::CAM::Effect) {
// igore
}
void NDKCameraInterface::SetFormat(Service::CAM::OutputFormat f) {
impl->SetFormat(f);
}
void NDKCameraInterface::SetFrameRate(Service::CAM::FrameRate frame_rate) {
// igore
}
std::vector<u16> NDKCameraInterface::ReceiveFrame() {
return impl->ReceiveFrame();
}
bool NDKCameraInterface::IsPreviewAvailable() {
return impl->IsPreviewAvailable();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// NDKCameraFactory
std::unique_ptr<Camera::CameraInterface> NDKCameraFactory::Create(const std::string &config, const Service::CAM::Flip &flip) {
return std::make_unique<NDKCameraInterface>(config, flip);;
}

View File

@@ -0,0 +1,38 @@
// Copyright 2020 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <camera/NdkCameraManager.h>
#include "core/frontend/camera/factory.h"
#include "core/frontend/camera/interface.h"
class NDKCameraInterface : public Camera::CameraInterface {
public:
NDKCameraInterface(const std::string &config, const Service::CAM::Flip& flip);
~NDKCameraInterface() override;
void StartCapture() override;
void StopCapture() override;
void SetResolution(const Service::CAM::Resolution&) override;
void SetFlip(Service::CAM::Flip) override;
void SetEffect(Service::CAM::Effect) override;
void SetFormat(Service::CAM::OutputFormat) override;
void SetFrameRate(Service::CAM::FrameRate frame_rate) override;
std::vector<u16> ReceiveFrame() override;
bool IsPreviewAvailable() override;
private:
class Impl;
std::shared_ptr<Impl> impl;
};
class NDKCameraFactory : public Camera::CameraFactory {
public:
std::unique_ptr<Camera::CameraInterface> Create(const std::string& config,
const Service::CAM::Flip& flip) override;
};

View File

@@ -6,10 +6,9 @@
#include "jni_common.h"
#include "camera/still_image_camera.h"
namespace Camera {
StillImageCamera::StillImageCamera(const std::string& config, const Service::CAM::Flip& flip)
: CameraBase(flip) {}
StillImageCamera::StillImageCamera(const std::string& config, const Service::CAM::Flip& flip) {
SetFlip(flip);
}
StillImageCamera::~StillImageCamera() = default;
@@ -31,19 +30,41 @@ void StillImageCamera::StartCapture() {
}
void StillImageCamera::StopCapture() {
// ignore
}
std::vector<u32>& StillImageCamera::DoReceiveFrame() {
return pixels;
void StillImageCamera::SetFormat(Service::CAM::OutputFormat output_format) {
output_rgb = output_format == Service::CAM::OutputFormat::RGB565;
}
void StillImageCamera::SetResolution(const Service::CAM::Resolution& resolution) {
width = resolution.width;
height = resolution.height;
}
void StillImageCamera::SetFlip(Service::CAM::Flip flip) {
using namespace Service::CAM;
flip_horizontal = basic_flip_horizontal ^ (flip == Flip::Horizontal || flip == Flip::Reverse);
flip_vertical = basic_flip_vertical ^ (flip == Flip::Vertical || flip == Flip::Reverse);
}
void StillImageCamera::SetEffect(Service::CAM::Effect effect) {
// ignore
}
void StillImageCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) {
// ignore
}
std::vector<u16> StillImageCamera::ReceiveFrame() {
return CameraUtil::ProcessImage(pixels, width, height, output_rgb, flip_horizontal, flip_vertical);
}
bool StillImageCamera::IsPreviewAvailable() {
return !pixels.empty();
return true;
}
std::unique_ptr<CameraInterface> StillImageCameraFactory::Create(const std::string& config,
std::unique_ptr<Camera::CameraInterface> StillImageCameraFactory::Create(const std::string& config,
const Service::CAM::Flip& flip) {
return std::make_unique<StillImageCamera>(config, flip);
}
} // namespace Camera

View File

@@ -4,33 +4,39 @@
#pragma once
#include "camera/camera_base.h"
#include <string>
#include "core/frontend/camera/factory.h"
#include "camera/camera_util.h"
#include "core/frontend/camera/interface.h"
#include "core/hle/service/cam/cam.h"
namespace Camera {
class StillImageCamera final : public CameraBase {
class StillImageCamera : public Camera::CameraInterface {
public:
StillImageCamera(const std::string& config, const Service::CAM::Flip& flip);
~StillImageCamera();
void StartCapture() override;
void StopCapture() override;
std::vector<u32>& DoReceiveFrame() override;
void SetResolution(const Service::CAM::Resolution&) override;
void SetFlip(Service::CAM::Flip) override;
void SetEffect(Service::CAM::Effect) override;
void SetFormat(Service::CAM::OutputFormat) override;
void SetFrameRate(Service::CAM::FrameRate frame_rate) override;
std::vector<u16> ReceiveFrame() override;
bool IsPreviewAvailable() override;
private:
int width = 0;
int height = 0;
bool output_rgb = false;
bool flip_horizontal, flip_vertical;
bool basic_flip_horizontal, basic_flip_vertical;
std::vector<u32> pixels;
};
class StillImageCameraFactory final : public BaseCameraFactory {
class StillImageCameraFactory : public Camera::CameraFactory {
public:
std::unique_ptr<CameraInterface> Create(const std::string& config,
std::unique_ptr<Camera::CameraInterface> Create(const std::string& config,
const Service::CAM::Flip& flip) override;
private:
friend class StillImageCamera;
};
} // namespace Camera

View File

@@ -57,6 +57,8 @@ void SaveDefault() {
s_layer.Set(ALLOW_SHADOW, ALLOW_SHADOW.default_value);
s_layer.Set(SHADER_TYPE, SHADER_TYPE.default_value);
s_layer.Set(USE_PRESENT_THREAD, USE_PRESENT_THREAD.default_value);
s_layer.Set(CPU_USAGE_LIMIT, CPU_USAGE_LIMIT.default_value);
s_layer.Set(LLE_MODULES, LLE_MODULES.default_value);
// custom layout
s_layer.Set(USE_CUSTOM_LAYOUT, USE_CUSTOM_LAYOUT.default_value);

View File

@@ -48,6 +48,8 @@ const ConfigInfo<std::string> CAMERA_DEVICE{{"Camera", "camera_type"}, "blank"};
const ConfigInfo<bool> ALLOW_SHADOW{{"Debug", "allow_shadow"}, false};
const ConfigInfo<u8> SHADER_TYPE{{"Debug", "shader_type"}, 1};
const ConfigInfo<bool> USE_PRESENT_THREAD{{"Debug", "use_present_thread"}, true};
const ConfigInfo<bool> CPU_USAGE_LIMIT{{"Debug", "cpu_usage_limit"}, false};
const ConfigInfo<std::string> LLE_MODULES{{"Debug", "lle_modules"}, ""};
const ConfigInfo<std::string> BAIDU_OCR_KEY{{"Debug", "baidu_ocr_key"}, ""};
const ConfigInfo<std::string> BAIDU_OCR_SECRET{{"Debug", "baidu_ocr_secret"}, ""};

View File

@@ -55,6 +55,8 @@ extern const ConfigInfo<std::string> CAMERA_DEVICE;
extern const ConfigInfo<bool> ALLOW_SHADOW;
extern const ConfigInfo<u8> SHADER_TYPE;
extern const ConfigInfo<bool> USE_PRESENT_THREAD;
extern const ConfigInfo<bool> CPU_USAGE_LIMIT;
extern const ConfigInfo<std::string> LLE_MODULES;
extern const ConfigInfo<std::string> BAIDU_OCR_KEY;
extern const ConfigInfo<std::string> BAIDU_OCR_SECRET;

View File

@@ -23,6 +23,7 @@
#include "video_core/video_core.h"
#include "camera/still_image_camera.h"
#include "camera/ndk_camera.h"
#include "config/main_settings.h"
#include "egl_android.h"
#include "input_manager.h"
@@ -245,7 +246,8 @@ JNIEXPORT void JNICALL Java_org_citra_emu_NativeLibrary_SetUserPath(JNIEnv* env,
s_keyboard = std::make_shared<AndroidKeyboard>();
Core::System::GetInstance().RegisterSoftwareKeyboard(s_keyboard);
Core::System::GetInstance().RegisterImageInterface(std::make_shared<PNGHandler>());
Camera::RegisterFactory("image", std::make_unique<Camera::StillImageCameraFactory>());
Camera::RegisterFactory("image", std::make_unique<StillImageCameraFactory>());
Camera::RegisterFactory("camera", std::make_unique<NDKCameraFactory>());
// Register real Mic factory
Frontend::Mic::RegisterRealMicFactory(std::make_unique<AndroidMicFactory>());
@@ -385,6 +387,7 @@ JNIEXPORT void JNICALL Java_org_citra_emu_NativeLibrary_Run(JNIEnv* env, jclass
// debug
Settings::values.allow_shadow = Config::Get(Config::ALLOW_SHADOW);
Settings::values.use_present_thread = Config::Get(Config::USE_PRESENT_THREAD);
Settings::values.core_downcount_hack = Config::Get(Config::CPU_USAGE_LIMIT);
u8 shaderType = Config::Get(Config::SHADER_TYPE);
if (shaderType == 0) {
Settings::values.use_separable_shader = false;
@@ -396,6 +399,7 @@ JNIEXPORT void JNICALL Java_org_citra_emu_NativeLibrary_Run(JNIEnv* env, jclass
Settings::values.use_separable_shader = true;
Settings::values.use_shader_cache = false;
}
Settings::SetLLEModules(Config::Get(Config::LLE_MODULES));
// custom layout
Settings::values.custom_layout = Config::Get(Config::USE_CUSTOM_LAYOUT);
Settings::values.custom_top_left = Config::Get(Config::CUSTOM_TOP_LEFT);

View File

@@ -151,9 +151,8 @@ public:
Memory::MemorySystem& memory;
};
ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, u32 id,
std::shared_ptr<Core::Timing::Timer> timer)
: ARM_Interface(id, timer), system(*system), memory(memory),
ARM_Dynarmic::ARM_Dynarmic(Core::System* system, u32 id, std::shared_ptr<Core::Timing::Timer> timer)
: ARM_Interface(id, timer), system(*system), memory(system->Memory()),
cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
SetPageTable(memory.GetCurrentPageTable());
}
@@ -287,6 +286,7 @@ void ARM_Dynarmic::ClearInstructionCache() {
void ARM_Dynarmic::InvalidateCacheRange(u32 start_address, std::size_t length) {
jit->InvalidateCacheRange(start_address, length);
LOG_DEBUG(Core_ARM11, "arm jit invalidate cache range");
}
Memory::PageTable* ARM_Dynarmic::GetPageTable() const {

View File

@@ -24,8 +24,7 @@ class DynarmicUserCallbacks;
class ARM_Dynarmic final : public ARM_Interface {
public:
ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, u32 id,
std::shared_ptr<Core::Timing::Timer> timer);
ARM_Dynarmic(Core::System* system, u32 id, std::shared_ptr<Core::Timing::Timer> timer);
~ARM_Dynarmic() override;
void Run() override;

View File

@@ -68,17 +68,14 @@ private:
u32 fpexc;
};
ARM_DynCom::ARM_DynCom(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode, u32 id,
std::shared_ptr<Core::Timing::Timer> timer)
: ARM_Interface(id, timer), system(system) {
state = std::make_unique<ARMul_State>(system, memory, initial_mode);
ARM_DynCom::ARM_DynCom(Core::System* system, u32 id, std::shared_ptr<Core::Timing::Timer> timer)
: ARM_Interface(id, timer) {
state = std::make_unique<ARMul_State>(*system, system->Memory(), USER32MODE);
}
ARM_DynCom::~ARM_DynCom() {}
void ARM_DynCom::Run() {
DEBUG_ASSERT(system != nullptr);
ExecuteInstructions(std::max<s64>(timer->GetDowncount(), 0));
}
@@ -155,10 +152,8 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) {
void ARM_DynCom::ExecuteInstructions(u64 num_instructions) {
state->NumInstrsToExecute = num_instructions;
unsigned ticks_executed = InterpreterMainLoop(state.get());
if (system != nullptr) {
timer->AddTicks(ticks_executed);
}
unsigned ticks_executed = InterpreterMainLoop(state.get(), timer.get());
timer->AddTicks(ticks_executed);
state->ServeBreak();
}

View File

@@ -20,9 +20,7 @@ class MemorySystem;
class ARM_DynCom final : public ARM_Interface {
public:
explicit ARM_DynCom(Core::System* system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode, u32 id,
std::shared_ptr<Core::Timing::Timer> timer);
explicit ARM_DynCom(Core::System* system, u32 id, std::shared_ptr<Core::Timing::Timer> timer);
~ARM_DynCom() override;
void Run() override;
@@ -58,6 +56,5 @@ protected:
private:
void ExecuteInstructions(u64 num_instructions);
Core::System* system;
std::unique_ptr<ARMul_State> state;
};

View File

@@ -921,7 +921,7 @@ static int clz(unsigned int x) {
MICROPROFILE_DEFINE(DynCom_Execute, "DynCom", "Execute", MP_RGB(255, 0, 0));
unsigned InterpreterMainLoop(ARMul_State* cpu) {
unsigned InterpreterMainLoop(ARMul_State* cpu, Core::Timing::Timer* timer) {
MICROPROFILE_SCOPE(DynCom_Execute);
/// Nearest upcoming GDB code execution breakpoint, relative to the last dispatch's address.
@@ -3870,14 +3870,13 @@ SUB_INST : {
}
SWI_INST : {
if (inst_base->cond == ConditionCode::AL || CondPassed(cpu, inst_base->cond)) {
DEBUG_ASSERT(cpu->system != nullptr);
swi_inst* const inst_cream = (swi_inst*)inst_base->component;
num_instrs = std::max(num_instrs, Settings::values.core_ticks_hack);
cpu->system->GetRunningCore().GetTimer().AddTicks(num_instrs);
timer->AddTicks(num_instrs);
cpu->NumInstrsToExecute =
num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
num_instrs = 0;
Kernel::SVCContext{*cpu->system}.CallSVC(inst_cream->num & 0xFFFF);
Kernel::SVCContext{cpu->system}.CallSVC(inst_cream->num & 0xFFFF);
// The kernel would call ERET to get here, which clears exclusive memory state.
cpu->UnsetExclusiveMemoryAddress();
}

View File

@@ -4,6 +4,8 @@
#pragma once
#include "core/core_timing.h"
struct ARMul_State;
unsigned InterpreterMainLoop(ARMul_State* state);
unsigned InterpreterMainLoop(ARMul_State* state, Core::Timing::Timer* timer);

View File

@@ -10,7 +10,7 @@
#include "core/core.h"
#include "core/memory.h"
ARMul_State::ARMul_State(Core::System* system, Memory::MemorySystem& memory,
ARMul_State::ARMul_State(Core::System& system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode)
: system(system), memory(memory) {
Reset();
@@ -609,9 +609,8 @@ void ARMul_State::ServeBreak() {
DEBUG_ASSERT(Reg[15] == last_bkpt.address);
}
DEBUG_ASSERT(system != nullptr);
Kernel::Thread* thread = system->Kernel().GetCurrentThreadManager().GetCurrentThread();
system->GetRunningCore().SaveContext(thread->context);
Kernel::Thread* thread = system.Kernel().GetCurrentThreadManager().GetCurrentThread();
system.Kernel().GetRunningCore().SaveContext(thread->context);
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;

View File

@@ -147,7 +147,7 @@ enum {
struct ARMul_State final {
public:
explicit ARMul_State(Core::System* system, Memory::MemorySystem& memory,
explicit ARMul_State(Core::System& system, Memory::MemorySystem& memory,
PrivilegeMode initial_mode);
void ChangePrivilegeMode(u32 new_mode);
@@ -206,7 +206,7 @@ public:
void ServeBreak();
Core::System* system;
Core::System& system;
Memory::MemorySystem& memory;
std::array<u32, 16> Reg{}; // The current register file

View File

@@ -40,7 +40,7 @@ namespace Core {
System System::s_instance;
System::ResultStatus System::RunLoop() {
return cpu_cores.size() > 1 ? RunLoopMultiCores() : RunLoopOneCore();
return Settings::values.is_new_3ds ? RunLoopMultiCores() : RunLoopSingleCore();
}
System::ResultStatus System::RunLoopMultiCores() {
@@ -61,12 +61,11 @@ System::ResultStatus System::RunLoopMultiCores() {
}
if (max_delay > 0) {
running_core = current_core_to_execute;
kernel->SetRunningCPU(running_core);
kernel->SetRunningCPU(current_core_to_execute);
if (kernel->GetCurrentThreadManager().GetCurrentThread() == nullptr) {
LOG_TRACE(Core_ARM11, "Core {} idling", current_core_to_execute->GetID());
current_core_to_execute->GetTimer().Idle();
PrepareReschedule();
kernel->PrepareReschedule();
} else {
current_core_to_execute->Run();
}
@@ -82,14 +81,13 @@ System::ResultStatus System::RunLoopMultiCores() {
cpu_core->GetTimer().Advance(max_slice);
}
for (auto& cpu_core : cpu_cores) {
running_core = cpu_core.get();
kernel->SetRunningCPU(running_core);
kernel->SetRunningCPU(cpu_core.get());
// If we don't have a currently active thread then don't execute instructions,
// instead advance to the next event and try to yield to the next thread
if (kernel->GetCurrentThreadManager().GetCurrentThread() == nullptr) {
LOG_TRACE(Core_ARM11, "Core {} idling", cpu_core->GetID());
cpu_core->GetTimer().Idle();
PrepareReschedule();
kernel->PrepareReschedule();
} else {
cpu_core->Run();
}
@@ -98,7 +96,7 @@ System::ResultStatus System::RunLoopMultiCores() {
}
HW::Update();
Reschedule();
kernel->RescheduleMultiCores();
if (reset_requested.exchange(false)) {
Reset();
@@ -109,20 +107,20 @@ System::ResultStatus System::RunLoopMultiCores() {
return status;
}
System::ResultStatus System::RunLoopOneCore() {
System::ResultStatus System::RunLoopSingleCore() {
// If we don't have a currently active thread then don't execute instructions,
// instead advance to the next event and try to yield to the next thread
if (kernel->GetCurrentThreadManager().GetCurrentThread() == nullptr) {
running_core->GetTimer().Idle();
running_core->GetTimer().Advance();
PrepareReschedule();
cpu_cores[0]->GetTimer().Idle();
cpu_cores[0]->GetTimer().Advance();
kernel->PrepareReschedule();
} else {
running_core->GetTimer().Advance();
running_core->Run();
cpu_cores[0]->GetTimer().Advance();
cpu_cores[0]->Run();
}
HW::Update();
Reschedule();
kernel->RescheduleSingleCore();
if (reset_requested.exchange(false)) {
Reset();
@@ -148,10 +146,10 @@ static void LoadOverrides(u64 title_id) {
Settings::values.skip_slow_draw = true;
} else if (title_id == 0x00040000001CCD00 || title_id == 0x00040000001B4500) {
// The Alliance Alive
Settings::SetFMVHack(true);
Settings::SetFMVHack(!Settings::values.core_downcount_hack);
} else if (title_id == 0x0004000000120900 || title_id == 0x0004000000164300) {
// Lord of Magna: Maiden Heaven
Settings::SetFMVHack(true);
Settings::SetFMVHack(!Settings::values.core_downcount_hack);
} else if (title_id == 0x000400000015CB00) {
// New Atelier Rorona
Settings::values.skip_slow_draw = true;
@@ -181,7 +179,7 @@ static void LoadOverrides(u64 title_id) {
} else if (title_id == 0x000400000008FE00) {
// 1001 Spikes [USA]
Settings::values.stream_buffer_hack = false;
Settings::SetFMVHack(true);
Settings::SetFMVHack(!Settings::values.core_downcount_hack);
} else if (title_id == 0x0004000000049100 || title_id == 0x0004000000030400 ||
title_id == 0x0004000000049000) {
// Star Fox 64
@@ -191,6 +189,22 @@ static void LoadOverrides(u64 title_id) {
Settings::values.y2r_perform_hack = true;
}
const std::array<u64, 7> cpu_limit_ids = {
0x000400000007C700, // Mario Tennis Open
0x000400000007C800, // Mario Tennis Open
0x0004000000064D00, // Mario Tennis Open
0x00040000000B9100, // Mario Tennis Open
0x00040000000DCD00, // Mario Golf: World Tour
0x00040000000A5300, // Mario Golf: World Tour
0x00040000000DCE00, // Mario Golf: World Tour
};
for (auto id : cpu_limit_ids) {
if (title_id == id) {
Settings::values.core_downcount_hack = true;
break;
}
}
const std::array<u64, 10> linear_ids = {
0x00040000001AA200, // Attack On Titan 2
0x0004000000134500, // Attack On Titan 1 CHAIN
@@ -201,6 +215,7 @@ static void LoadOverrides(u64 title_id) {
for (auto id : linear_ids) {
if (title_id == id) {
Settings::values.use_linear_filter = true;
break;
}
}
@@ -221,10 +236,11 @@ static void LoadOverrides(u64 title_id) {
for (auto id : fifa_ids) {
if (title_id == id) {
Settings::values.y2r_event_delay = true;
break;
}
}
const std::array<u64, 43> accurate_mul_ids = {
const std::array<u64, 49> accurate_mul_ids = {
0x0004000000134500, // Attack on Titan
0x00040000000DF800, // Attack on Titan
0x0004000000152000, // Attack on Titan
@@ -268,14 +284,21 @@ static void LoadOverrides(u64 title_id) {
0x0004000000125600, // The Legend of Zelda: Majoras Mask 3D
0x0004000000125500, // The Legend of Zelda: Majoras Mask 3D
0x00040000000D6E00, // The Legend of Zelda: Majoras Mask 3D
0x0004000000154700, // Lego City Undercover
0x00040000000AD600, // Lego City Undercover
0x00040000000AD500, // Lego City Undercover
0x00040000001D1800, // Luigi's Mansion
0x00040000001D1A00, // Luigi's Mansion
0x00040000001D1900, // Luigi's Mansion
};
for (auto id : accurate_mul_ids) {
if (title_id == id) {
Settings::values.shaders_accurate_mul = Settings::AccurateMul::FAST;
break;
}
}
const std::array<u64, 23> new3ds_game_ids = {
const std::array<u64, 30> new3ds_game_ids = {
0x000400000F700000, // Xenoblade Chronicles 3D [JPN]
0x000400000F700100, // Xenoblade Chronicles 3D [USA]
0x000400000F700200, // Xenoblade Chronicles 3D [EUR]
@@ -299,10 +322,18 @@ static void LoadOverrides(u64 title_id) {
0x00040000001B8700, // Minecraft [USA]
0x000400000F707F00, // Hyperlight EX [USA]
0x000400000008FE00, // 1001 Spikes [USA]
0x000400000007C700, // Mario Tennis Open
0x000400000007C800, // Mario Tennis Open
0x0004000000064D00, // Mario Tennis Open
0x00040000000B9100, // Mario Tennis Open
0x00040000000DCD00, // Mario Golf: World Tour
0x00040000000A5300, // Mario Golf: World Tour
0x00040000000DCE00, // Mario Golf: World Tour
};
for (auto id : new3ds_game_ids) {
if (title_id == id) {
Settings::values.is_new_3ds = true;
break;
}
}
}
@@ -388,65 +419,41 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
return status;
}
void System::PrepareReschedule() {
running_core->PrepareReschedule();
reschedule_pending = true;
}
PerfStats::Results System::GetAndResetPerfStats() {
return perf_stats->GetAndResetStats(timing->GetGlobalTimeUs());
}
void System::Reschedule() {
if (!reschedule_pending) {
return;
}
reschedule_pending = false;
for (const auto& core : cpu_cores) {
LOG_TRACE(Core_ARM11, "Reschedule core {}", core->GetID());
kernel->GetThreadManager(core->GetID()).Reschedule();
}
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode) {
LOG_DEBUG(HW_Memory, "initialized OK");
u32 num_cores = 1;
if (Settings::values.is_new_3ds) {
num_cores = 4;
}
memory = std::make_unique<Memory::MemorySystem>();
timing = std::make_unique<Timing>(num_cores);
kernel = std::make_unique<Kernel::KernelSystem>(
*memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores, n3ds_mode);
timing = std::make_unique<Timing>();
kernel = std::make_unique<Kernel::KernelSystem>(*memory, *timing, system_mode, n3ds_mode);
if (Settings::values.use_cpu_jit) {
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_ARM64)
for (u32 i = 0; i < num_cores; ++i) {
cpu_cores.push_back(
std::make_shared<ARM_Dynarmic>(this, *memory, i, timing->GetTimer(i)));
for (u32 i = 0; i < 4; ++i) {
cpu_cores[i] = std::make_shared<ARM_Dynarmic>(this, i, timing->GetTimer(i));
kernel->GetThreadManager(i).SetCPU(cpu_cores[i].get());
}
#else
for (u32 i = 0; i < num_cores; ++i) {
cpu_cores.push_back(
std::make_shared<ARM_DynCom>(this, *memory, USER32MODE, i, timing->GetTimer(i)));
for (u32 i = 0; i < 4; ++i) {
cpu_cores[i] = std::make_shared<ARM_DynCom>(this, i, timing->GetTimer(i));
kernel->GetThreadManager(i).SetCPU(cpu_cores[i].get());
}
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
for (u32 i = 0; i < num_cores; ++i) {
cpu_cores.push_back(
std::make_shared<ARM_DynCom>(this, *memory, USER32MODE, i, timing->GetTimer(i)));
for (u32 i = 0; i < 4; ++i) {
cpu_cores[i] = std::make_shared<ARM_DynCom>(this, i, timing->GetTimer(i));
kernel->GetThreadManager(i).SetCPU(cpu_cores[i].get());
}
}
running_core = cpu_cores[0].get();
kernel->SetCPUs(cpu_cores);
kernel->SetRunningCPU(cpu_cores[0].get());
if (Settings::values.core_downcount_hack) {
SetCpuUsageLimit(true);
}
if (Settings::values.enable_dsp_lle) {
dsp_core = std::make_unique<AudioCore::DspLle>(*memory,
@@ -486,10 +493,6 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
return ResultStatus::Success;
}
VideoCore::RendererBase& System::Renderer() {
return *VideoCore::g_renderer;
}
Service::SM::ServiceManager& System::ServiceManager() {
return *service_manager;
}
@@ -558,6 +561,19 @@ void System::RegisterImageInterface(std::shared_ptr<Frontend::ImageInterface> im
registered_image_interface = std::move(image_interface);
}
void System::SetCpuUsageLimit(bool enabled) {
if (enabled) {
u32 hacks[4] = {1, 4, 2, 2};
for (u32 i = 0; i < 4; ++i) {
timing->GetTimer(i)->SetDowncountHack(hacks[i]);
}
} else {
for (u32 i = 0; i < 4; ++i) {
timing->GetTimer(i)->SetDowncountHack(0);
}
}
}
void System::Shutdown() {
// Shutdown emulation session
GDBStub::Shutdown();
@@ -569,7 +585,7 @@ void System::Shutdown() {
cheat_engine.reset();
archive_manager.reset();
service_manager.reset();
cpu_cores.clear();
cpu_cores = {};
dsp_core.reset();
kernel.reset();
timing.reset();
@@ -577,9 +593,6 @@ void System::Shutdown() {
app_loader.reset();
custom_tex_cache.reset();
running_core = nullptr;
reschedule_pending = false;
if (auto room_member = Network::GetRoomMember().lock()) {
Network::GameInfo game_info{};
room_member->SendGameInfo(game_info);

View File

@@ -51,10 +51,6 @@ namespace Cheats {
class CheatEngine;
}
namespace VideoCore {
class RendererBase;
}
namespace Core {
class Timing;
@@ -101,7 +97,7 @@ public:
*/
ResultStatus RunLoop();
ResultStatus RunLoopMultiCores();
ResultStatus RunLoopOneCore();
ResultStatus RunLoopSingleCore();
/**
* Step the CPU one instruction
@@ -140,10 +136,8 @@ public:
* @returns True if the emulated system is powered on, otherwise false.
*/
bool IsPoweredOn() const {
return cpu_cores.size() > 0 &&
std::all_of(cpu_cores.begin(), cpu_cores.end(),
return std::all_of(cpu_cores.begin(), cpu_cores.end(),
[](std::shared_ptr<ARM_Interface> ptr) { return ptr != nullptr; });
;
}
/**
@@ -154,20 +148,8 @@ public:
return *telemetry_session;
}
/// Prepare the core emulation for a reschedule
void PrepareReschedule();
PerfStats::Results GetAndResetPerfStats();
/**
* Gets a reference to the emulated CPU.
* @returns A reference to the emulated CPU.
*/
ARM_Interface& GetRunningCore() {
return *running_core;
};
/**
* Gets a reference to the emulated CPU.
* @param core_id The id of the core requested.
@@ -196,8 +178,6 @@ public:
return *dsp_core;
}
VideoCore::RendererBase& Renderer();
/**
* Gets a reference to the service manager.
* @returns A reference to the service manager.
@@ -246,9 +226,6 @@ public:
/// Gets a const reference to the custom texture cache system
const Core::CustomTexCache& CustomTexCache() const;
/// Handles loading all custom textures from disk into cache.
void PreloadCustomTextures();
std::unique_ptr<PerfStats> perf_stats;
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
@@ -292,6 +269,9 @@ public:
using ScanningCallback = void(bool);
std::function<ScanningCallback> nfc_scanning_callback;
///
void SetCpuUsageLimit(bool enabled);
private:
/**
* Initialize the emulated system.
@@ -302,22 +282,15 @@ private:
*/
ResultStatus Init(Frontend::EmuWindow& emu_window, u32 system_mode, u8 n3ds_mode);
/// Reschedule the core emulation
void Reschedule();
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
/// ARM11 CPU core
std::vector<std::shared_ptr<ARM_Interface>> cpu_cores;
ARM_Interface* running_core = nullptr;
std::array<std::shared_ptr<ARM_Interface>, 4> cpu_cores;
/// DSP core
std::unique_ptr<AudioCore::DspInterface> dsp_core;
/// When true, signals that a reschedule should happen
bool reschedule_pending = false;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
@@ -360,7 +333,7 @@ private:
};
inline ARM_Interface& GetRunningCore() {
return System::GetInstance().GetRunningCore();
return System::GetInstance().Kernel().GetRunningCore();
}
inline ARM_Interface& GetCore(u32 core_id) {

View File

@@ -20,9 +20,8 @@ bool Timing::Event::operator<(const Timing::Event& right) const {
return std::tie(time, fifo_order) < std::tie(right.time, right.fifo_order);
}
Timing::Timing(std::size_t num_cores) {
timers.resize(num_cores);
for (std::size_t i = 0; i < num_cores; ++i) {
Timing::Timing() {
for (u32 i = 0; i < timers.size(); ++i) {
timers[i] = std::make_shared<Timer>();
}
current_timer = timers[0].get();
@@ -128,10 +127,6 @@ void Timing::Timer::AddTicks(u64 ticks) {
downcount -= ticks;
}
u64 Timing::Timer::GetIdleTicks() const {
return static_cast<u64>(idled_cycles);
}
void Timing::Timer::ForceExceptionCheck(s64 cycles) {
cycles = std::max<s64>(0, cycles);
if (downcount > cycles) {
@@ -181,7 +176,7 @@ void Timing::Timer::Advance(s64 max_slice_length) {
std::min<s64>(event_queue.front().time - executed_ticks, max_slice_length));
}
downcount = slice_length;
downcount = slice_length >> downcount_hack;
}
void Timing::Timer::Idle() {

View File

@@ -22,6 +22,7 @@
#include <limits>
#include <string>
#include <unordered_map>
#include <array>
#include <vector>
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -149,7 +150,6 @@ public:
void Idle();
u64 GetTicks() const;
u64 GetIdleTicks() const;
void AddTicks(u64 ticks);
@@ -159,6 +159,10 @@ public:
void MoveEvents();
void SetDowncountHack(u32 hack) {
downcount_hack = hack;
}
private:
friend class Timing;
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
@@ -183,9 +187,10 @@ public:
s64 downcount = MAX_SLICE_LENGTH;
s64 executed_ticks = 0;
u64 idled_cycles = 0;
u32 downcount_hack = 0;
};
explicit Timing(std::size_t num_cores);
explicit Timing();
~Timing(){};
@@ -223,7 +228,7 @@ private:
// elements remain stable regardless of rehashes/resizing.
std::unordered_map<std::string, TimingEventType> event_types;
std::vector<std::shared_ptr<Timer>> timers;
std::array<std::shared_ptr<Timer>, 4> timers;
Timer* current_timer = nullptr;
};

View File

@@ -43,10 +43,6 @@ private:
};
EmuWindow::EmuWindow() {
// TODO: Find a better place to set this.
config.min_client_area_size =
std::make_pair(Core::kScreenTopWidth, Core::kScreenTopHeight + Core::kScreenBottomHeight);
active_config = config;
touch_state = std::make_shared<TouchState>();
Input::RegisterFactory<Input::TouchDevice>("emu_window", touch_state);
}

View File

@@ -32,14 +32,6 @@ namespace Frontend {
*/
class EmuWindow {
public:
/// Data structure to store emuwindow configuration
struct WindowConfig {
bool fullscreen = false;
int res_width = 0;
int res_height = 0;
std::pair<unsigned, unsigned> min_client_area_size;
};
/// Polls window events
virtual void PollEvents() = 0;
@@ -69,25 +61,6 @@ public:
*/
void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y);
/**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in
* another thread
*/
const WindowConfig& GetActiveConfig() const {
return active_config;
}
/**
* Requests the internal configuration to be replaced by the specified argument at some point in
* the future.
* @note This method is thread-safe, because it delays configuration changes to the GUI event
* loop. Hence there is no guarantee on when the requested configuration will be active.
*/
void SetConfig(const WindowConfig& val) {
config = val;
}
/**
* Gets the framebuffer layout (width, height, and screen regions)
* @note This method is thread-safe
@@ -106,23 +79,6 @@ protected:
EmuWindow();
virtual ~EmuWindow();
/**
* Processes any pending configuration changes from the last SetConfig call.
* This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration
* field changed.
* @note Implementations will usually want to call this from the GUI thread.
* @todo Actually call this in existing implementations.
*/
void ProcessConfigurationChanges() {
// TODO: For proper thread safety, we should eventually implement a proper
// multiple-writer/single-reader queue...
if (config.min_client_area_size != active_config.min_client_area_size) {
OnMinimalClientAreaChangeRequest(config.min_client_area_size);
active_config.min_client_area_size = config.min_client_area_size;
}
}
/**
* Update framebuffer layout with the given parameter.
* @note EmuWindow implementations will usually use this in window resize event handlers.
@@ -132,21 +88,9 @@ protected:
}
private:
/**
* Handler called when the minimal client area was requested to be changed via SetConfig.
* For the request to be honored, EmuWindow implementations will usually reimplement this
* function.
*/
virtual void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) {
// By default, ignore this request and do nothing.
}
Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
WindowConfig config; ///< Internal configuration (changes pending for being applied in
/// ProcessConfigurationChanges)
WindowConfig active_config; ///< Internal active configuration
class TouchState;
std::shared_ptr<TouchState> touch_state;

View File

@@ -17,20 +17,17 @@
namespace Kernel {
/// Initialize the kernel
KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
std::function<void()> prepare_reschedule_callback, u32 system_mode,
u32 num_cores, u8 n3ds_mode)
: memory(memory), timing(timing),
prepare_reschedule_callback(std::move(prepare_reschedule_callback)) {
KernelSystem::KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, u32 system_mode,
u8 n3ds_mode)
: memory(memory), timing(timing) {
MemoryInit(system_mode, n3ds_mode);
resource_limits = std::make_unique<ResourceLimitList>(*this);
for (u32 core_id = 0; core_id < num_cores; ++core_id) {
thread_managers.push_back(std::make_unique<ThreadManager>(*this, core_id));
for (u32 i = 0; i < thread_managers.size(); ++i) {
thread_managers[i] = std::make_unique<ThreadManager>(*this, i);
}
timer_manager = std::make_unique<TimerManager>(timing);
ipc_recorder = std::make_unique<IPCDebugger::Recorder>();
stored_processes.assign(num_cores, nullptr);
next_thread_id = 1;
}
@@ -78,14 +75,6 @@ void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) {
}
}
void KernelSystem::SetCPUs(const std::vector<std::shared_ptr<ARM_Interface>>& cpus) {
ASSERT(cpus.size() == thread_managers.size());
u32 i = 0;
for (const auto& cpu : cpus) {
thread_managers[i++]->SetCPU(*cpu);
}
}
void KernelSystem::SetRunningCPU(ARM_Interface* cpu) {
if (current_process) {
stored_processes[current_cpu->GetID()] = current_process;
@@ -141,6 +130,32 @@ void KernelSystem::AddNamedPort(std::string name, std::shared_ptr<ClientPort> po
named_ports.emplace(std::move(name), std::move(port));
}
void KernelSystem::PrepareReschedule() {
current_cpu->PrepareReschedule();
reschedule_pending = true;
}
/// Reschedule the core emulation
void KernelSystem::RescheduleMultiCores() {
if (!reschedule_pending) {
return;
}
reschedule_pending = false;
for (const auto& manager : thread_managers) {
manager->Reschedule();
}
}
void KernelSystem::RescheduleSingleCore() {
if (!reschedule_pending) {
return;
}
reschedule_pending = false;
thread_managers[0]->Reschedule();
}
u32 KernelSystem::NewThreadId() {
return next_thread_id++;
}

View File

@@ -84,9 +84,8 @@ enum class MemoryRegion : u16 {
class KernelSystem {
public:
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing,
std::function<void()> prepare_reschedule_callback, u32 system_mode,
u32 num_cores, u8 n3ds_mode);
explicit KernelSystem(Memory::MemorySystem& memory, Core::Timing& timing, u32 system_mode,
u8 n3ds_mode);
~KernelSystem();
using PortPair = std::pair<std::shared_ptr<ServerPort>, std::shared_ptr<ClientPort>>;
@@ -217,8 +216,6 @@ public:
void SetCurrentMemoryPageTable(Memory::PageTable* page_table);
void SetCPUs(const std::vector<std::shared_ptr<ARM_Interface>>& cpu);
void SetRunningCPU(ARM_Interface* cpu);
ThreadManager& GetThreadManager(u32 core_id);
@@ -249,9 +246,16 @@ public:
/// Adds a port to the named port table
void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port);
void PrepareReschedule() {
prepare_reschedule_callback();
}
void PrepareReschedule();
/// Reschedule the core emulation
void RescheduleMultiCores();
void RescheduleSingleCore();
/// Gets a reference to the emulated CPU
ARM_Interface& GetRunningCore() {
return *current_cpu;
};
u32 NewThreadId();
@@ -269,8 +273,6 @@ public:
private:
void MemoryInit(u32 mem_type, u8 n3ds_mode);
std::function<void()> prepare_reschedule_callback;
std::unique_ptr<ResourceLimitList> resource_limits;
std::atomic<u32> next_object_id{0};
@@ -283,6 +285,9 @@ private:
std::unique_ptr<TimerManager> timer_manager;
/// When true, signals that a reschedule should happen
bool reschedule_pending = false;
// TODO(Subv): Start the process ids from 10 for now, as lower PIDs are
// reserved for low-level services
u32 next_process_id = 10;
@@ -291,9 +296,9 @@ private:
std::vector<std::shared_ptr<Process>> process_list;
std::shared_ptr<Process> current_process;
std::vector<std::shared_ptr<Process>> stored_processes;
std::array<std::shared_ptr<Process>, 4> stored_processes;
std::vector<std::unique_ptr<ThreadManager>> thread_managers;
std::array<std::unique_ptr<ThreadManager>, 4> thread_managers;
std::unique_ptr<ConfigMem::Handler> config_mem_handler;
std::unique_ptr<SharedPage::Handler> shared_page_handler;

View File

@@ -183,7 +183,6 @@ private:
};
static const FunctionDef SVC_Table[];
static const FunctionDef* GetSVCInfo(u32 func_num);
};
/// Map application or GSP heap memory
@@ -298,8 +297,7 @@ void SVC::ExitProcess() {
// Kill the current thread
kernel.GetCurrentThreadManager().GetCurrentThread()->Stop();
system.PrepareReschedule();
kernel.PrepareReschedule();
}
/// Maps a memory block to specified address
@@ -386,7 +384,7 @@ ResultCode SVC::SendSyncRequest(Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
system.PrepareReschedule();
kernel.PrepareReschedule();
auto thread = SharedFrom(kernel.GetCurrentThreadManager().GetCurrentThread());
@@ -482,7 +480,7 @@ ResultCode SVC::WaitSynchronization1(Handle handle, s64 nano_seconds) {
thread->wakeup_callback = std::make_shared<SVC_SyncCallback>(false);
system.PrepareReschedule();
kernel.PrepareReschedule();
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread
// resumes due to a signal in its wait objects.
@@ -557,7 +555,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle
thread->wakeup_callback = std::make_shared<SVC_SyncCallback>(false);
system.PrepareReschedule();
kernel.PrepareReschedule();
// This value gets set to -1 by default in this case, it is not modified after this.
*out = -1;
@@ -604,7 +602,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle
thread->wakeup_callback = std::make_shared<SVC_SyncCallback>(true);
system.PrepareReschedule();
kernel.PrepareReschedule();
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
// signal in one of its wait objects.
@@ -746,7 +744,7 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co
thread->wakeup_callback = std::make_shared<SVC_IPCCallback>(system);
system.PrepareReschedule();
kernel.PrepareReschedule();
// Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
// signal in one of its wait objects, or to 0xC8A01836 if there was a translation error.
@@ -922,7 +920,7 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(thread)));
system.PrepareReschedule();
kernel.PrepareReschedule();
LOG_TRACE(Kernel_SVC,
"called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
@@ -934,10 +932,10 @@ ResultCode SVC::CreateThread(Handle* out_handle, u32 entry_point, u32 arg, VAddr
/// Called when a thread exits
void SVC::ExitThread() {
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.GetRunningCore().GetPC());
LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", kernel.GetRunningCore().GetPC());
kernel.GetCurrentThreadManager().ExitCurrentThread();
system.PrepareReschedule();
kernel.PrepareReschedule();
}
/// Gets the priority for the specified thread
@@ -975,14 +973,14 @@ ResultCode SVC::SetThreadPriority(Handle handle, u32 priority) {
for (auto& mutex : thread->pending_mutexes)
mutex->UpdatePriority();
system.PrepareReschedule();
kernel.PrepareReschedule();
return RESULT_SUCCESS;
}
/// Create a mutex
ResultCode SVC::CreateMutex(Handle* out_handle, u32 initial_locked) {
std::shared_ptr<Mutex> mutex = kernel.CreateMutex(initial_locked != 0);
mutex->name = fmt::format("mutex-{:08x}", system.GetRunningCore().GetReg(14));
mutex->name = fmt::format("mutex-{:08x}", kernel.GetRunningCore().GetReg(14));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(mutex)));
LOG_TRACE(Kernel_SVC, "called initial_locked={} : created handle=0x{:08X}",
@@ -1049,7 +1047,7 @@ ResultCode SVC::GetThreadId(u32* thread_id, Handle handle) {
ResultCode SVC::CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) {
CASCADE_RESULT(std::shared_ptr<Semaphore> semaphore,
kernel.CreateSemaphore(initial_count, max_count));
semaphore->name = fmt::format("semaphore-{:08x}", system.GetRunningCore().GetReg(14));
semaphore->name = fmt::format("semaphore-{:08x}", kernel.GetRunningCore().GetReg(14));
CASCADE_RESULT(*out_handle,
kernel.GetCurrentProcess()->handle_table.Create(std::move(semaphore)));
@@ -1121,7 +1119,7 @@ ResultCode SVC::QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, u32 ad
ResultCode SVC::CreateEvent(Handle* out_handle, u32 reset_type) {
std::shared_ptr<Event> evt =
kernel.CreateEvent(static_cast<ResetType>(reset_type),
fmt::format("event-{:08x}", system.GetRunningCore().GetReg(14)));
fmt::format("event-{:08x}", kernel.GetRunningCore().GetReg(14)));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(evt)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
@@ -1165,7 +1163,7 @@ ResultCode SVC::ClearEvent(Handle handle) {
ResultCode SVC::CreateTimer(Handle* out_handle, u32 reset_type) {
std::shared_ptr<Timer> timer =
kernel.CreateTimer(static_cast<ResetType>(reset_type),
fmt ::format("timer-{:08x}", system.GetRunningCore().GetReg(14)));
fmt ::format("timer-{:08x}", kernel.GetRunningCore().GetReg(14)));
CASCADE_RESULT(*out_handle, kernel.GetCurrentProcess()->handle_table.Create(std::move(timer)));
LOG_TRACE(Kernel_SVC, "called reset_type=0x{:08X} : created handle=0x{:08X}", reset_type,
@@ -1232,16 +1230,16 @@ void SVC::SleepThread(s64 nanoseconds) {
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread_manager.GetCurrentThread()->WakeAfterDelay(nanoseconds);
system.PrepareReschedule();
kernel.PrepareReschedule();
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
s64 SVC::GetSystemTick() {
// TODO: Use globalTicks here?
s64 result = system.GetRunningCore().GetTimer().GetTicks();
s64 result = kernel.GetRunningCore().GetTimer().GetTicks();
// Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end.
// Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b
system.GetRunningCore().GetTimer().AddTicks(150);
kernel.GetRunningCore().GetTimer().AddTicks(150);
return result;
}
@@ -1295,7 +1293,7 @@ ResultCode SVC::CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my
static_cast<MemoryPermission>(other_permission), addr, region));
CASCADE_RESULT(*out_handle, current_process->handle_table.Create(std::move(shared_memory)));
LOG_WARNING(Kernel_SVC, "called addr=0x{:08X}", addr);
LOG_WARNING(Kernel_SVC, "SVC::CreateMemoryBlock called addr=0x{:08X}, size={}", addr, size);
return RESULT_SUCCESS;
}
@@ -1600,43 +1598,35 @@ const SVC::FunctionDef SVC::SVC_Table[] = {
{0x7D, &SVC::Wrap<&SVC::QueryProcessMemory>, "QueryProcessMemory"},
};
const SVC::FunctionDef* SVC::GetSVCInfo(u32 func_num) {
if (func_num >= ARRAY_SIZE(SVC_Table)) {
LOG_ERROR(Kernel_SVC, "unknown svc=0x{:02X}", func_num);
return nullptr;
}
return &SVC_Table[func_num];
}
MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70));
void SVC::CallSVC(u32 immediate) {
MICROPROFILE_SCOPE(Kernel_SVC);
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard lock{HLE::g_hle_lock};
DEBUG_ASSERT_MSG(kernel.GetCurrentProcess()->status == ProcessStatus::Running,
"Running threads from exiting processes is unimplemented");
const FunctionDef* info = GetSVCInfo(immediate);
if (info) {
if (info->func) {
(this->*(info->func))();
if (immediate < ARRAY_SIZE(SVC_Table)) {
const auto& info = SVC_Table[immediate];
if (info.func) {
(this->*(info.func))();
} else {
LOG_ERROR(Kernel_SVC, "unimplemented SVC function {}(..)", info->name);
LOG_ERROR(Kernel_SVC, "unimplemented SVC function {}(..)", info.name);
}
} else {
LOG_ERROR(Kernel_SVC, "unknown svc=0x{:02X}", immediate);
}
}
SVC::SVC(Core::System& system) : system(system), kernel(system.Kernel()), memory(system.Memory()) {}
u32 SVC::GetReg(std::size_t n) {
return system.GetRunningCore().GetReg(static_cast<int>(n));
return kernel.GetRunningCore().GetReg(static_cast<int>(n));
}
void SVC::SetReg(std::size_t n, u32 value) {
system.GetRunningCore().SetReg(static_cast<int>(n), value);
kernel.GetRunningCore().SetReg(static_cast<int>(n), value);
}
SVCContext::SVCContext(Core::System& system) : impl(std::make_unique<SVC>(system)) {}

View File

@@ -13,6 +13,7 @@
#include "core/arm/arm_interface.h"
#include "core/arm/skyeye_common/armstate.h"
#include "core/core.h"
#include "core/settings.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
@@ -268,13 +269,13 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(std::string name,
// TODO(yuriks): Other checks, returning 0xD9001BEA
if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:08x}", name, entry_point);
LOG_ERROR(Kernel_SVC, "(name={}): create thread invalid entry {:08x}", name, entry_point);
// TODO: Verify error
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
}
if (processor_id >= thread_managers.size()) {
if (!Settings::values.is_new_3ds || processor_id >= thread_managers.size()) {
processor_id = 0;
}
@@ -371,20 +372,19 @@ void Thread::BoostPriority(u32 priority) {
current_priority = priority;
}
std::shared_ptr<Thread> SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority,
std::shared_ptr<Process> owner_process) {
void SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority, Process& owner_process) {
// Initialize new "main" thread
auto thread_res =
kernel.CreateThread("main", entry_point, priority, 0, owner_process->ideal_processor,
Memory::HEAP_VADDR_END, *owner_process);
kernel.CreateThread("main", entry_point, priority, 0, owner_process.ideal_processor,
Memory::HEAP_VADDR_END, owner_process);
if (thread_res.Failed()) {
ASSERT(false);
return;
}
std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
thread->context->SetFpscr(FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO |
FPSCR_IXC); // 0x03C00010
// Note: The newly created thread will be run when the scheduler fires.
return thread;
}
bool ThreadManager::HaveReadyThreads() {

View File

@@ -101,8 +101,8 @@ public:
*/
const std::vector<std::shared_ptr<Thread>>& GetThreadList();
void SetCPU(ARM_Interface& cpu) {
this->cpu = &cpu;
void SetCPU(ARM_Interface* cpu) {
this->cpu = cpu;
}
std::unique_ptr<ARM_Interface::ThreadContext> NewContext() {
@@ -299,9 +299,7 @@ private:
* @param entry_point The address at which the thread should start execution
* @param priority The priority to give the main thread
* @param owner_process The parent process for the main thread
* @return A shared pointer to the main thread
*/
std::shared_ptr<Thread> SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority,
std::shared_ptr<Process> owner_process);
void SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority, Process& owner_process);
} // namespace Kernel

View File

@@ -267,7 +267,7 @@ void Module::APTInterface::GetSharedFont(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(-1); // TODO: Find the right error code
rb.Push<u32>(0);
rb.PushCopyObjects<Kernel::Object>(nullptr);
apt->system.SetStatus(Core::System::ResultStatus::ErrorSystemFiles, "Shared fonts");
apt->system.SetStatus(Core::System::ResultStatus::ErrorSystemFiles, "Shared fonts missing");
return;
}
}

View File

@@ -153,8 +153,9 @@ void ERR_F::ThrowFatalError(Kernel::HLERequestContext& ctx) {
LOG_CRITICAL(Service_ERR, "Fatal error");
const ErrInfo errinfo = rp.PopRaw<ErrInfo>();
LOG_CRITICAL(Service_ERR, "Fatal error type: {}", GetErrType(errinfo.errinfo_common.specifier));
system.SetStatus(Core::System::ResultStatus::ErrorUnknown);
const std::string errdetail = GetErrType(errinfo.errinfo_common.specifier);
LOG_CRITICAL(Service_ERR, "Fatal error type: {}", );
system.SetStatus(Core::System::ResultStatus::ErrorUnknown, errdetail.c_str());
// Generic Info
LogGenericInfo(errinfo.errinfo_common);

View File

@@ -345,4 +345,9 @@ ResultStatus AppLoader_THREEDSX::ReadIcon(std::vector<u8>& buffer) {
return ResultStatus::ErrorNotUsed;
}
ResultStatus AppLoader_THREEDSX::ReadTitle(std::string& title) {
title = filename;
return ResultStatus::Success;
}
} // namespace Loader

View File

@@ -38,6 +38,8 @@ public:
ResultStatus ReadRomFS(std::shared_ptr<FileSys::RomFSReader>& romfs_file) override;
ResultStatus ReadTitle(std::string& title) override;
private:
std::string filename;
std::string filepath;

View File

@@ -409,4 +409,9 @@ ResultStatus AppLoader_ELF::Load(std::shared_ptr<Kernel::Process>& process) {
return ResultStatus::Success;
}
ResultStatus AppLoader_ELF::ReadTitle(std::string& title) {
title = filename;
return ResultStatus::Success;
}
} // namespace Loader

View File

@@ -33,6 +33,8 @@ public:
ResultStatus Load(std::shared_ptr<Kernel::Process>& process) override;
ResultStatus ReadTitle(std::string& title) override;
private:
std::string filename;
};

View File

@@ -117,6 +117,55 @@ void SetFMVHack(bool enable) {
}
}
void SetLLEModules(const std::string& modules) {
std::size_t first = 0;
std::size_t last = 0;
std::size_t iter;
Settings::values.lle_modules.clear();
while (true) {
iter = modules.find(',', first);
if (iter != std::string::npos) {
last = iter - 1;
} else if (first < modules.size()) {
last = modules.size() - 1;
} else {
break;
}
// trim spaces
while (std::isspace(modules[first])) {
if (first < last) {
++first;
} else {
break;
}
}
while (std::isspace(modules[last])) {
if (last > first) {
--last;
} else {
break;
}
}
// set module
if (last > first) {
std::string module_name;
for (u32 i = first; i <= last; ++i) {
module_name += std::toupper(modules[i]);
}
Settings::values.lle_modules[module_name] = true;
}
// continue
if (iter != std::string::npos) {
first = iter + 1;
} else {
break;
}
}
}
void LoadProfile(int index) {
Settings::values.current_input_profile = Settings::values.input_profiles[index];
Settings::values.current_input_profile_index = index;

View File

@@ -196,6 +196,7 @@ struct Values {
std::unordered_map<std::string, bool> lle_modules;
u32 core_ticks_hack;
bool core_downcount_hack;
bool allow_shadow;
bool use_separable_shader;
bool use_shader_cache;
@@ -225,6 +226,7 @@ void Apply();
void LogSettings();
void SetFMVHack(bool enable);
void SetLLEModules(const std::string& modules);
// Input profiles
void LoadProfile(int index);

View File

@@ -159,7 +159,7 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
while (IsConnected()) {
std::lock_guard lock(network_mutex);
ENetEvent event;
if (enet_host_service(client, &event, 100) > 0) {
if (enet_host_service(client, &event, 32) > 0) {
switch (event.type) {
case ENET_EVENT_TYPE_RECEIVE:
switch (event.packet->data[0]) {
@@ -251,16 +251,18 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
break;
}
}
std::list<Packet> packets;
{
std::lock_guard lock(send_list_mutex);
for (const auto& packet : send_list) {
ENetPacket* enetPacket = enet_packet_create(packet.GetData(), packet.GetDataSize(),
ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(server, 0, enetPacket);
}
enet_host_flush(client);
send_list.clear();
packets.swap(send_list);
}
for (const auto& packet : packets) {
ENetPacket* enetPacket = enet_packet_create(packet.GetData(), packet.GetDataSize(),
ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(server, 0, enetPacket);
}
enet_host_flush(client);
}
Disconnect();
};

View File

@@ -51,7 +51,12 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
void OGLStreamBuffer::Unmap(GLsizeiptr size) {
if (size > 0) {
glFlushMappedBufferRange(gl_target, buffer_pos, size);
// flush is relative to the start of the currently mapped range of buffer
glFlushMappedBufferRange(gl_target, 0, size);
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
LOG_DEBUG(Render_OpenGL, "flush mapped buffer range error: {:04X}, target: {:04X}, offset: {}, size: {}, total: {}", error, gl_target, buffer_pos, size, buffer_size);
}
}
glUnmapBuffer(gl_target);
buffer_pos += size;

View File

@@ -18,6 +18,7 @@ enum class MessageType {
Typeless,
HWShader,
CPUJit,
New3DS,
};
namespace Color {

View File

@@ -516,8 +516,6 @@ bool RendererOpenGL::TryPresent() {
frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
return true;
}
@@ -704,6 +702,10 @@ void RendererOpenGL::InitOpenGLObjects() {
// init
OSD::Initialize();
if (Settings::values.is_new_3ds) {
OSD::AddMessage("New 3DS Model", OSD::MessageType::New3DS, OSD::Duration::NORMAL,
OSD::Color::YELLOW);
}
if (!Settings::values.use_hw_shader) {
OSD::AddMessage("HW Shader Off", OSD::MessageType::HWShader, OSD::Duration::NORMAL,
OSD::Color::YELLOW);