Move to Callback for Input Initialization + ConditionalVariable for Surface

This commit is contained in:
◱ PixelyIon 2020-08-21 16:44:27 +05:30 committed by ◱ PixelyIon
parent 07c2f2d891
commit 7290a80c3e
10 changed files with 65 additions and 61 deletions

View File

@ -53,6 +53,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
try {
skyline::kernel::OS os(jvmManager, logger, settings, std::string(appFilesPath));
inputWeak = os.state.input;
jvmManager->InitializeControllers();
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
auto romUri = env->GetStringUTFChars(romUriJstring, nullptr);
@ -100,27 +101,22 @@ extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIE
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
while (inputWeak.expired()); // If this isn't called then the guest won't know that the following host controller exists
auto input = inputWeak.lock();
std::lock_guard guard(input->npad.mutex);
input->npad.controllers[index] = skyline::input::GuestController{static_cast<skyline::input::NpadControllerType>(type), static_cast<skyline::i8>(partnerIndex)};
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_updateControllers(JNIEnv *, jobject) {
while (inputWeak.expired()); // If this isn't called then the mappings will not update unless the guest initiates an update itself
auto input = inputWeak.lock();
std::unique_lock lock(input->npad.mutex);
input->npad.Update(lock, true);
inputWeak.lock()->npad.Update();
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jint index, jlong mask, jint state) {
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jint index, jlong mask, jboolean pressed) {
try {
auto input = inputWeak.lock();
auto device = input->npad.controllers[index].device;
skyline::input::NpadButton button{.raw = static_cast<skyline::u64>(mask)};
if (device)
device->SetButtonState(button, state);
} catch (const std::bad_weak_ptr) {
device->SetButtonState(skyline::input::NpadButton{.raw = static_cast<skyline::u64>(mask)}, pressed);
} catch (const std::bad_weak_ptr&) {
// We don't mind if we miss button updates while input hasn't been initialized
}
}
@ -131,7 +127,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValu
auto device = input->npad.controllers[index].device;
if (device)
device->SetAxisValue(static_cast<skyline::input::NpadAxisId>(axis), value);
} catch (const std::bad_weak_ptr) {
} catch (const std::bad_weak_ptr&) {
// We don't mind if we miss axis updates while input hasn't been initialized
}
}

View File

