From 4f95e094e484fc7f088fbebee51ec3aae1ba669f Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 12 Sep 2021 14:32:41 +0300 Subject: [PATCH 1/5] [GPU] Remove outdated forward declarations from trace_dump.h --- src/xenia/gpu/trace_dump.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/xenia/gpu/trace_dump.h b/src/xenia/gpu/trace_dump.h index b05d11950..9815eacaf 100644 --- a/src/xenia/gpu/trace_dump.h +++ b/src/xenia/gpu/trace_dump.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -19,13 +19,6 @@ #include "xenia/gpu/xenos.h" #include "xenia/memory.h" -namespace xe { -namespace ui { -class Loop; -class Window; -} // namespace ui -} // namespace xe - namespace xe { namespace gpu { From b77e6eb8e60f9245722453a5abe07e09270a73ff Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 12 Sep 2021 17:12:33 +0300 Subject: [PATCH 2/5] [D3D12] Fix syntax warnings reported by Clang --- src/xenia/gpu/d3d12/d3d12_primitive_processor.cc | 2 +- src/xenia/gpu/d3d12/pipeline_cache.h | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/xenia/gpu/d3d12/d3d12_primitive_processor.cc b/src/xenia/gpu/d3d12/d3d12_primitive_processor.cc index c93275b95..ec1e576b0 100644 --- a/src/xenia/gpu/d3d12/d3d12_primitive_processor.cc +++ b/src/xenia/gpu/d3d12/d3d12_primitive_processor.cc @@ -156,7 +156,7 @@ void* D3D12PrimitiveProcessor::RequestHostConvertedIndexBufferForCurrentFrame( (coalign_for_simd ? XE_GPU_PRIMITIVE_PROCESSOR_SIMD_SIZE : 0), index_size, nullptr, nullptr, &gpu_address); if (!mapping) { - return false; + return nullptr; } if (coalign_for_simd) { ptrdiff_t coalignment_offset = diff --git a/src/xenia/gpu/d3d12/pipeline_cache.h b/src/xenia/gpu/d3d12/pipeline_cache.h index b5d7a4209..290c47996 100644 --- a/src/xenia/gpu/d3d12/pipeline_cache.h +++ b/src/xenia/gpu/d3d12/pipeline_cache.h @@ -70,12 +70,11 @@ class PipelineCache { // Retrieves the shader modification for the current state. The shader must // have microcode analyzed. - DxbcShaderTranslator::Modification - PipelineCache::GetCurrentVertexShaderModification( + DxbcShaderTranslator::Modification GetCurrentVertexShaderModification( const Shader& shader, Shader::HostVertexShaderType host_vertex_shader_type) const; - DxbcShaderTranslator::Modification - PipelineCache::GetCurrentPixelShaderModification(const Shader& shader) const; + DxbcShaderTranslator::Modification GetCurrentPixelShaderModification( + const Shader& shader) const; // If draw_util::IsRasterizationPotentiallyDone is false, the pixel shader // MUST be made nullptr BEFORE calling this! From acbd22840dcf95cb873466a2cede87dd5b6ce224 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Mon, 13 Sep 2021 22:53:19 +0300 Subject: [PATCH 3/5] [Base] Android log sink + sink cleanup --- src/xenia/base/logging.cc | 166 +++++++++++++++++++++++++++++++++++--- src/xenia/base/logging.h | 21 +++-- 2 files changed, 166 insertions(+), 21 deletions(-) diff --git a/src/xenia/base/logging.cc b/src/xenia/base/logging.cc index b78ad88e3..1bf3870a0 100644 --- a/src/xenia/base/logging.cc +++ b/src/xenia/base/logging.cc @@ -2,14 +2,16 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/base/logging.h" +#include #include +#include #include #include @@ -17,6 +19,7 @@ #include "third_party/disruptorplus/include/disruptorplus/ring_buffer.hpp" #include "third_party/disruptorplus/include/disruptorplus/sequence_barrier.hpp" #include "third_party/disruptorplus/include/disruptorplus/spin_wait_strategy.hpp" +#include "xenia/base/assert.h" #include "xenia/base/atomic.h" #include "xenia/base/console.h" #include "xenia/base/cvar.h" @@ -24,22 +27,30 @@ #include "xenia/base/filesystem.h" #include "xenia/base/math.h" #include "xenia/base/memory.h" +#include "xenia/base/platform.h" #include "xenia/base/ring_buffer.h" #include "xenia/base/string.h" #include "xenia/base/system.h" #include "xenia/base/threading.h" -// For MessageBox: // TODO(benvanik): generic API? logging_win.cc? -#if XE_PLATFORM_WIN32 +#if XE_PLATFORM_ANDROID +#include +#elif XE_PLATFORM_WIN32 +// For MessageBox: #include "xenia/base/platform_win.h" -#endif // XE_PLATFORM_WIN32 +#endif // XE_PLATFORM #include "third_party/fmt/include/fmt/format.h" +#if XE_PLATFORM_ANDROID +DEFINE_bool(log_to_logcat, true, "Write log output to Android Logcat.", + "Logging"); +#else DEFINE_path(log_file, "", "Logs are written to the given file", "Logging"); DEFINE_bool(log_to_stdout, true, "Write log output to stdout", "Logging"); DEFINE_bool(log_to_debugprint, false, "Dump the log to DebugPrint.", "Logging"); +#endif // XE_PLATFORM_ANDROID DEFINE_bool(flush_log, true, "Flush log file after each log line batch.", "Logging"); DEFINE_int32( @@ -65,6 +76,15 @@ struct LogLine { thread_local char thread_log_buffer_[64 * 1024]; +FileLogSink::~FileLogSink() { + if (file_) { + fflush(file_); + if (owns_file_) { + fclose(file_); + } + } +} + void FileLogSink::Write(const char* buf, size_t size) { if (file_) { fwrite(buf, 1, size, file_); @@ -77,6 +97,120 @@ void FileLogSink::Flush() { } } +void DebugPrintLogSink::Write(const char* buf, size_t size) { + debugging::DebugPrint("{}", std::string_view(buf, size)); +} + +#if XE_PLATFORM_ANDROID +class AndroidLogSink final : public LogSink { + public: + explicit AndroidLogSink(const std::string_view tag) : tag_(tag) {} + + void Write(const char* buf, size_t size) override; + void Flush() override; + + private: + // May be called with an empty line_buffer_ to write an empty log message (if + // the original message contains a blank line as a separator, for instance). + void WriteLineBuffer(); + + const std::string tag_; + android_LogPriority current_priority_ = ANDROID_LOG_INFO; + bool is_line_continuation_ = false; + size_t line_buffer_used_ = 0; + // LOGGER_ENTRY_MAX_PAYLOAD is defined as 4076 on older Android versions and + // as 4068 on newer ones. An attempt to write more than that amount to the + // kernel logger will result in a truncated log entry. 4000 is the commonly + // used limit considered safe. + // However, "Log message text may be truncated to less than an + // implementation-specific limit (1023 bytes)" - android/log.h. + char line_buffer_[1023 + 1]; +}; + +void AndroidLogSink::Write(const char* buf, size_t size) { + // A null character, if appears, is fine - it will just truncate a single + // line, but since every message ends with a newline, and WriteLineBuffer is + // done after every line, it won't have lasting effects. + // Using memchr and memcpy as they are vectorized in Bionic. + while (size) { + const void* newline = std::memchr(buf, '\n', size); + size_t line_remaining = + newline ? size_t(static_cast(newline) - buf) : size; + while (line_remaining) { + assert_true(line_buffer_used_ < xe::countof(line_buffer_)); + size_t append_count = + std::min(line_remaining, + xe::countof(line_buffer_) - size_t(1) - line_buffer_used_); + std::memcpy(line_buffer_ + line_buffer_used_, buf, append_count); + buf += append_count; + size -= append_count; + line_remaining -= append_count; + line_buffer_used_ += append_count; + if (line_buffer_used_ >= xe::countof(line_buffer_) - size_t(1)) { + WriteLineBuffer(); + // The next WriteLineBuffer belongs to the same line. + is_line_continuation_ = true; + } + } + if (newline) { + // If the end of the buffer was reached right before a newline character, + // so line_buffer_used_ is 0, and is_line_continuation_ has been set, the + // line break has already been done by Android itself - don't write an + // empty message. However, if the message intentionally contains blank + // lines, write them. + if (line_buffer_used_ || !is_line_continuation_) { + WriteLineBuffer(); + } + is_line_continuation_ = false; + ++buf; + --size; + } + } +} + +void AndroidLogSink::Flush() { + if (line_buffer_used_) { + WriteLineBuffer(); + } +} + +void AndroidLogSink::WriteLineBuffer() { + // If this is a new line, check if it's a new log message, and if it is, + // update the priority based on the prefix. + if (!is_line_continuation_ && line_buffer_used_ >= 3 && + line_buffer_[1] == '>' && line_buffer_[2] == ' ') { + switch (line_buffer_[0]) { + case 'd': + current_priority_ = ANDROID_LOG_DEBUG; + break; + case 'w': + current_priority_ = ANDROID_LOG_WARN; + break; + case '!': + current_priority_ = ANDROID_LOG_ERROR; + break; + case 'x': + current_priority_ = ANDROID_LOG_FATAL; + break; + default: + current_priority_ = ANDROID_LOG_INFO; + break; + } + } + // Android skips blank lines, but if writing a blank line was requested + // explicitly for formatting, write a non-newline character. + if (!line_buffer_used_) { + line_buffer_[line_buffer_used_++] = ' '; + } + // Terminate the text. + assert_true(line_buffer_used_ < xe::countof(line_buffer_)); + line_buffer_[line_buffer_used_] = '\0'; + // Write. + __android_log_write(current_priority_, tag_.c_str(), line_buffer_); + line_buffer_used_ = 0; +} +#endif // XE_PLATFORM_ANDROID + class Logger { public: explicit Logger(const std::string_view app_name) @@ -130,9 +264,6 @@ class Logger { for (const auto& sink : sinks_) { sink->Write(buf, size); } - if (cvars::log_to_debugprint) { - debugging::DebugPrint("{}", std::string_view(buf, size)); - } } void WriteThread() { @@ -294,8 +425,14 @@ void InitializeLogging(const std::string_view app_name) { auto mem = memory::AlignedAlloc(0x10); logger_ = new (mem) Logger(app_name); +#if XE_PLATFORM_ANDROID + // TODO(Triang3l): Enable file logging, but not by default as logs may be + // huge. + if (cvars::log_to_logcat) { + logger_->AddLogSink(std::make_unique(app_name)); + } +#else FILE* log_file = nullptr; - if (cvars::log_file.empty()) { // Default to app name. auto file_name = fmt::format("{}.log", app_name); @@ -307,13 +444,16 @@ void InitializeLogging(const std::string_view app_name) { xe::filesystem::CreateParentFolder(cvars::log_file); log_file = xe::filesystem::OpenFile(cvars::log_file, "wt"); } - auto sink = std::make_unique(log_file); - logger_->AddLogSink(std::move(sink)); + logger_->AddLogSink(std::make_unique(log_file, true)); if (cvars::log_to_stdout) { - auto stdout_sink = std::make_unique(stdout); - logger_->AddLogSink(std::move(stdout_sink)); + logger_->AddLogSink(std::make_unique(stdout, false)); } + + if (cvars::log_to_debugprint) { + logger_->AddLogSink(std::make_unique()); + } +#endif // XE_PLATFORM_ANDROID } void ShutdownLogging() { @@ -352,7 +492,7 @@ void logging::AppendLogLine(LogLevel log_level, const char prefix_char, } void FatalError(const std::string_view str) { - logging::AppendLogLine(LogLevel::Error, 'X', str); + logging::AppendLogLine(LogLevel::Error, 'x', str); if (!xe::has_console_attached()) { ShowSimpleMessageBox(SimpleMessageBoxType::Error, str); diff --git a/src/xenia/base/logging.h b/src/xenia/base/logging.h index d2df15cce..208abd3e3 100644 --- a/src/xenia/base/logging.h +++ b/src/xenia/base/logging.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -44,19 +44,24 @@ class LogSink { class FileLogSink final : public LogSink { public: - explicit FileLogSink(FILE* file) : file_(file) {} - virtual ~FileLogSink() { - if (file_) { - fflush(file_); - fclose(file_); - } - } + explicit FileLogSink(FILE* file, bool own_file) + : file_(file), owns_file_(own_file) {} + ~FileLogSink(); void Write(const char* buf, size_t size) override; void Flush() override; private: FILE* file_; + bool owns_file_; +}; + +class DebugPrintLogSink final : public LogSink { + public: + DebugPrintLogSink() = default; + + void Write(const char* buf, size_t size) override; + void Flush() override {} }; // Initializes the logging system and any outputs requested. From 7aeac37eb6b255924547d22b1cad74f342d140c4 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Mon, 13 Sep 2021 23:09:28 +0300 Subject: [PATCH 4/5] [Base/UI] Android globals initialization + WindowedAppContext parts --- src/xenia/base/console_app_main_android.cc | 31 ++++++++- src/xenia/base/main_android.cc | 66 ++++++++++++++++++++ src/xenia/base/main_android.h | 40 ++++++++++++ src/xenia/base/memory.h | 5 ++ src/xenia/base/memory_posix.cc | 40 ++++++++++-- src/xenia/base/platform_android.cc | 65 ------------------- src/xenia/base/platform_android.h | 48 -------------- src/xenia/base/threading.h | 6 ++ src/xenia/base/threading_posix.cc | 43 +++++++++++-- src/xenia/ui/window_android.cc | 54 ++++++++++++++++ src/xenia/ui/window_android.h | 64 +++++++++++++++++++ src/xenia/ui/windowed_app_context_android.cc | 21 ++++++- src/xenia/ui/windowed_app_context_android.h | 10 +++ src/xenia/ui/windowed_app_main_android.cc | 10 +++ 14 files changed, 375 insertions(+), 128 deletions(-) create mode 100644 src/xenia/base/main_android.cc create mode 100644 src/xenia/base/main_android.h delete mode 100644 src/xenia/base/platform_android.cc delete mode 100644 src/xenia/base/platform_android.h create mode 100644 src/xenia/ui/window_android.cc create mode 100644 src/xenia/ui/window_android.h create mode 100644 src/xenia/ui/windowed_app_main_android.cc diff --git a/src/xenia/base/console_app_main_android.cc b/src/xenia/base/console_app_main_android.cc index 70e69fc5e..320128bc1 100644 --- a/src/xenia/base/console_app_main_android.cc +++ b/src/xenia/base/console_app_main_android.cc @@ -7,4 +7,33 @@ ****************************************************************************** */ -#include "xenia/base/console_app_posix.cc" +#include +#include + +#include "xenia/base/console_app_main.h" +#include "xenia/base/cvar.h" +#include "xenia/base/main_android.h" + +extern "C" int main(int argc, char** argv) { + xe::ConsoleAppEntryInfo entry_info = xe::GetConsoleAppEntryInfo(); + + if (!entry_info.transparent_options) { + cvar::ParseLaunchArguments(argc, argv, entry_info.positional_usage, + entry_info.positional_options); + } + + // Initialize Android globals, including logging. Needs parsed cvars. + // TODO(Triang3l): Obtain the actual API level. + xe::InitializeAndroidAppFromMainThread(__ANDROID_API__); + + std::vector args; + for (int n = 0; n < argc; n++) { + args.emplace_back(argv[n]); + } + + int result = entry_info.entry_point(args); + + xe::ShutdownAndroidAppFromMainThread(); + + return result; +} diff --git a/src/xenia/base/main_android.cc b/src/xenia/base/main_android.cc new file mode 100644 index 000000000..56211fee2 --- /dev/null +++ b/src/xenia/base/main_android.cc @@ -0,0 +1,66 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/main_android.h" + +#include + +#include "xenia/base/assert.h" +#include "xenia/base/logging.h" +#include "xenia/base/memory.h" +#include "xenia/base/threading.h" + +namespace xe { + +static size_t android_initializations_ = 0; + +static int32_t android_api_level_ = __ANDROID_API__; + +void InitializeAndroidAppFromMainThread(int32_t api_level) { + if (android_initializations_++) { + // Already initialized for another component in the process. + return; + } + + // Set the API level before everything else if available - may be needed by + // subsystem initialization itself. + android_api_level_ = api_level; + + // Logging uses threading. + xe::threading::AndroidInitialize(); + + // Multiple apps can be launched within one process - don't pass the actual + // app name. + xe::InitializeLogging("xenia"); + + xe::memory::AndroidInitialize(); +} + +void ShutdownAndroidAppFromMainThread() { + assert_not_zero(android_initializations_); + if (!android_initializations_) { + return; + } + if (--android_initializations_) { + // Other components are still running. + return; + } + + xe::memory::AndroidShutdown(); + + xe::ShutdownLogging(); + + xe::threading::AndroidShutdown(); + + android_api_level_ = __ANDROID_API__; +} + +int32_t GetAndroidApiLevel() { return android_api_level_; } + +} // namespace xe diff --git a/src/xenia/base/main_android.h b/src/xenia/base/main_android.h new file mode 100644 index 000000000..1f2d23dd2 --- /dev/null +++ b/src/xenia/base/main_android.h @@ -0,0 +1,40 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_BASE_MAIN_ANDROID_H_ +#define XENIA_BASE_MAIN_ANDROID_H_ + +#include + +#include "xenia/base/platform.h" + +namespace xe { + +// In activities, these functions must be called in onCreate and onDestroy. +// +// Multiple components may run in the same process, and they will be +// instantiated in the main thread, which is, for regular applications (the +// system application exception doesn't apply to Xenia), the UI thread. +// +// For this reason, it's okay to call these functions multiple times if +// different Xenia windowed applications run in one process - there is call +// counting internally. +// +// In standalone console apps built with $(BUILD_EXECUTABLE), these functions +// must be called in `main`. +void InitializeAndroidAppFromMainThread(int32_t api_level); +void ShutdownAndroidAppFromMainThread(); + +// May be the minimum supported level if the initialization was done without a +// configuration. +int32_t GetAndroidApiLevel(); + +} // namespace xe + +#endif // XENIA_BASE_MAIN_ANDROID_H_ diff --git a/src/xenia/base/memory.h b/src/xenia/base/memory.h index 459f09db3..14fb65968 100644 --- a/src/xenia/base/memory.h +++ b/src/xenia/base/memory.h @@ -24,6 +24,11 @@ namespace xe { namespace memory { +#if XE_PLATFORM_ANDROID +void AndroidInitialize(); +void AndroidShutdown(); +#endif + // Returns the native page size of the system, in bytes. // This should be ~4KiB. size_t page_size(); diff --git a/src/xenia/base/memory_posix.cc b/src/xenia/base/memory_posix.cc index 271249a87..2ff36a603 100644 --- a/src/xenia/base/memory_posix.cc +++ b/src/xenia/base/memory_posix.cc @@ -12,22 +12,52 @@ #include #include #include +#include #include "xenia/base/math.h" #include "xenia/base/platform.h" #include "xenia/base/string.h" #if XE_PLATFORM_ANDROID +#include #include #include #include -#include "xenia/base/platform_android.h" +#include "xenia/base/main_android.h" #endif namespace xe { namespace memory { +#if XE_PLATFORM_ANDROID +// May be null if no dynamically loaded functions are required. +static void* libandroid_; +// API 26+. +static int (*android_ASharedMemory_create_)(const char* name, size_t size); + +void AndroidInitialize() { + if (xe::GetAndroidApiLevel() >= 26) { + libandroid_ = dlopen("libandroid.so", RTLD_NOW); + assert_not_null(libandroid_); + if (libandroid_) { + android_ASharedMemory_create_ = + reinterpret_cast( + dlsym(libandroid_, "ASharedMemory_create")); + assert_not_null(android_ASharedMemory_create_); + } + } +} + +void AndroidShutdown() { + android_ASharedMemory_create_ = nullptr; + if (libandroid_) { + dlclose(libandroid_); + libandroid_ = nullptr; + } +} +#endif + size_t page_size() { return getpagesize(); } size_t allocation_granularity() { return page_size(); } @@ -86,11 +116,9 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path, size_t length, PageAccess access, bool commit) { #if XE_PLATFORM_ANDROID - if (xe::platform::android::api_level() >= 26) { - // TODO(Triang3l): Check if memfd can be used instead on API 30+. - int sharedmem_fd = - xe::platform::android::api_functions().api_26.ASharedMemory_create( - path.c_str(), length); + // TODO(Triang3l): Check if memfd can be used instead on API 30+. + if (android_ASharedMemory_create_) { + int sharedmem_fd = android_ASharedMemory_create_(path.c_str(), length); return sharedmem_fd >= 0 ? sharedmem_fd : kFileMappingHandleInvalid; } diff --git a/src/xenia/base/platform_android.cc b/src/xenia/base/platform_android.cc deleted file mode 100644 index 3db9a7f9f..000000000 --- a/src/xenia/base/platform_android.cc +++ /dev/null @@ -1,65 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#include "xenia/base/platform_android.h" - -#include -#include - -#include "xenia/base/assert.h" - -namespace xe { -namespace platform { -namespace android { - -static bool initialized = false; - -static int32_t api_level_ = __ANDROID_API__; - -static ApiFunctions api_functions_; - -void Initialize(const ANativeActivity* activity) { - if (initialized) { - return; - } - - AConfiguration* configuration = AConfiguration_new(); - AConfiguration_fromAssetManager(configuration, activity->assetManager); - api_level_ = AConfiguration_getSdkVersion(configuration); - AConfiguration_delete(configuration); - - if (api_level_ >= 26) { - // Leaked intentionally as these will be usable anywhere, already loaded - // into the address space as the application is linked against them. - // https://chromium.googlesource.com/chromium/src/+/master/third_party/ashmem/ashmem-dev.c#201 - void* libandroid = dlopen("libandroid.so", RTLD_NOW); - assert_not_null(libandroid); - void* libc = dlopen("libc.so", RTLD_NOW); - assert_not_null(libc); -#define XE_PLATFORM_ANDROID_LOAD_API_FUNCTION(lib, name, api) \ - api_functions_.api_##api.name = \ - reinterpret_cast( \ - dlsym(lib, #name)); \ - assert_not_null(api_functions_.api_##api.name); - XE_PLATFORM_ANDROID_LOAD_API_FUNCTION(libandroid, ASharedMemory_create, 26); - // pthreads are a part of Bionic libc on Android. - XE_PLATFORM_ANDROID_LOAD_API_FUNCTION(libc, pthread_getname_np, 26); -#undef XE_PLATFORM_ANDROID_LOAD_API_FUNCTION - } - - initialized = true; -} - -int32_t api_level() { return api_level_; } - -const ApiFunctions& api_functions() { return api_functions_; } - -} // namespace android -} // namespace platform -} // namespace xe diff --git a/src/xenia/base/platform_android.h b/src/xenia/base/platform_android.h deleted file mode 100644 index 711084578..000000000 --- a/src/xenia/base/platform_android.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - ****************************************************************************** - * Xenia : Xbox 360 Emulator Research Project * - ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * - * Released under the BSD license - see LICENSE in the root for more details. * - ****************************************************************************** - */ - -#ifndef XENIA_BASE_PLATFORM_ANDROID_H_ -#define XENIA_BASE_PLATFORM_ANDROID_H_ - -// NOTE: if you're including this file it means you are explicitly depending -// on Android-specific headers. This is bad for portability and should be -// avoided! - -#include -#include -#include -#include - -namespace xe { -namespace platform { -namespace android { - -// Must be called in onCreate of the first activity. -void Initialize(const ANativeActivity* activity); - -// Returns the device API level - if not initialized, will return the minimum -// level supported by Xenia. -int32_t api_level(); - -// Android API functions added after the minimum supported API version. -struct ApiFunctions { - struct { - // libandroid - int (*ASharedMemory_create)(const char* name, size_t size); - // libc - int (*pthread_getname_np)(pthread_t pthread, char* buf, size_t n); - } api_26; -}; -const ApiFunctions& api_functions(); - -} // namespace android -} // namespace platform -} // namespace xe - -#endif // XENIA_BASE_PLATFORM_ANDROID_H_ diff --git a/src/xenia/base/threading.h b/src/xenia/base/threading.h index 36665d7a4..520c2d531 100644 --- a/src/xenia/base/threading.h +++ b/src/xenia/base/threading.h @@ -25,10 +25,16 @@ #include #include "xenia/base/assert.h" +#include "xenia/base/platform.h" namespace xe { namespace threading { +#if XE_PLATFORM_ANDROID +void AndroidInitialize(); +void AndroidShutdown(); +#endif + // This is more like an Event with self-reset when returning from Wait() class Fence { public: diff --git a/src/xenia/base/threading_posix.cc b/src/xenia/base/threading_posix.cc index 7766df05e..05fc616b7 100644 --- a/src/xenia/base/threading_posix.cc +++ b/src/xenia/base/threading_posix.cc @@ -21,20 +21,51 @@ #include #include #include +#include #include #include #include #if XE_PLATFORM_ANDROID +#include #include -#include "xenia/base/platform_android.h" +#include "xenia/base/main_android.h" #include "xenia/base/string_util.h" #endif namespace xe { namespace threading { +#if XE_PLATFORM_ANDROID +// May be null if no dynamically loaded functions are required. +static void* android_libc_; +// API 26+. +static int (*android_pthread_getname_np_)(pthread_t pthread, char* buf, + size_t n); + +void AndroidInitialize() { + if (xe::GetAndroidApiLevel() >= 26) { + android_libc_ = dlopen("libc.so", RTLD_NOW); + assert_not_null(android_libc_); + if (android_libc_) { + android_pthread_getname_np_ = + reinterpret_cast( + dlsym(android_libc_, "pthread_getname_np")); + assert_not_null(android_pthread_getname_np_); + } + } +} + +void AndroidShutdown() { + android_pthread_getname_np_ = nullptr; + if (android_libc_) { + dlclose(android_libc_); + android_libc_ = nullptr; + } +} +#endif + template inline timespec DurationToTimeSpec( std::chrono::duration<_Rep, _Period> duration) { @@ -577,9 +608,9 @@ class PosixCondition : public PosixConditionBase { // pthread_getname_np was added in API 26 - below that, store the name in // this object, which may be only modified through Xenia threading, but // should be enough in most cases. - if (xe::platform::android::api_level() >= 26) { - if (xe::platform::android::api_functions().api_26.pthread_getname_np( - thread_, result.data(), result.size() - 1) != 0) { + if (android_pthread_getname_np_) { + if (android_pthread_getname_np_(thread_, result.data(), + result.size() - 1) != 0) { assert_always(); } } else { @@ -608,7 +639,7 @@ class PosixCondition : public PosixConditionBase { #if XE_PLATFORM_ANDROID void SetAndroidPreApi26Name(const std::string_view name) { - if (xe::platform::android::api_level() >= 26) { + if (android_pthread_getname_np_) { return; } std::lock_guard lock(android_pre_api_26_name_mutex_); @@ -1145,7 +1176,7 @@ void Thread::Exit(int exit_code) { void set_name(const std::string_view name) { pthread_setname_np(pthread_self(), std::string(name).c_str()); #if XE_PLATFORM_ANDROID - if (xe::platform::android::api_level() < 26 && current_thread_) { + if (!android_pthread_getname_np_ && current_thread_) { current_thread_->condition().SetAndroidPreApi26Name(name); } #endif diff --git a/src/xenia/ui/window_android.cc b/src/xenia/ui/window_android.cc new file mode 100644 index 000000000..cd5aaba80 --- /dev/null +++ b/src/xenia/ui/window_android.cc @@ -0,0 +1,54 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/window_android.h" + +#include +#include + +#include "xenia/base/assert.h" +#include "xenia/ui/windowed_app_context_android.h" + +namespace xe { +namespace ui { + +std::unique_ptr Window::Create(WindowedAppContext& app_context, + const std::string& title) { + // The window is a proxy between the main activity and Xenia, so there can be + // only one for an activity. + AndroidWindowedAppContext& android_app_context = + static_cast(app_context); + AndroidWindow* current_activity_window = + android_app_context.GetActivityWindow(); + assert_null(current_activity_window); + if (current_activity_window) { + return nullptr; + } + auto window = std::make_unique(app_context, title); + android_app_context.SetActivityWindow(window.get()); + return std::move(window); +} + +AndroidWindow::~AndroidWindow() { + AndroidWindowedAppContext& android_app_context = + static_cast(app_context()); + if (android_app_context.GetActivityWindow() == this) { + android_app_context.SetActivityWindow(nullptr); + } +} + +std::unique_ptr MenuItem::Create(Type type, + const std::string& text, + const std::string& hotkey, + std::function callback) { + return std::make_unique(type, text, hotkey, callback); +} + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/window_android.h b/src/xenia/ui/window_android.h new file mode 100644 index 000000000..780c1d77d --- /dev/null +++ b/src/xenia/ui/window_android.h @@ -0,0 +1,64 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_WINDOW_ANDROID_H_ +#define XENIA_UI_WINDOW_ANDROID_H_ + +#include "xenia/ui/menu_item.h" +#include "xenia/ui/window.h" + +namespace xe { +namespace ui { + +class AndroidWindow : public Window { + public: + // Many functions are left unimplemented because the layout is configured from + // XML and Java. + + AndroidWindow(WindowedAppContext& app_context, const std::string& title) + : Window(app_context, title) {} + ~AndroidWindow(); + + NativePlatformHandle native_platform_handle() const override { + return nullptr; + } + // TODO(Triang3l): ANativeWindow for Vulkan surface creation. + NativeWindowHandle native_handle() const override { return nullptr; } + + void EnableMainMenu() override {} + void DisableMainMenu() override {} + + bool SetIcon(const void* buffer, size_t size) override { return false; } + + bool CaptureMouse() override { return false; } + bool ReleaseMouse() override { return false; } + + int get_medium_dpi() const override { return 160; } + + // TODO(Triang3l): Call the close event, which may finish the activity. + void Close() override {} +}; + +// Dummy for the menu item - menus are controlled by the layout. +// TODO(Triang3l): Make something like MenuItem work as the basic common action +// interface for Java buttons. +class AndroidMenuItem final : public MenuItem { + public: + AndroidMenuItem(Type type, const std::string& text, const std::string& hotkey, + std::function callback) + : MenuItem(type, text, hotkey, callback) {} + + void EnableMenuItem(Window& window) override {} + void DisableMenuItem(Window& window) override {} +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_WINDOW_ANDROID_H_ diff --git a/src/xenia/ui/windowed_app_context_android.cc b/src/xenia/ui/windowed_app_context_android.cc index e2dbe1aa5..c1909d02e 100644 --- a/src/xenia/ui/windowed_app_context_android.cc +++ b/src/xenia/ui/windowed_app_context_android.cc @@ -9,9 +9,12 @@ #include "xenia/ui/windowed_app_context_android.h" +#include #include +#include #include "xenia/base/assert.h" +#include "xenia/base/main_android.h" #include "xenia/ui/windowed_app.h" namespace xe { @@ -22,6 +25,8 @@ void AndroidWindowedAppContext::StartAppOnNativeActivityCreate( [[maybe_unused]] size_t saved_state_size, std::unique_ptr (*app_creator)( WindowedAppContext& app_context)) { + // TODO(Triang3l): Pass the launch options from the Intent or the saved + // instance state. AndroidWindowedAppContext* app_context = new AndroidWindowedAppContext(activity); // The pointer is now held by the Activity as its ANativeActivity::instance, @@ -35,6 +40,8 @@ void AndroidWindowedAppContext::StartAppOnNativeActivityCreate( AndroidWindowedAppContext::~AndroidWindowedAppContext() { // TODO(Triang3l): Unregister activity callbacks. activity_->instance = nullptr; + + xe::ShutdownAndroidAppFromMainThread(); } void AndroidWindowedAppContext::NotifyUILoopOfPendingFunctions() { @@ -42,11 +49,21 @@ void AndroidWindowedAppContext::NotifyUILoopOfPendingFunctions() { } void AndroidWindowedAppContext::PlatformQuitFromUIThread() { - ANativeActivity_finish(activity); + ANativeActivity_finish(activity_); } AndroidWindowedAppContext::AndroidWindowedAppContext(ANativeActivity* activity) : activity_(activity) { + int32_t api_level; + { + AConfiguration* configuration = AConfiguration_new(); + AConfiguration_fromAssetManager(configuration, activity->assetManager); + api_level = AConfiguration_getSdkVersion(configuration); + AConfiguration_delete(configuration); + } + + xe::InitializeAndroidAppFromMainThread(api_level); + activity_->instance = this; // TODO(Triang3l): Register activity callbacks. } @@ -54,7 +71,7 @@ AndroidWindowedAppContext::AndroidWindowedAppContext(ANativeActivity* activity) bool AndroidWindowedAppContext::InitializeApp(std::unique_ptr ( *app_creator)(WindowedAppContext& app_context)) { assert_null(app_); - app_ = app_creator(this); + app_ = app_creator(*this); if (!app_->OnInitialize()) { app_->InvokeOnDestroy(); app_.reset(); diff --git a/src/xenia/ui/windowed_app_context_android.h b/src/xenia/ui/windowed_app_context_android.h index 08ddc31bc..cfdc16ed1 100644 --- a/src/xenia/ui/windowed_app_context_android.h +++ b/src/xenia/ui/windowed_app_context_android.h @@ -18,6 +18,7 @@ namespace xe { namespace ui { +class AndroidWindow; class WindowedApp; class AndroidWindowedAppContext final : public WindowedAppContext { @@ -39,6 +40,13 @@ class AndroidWindowedAppContext final : public WindowedAppContext { void PlatformQuitFromUIThread() override; + // The single Window instance that will be receiving window callbacks. + // Multiple windows cannot be created as one activity or fragment can have + // only one layout. This window acts purely as a proxy between the activity + // and the Xenia logic. + AndroidWindow* GetActivityWindow() const { return activity_window_; } + void SetActivityWindow(AndroidWindow* window) { activity_window_ = window; } + private: explicit AndroidWindowedAppContext(ANativeActivity* activity); bool InitializeApp(std::unique_ptr (*app_creator)( @@ -50,6 +58,8 @@ class AndroidWindowedAppContext final : public WindowedAppContext { ANativeActivity* activity_; std::unique_ptr app_; + AndroidWindow* activity_window_ = nullptr; + // TODO(Triang3l): The rest of the context, including quit handler (and the // destructor) calling `finish` on the activity, UI looper notification // posting, etc. diff --git a/src/xenia/ui/windowed_app_main_android.cc b/src/xenia/ui/windowed_app_main_android.cc new file mode 100644 index 000000000..1e2fd6c92 --- /dev/null +++ b/src/xenia/ui/windowed_app_main_android.cc @@ -0,0 +1,10 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +// Not needed - no single entry point, the event loop is polled by the OS. From f91b895c9a3546150b25af1adc1b3148664b9a6c Mon Sep 17 00:00:00 2001 From: Triang3l Date: Mon, 13 Sep 2021 23:13:02 +0300 Subject: [PATCH 5/5] [Base] Don't use raw clock where unsupported --- src/xenia/base/clock.cc | 10 ++++++---- src/xenia/base/clock.h | 9 +++++++++ src/xenia/base/clock_x64.cc | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/xenia/base/clock.cc b/src/xenia/base/clock.cc index e5c237101..058eae43a 100644 --- a/src/xenia/base/clock.cc +++ b/src/xenia/base/clock.cc @@ -109,18 +109,20 @@ inline uint64_t QueryGuestSystemTimeOffset() { } uint64_t Clock::QueryHostTickFrequency() { +#if XE_CLOCK_RAW_AVAILABLE if (cvars::clock_source_raw) { return host_tick_frequency_raw(); - } else { - return host_tick_frequency_platform(); } +#endif + return host_tick_frequency_platform(); } uint64_t Clock::QueryHostTickCount() { +#if XE_CLOCK_RAW_AVAILABLE if (cvars::clock_source_raw) { return host_tick_count_raw(); - } else { - return host_tick_count_platform(); } +#endif + return host_tick_count_platform(); } double Clock::guest_time_scalar() { return guest_time_scalar_; } diff --git a/src/xenia/base/clock.h b/src/xenia/base/clock.h index 9ab3728c4..eeee36fb8 100644 --- a/src/xenia/base/clock.h +++ b/src/xenia/base/clock.h @@ -13,6 +13,11 @@ #include #include "xenia/base/cvar.h" +#include "xenia/base/platform.h" + +#if XE_ARCH_AMD64 +#define XE_CLOCK_RAW_AVAILABLE 1 +#endif DECLARE_bool(clock_no_scaling); DECLARE_bool(clock_source_raw); @@ -24,10 +29,14 @@ class Clock { // Host ticks-per-second. Generally QueryHostTickFrequency should be used. // Either from platform suplied time source or from hardware directly. static uint64_t host_tick_frequency_platform(); +#if XE_CLOCK_RAW_AVAILABLE static uint64_t host_tick_frequency_raw(); +#endif // Host tick count. Generally QueryHostTickCount() should be used. static uint64_t host_tick_count_platform(); +#if XE_CLOCK_RAW_AVAILABLE static uint64_t host_tick_count_raw(); +#endif // Queries the host tick frequency. static uint64_t QueryHostTickFrequency(); diff --git a/src/xenia/base/clock_x64.cc b/src/xenia/base/clock_x64.cc index 3a573f489..e84d5baf2 100644 --- a/src/xenia/base/clock_x64.cc +++ b/src/xenia/base/clock_x64.cc @@ -7,11 +7,11 @@ ****************************************************************************** */ +#include "xenia/base/clock.h" #include "xenia/base/platform.h" -#if XE_ARCH_AMD64 +#if XE_ARCH_AMD64 && XE_CLOCK_RAW_AVAILABLE -#include "xenia/base/clock.h" #include "xenia/base/logging.h" // Wrap all these different cpu compiler intrinsics.