From a451e7f1779c9e40d3fec80c6cfe6a4d9e30971f Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 5 Jul 2020 21:31:17 +1000 Subject: [PATCH] Android: Get it running again Currently settings are not changable. --- android/app/src/cpp/CMakeLists.txt | 4 +- .../app/src/cpp/android_gles_host_display.cpp | 399 ------------------ .../app/src/cpp/android_gles_host_display.h | 61 --- .../app/src/cpp/android_host_interface.cpp | 103 ++++- android/app/src/cpp/android_host_interface.h | 19 +- .../stenzek/duckstation/GameListEntry.java | 30 +- 6 files changed, 121 insertions(+), 495 deletions(-) delete mode 100644 android/app/src/cpp/android_gles_host_display.cpp delete mode 100644 android/app/src/cpp/android_gles_host_display.h diff --git a/android/app/src/cpp/CMakeLists.txt b/android/app/src/cpp/CMakeLists.txt index 9e59c24ec..83e3d3240 100644 --- a/android/app/src/cpp/CMakeLists.txt +++ b/android/app/src/cpp/CMakeLists.txt @@ -1,10 +1,8 @@ set(SRCS android_host_interface.cpp android_host_interface.h - android_gles_host_display.cpp - android_gles_host_display.h main.cpp ) add_library(duckstation-native SHARED ${SRCS}) -target_link_libraries(duckstation-native PRIVATE android frontend-common core common glad imgui EGL::EGL) +target_link_libraries(duckstation-native PRIVATE android frontend-common core common glad imgui) diff --git a/android/app/src/cpp/android_gles_host_display.cpp b/android/app/src/cpp/android_gles_host_display.cpp deleted file mode 100644 index 4a85b79bf..000000000 --- a/android/app/src/cpp/android_gles_host_display.cpp +++ /dev/null @@ -1,399 +0,0 @@ -#include "android_gles_host_display.h" -#include "common/assert.h" -#include "common/log.h" -#include -#include -#include -#include -#include -Log_SetChannel(AndroidGLESHostDisplay); - -class AndroidGLESHostDisplayTexture : public HostDisplayTexture -{ -public: - AndroidGLESHostDisplayTexture(GLuint id, u32 width, u32 height) : m_id(id), m_width(width), m_height(height) {} - ~AndroidGLESHostDisplayTexture() override { glDeleteTextures(1, &m_id); } - - void* GetHandle() const override { return reinterpret_cast(static_cast(m_id)); } - u32 GetWidth() const override { return m_width; } - u32 GetHeight() const override { return m_height; } - - GLuint GetGLID() const { return m_id; } - - static std::unique_ptr Create(u32 width, u32 height, const void* initial_data, - u32 initial_data_stride) - { - GLuint id; - glGenTextures(1, &id); - - GLint old_texture_binding = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding); - - // TODO: Set pack width - Assert(!initial_data || initial_data_stride == (width * sizeof(u32))); - - glBindTexture(GL_TEXTURE_2D, id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, initial_data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glBindTexture(GL_TEXTURE_2D, id); - return std::make_unique(id, width, height); - } - -private: - GLuint m_id; - u32 m_width; - u32 m_height; -}; - -AndroidGLESHostDisplay::AndroidGLESHostDisplay(ANativeWindow* window) - : m_window(window), m_window_width(ANativeWindow_getWidth(window)), m_window_height(ANativeWindow_getHeight(window)) -{ -} - -AndroidGLESHostDisplay::~AndroidGLESHostDisplay() -{ - if (m_egl_context != EGL_NO_CONTEXT) - { - m_display_program.Destroy(); - ImGui_ImplOpenGL3_Shutdown(); - eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroyContext(m_egl_display, m_egl_context); - } - - if (m_egl_surface != EGL_NO_SURFACE) - eglDestroySurface(m_egl_display, m_egl_surface); -} - -HostDisplay::RenderAPI AndroidGLESHostDisplay::GetRenderAPI() const -{ - return HostDisplay::RenderAPI::OpenGLES; -} - -void* AndroidGLESHostDisplay::GetRenderDevice() const -{ - return nullptr; -} - -void* AndroidGLESHostDisplay::GetRenderContext() const -{ - return m_egl_context; -} - -void* AndroidGLESHostDisplay::GetRenderWindow() const -{ - return m_window; -} - -void AndroidGLESHostDisplay::ChangeRenderWindow(void* new_window) -{ - eglMakeCurrent(m_egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - DestroySurface(); - - m_window = static_cast(new_window); - - if (!CreateSurface()) - Panic("Failed to recreate surface after window change"); - - if (!eglMakeCurrent(m_egl_display, m_egl_surface, m_egl_surface, m_egl_context)) - Panic("Failed to make context current after window change"); -} - -std::unique_ptr AndroidGLESHostDisplay::CreateTexture(u32 width, u32 height, const void* data, - 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) -{ - AndroidGLESHostDisplayTexture* tex = static_cast(texture); - Assert(data_stride == (width * sizeof(u32))); - - GLint old_texture_binding = 0; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_texture_binding); - - glBindTexture(GL_TEXTURE_2D, tex->GetGLID()); - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); - - glBindTexture(GL_TEXTURE_2D, old_texture_binding); -} - -bool AndroidGLESHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, - void* out_data, u32 out_data_stride) -{ - 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)); - - 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); - - glPixelStorei(GL_PACK_ALIGNMENT, old_alignment); - glPixelStorei(GL_PACK_ROW_LENGTH, old_row_length); - return true; -} - -void AndroidGLESHostDisplay::SetVSync(bool enabled) -{ - eglSwapInterval(m_egl_display, enabled ? 1 : 0); -} - -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); - ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); - Log_InfoPrintf("WindowResized %dx%d", m_window_width, m_window_height); -} - -const char* AndroidGLESHostDisplay::GetGLSLVersionString() const -{ - return "#version 100"; -} - -std::string AndroidGLESHostDisplay::GetGLSLVersionHeader() const -{ - return R"( -#version 100 - -precision highp float; -precision highp int; -)"; -} - -bool AndroidGLESHostDisplay::CreateGLContext() -{ - m_egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (!m_egl_display) - { - Log_ErrorPrint("eglGetDisplay() failed"); - return false; - } - - EGLint egl_major_version, egl_minor_version; - if (!eglInitialize(m_egl_display, &egl_major_version, &egl_minor_version)) - { - Log_ErrorPrint("eglInitialize() failed"); - return false; - } - - Log_InfoPrintf("EGL version %d.%d initialized", egl_major_version, egl_minor_version); - - static constexpr std::array egl_surface_attribs = {{EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_SURFACE_TYPE, - EGL_WINDOW_BIT, EGL_NONE}}; - - int num_m_egl_configs; - if (!eglChooseConfig(m_egl_display, egl_surface_attribs.data(), &m_egl_config, 1, &num_m_egl_configs)) - { - Log_ErrorPrint("eglChooseConfig() failed"); - return false; - } - - eglBindAPI(EGL_OPENGL_ES_API); - - // Try GLES 3, then fall back to GLES 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()); - if (m_egl_context) - break; - } - - if (!m_egl_context) - { - Log_ErrorPrint("eglCreateContext() failed"); - return false; - } - - if (!CreateSurface()) - return false; - - if (!eglMakeCurrent(m_egl_display, m_egl_surface, m_egl_surface, m_egl_context)) - { - Log_ErrorPrint("eglMakeCurrent() failed"); - return false; - } - - // Load GLAD. - if (!gladLoadGLES2Loader(reinterpret_cast(eglGetProcAddress))) - { - Log_ErrorPrintf("Failed to load GL functions"); - return false; - } - - Log_InfoPrintf("GLES Version: %s", glGetString(GL_VERSION)); - Log_InfoPrintf("GLES Renderer: %s", glGetString(GL_RENDERER)); - return true; -} - -bool AndroidGLESHostDisplay::CreateSurface() -{ - EGLint native_visual; - eglGetConfigAttrib(m_egl_display, m_egl_config, EGL_NATIVE_VISUAL_ID, &native_visual); - ANativeWindow_setBuffersGeometry(m_window, 0, 0, native_visual); - m_window_width = ANativeWindow_getWidth(m_window); - m_window_height = ANativeWindow_getHeight(m_window); - - m_egl_surface = eglCreateWindowSurface(m_egl_display, m_egl_config, m_window, nullptr); - if (!m_egl_surface) - { - Log_ErrorPrint("eglCreateWindowSurface() failed"); - return false; - } - - WindowResized(m_window_width, m_window_height); - return true; -} - -void AndroidGLESHostDisplay::DestroySurface() -{ - eglDestroySurface(m_egl_display, m_egl_surface); - m_egl_surface = EGL_NO_SURFACE; -} - -bool AndroidGLESHostDisplay::CreateImGuiContext() -{ - if (!ImGui_ImplOpenGL3_Init(GetGLSLVersionString())) - return false; - - ImGui_ImplOpenGL3_NewFrame(); - ImGui::GetIO().DisplaySize.x = static_cast(m_window_width); - ImGui::GetIO().DisplaySize.y = static_cast(m_window_height); - return true; -} - -bool AndroidGLESHostDisplay::CreateGLResources() -{ - static constexpr char fullscreen_quad_vertex_shader[] = R"( -attribute vec2 a_pos; -attribute vec2 a_tex0; - -varying vec2 v_tex0; - -void main() -{ - v_tex0 = a_tex0; - gl_Position = vec4(a_pos, 0.0, 1.0); -} -)"; - - static constexpr char display_fragment_shader[] = R"( -uniform sampler2D samp0; - -varying vec2 v_tex0; - -void main() -{ - gl_FragColor = texture2D(samp0, v_tex0); -} -)"; - - if (!m_display_program.Compile(GetGLSLVersionHeader() + fullscreen_quad_vertex_shader, - GetGLSLVersionHeader() + display_fragment_shader)) - { - Log_ErrorPrintf("Failed to compile display shaders"); - return false; - } - - m_display_program.BindAttribute(0, "a_pos"); - m_display_program.BindAttribute(1, "a_tex0"); - - if (!m_display_program.Link()) - { - Log_ErrorPrintf("Failed to link display program"); - return false; - } - - m_display_program.Bind(); - m_display_program.RegisterUniform("samp0"); - m_display_program.Uniform1i(0, 0); - - return true; -} - -std::unique_ptr AndroidGLESHostDisplay::Create(ANativeWindow* window) -{ - std::unique_ptr display = std::make_unique(window); - if (!display->CreateGLContext() || !display->CreateImGuiContext() || !display->CreateGLResources()) - return nullptr; - - Log_DevPrintf("%dx%d display created", display->m_window_width, display->m_window_height); - return display; -} - -void AndroidGLESHostDisplay::Render() -{ - glDisable(GL_SCISSOR_TEST); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - - RenderDisplay(); - - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - eglSwapBuffers(m_egl_display, m_egl_surface); - - ImGui::NewFrame(); - ImGui_ImplOpenGL3_NewFrame(); - - GL::Program::ResetLastProgram(); -} - -void AndroidGLESHostDisplay::RenderDisplay() -{ - if (!m_display_texture_handle) - return; - - 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); - - glDisable(GL_BLEND); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glDisable(GL_SCISSOR_TEST); - glDepthMask(GL_FALSE); - m_display_program.Bind(); - - 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_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 - {{-1.0f, 1.0f, tex_left, tex_top}}, // top-left - {{1.0f, 1.0f, tex_right, tex_top}}, // top-right - }}; - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), &vertices[0][0]); - glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vertices[0]), &vertices[0][2]); - glEnableVertexAttribArray(1); - - glBindTexture(GL_TEXTURE_2D, static_cast(reinterpret_cast(m_display_texture_handle))); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(0); -} diff --git a/android/app/src/cpp/android_gles_host_display.h b/android/app/src/cpp/android_gles_host_display.h deleted file mode 100644 index b01e80287..000000000 --- a/android/app/src/cpp/android_gles_host_display.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once -#include "common/gl/program.h" -#include "common/gl/texture.h" -#include "core/host_display.h" -#include -#include -#include -#include -#include - -class AndroidGLESHostDisplay final : public HostDisplay -{ -public: - AndroidGLESHostDisplay(ANativeWindow* window); - ~AndroidGLESHostDisplay(); - - static std::unique_ptr Create(ANativeWindow* window); - - RenderAPI GetRenderAPI() const override; - void* GetRenderDevice() const override; - void* GetRenderContext() const override; - 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; - 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; - -private: - const char* GetGLSLVersionString() const; - std::string GetGLSLVersionHeader() const; - - bool CreateSurface(); - void DestroySurface(); - - bool CreateGLContext(); - bool CreateImGuiContext(); - bool CreateGLResources(); - - void RenderDisplay(); - - ANativeWindow* m_window = nullptr; - int m_window_width = 0; - int m_window_height = 0; - - EGLDisplay m_egl_display = EGL_NO_DISPLAY; - EGLSurface m_egl_surface = EGL_NO_SURFACE; - EGLContext m_egl_context = EGL_NO_CONTEXT; - EGLConfig m_egl_config = {}; - - GL::Program m_display_program; -}; diff --git a/android/app/src/cpp/android_host_interface.cpp b/android/app/src/cpp/android_host_interface.cpp index 1f1f7c4e8..d9db45ebf 100644 --- a/android/app/src/cpp/android_host_interface.cpp +++ b/android/app/src/cpp/android_host_interface.cpp @@ -1,15 +1,16 @@ #include "android_host_interface.h" -#include "android_gles_host_display.h" #include "common/assert.h" #include "common/audio_stream.h" #include "common/log.h" #include "common/string.h" +#include "common/timestamp.h" #include "core/controller.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 "frontend-common/opengl_host_display.h" +#include "frontend-common/vulkan_host_display.h" #include #include #include @@ -64,7 +65,7 @@ AndroidHostInterface::~AndroidHostInterface() bool AndroidHostInterface::Initialize() { - if (!HostInterface::Initialize()) + if (!CommonHostInterface::Initialize()) return false; return true; @@ -75,6 +76,16 @@ void AndroidHostInterface::Shutdown() HostInterface::Shutdown(); } +const char* AndroidHostInterface::GetFrontendName() const +{ + return "DuckStation Android"; +} + +void AndroidHostInterface::RequestExit() +{ + ReportError("Ignoring RequestExit()"); +} + void AndroidHostInterface::ReportError(const char* message) { HostInterface::ReportError(message); @@ -85,6 +96,11 @@ void AndroidHostInterface::ReportMessage(const char* message) HostInterface::ReportMessage(message); } +std::string AndroidHostInterface::GetSettingValue(const char* section, const char* key, const char* default_value) +{ + return m_settings_interface->GetStringValue(section, key, default_value); +} + void AndroidHostInterface::SetUserDirectory() { // TODO: Should this be customizable or use an API-determined path? @@ -93,10 +109,13 @@ void AndroidHostInterface::SetUserDirectory() void AndroidHostInterface::LoadSettings() { - // 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); }); + m_settings_interface = std::make_unique(GetSettingsFileName()); + CommonHostInterface::LoadSettings(*m_settings_interface); +} + +void AndroidHostInterface::UpdateInputMap() +{ + CommonHostInterface::UpdateInputMap(*m_settings_interface); } bool AndroidHostInterface::StartEmulationThread(ANativeWindow* initial_surface, SystemBootParameters boot_params) @@ -157,6 +176,7 @@ void AndroidHostInterface::RunOnEmulationThread(std::function function, void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surface, SystemBootParameters boot_params) { CreateImGuiContext(); + m_surface = initial_surface; // Boot system. if (!BootSystem(boot_params)) @@ -172,8 +192,6 @@ void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surf m_emulation_thread_start_result.store(true); m_emulation_thread_started.Signal(); - ImGui::NewFrame(); - while (!m_emulation_thread_stop_request.load()) { // run any events @@ -203,6 +221,7 @@ void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surf m_system->GetGPU()->ResetGraphicsAPIState(); m_display->Render(); + ImGui::NewFrame(); if (m_system) { @@ -221,21 +240,41 @@ void AndroidHostInterface::EmulationThreadEntryPoint(ANativeWindow* initial_surf bool AndroidHostInterface::AcquireHostDisplay() { - std::unique_ptr display = AndroidGLESHostDisplay::Create(m_surface); - if (!display) + WindowInfo wi; + wi.type = WindowInfo::Type::Android; + wi.window_handle = m_surface; + wi.surface_width = ANativeWindow_getWidth(m_surface); + wi.surface_height = ANativeWindow_getHeight(m_surface); + + std::unique_ptr display; + switch (m_settings.gpu_renderer) { - Log_ErrorPrintf("Failed to create GLES host display"); + case GPURenderer::HardwareVulkan: + display = std::make_unique(); + break; + + case GPURenderer::HardwareOpenGL: + default: + display = std::make_unique(); + break; + } + + if (!display->CreateRenderDevice(wi, {}, m_settings.gpu_use_debug_device) || + !display->InitializeRenderDevice(GetShaderCacheBasePath(), m_settings.gpu_use_debug_device)) + { + ReportError("Failed to acquire host display."); return false; } - m_display = display.release(); + m_display = std::move(display); + ImGui::NewFrame(); return true; } void AndroidHostInterface::ReleaseHostDisplay() { - delete m_display; - m_display = nullptr; + m_display->DestroyRenderDevice(); + m_display.reset(); } std::unique_ptr AndroidHostInterface::CreateAudioStream(AudioBackend backend) @@ -269,14 +308,23 @@ void AndroidHostInterface::SurfaceChanged(ANativeWindow* surface, int format, in if (m_surface == surface) { if (m_display) - m_display->WindowResized(width, height); + m_display->ResizeRenderWindow(width, height); return; } m_surface = surface; + if (m_display) - m_display->ChangeRenderWindow(surface); + { + WindowInfo wi; + wi.type = WindowInfo::Type::Android; + wi.window_handle = surface; + wi.surface_width = width; + wi.surface_height = height; + + m_display->ChangeRenderWindow(wi); + } } void AndroidHostInterface::CreateImGuiContext() @@ -369,6 +417,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) DEFINE_JNI_ARGS_METHOD(jobject, AndroidHostInterface_create, jobject unused) { + Log::SetDebugOutputParams(true, nullptr, LOGLEVEL_DEBUG); + // initialize the java side jobject java_obj = env->NewObject(s_AndroidHostInterface_class, s_AndroidHostInterface_constructor); if (!java_obj) @@ -382,7 +432,7 @@ DEFINE_JNI_ARGS_METHOD(jobject, AndroidHostInterface_create, jobject unused) // initialize the C++ side AndroidHostInterface* cpp_obj = new AndroidHostInterface(java_obj_ref); - if (!cpp_obj) + if (!cpp_obj->Initialize()) { // TODO: Do we need to release the original java object reference? Log_ErrorPrint("Failed to create C++ AndroidHostInterface"); @@ -478,12 +528,14 @@ DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_ca gl.AddDirectory(search_dir.c_str(), search_recursively); } + gl.Refresh(false, false, nullptr); + 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;JLjava/lang/" + "String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); Assert(entry_constructor != nullptr); jobjectArray entry_array = env->NewObjectArray(gl.GetEntryCount(), entry_class, nullptr); @@ -492,14 +544,21 @@ DEFINE_JNI_ARGS_METHOD(jarray, GameList_getEntries, jobject unused, jstring j_ca u32 counter = 0; for (const GameListEntry& entry : gl.GetEntries()) { + const Timestamp modified_ts( + Timestamp::FromUnixTimestamp(static_cast(entry.last_modified_time))); + 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::GetDiscRegionName(entry.region)); jstring type = env->NewStringUTF(GameList::EntryTypeToString(entry.type)); + jstring compatibility_rating = + env->NewStringUTF(GameList::EntryCompatibilityRatingToString(entry.compatibility_rating)); + jstring modified_time = env->NewStringUTF(modified_ts.ToString("%Y/%m/%d, %H:%M:%S")); jlong size = entry.total_size; - jobject entry_jobject = env->NewObject(entry_class, entry_constructor, path, code, title, region, type, size); + jobject entry_jobject = env->NewObject(entry_class, entry_constructor, path, code, title, size, modified_time, + region, type, compatibility_rating); env->SetObjectArrayElement(entry_array, counter++, entry_jobject); } diff --git a/android/app/src/cpp/android_host_interface.h b/android/app/src/cpp/android_host_interface.h index 56115ca25..fedf2ebca 100644 --- a/android/app/src/cpp/android_host_interface.h +++ b/android/app/src/cpp/android_host_interface.h @@ -1,6 +1,7 @@ #pragma once #include "common/event.h" -#include "core/host_interface.h" +#include "frontend-common/common_host_interface.h" +#include "frontend-common/ini_settings_interface.h" #include #include #include @@ -12,7 +13,7 @@ struct ANativeWindow; class Controller; -class AndroidHostInterface final : public HostInterface +class AndroidHostInterface final : public CommonHostInterface { public: AndroidHostInterface(jobject java_object); @@ -21,9 +22,14 @@ public: bool Initialize() override; void Shutdown() override; + const char* GetFrontendName() const override; + void RequestExit() override; + void ReportError(const char* message) override; void ReportMessage(const char* message) override; + std::string GetSettingValue(const char* section, const char* key, const char* default_value = "") override; + bool IsEmulationThreadRunning() const { return m_emulation_thread.joinable(); } bool StartEmulationThread(ANativeWindow* initial_surface, SystemBootParameters boot_params); void RunOnEmulationThread(std::function function, bool blocking = false); @@ -37,16 +43,13 @@ public: protected: void SetUserDirectory() override; void LoadSettings() override; + void UpdateInputMap() 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, SystemBootParameters boot_params); void CreateImGuiContext(); @@ -54,6 +57,8 @@ private: jobject m_java_object = {}; + std::unique_ptr m_settings_interface; + ANativeWindow* m_surface = nullptr; std::mutex m_callback_mutex; diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/GameListEntry.java b/android/app/src/main/java/com/github/stenzek/duckstation/GameListEntry.java index d61e5aada..5b01e43ce 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/GameListEntry.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/GameListEntry.java @@ -13,19 +13,33 @@ public class GameListEntry { PSExe } + public enum CompatibilityRating + { + Unknown, + DoesntBoot, + CrashesInIntro, + CrashesInGame, + GraphicalAudioIssues, + NoIssues, + } + private String mPath; private String mCode; private String mTitle; + private long mSize; + private String mModifiedTime; private ConsoleRegion mRegion; private EntryType mType; - private long mSize; + private CompatibilityRating mCompatibilityRating; - public GameListEntry(String path, String code, String title, String region, - String type, long size) { + + public GameListEntry(String path, String code, String title, long size, String modifiedTime, String region, + String type, String compatibilityRating) { mPath = path; mCode = code; mTitle = title; mSize = size; + mModifiedTime = modifiedTime; try { mRegion = ConsoleRegion.valueOf(region); @@ -38,6 +52,12 @@ public class GameListEntry { } catch (IllegalArgumentException e) { mType = EntryType.Disc; } + + try { + mCompatibilityRating = CompatibilityRating.valueOf(compatibilityRating); + } catch (IllegalArgumentException e) { + mCompatibilityRating = CompatibilityRating.Unknown; + } } public String getPath() { @@ -52,12 +72,16 @@ public class GameListEntry { return mTitle; } + public String getModifiedTime() { return mModifiedTime; } + public ConsoleRegion getRegion() { return mRegion; } public EntryType getType() { return mType; } + public CompatibilityRating getCompatibilityRating() { return mCompatibilityRating; } + public void fillView(View view) { ((TextView) view.findViewById(R.id.game_list_view_entry_title)).setText(mTitle); ((TextView) view.findViewById(R.id.game_list_view_entry_path)).setText(mPath);