@ -13,15 +13,8 @@ namespace skyline::input {
{*this, hid->npad[8], NpadId::Unknown}, {*this, hid->npad[9], NpadId::Handheld},
} {}
void NpadManager::Update(std::unique_lock<std::mutex> &lock, bool host) {
if (host) {
updated = true;
} else if (!updated) {
lock.unlock();
while (!updated);
lock.lock();
return;
}
void NpadManager::Update() {
std::lock_guard guard(mutex);
if (!activated)
return;
@ -85,17 +78,17 @@ namespace skyline::input {
}
void NpadManager::Activate() {
std::unique_lock lock(mutex);
std::lock_guard guard(mutex);
supportedIds = {NpadId::Handheld, NpadId::Player1, NpadId::Player2, NpadId::Player3, NpadId::Player4, NpadId::Player5, NpadId::Player6, NpadId::Player7, NpadId::Player8};
styles = {.proController = true, .joyconHandheld = true, .joyconDual = true, .joyconLeft = true, .joyconRight = true};
activated = true;
Update(lock);
Update();
}
void NpadManager::Deactivate() {
std::unique_lock lock(mutex);
std::lock_guard guard(mutex);
supportedIds = {};
styles = {};

View File

@ -22,7 +22,6 @@ namespace skyline::input {
private:
const DeviceState &state;
bool activated{false}; //!< If this NpadManager is activated or not
std::atomic<bool> updated{false}; //!< If this NpadManager has been updated by the guest
friend NpadDevice;
@ -43,7 +42,7 @@ namespace skyline::input {
}
public:
std::mutex mutex; //!< This mutex must be locked before any modifications to class members
std::recursive_mutex mutex; //!< This mutex must be locked before any modifications to class members
std::array<NpadDevice, constant::NpadCount> npads;
std::array<GuestController, constant::ControllerCount> controllers;
std::vector<NpadId> supportedIds; //!< The NpadId(s) that are supported by the application
@ -71,10 +70,9 @@ namespace skyline::input {
/**
* @brief This deduces all the mappings from guest controllers -> players based on the configuration supplied by HID services and available controllers
* @param lock A unique_lock which locks the mutex in the class, it should be locked before modifications to any members and must not be passed in an unlocked state
* @param host If the update is host-initiated rather than the guest
* @note If any class members were edited, the mutex shouldn't be released till this is called
*/
void Update(std::unique_lock<std::mutex> &lock, bool host = false);
void Update();
/**
* @brief This activates the mapping between guest controllers -> players, a call to this is required for function

View File

@ -6,7 +6,7 @@
thread_local JNIEnv *env;
namespace skyline {
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(instance), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))) {
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(instance), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))), initializeControllersId(environ->GetMethodID(instanceClass, "initializeControllers", "()V")) {
env = environ;
if (env->GetJavaVM(&vm) < 0)
throw exception("Cannot get JavaVM from environment");
@ -37,4 +37,8 @@ namespace skyline {
bool JvmManager::CheckNull(jobject &object) {
return env->IsSameObject(object, nullptr);
}
void JvmManager::InitializeControllers() {
env->CallVoidMethod(instance, initializeControllersId);
}
}

View File

@ -35,7 +35,7 @@ namespace skyline {
/**
* @brief Returns a pointer to the JNI environment for the current thread
*/
JNIEnv *GetEnv();
static JNIEnv *GetEnv();
/**
* @brief Retrieves a specific field of the given type from the activity
@ -85,6 +85,14 @@ namespace skyline {
* @param object The jobject to check
* @return If the object is null or not
*/
bool CheckNull(jobject &object);
static bool CheckNull(jobject &object);
/**
* @brief A call to EmulationActivity.initializeControllers in Kotlin
*/
void InitializeControllers();
private:
jmethodID initializeControllersId;
};
}

View File

@ -29,9 +29,9 @@ namespace skyline::service::hid {
void IHidServer::SetSupportedNpadStyleSet(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto styleSet = request.Pop<NpadStyleSet>();
std::unique_lock lock(state.input->npad.mutex);
std::lock_guard lock(state.input->npad.mutex);
state.input->npad.styles = styleSet;
state.input->npad.Update(lock);
state.input->npad.Update();
state.logger->Debug("Controller Support:\nPro-Controller: {}\nJoy-Con: Handheld: {}, Dual: {}, L: {}, R: {}\nGameCube: {}\nPokeBall: {}\nNES: {}, NES Handheld: {}, SNES: {}", static_cast<bool>(styleSet.proController), static_cast<bool>(styleSet.joyconHandheld), static_cast<bool>(styleSet.joyconDual), static_cast<bool>(styleSet.joyconLeft), static_cast<bool>
(styleSet.joyconRight), static_cast<bool>(styleSet.gamecube), static_cast<bool>(styleSet.palma), static_cast<bool>(styleSet.nes), static_cast<bool>(styleSet.nesHandheld), static_cast<bool>(styleSet.snes));
@ -45,16 +45,16 @@ namespace skyline::service::hid {
const auto &buffer = request.inputBuf.at(0);
u64 address = buffer.address;
size_t size = buffer.size / sizeof(NpadId);
std::vector<NpadId> supportedIds;
std::vector<NpadId> supportedIds(size);
for (size_t i = 0; i < size; i++) {
supportedIds.push_back(state.process->GetObject<NpadId>(address));
supportedIds[i] = state.process->GetObject<NpadId>(address);
address += sizeof(NpadId);
}
std::unique_lock lock(state.input->npad.mutex);
std::lock_guard lock(state.input->npad.mutex);
state.input->npad.supportedIds = supportedIds;
state.input->npad.Update(lock);
state.input->npad.Update();
}
void IHidServer::ActivateNpad(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
@ -75,10 +75,10 @@ namespace skyline::service::hid {
}
void IHidServer::SetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::unique_lock lock(state.input->npad.mutex);
std::lock_guard lock(state.input->npad.mutex);
request.Skip<u64>();
state.input->npad.orientation = request.Pop<NpadJoyOrientation>();
state.input->npad.Update(lock);
state.input->npad.Update();
}
void IHidServer::GetNpadJoyHoldType(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
@ -87,22 +87,22 @@ namespace skyline::service::hid {
void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto id = request.Pop<NpadId>();
std::unique_lock lock(state.input->npad.mutex);
std::lock_guard lock(state.input->npad.mutex);
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single);
state.input->npad.Update(lock);
state.input->npad.Update();
}
void IHidServer::SetNpadJoyAssignmentModeSingle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto id = request.Pop<NpadId>();
std::unique_lock lock(state.input->npad.mutex);
std::lock_guard lock(state.input->npad.mutex);
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Single);
state.input->npad.Update(lock);
state.input->npad.Update();
}
void IHidServer::SetNpadJoyAssignmentModeDual(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto id = request.Pop<NpadId>();
std::unique_lock lock(state.input->npad.mutex);
std::lock_guard lock(state.input->npad.mutex);
state.input->npad.at(id).SetAssignment(NpadJoyAssignment::Dual);
state.input->npad.Update(lock);
state.input->npad.Update();
}
}

View File

@ -9,7 +9,7 @@ namespace skyline::service::settings {
{0x3, SFUNC(ISystemSettingsServer::GetFirmwareVersion)}}) {}
void ISystemSettingsServer::GetFirmwareVersion(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
SysVerTitle title{.major=5, .minor=0, .micro=0, .revMajor=4, .revMinor=0, .platform="NX", .verHash="4de65c071fd0869695b7629f75eb97b2551dbf2f", .dispVer="9.0.0", .dispTitle="NintendoSDK Firmware for NX 9.0.0-4.0"};
SysVerTitle title{.major=9, .minor=0, .micro=0, .revMajor=4, .revMinor=0, .platform="NX", .verHash="4de65c071fd0869695b7629f75eb97b2551dbf2f", .dispVer="9.0.0", .dispTitle="NintendoSDK Firmware for NX 9.0.0-4.0"};
state.process->WriteMemory(title, request.outputBuf.at(0).address);
}
}

