diff --git a/android/app/src/cpp/CMakeLists.txt b/android/app/src/cpp/CMakeLists.txt index 62e34222c..9e59c24ec 100644 --- a/android/app/src/cpp/CMakeLists.txt +++ b/android/app/src/cpp/CMakeLists.txt @@ -1,6 +1,4 @@ set(SRCS - android_audio_stream.cpp - android_audio_stream.h android_host_interface.cpp android_host_interface.h android_gles_host_display.cpp @@ -9,4 +7,4 @@ set(SRCS ) add_library(duckstation-native SHARED ${SRCS}) -target_link_libraries(duckstation-native PRIVATE android core common glad imgui EGL::EGL) +target_link_libraries(duckstation-native PRIVATE android frontend-common core common glad imgui EGL::EGL) diff --git a/android/app/src/cpp/android_audio_stream.cpp b/android/app/src/cpp/android_audio_stream.cpp deleted file mode 100644 index 3b4f134ee..000000000 --- a/android/app/src/cpp/android_audio_stream.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "android_audio_stream.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" -Log_SetChannel(AndroidAudioStream); - -AndroidAudioStream::AndroidAudioStream() = default; - -AndroidAudioStream::~AndroidAudioStream() -{ - if (m_is_open) - AndroidAudioStream::CloseDevice(); -} - -std::unique_ptr AndroidAudioStream::Create() -{ - return std::make_unique(); -} - -bool AndroidAudioStream::OpenDevice() -{ - DebugAssert(!m_is_open); -#if 0 - SDL_AudioSpec spec = {}; - spec.freq = m_output_sample_rate; - spec.channels = static_cast(m_channels); - spec.format = AUDIO_S16; - spec.samples = static_cast(m_buffer_size); - spec.callback = AudioCallback; - spec.userdata = static_cast(this); - - SDL_AudioSpec obtained = {}; - if (SDL_OpenAudio(&spec, &obtained) < 0) - { - Log_ErrorPrintf("SDL_OpenAudio failed"); - return false; - } -#endif - - m_is_open = true; - return true; -} - -void AndroidAudioStream::PauseDevice(bool paused) -{ - // SDL_PauseAudio(paused ? 1 : 0); -} - -void AndroidAudioStream::CloseDevice() -{ - DebugAssert(m_is_open); - // SDL_CloseAudio(); - m_is_open = false; -} - -#if 0 -void AndroidAudioStream::AudioCallback(void* userdata, uint8_t* stream, int len) -{ - AndroidAudioStream* const this_ptr = static_cast(userdata); - const u32 num_samples = len / sizeof(SampleType) / this_ptr->m_channels; - const u32 read_samples = this_ptr->ReadSamples(reinterpret_cast(stream), num_samples); - const u32 silence_samples = num_samples - read_samples; - if (silence_samples > 0) - { - std::memset(reinterpret_cast(stream) + (read_samples * this_ptr->m_channels), 0, - silence_samples * this_ptr->m_channels * sizeof(SampleType)); - } -} -#endif \ No newline at end of file diff --git a/android/app/src/cpp/android_audio_stream.h b/android/app/src/cpp/android_audio_stream.h deleted file mode 100644 index 84a78f808..000000000 --- a/android/app/src/cpp/android_audio_stream.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "common/audio_stream.h" -#include - -class AndroidAudioStream final : public AudioStream -{ -public: - AndroidAudioStream(); - ~AndroidAudioStream(); - - static std::unique_ptr Create(); - -protected: - bool OpenDevice() override; - void PauseDevice(bool paused) override; - void CloseDevice() override; - - // static void AudioCallback(void* userdata, uint8_t* stream, int len); - - bool m_is_open = false; -}; diff --git a/android/app/src/cpp/android_gles_host_display.cpp b/android/app/src/cpp/android_gles_host_display.cpp index 3f657dd80..4a85b79bf 100644 --- a/android/app/src/cpp/android_gles_host_display.cpp +++ b/android/app/src/cpp/android_gles_host_display.cpp @@ -1,5 +1,6 @@ #include "android_gles_host_display.h" -#include "YBaseLib/Log.h" +#include "common/assert.h" +#include "common/log.h" #include #include #include @@ -20,7 +21,7 @@ public: GLuint GetGLID() const { return m_id; } static std::unique_ptr Create(u32 width, u32 height, const void* initial_data, - u32 initial_data_stride) + u32 initial_data_stride) { GLuint id; glGenTextures(1, &id); @@ -102,13 +103,13 @@ void AndroidGLESHostDisplay::ChangeRenderWindow(void* new_window) } std::unique_ptr AndroidGLESHostDisplay::CreateTexture(u32 width, u32 height, const void* data, - u32 data_stride, bool dynamic) + u32 data_stride, bool dynamic) { return AndroidGLESHostDisplayTexture::Create(width, height, data, data_stride); } void AndroidGLESHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, - const void* data, u32 data_stride) + const void* data, u32 data_stride) { AndroidGLESHostDisplayTexture* tex = static_cast(texture); Assert(data_stride == (width * sizeof(u32))); @@ -122,28 +123,22 @@ void AndroidGLESHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u glBindTexture(GL_TEXTURE_2D, old_texture_binding); } -void AndroidGLESHostDisplay::SetDisplayTexture(void* texture, s32 offset_x, s32 offset_y, s32 width, s32 height, - u32 texture_width, u32 texture_height, float aspect_ratio) +bool AndroidGLESHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, + void* out_data, u32 out_data_stride) { - m_display_texture_id = static_cast(reinterpret_cast(texture)); - m_display_offset_x = offset_x; - m_display_offset_y = offset_y; - m_display_width = width; - m_display_height = height; - m_display_texture_width = texture_width; - m_display_texture_height = texture_height; - m_display_aspect_ratio = aspect_ratio; - m_display_texture_changed = true; -} + GLint old_alignment = 0, old_row_length = 0; + glGetIntegerv(GL_PACK_ALIGNMENT, &old_alignment); + glGetIntegerv(GL_PACK_ROW_LENGTH, &old_row_length); + glPixelStorei(GL_PACK_ALIGNMENT, sizeof(u32)); + glPixelStorei(GL_PACK_ROW_LENGTH, out_data_stride / sizeof(u32)); -void AndroidGLESHostDisplay::SetDisplayLinearFiltering(bool enabled) -{ - m_display_linear_filtering = enabled; -} + const GLuint texture = static_cast(reinterpret_cast(texture_handle)); + GL::Texture::GetTextureSubImage(texture, 0, x, y, 0, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, + height * out_data_stride, out_data); -void AndroidGLESHostDisplay::SetDisplayTopMargin(int height) -{ - m_display_top_margin = height; + glPixelStorei(GL_PACK_ALIGNMENT, old_alignment); + glPixelStorei(GL_PACK_ROW_LENGTH, old_row_length); + return true; } void AndroidGLESHostDisplay::SetVSync(bool enabled) @@ -151,13 +146,9 @@ void AndroidGLESHostDisplay::SetVSync(bool enabled) eglSwapInterval(m_egl_display, enabled ? 1 : 0); } -std::tuple AndroidGLESHostDisplay::GetWindowSize() const -{ - return std::make_tuple(static_cast(m_window_width), static_cast(m_window_height)); -} - -void AndroidGLESHostDisplay::WindowResized() +void AndroidGLESHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height) { + HostDisplay::WindowResized(new_window_width, new_window_height); m_window_width = ANativeWindow_getWidth(m_window); m_window_height = ANativeWindow_getHeight(m_window); ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); @@ -212,10 +203,10 @@ bool AndroidGLESHostDisplay::CreateGLContext() eglBindAPI(EGL_OPENGL_ES_API); // Try GLES 3, then fall back to GLES 2. - for (int major_version : {3, 2}) { + for (int major_version : {3, 2}) + { std::array egl_context_attribs = {{EGL_CONTEXT_CLIENT_VERSION, major_version, EGL_NONE}}; - m_egl_context = eglCreateContext(m_egl_display, m_egl_config, EGL_NO_CONTEXT, - egl_context_attribs.data()); + m_egl_context = eglCreateContext(m_egl_display, m_egl_config, EGL_NO_CONTEXT, egl_context_attribs.data()); if (m_egl_context) break; } @@ -262,7 +253,7 @@ bool AndroidGLESHostDisplay::CreateSurface() return false; } - WindowResized(); + WindowResized(m_window_width, m_window_height); return true; } @@ -351,10 +342,12 @@ void AndroidGLESHostDisplay::Render() RenderDisplay(); + ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); eglSwapBuffers(m_egl_display, m_egl_surface); + ImGui::NewFrame(); ImGui_ImplOpenGL3_NewFrame(); GL::Program::ResetLastProgram(); @@ -362,14 +355,13 @@ void AndroidGLESHostDisplay::Render() void AndroidGLESHostDisplay::RenderDisplay() { - if (!m_display_texture_id) + if (!m_display_texture_handle) return; - // - 20 for main menu padding - const auto [vp_left, vp_top, vp_width, vp_height] = - CalculateDrawRect(m_window_width, std::max(m_window_height - m_display_top_margin, 1), m_display_aspect_ratio); + const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect(); + + glViewport(vp_left, m_window_height - vp_top - vp_height, vp_width, vp_height); - glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height); glDisable(GL_BLEND); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); @@ -377,11 +369,14 @@ void AndroidGLESHostDisplay::RenderDisplay() glDepthMask(GL_FALSE); m_display_program.Bind(); - const float tex_left = static_cast(m_display_offset_x) / static_cast(m_display_texture_width); - const float tex_right = tex_left + static_cast(m_display_width) / static_cast(m_display_texture_width); - const float tex_top = static_cast(m_display_offset_y) / static_cast(m_display_texture_height); + const float tex_left = + (static_cast(m_display_texture_view_x) + 0.25f) / static_cast(m_display_texture_width); + const float tex_top = + (static_cast(m_display_texture_view_y) - 0.25f) / static_cast(m_display_texture_height); + const float tex_right = + (tex_left + static_cast(m_display_texture_view_width) - 0.5f) / static_cast(m_display_texture_width); const float tex_bottom = - tex_top + static_cast(m_display_height) / static_cast(m_display_texture_height); + (tex_top + static_cast(m_display_texture_view_height) + 0.5f) / static_cast(m_display_texture_height); const std::array, 4> vertices = {{ {{-1.0f, -1.0f, tex_left, tex_bottom}}, // bottom-left {{1.0f, -1.0f, tex_right, tex_bottom}}, // bottom-right @@ -395,7 +390,7 @@ void AndroidGLESHostDisplay::RenderDisplay() glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), &vertices[0][2]); glEnableVertexAttribArray(1); - glBindTexture(GL_TEXTURE_2D, m_display_texture_id); + glBindTexture(GL_TEXTURE_2D, static_cast(reinterpret_cast(m_display_texture_handle))); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); diff --git a/android/app/src/cpp/android_gles_host_display.h b/android/app/src/cpp/android_gles_host_display.h index b5b6c06ba..b01e80287 100644 --- a/android/app/src/cpp/android_gles_host_display.h +++ b/android/app/src/cpp/android_gles_host_display.h @@ -22,24 +22,19 @@ public: void* GetRenderWindow() const override; void ChangeRenderWindow(void* new_window) override; + void WindowResized(s32 new_window_width, s32 new_window_height) override; std::unique_ptr CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic) override; void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride) override; - - void SetDisplayTexture(void* texture, s32 offset_x, s32 offset_y, s32 width, s32 height, u32 texture_width, - u32 texture_height, float aspect_ratio) override; - void SetDisplayLinearFiltering(bool enabled) override; - void SetDisplayTopMargin(int height) override; + bool DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data, + u32 out_data_stride) override; void SetVSync(bool enabled) override; void Render() override; - std::tuple GetWindowSize() const override; - void WindowResized() override; - private: const char* GetGLSLVersionString() const; std::string GetGLSLVersionHeader() const; @@ -63,16 +58,4 @@ private: EGLConfig m_egl_config = {}; GL::Program m_display_program; - GLuint m_display_texture_id = 0; - s32 m_display_offset_x = 0; - s32 m_display_offset_y = 0; - s32 m_display_width = 0; - s32 m_display_height = 0; - u32 m_display_texture_width = 0; - u32 m_display_texture_height = 0; - int m_display_top_margin = 0; - float m_display_aspect_ratio = 1.0f; - - bool m_display_texture_changed = false; - bool m_display_linear_filtering = false; }; diff --git a/android/app/src/cpp/android_host_interface.cpp b/android/app/src/cpp/android_host_interface.cpp index 7d145f3f0..3c64a7a68 100644 --- a/android/app/src/cpp/android_host_interface.cpp +++ b/android/app/src/cpp/android_host_interface.cpp @@ -1,14 +1,15 @@ #include "android_host_interface.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" -#include "android_audio_stream.h" #include "android_gles_host_display.h" +#include "common/assert.h" +#include "common/log.h" +#include "common/string.h" +#include "common/audio_stream.h" #include "core/controller.h" -#include "core/gpu.h" #include "core/game_list.h" +#include "core/gpu.h" #include "core/host_display.h" #include "core/system.h" +#include "frontend-common/ini_settings_interface.h" #include #include #include @@ -53,20 +54,7 @@ static std::string JStringToString(JNIEnv* env, jstring str) return ret; } -AndroidHostInterface::AndroidHostInterface(jobject java_object) : m_java_object(java_object) -{ - m_settings.SetDefaults(); - m_settings.bios_path = "/sdcard/PSX/BIOS/scph1001.bin"; - m_settings.controller_types[0] = ControllerType::DigitalController; - m_settings.memory_card_paths[0] = "/sdcard/PSX/memory_card_1.mcd"; - m_settings.cpu_execution_mode = CPUExecutionMode::Recompiler; - //m_settings.cpu_execution_mode = CPUExecutionMode::CachedInterpreter; - //m_settings.gpu_renderer = GPURenderer::Software; - m_settings.speed_limiter_enabled = false; - m_settings.video_sync_enabled = false; - m_settings.audio_sync_enabled = false; - //m_settings.debugging.show_vram = true; -} +AndroidHostInterface::AndroidHostInterface(jobject java_object) : m_java_object(java_object) {} AndroidHostInterface::~AndroidHostInterface() { @@ -74,6 +62,24 @@ AndroidHostInterface::~AndroidHostInterface() GetJNIEnv()->DeleteGlobalRef(m_java_object); } +bool AndroidHostInterface::Initialize() +{ + if (!HostInterface::Initialize()) + return false; + + // check settings version, if invalid set defaults, then load settings + INISettingsInterface settings_interface(GetSettingsFileName()); + CheckSettings(settings_interface); + UpdateSettings([this, &settings_interface]() { m_settings.Load(settings_interface); }); + + return true; +} + +void AndroidHostInterface::Shutdown() +{ + HostInterface::Shutdown(); +} + void AndroidHostInterface::ReportError(const char* message) { HostInterface::ReportError(message); @@ -84,15 +90,20 @@ void AndroidHostInterface::ReportMessage(const char* message) HostInterface::ReportMessage(message); } -bool AndroidHostInterface::StartEmulationThread(ANativeWindow* initial_surface, std::string initial_filename, - std::string initial_state_filename) +void AndroidHostInterface::SetUserDirectory() +{ + // TODO: Should this be customizable or use an API-determined path? + m_user_directory = "/sdcard/duckstation"; +} + +bool AndroidHostInterface::StartEmulationThread(ANativeWindow* initial_surface, SystemBootParameters boot_params) { Assert(!IsEmulationThreadRunning()); Log_DevPrintf("Starting emulation thread..."); m_emulation_thread_stop_request.store(false); m_emulation_thread = std::thread(&AndroidHostInterface::EmulationThreadEntryPoint, this, initial_surface, - std::move(initial_filename), std::move(initial_state_filename)); + std::move(boot_params)); m_emulation_thread_started.Wait(); if (!m_emulation_thread_start_result.load()) { @@ -140,43 +151,14 @@ void AndroidHostInterface::RunOnEmulationThread(std::function function, m_callback_mutex.unlock(); } -void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surface, std::string initial_filename, - std::string initial_state_filename) +void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surface, SystemBootParameters boot_params) { CreateImGuiContext(); - // Create display. - m_display = AndroidGLESHostDisplay::Create(initial_surface); - if (!m_display) - { - Log_ErrorPrint("Failed to create display on emulation thread."); - DestroyImGuiContext(); - m_emulation_thread_start_result.store(false); - m_emulation_thread_started.Signal(); - return; - } - - // Create audio stream. - m_audio_stream = AndroidAudioStream::Create(); - if (!m_audio_stream || !m_audio_stream->Reconfigure(44100, 2)) - { - Log_ErrorPrint("Failed to create audio stream on emulation thread."); - m_audio_stream.reset(); - m_display.reset(); - DestroyImGuiContext(); - m_emulation_thread_start_result.store(false); - m_emulation_thread_started.Signal(); - return; - } - // Boot system. - if (!CreateSystem() || !BootSystem(initial_filename.empty() ? nullptr : initial_filename.c_str(), - initial_state_filename.empty() ? nullptr : initial_state_filename.c_str())) + if (!BootSystem(boot_params)) { - Log_ErrorPrintf("Failed to boot system on emulation thread (file:%s state:%s).", initial_filename.c_str(), - initial_state_filename.c_str()); - m_audio_stream.reset(); - m_display.reset(); + Log_ErrorPrintf("Failed to boot system on emulation thread (file:%s).", boot_params.filename.c_str()); DestroyImGuiContext(); m_emulation_thread_start_result.store(false); m_emulation_thread_started.Signal(); @@ -212,33 +194,88 @@ void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surf // rendering { - DrawImGui(); + DrawImGuiWindows(); if (m_system) m_system->GetGPU()->ResetGraphicsAPIState(); - ImGui::Render(); m_display->Render(); - ImGui::NewFrame(); - if (m_system) { m_system->GetGPU()->RestoreGraphicsAPIState(); + m_system->UpdatePerformanceCounters(); if (m_speed_limiter_enabled) - Throttle(); + m_system->Throttle(); } - - UpdatePerformanceCounters(); } } - m_display.reset(); - m_audio_stream.reset(); + DestroySystem(); DestroyImGuiContext(); } +bool AndroidHostInterface::AcquireHostDisplay() +{ + std::unique_ptr display = AndroidGLESHostDisplay::Create(m_surface); + if (!display) + { + Log_ErrorPrintf("Failed to create GLES host display"); + return false; + } + + m_display = display.release(); + return true; +} + +void AndroidHostInterface::ReleaseHostDisplay() +{ + delete m_display; + m_display = nullptr; +} + +std::unique_ptr AndroidHostInterface::CreateAudioStream(AudioBackend backend) +{ + std::unique_ptr stream; + + switch (m_settings.audio_backend) + { + case AudioBackend::Cubeb: + stream = AudioStream::CreateCubebAudioStream(); + break; + + default: + stream = AudioStream::CreateNullAudioStream(); + break; + } + + if (!stream) + { + ReportFormattedError("Failed to create %s audio stream, falling back to null", + Settings::GetAudioBackendName(m_settings.audio_backend)); + stream = AudioStream::CreateNullAudioStream(); + } + + return stream; +} + +void AndroidHostInterface::SurfaceChanged(ANativeWindow* surface, int format, int width, int height) +{ + Log_InfoPrintf("SurfaceChanged %p %d %d %d", surface, format, width, height); + if (m_surface == surface) + { + if (m_display) + m_display->WindowResized(width, height); + + return; + } + + m_surface = surface; + if (m_display) + m_display->ChangeRenderWindow(surface); +} + void AndroidHostInterface::CreateImGuiContext() { ImGui::CreateContext(); @@ -253,88 +290,10 @@ void AndroidHostInterface::DestroyImGuiContext() ImGui::DestroyContext(); } -void AndroidHostInterface::DrawImGui() -{ - DrawFPSWindow(); - DrawOSDMessages(); - - ImGui::Render(); -} - -void AndroidHostInterface::DrawFPSWindow() -{ - const bool show_fps = true; - const bool show_vps = true; - const bool show_speed = true; - - ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - 175.0f, 0.0f), ImGuiCond_Always); - ImGui::SetNextWindowSize(ImVec2(175.0f, 16.0f)); - - if (!ImGui::Begin("FPSWindow", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse | - ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoBringToFrontOnFocus)) - { - ImGui::End(); - return; - } - - bool first = true; - if (show_fps) - { - ImGui::Text("%.2f", m_fps); - first = false; - } - if (show_vps) - { - if (first) { - first = false; - } - else { - ImGui::SameLine(); - ImGui::Text("/"); - ImGui::SameLine(); - } - - ImGui::Text("%.2f", m_vps); - } - if (show_speed) - { - if (first) { - first = false; - } - else { - ImGui::SameLine(); - ImGui::Text("/"); - ImGui::SameLine(); - } - - const u32 rounded_speed = static_cast(std::round(m_speed)); - if (m_speed < 90.0f) - ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed); - else if (m_speed < 110.0f) - ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "%u%%", rounded_speed); - else - ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "%u%%", rounded_speed); - } - - ImGui::End(); -} - -void AndroidHostInterface::SurfaceChanged(ANativeWindow* window, int format, int width, int height) -{ - Log_InfoPrintf("SurfaceChanged %p %d %d %d", window, format, width, height); - if (m_display->GetRenderWindow() == window) - { - m_display->WindowResized(); - return; - } - - m_display->ChangeRenderWindow(window); -} - void AndroidHostInterface::SetControllerType(u32 index, std::string_view type_name) { - ControllerType type = Settings::ParseControllerTypeName(std::string(type_name).c_str()).value_or(ControllerType::None); + ControllerType type = + Settings::ParseControllerTypeName(std::string(type_name).c_str()).value_or(ControllerType::None); if (!IsEmulationThreadRunning()) { @@ -342,11 +301,13 @@ void AndroidHostInterface::SetControllerType(u32 index, std::string_view type_na return; } - RunOnEmulationThread([this, index, type]() { - Log_InfoPrintf("Changing controller slot %d to %s", index, Settings::GetControllerTypeName(type)); - m_settings.controller_types[index] = type; - m_system->UpdateControllers(); - }, false); + RunOnEmulationThread( + [this, index, type]() { + Log_InfoPrintf("Changing controller slot %d to %s", index, Settings::GetControllerTypeName(type)); + m_settings.controller_types[index] = type; + m_system->UpdateControllers(); + }, + false); } void AndroidHostInterface::SetControllerButtonState(u32 index, s32 button_code, bool pressed) @@ -354,18 +315,20 @@ void AndroidHostInterface::SetControllerButtonState(u32 index, s32 button_code, if (!IsEmulationThreadRunning()) return; - RunOnEmulationThread([this, index, button_code, pressed]() { - Controller* controller = m_system->GetController(index); - if (!controller) - return; + RunOnEmulationThread( + [this, index, button_code, pressed]() { + Controller* controller = m_system->GetController(index); + if (!controller) + return; - controller->SetButtonState(button_code, pressed); - }, false); + controller->SetButtonState(button_code, pressed); + }, + false); } extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) { - Log::GetInstance().SetDebugOutputParams(true, nullptr, LOGLEVEL_DEV); + Log::SetDebugOutputParams(true, nullptr, LOGLEVEL_DEV); s_jvm = vm; JNIEnv* env = GetJNIEnv(); @@ -445,8 +408,12 @@ DEFINE_JNI_ARGS_METHOD(jboolean, AndroidHostInterface_startEmulationThread, jobj return false; } - return GetNativeClass(env, obj)->StartEmulationThread(native_surface, JStringToString(env, filename), - JStringToString(env, state_filename)); + std::string state_filename_str = JStringToString(env, state_filename); + + SystemBootParameters boot_params; + boot_params.filename = JStringToString(env, filename); + + return GetNativeClass(env, obj)->StartEmulationThread(native_surface, std::move(boot_params)); } DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_stopEmulationThread, jobject obj) @@ -471,12 +438,14 @@ DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_setControllerType, jobject obj GetNativeClass(env, obj)->SetControllerType(index, JStringToString(env, controller_type)); } -DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_setControllerButtonState, jobject obj, jint index, jint button_code, jboolean pressed) +DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_setControllerButtonState, jobject obj, jint index, jint button_code, + jboolean pressed) { GetNativeClass(env, obj)->SetControllerButtonState(index, button_code, pressed); } -DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerButtonCode, jobject unused, jstring controller_type, jstring button_name) +DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerButtonCode, jobject unused, jstring controller_type, + jstring button_name) { std::optional type = Settings::ParseControllerTypeName(JStringToString(env, controller_type).c_str()); if (!type) @@ -486,14 +455,16 @@ DEFINE_JNI_ARGS_METHOD(jint, AndroidHostInterface_getControllerButtonCode, jobje return code.value_or(-1); } -DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_cache_path, jstring j_redump_dat_path, jarray j_search_directories, jboolean search_recursively) +DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_cache_path, jstring j_redump_dat_path, + jarray j_search_directories, jboolean search_recursively) { - const std::string cache_path = JStringToString(env, j_cache_path); - const std::string redump_dat_path = JStringToString(env, j_redump_dat_path); + //const std::string cache_path = JStringToString(env, j_cache_path); + std::string redump_dat_path = JStringToString(env, j_redump_dat_path); + // TODO: This should use the base HostInterface. GameList gl; if (!redump_dat_path.empty()) - gl.ParseRedumpDatabase(redump_dat_path.c_str()); + gl.SetDatabaseFilename(std::move(redump_dat_path)); const jsize search_directories_size = env->GetArrayLength(j_search_directories); for (jsize i = 0; i < search_directories_size; i++) @@ -507,19 +478,21 @@ DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_ca jclass entry_class = env->FindClass("com/github/stenzek/duckstation/GameListEntry"); Assert(entry_class != nullptr); - jmethodID entry_constructor = env->GetMethodID(entry_class, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V"); + jmethodID entry_constructor = + env->GetMethodID(entry_class, "", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V"); Assert(entry_constructor != nullptr); jobjectArray entry_array = env->NewObjectArray(gl.GetEntryCount(), entry_class, nullptr); Assert(entry_array != nullptr); u32 counter = 0; - for (const GameList::GameListEntry& entry : gl.GetEntries()) + for (const GameListEntry& entry : gl.GetEntries()) { jstring path = env->NewStringUTF(entry.path.c_str()); jstring code = env->NewStringUTF(entry.code.c_str()); jstring title = env->NewStringUTF(entry.title.c_str()); - jstring region = env->NewStringUTF(Settings::GetConsoleRegionName(entry.region)); + jstring region = env->NewStringUTF(Settings::GetDiscRegionName(entry.region)); jstring type = env->NewStringUTF(GameList::EntryTypeToString(entry.type)); jlong size = entry.total_size; @@ -529,4 +502,4 @@ DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_ca } return entry_array; -} \ No newline at end of file +} diff --git a/android/app/src/cpp/android_host_interface.h b/android/app/src/cpp/android_host_interface.h index 94a41e45a..e79d22ef8 100644 --- a/android/app/src/cpp/android_host_interface.h +++ b/android/app/src/cpp/android_host_interface.h @@ -1,6 +1,5 @@ #pragma once -#include "YBaseLib/Event.h" -#include "YBaseLib/Timer.h" +#include "common/event.h" #include "core/host_interface.h" #include #include @@ -19,42 +18,48 @@ public: AndroidHostInterface(jobject java_object); ~AndroidHostInterface() override; + bool Initialize() override; + void Shutdown() override; + void ReportError(const char* message) override; void ReportMessage(const char* message) override; bool IsEmulationThreadRunning() const { return m_emulation_thread.joinable(); } - bool StartEmulationThread(ANativeWindow* initial_surface, std::string initial_filename, - std::string initial_state_filename); + bool StartEmulationThread(ANativeWindow* initial_surface, SystemBootParameters boot_params); void RunOnEmulationThread(std::function function, bool blocking = false); void StopEmulationThread(); - void SurfaceChanged(ANativeWindow* window, int format, int width, int height); + void SurfaceChanged(ANativeWindow* surface, int format, int width, int height); void SetControllerType(u32 index, std::string_view type_name); void SetControllerButtonState(u32 index, s32 button_code, bool pressed); +protected: + void SetUserDirectory() override; + bool AcquireHostDisplay() override; + void ReleaseHostDisplay() override; + std::unique_ptr CreateAudioStream(AudioBackend backend) override; + private: enum : u32 { NUM_CONTROLLERS = 2 }; - void EmulationThreadEntryPoint(ANativeWindow* initial_surface, std::string initial_filename, - std::string initial_state_filename); + void EmulationThreadEntryPoint(ANativeWindow* initial_surface, SystemBootParameters boot_params); void CreateImGuiContext(); void DestroyImGuiContext(); - void DrawImGui(); - - void DrawFPSWindow(); jobject m_java_object = {}; + ANativeWindow* m_surface = nullptr; + std::mutex m_callback_mutex; std::deque> m_callback_queue; std::thread m_emulation_thread; std::atomic_bool m_emulation_thread_stop_request{false}; std::atomic_bool m_emulation_thread_start_result{false}; - Event m_emulation_thread_started; + Common::Event m_emulation_thread_started; };