View File

@ -10,6 +10,7 @@ import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.ConditionVariable
import android.os.ParcelFileDescriptor
import android.util.Log
import android.view.*
@ -52,6 +53,11 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
@Volatile
private var surface : Surface? = null
/**
* A condition variable keeping track of if the surface is ready or not
*/
private var surfaceReady = ConditionVariable()
/**
* A boolean flag denoting if the emulation thread should call finish() or not
*/
@ -120,9 +126,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
*
* @param index The index of the controller this is directed to
* @param mask The mask of the button that are being set
* @param state The state to set the button to
* @param pressed If the buttons are being pressed or released
*/
private external fun setButtonState(index : Int, mask : Long, state : Int)
private external fun setButtonState(index : Int, mask : Long, pressed : Boolean)
/**
* This sets the value of a specific axis on a specific controller
@ -141,13 +147,13 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
val controller = entry.value
if (controller.type != ControllerType.None) {
val type : Int = when (controller.type) {
val type = when (controller.type) {
ControllerType.None -> throw IllegalArgumentException()
ControllerType.HandheldProController -> if (operationMode) ControllerType.ProController.id else ControllerType.HandheldProController.id
ControllerType.ProController, ControllerType.JoyConLeft, ControllerType.JoyConRight -> controller.type.id
}
val partnerIndex : Int? = when (controller) {
val partnerIndex = when (controller) {
is JoyConLeftController -> controller.partnerId
is JoyConRightController -> controller.partnerId
else -> null
@ -170,10 +176,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
romFd = contentResolver.openFileDescriptor(rom, "r")!!
emulationThread = Thread {
while (surface == null)
Thread.yield()
runOnUiThread { initializeControllers() }
surfaceReady.block()
executeApplication(rom.toString(), romType, romFd.fd, preferenceFd.fd, applicationContext.filesDir.canonicalPath + "/")
@ -216,14 +219,12 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
if (sharedPreferences.getBoolean("perf_stats", false)) {
val perfRunnable = object : Runnable {
perf_stats.postDelayed(object : Runnable {
override fun run() {
perf_stats.text = "${getFps()} FPS\n${getFrametime()}ms"
perf_stats.postDelayed(this, 250)
}
}
perf_stats.postDelayed(perfRunnable, 250)
}, 250)
}
operationMode = sharedPreferences.getBoolean("operation_mode", operationMode)
@ -274,6 +275,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
Log.d("surfaceCreated", "Holder: $holder")
surface = holder.surface
setSurface(surface)
surfaceReady.open()
}
/**
@ -288,6 +290,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
*/
override fun surfaceDestroyed(holder : SurfaceHolder) {
Log.d("surfaceDestroyed", "Holder: $holder")
surfaceReady.close()
surface = null
setSurface(surface)
}
@ -299,7 +302,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
if (event.repeatCount != 0)
return super.dispatchKeyEvent(event)
val action : ButtonState = when (event.action) {
val action = when (event.action) {
KeyEvent.ACTION_DOWN -> ButtonState.Pressed
KeyEvent.ACTION_UP -> ButtonState.Released
else -> return super.dispatchKeyEvent(event)
@ -308,7 +311,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
return when (val guestEvent = input.eventMap[KeyHostEvent(event.device.descriptor, event.keyCode)]) {
is ButtonGuestEvent -> {
if (guestEvent.button != ButtonId.Menu)
setButtonState(guestEvent.id, guestEvent.button.value(), action.ordinal)
setButtonState(guestEvent.id, guestEvent.button.value(), action.state)
true
}
@ -356,7 +359,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
when (guestEvent) {
is ButtonGuestEvent -> {
if (guestEvent.button != ButtonId.Menu)
setButtonState(guestEvent.id, guestEvent.button.value(), if (abs(value) >= guestEvent.threshold) ButtonState.Pressed.ordinal else ButtonState.Released.ordinal)
setButtonState(guestEvent.id, guestEvent.button.value(), if (abs(value) >= guestEvent.threshold) ButtonState.Pressed.state else ButtonState.Released.state)
}
is AxisGuestEvent -> {

View File

@ -179,7 +179,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener {
setupAppList()
app_list.addOnScrollListener(object : RecyclerView.OnScrollListener() {
var y : Int = 0
var y = 0
override fun onScrolled(recyclerView : RecyclerView, dx : Int, dy : Int) {
y += dy

View File

@ -65,12 +65,14 @@
android:key="category_input"
android:title="@string/input"
app:initialExpandedChildrenCount="4">
<!--
<CheckBoxPreference
android:defaultValue="true"
android:summaryOff="@string/osc_not_shown"
android:summaryOn="@string/osc_shown"
app:key="show_osc"
app:title="@string/show_osc" />
-->
<emu.skyline.preference.ControllerPreference index="0" />
<emu.skyline.preference.ControllerPreference index="1" />
<emu.skyline.preference.ControllerPreference index="2" />