Merge branch 'master' into vulkan
This commit is contained in:
commit
cf85bf2efd
|
@ -109,18 +109,20 @@ inline uint64_t QueryGuestSystemTimeOffset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t Clock::QueryHostTickFrequency() {
|
uint64_t Clock::QueryHostTickFrequency() {
|
||||||
|
#if XE_CLOCK_RAW_AVAILABLE
|
||||||
if (cvars::clock_source_raw) {
|
if (cvars::clock_source_raw) {
|
||||||
return host_tick_frequency_raw();
|
return host_tick_frequency_raw();
|
||||||
} else {
|
|
||||||
return host_tick_frequency_platform();
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
return host_tick_frequency_platform();
|
||||||
}
|
}
|
||||||
uint64_t Clock::QueryHostTickCount() {
|
uint64_t Clock::QueryHostTickCount() {
|
||||||
|
#if XE_CLOCK_RAW_AVAILABLE
|
||||||
if (cvars::clock_source_raw) {
|
if (cvars::clock_source_raw) {
|
||||||
return host_tick_count_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_; }
|
double Clock::guest_time_scalar() { return guest_time_scalar_; }
|
||||||
|
|
|
@ -13,6 +13,11 @@
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "xenia/base/cvar.h"
|
#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_no_scaling);
|
||||||
DECLARE_bool(clock_source_raw);
|
DECLARE_bool(clock_source_raw);
|
||||||
|
@ -24,10 +29,14 @@ class Clock {
|
||||||
// Host ticks-per-second. Generally QueryHostTickFrequency should be used.
|
// Host ticks-per-second. Generally QueryHostTickFrequency should be used.
|
||||||
// Either from platform suplied time source or from hardware directly.
|
// Either from platform suplied time source or from hardware directly.
|
||||||
static uint64_t host_tick_frequency_platform();
|
static uint64_t host_tick_frequency_platform();
|
||||||
|
#if XE_CLOCK_RAW_AVAILABLE
|
||||||
static uint64_t host_tick_frequency_raw();
|
static uint64_t host_tick_frequency_raw();
|
||||||
|
#endif
|
||||||
// Host tick count. Generally QueryHostTickCount() should be used.
|
// Host tick count. Generally QueryHostTickCount() should be used.
|
||||||
static uint64_t host_tick_count_platform();
|
static uint64_t host_tick_count_platform();
|
||||||
|
#if XE_CLOCK_RAW_AVAILABLE
|
||||||
static uint64_t host_tick_count_raw();
|
static uint64_t host_tick_count_raw();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Queries the host tick frequency.
|
// Queries the host tick frequency.
|
||||||
static uint64_t QueryHostTickFrequency();
|
static uint64_t QueryHostTickFrequency();
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "xenia/base/clock.h"
|
||||||
#include "xenia/base/platform.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"
|
#include "xenia/base/logging.h"
|
||||||
|
|
||||||
// Wrap all these different cpu compiler intrinsics.
|
// Wrap all these different cpu compiler intrinsics.
|
||||||
|
|
|
@ -7,4 +7,33 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/base/console_app_posix.cc"
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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<std::string> args;
|
||||||
|
for (int n = 0; n < argc; n++) {
|
||||||
|
args.emplace_back(argv[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = entry_info.entry_point(args);
|
||||||
|
|
||||||
|
xe::ShutdownAndroidAppFromMainThread();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -2,14 +2,16 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstring>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -17,6 +19,7 @@
|
||||||
#include "third_party/disruptorplus/include/disruptorplus/ring_buffer.hpp"
|
#include "third_party/disruptorplus/include/disruptorplus/ring_buffer.hpp"
|
||||||
#include "third_party/disruptorplus/include/disruptorplus/sequence_barrier.hpp"
|
#include "third_party/disruptorplus/include/disruptorplus/sequence_barrier.hpp"
|
||||||
#include "third_party/disruptorplus/include/disruptorplus/spin_wait_strategy.hpp"
|
#include "third_party/disruptorplus/include/disruptorplus/spin_wait_strategy.hpp"
|
||||||
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/atomic.h"
|
#include "xenia/base/atomic.h"
|
||||||
#include "xenia/base/console.h"
|
#include "xenia/base/console.h"
|
||||||
#include "xenia/base/cvar.h"
|
#include "xenia/base/cvar.h"
|
||||||
|
@ -24,22 +27,30 @@
|
||||||
#include "xenia/base/filesystem.h"
|
#include "xenia/base/filesystem.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/memory.h"
|
#include "xenia/base/memory.h"
|
||||||
|
#include "xenia/base/platform.h"
|
||||||
#include "xenia/base/ring_buffer.h"
|
#include "xenia/base/ring_buffer.h"
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
#include "xenia/base/system.h"
|
#include "xenia/base/system.h"
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
|
|
||||||
// For MessageBox:
|
|
||||||
// TODO(benvanik): generic API? logging_win.cc?
|
// TODO(benvanik): generic API? logging_win.cc?
|
||||||
#if XE_PLATFORM_WIN32
|
#if XE_PLATFORM_ANDROID
|
||||||
|
#include <android/log.h>
|
||||||
|
#elif XE_PLATFORM_WIN32
|
||||||
|
// For MessageBox:
|
||||||
#include "xenia/base/platform_win.h"
|
#include "xenia/base/platform_win.h"
|
||||||
#endif // XE_PLATFORM_WIN32
|
#endif // XE_PLATFORM
|
||||||
|
|
||||||
#include "third_party/fmt/include/fmt/format.h"
|
#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_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_stdout, true, "Write log output to stdout", "Logging");
|
||||||
DEFINE_bool(log_to_debugprint, false, "Dump the log to DebugPrint.", "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.",
|
DEFINE_bool(flush_log, true, "Flush log file after each log line batch.",
|
||||||
"Logging");
|
"Logging");
|
||||||
DEFINE_int32(
|
DEFINE_int32(
|
||||||
|
@ -65,6 +76,15 @@ struct LogLine {
|
||||||
|
|
||||||
thread_local char thread_log_buffer_[64 * 1024];
|
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) {
|
void FileLogSink::Write(const char* buf, size_t size) {
|
||||||
if (file_) {
|
if (file_) {
|
||||||
fwrite(buf, 1, size, 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<const char*>(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 {
|
class Logger {
|
||||||
public:
|
public:
|
||||||
explicit Logger(const std::string_view app_name)
|
explicit Logger(const std::string_view app_name)
|
||||||
|
@ -130,9 +264,6 @@ class Logger {
|
||||||
for (const auto& sink : sinks_) {
|
for (const auto& sink : sinks_) {
|
||||||
sink->Write(buf, size);
|
sink->Write(buf, size);
|
||||||
}
|
}
|
||||||
if (cvars::log_to_debugprint) {
|
|
||||||
debugging::DebugPrint("{}", std::string_view(buf, size));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteThread() {
|
void WriteThread() {
|
||||||
|
@ -294,8 +425,14 @@ void InitializeLogging(const std::string_view app_name) {
|
||||||
auto mem = memory::AlignedAlloc<Logger>(0x10);
|
auto mem = memory::AlignedAlloc<Logger>(0x10);
|
||||||
logger_ = new (mem) Logger(app_name);
|
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<AndroidLogSink>(app_name));
|
||||||
|
}
|
||||||
|
#else
|
||||||
FILE* log_file = nullptr;
|
FILE* log_file = nullptr;
|
||||||
|
|
||||||
if (cvars::log_file.empty()) {
|
if (cvars::log_file.empty()) {
|
||||||
// Default to app name.
|
// Default to app name.
|
||||||
auto file_name = fmt::format("{}.log", 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);
|
xe::filesystem::CreateParentFolder(cvars::log_file);
|
||||||
log_file = xe::filesystem::OpenFile(cvars::log_file, "wt");
|
log_file = xe::filesystem::OpenFile(cvars::log_file, "wt");
|
||||||
}
|
}
|
||||||
auto sink = std::make_unique<FileLogSink>(log_file);
|
logger_->AddLogSink(std::make_unique<FileLogSink>(log_file, true));
|
||||||
logger_->AddLogSink(std::move(sink));
|
|
||||||
|
|
||||||
if (cvars::log_to_stdout) {
|
if (cvars::log_to_stdout) {
|
||||||
auto stdout_sink = std::make_unique<FileLogSink>(stdout);
|
logger_->AddLogSink(std::make_unique<FileLogSink>(stdout, false));
|
||||||
logger_->AddLogSink(std::move(stdout_sink));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cvars::log_to_debugprint) {
|
||||||
|
logger_->AddLogSink(std::make_unique<DebugPrintLogSink>());
|
||||||
|
}
|
||||||
|
#endif // XE_PLATFORM_ANDROID
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShutdownLogging() {
|
void ShutdownLogging() {
|
||||||
|
@ -352,7 +492,7 @@ void logging::AppendLogLine(LogLevel log_level, const char prefix_char,
|
||||||
}
|
}
|
||||||
|
|
||||||
void FatalError(const std::string_view str) {
|
void FatalError(const std::string_view str) {
|
||||||
logging::AppendLogLine(LogLevel::Error, 'X', str);
|
logging::AppendLogLine(LogLevel::Error, 'x', str);
|
||||||
|
|
||||||
if (!xe::has_console_attached()) {
|
if (!xe::has_console_attached()) {
|
||||||
ShowSimpleMessageBox(SimpleMessageBoxType::Error, str);
|
ShowSimpleMessageBox(SimpleMessageBoxType::Error, str);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -44,19 +44,24 @@ class LogSink {
|
||||||
|
|
||||||
class FileLogSink final : public LogSink {
|
class FileLogSink final : public LogSink {
|
||||||
public:
|
public:
|
||||||
explicit FileLogSink(FILE* file) : file_(file) {}
|
explicit FileLogSink(FILE* file, bool own_file)
|
||||||
virtual ~FileLogSink() {
|
: file_(file), owns_file_(own_file) {}
|
||||||
if (file_) {
|
~FileLogSink();
|
||||||
fflush(file_);
|
|
||||||
fclose(file_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Write(const char* buf, size_t size) override;
|
void Write(const char* buf, size_t size) override;
|
||||||
void Flush() override;
|
void Flush() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FILE* file_;
|
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.
|
// Initializes the logging system and any outputs requested.
|
||||||
|
|
|
@ -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 <cstddef>
|
||||||
|
|
||||||
|
#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
|
|
@ -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 <cstdint>
|
||||||
|
|
||||||
|
#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_
|
|
@ -24,6 +24,11 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace memory {
|
namespace memory {
|
||||||
|
|
||||||
|
#if XE_PLATFORM_ANDROID
|
||||||
|
void AndroidInitialize();
|
||||||
|
void AndroidShutdown();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Returns the native page size of the system, in bytes.
|
// Returns the native page size of the system, in bytes.
|
||||||
// This should be ~4KiB.
|
// This should be ~4KiB.
|
||||||
size_t page_size();
|
size_t page_size();
|
||||||
|
|
|
@ -12,22 +12,52 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
#include "xenia/base/string.h"
|
#include "xenia/base/string.h"
|
||||||
|
|
||||||
#if XE_PLATFORM_ANDROID
|
#if XE_PLATFORM_ANDROID
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <linux/ashmem.h>
|
#include <linux/ashmem.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#include "xenia/base/platform_android.h"
|
#include "xenia/base/main_android.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace memory {
|
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<decltype(android_ASharedMemory_create_)>(
|
||||||
|
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 page_size() { return getpagesize(); }
|
||||||
size_t allocation_granularity() { return page_size(); }
|
size_t allocation_granularity() { return page_size(); }
|
||||||
|
|
||||||
|
@ -86,11 +116,9 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
|
||||||
size_t length, PageAccess access,
|
size_t length, PageAccess access,
|
||||||
bool commit) {
|
bool commit) {
|
||||||
#if XE_PLATFORM_ANDROID
|
#if XE_PLATFORM_ANDROID
|
||||||
if (xe::platform::android::api_level() >= 26) {
|
|
||||||
// TODO(Triang3l): Check if memfd can be used instead on API 30+.
|
// TODO(Triang3l): Check if memfd can be used instead on API 30+.
|
||||||
int sharedmem_fd =
|
if (android_ASharedMemory_create_) {
|
||||||
xe::platform::android::api_functions().api_26.ASharedMemory_create(
|
int sharedmem_fd = android_ASharedMemory_create_(path.c_str(), length);
|
||||||
path.c_str(), length);
|
|
||||||
return sharedmem_fd >= 0 ? sharedmem_fd : kFileMappingHandleInvalid;
|
return sharedmem_fd >= 0 ? sharedmem_fd : kFileMappingHandleInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 <android/configuration.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
|
|
||||||
#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<decltype(api_functions_.api_##api.name)>( \
|
|
||||||
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
|
|
|
@ -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 <android/native_activity.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
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_
|
|
|
@ -25,10 +25,16 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
|
#include "xenia/base/platform.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace threading {
|
namespace threading {
|
||||||
|
|
||||||
|
#if XE_PLATFORM_ANDROID
|
||||||
|
void AndroidInitialize();
|
||||||
|
void AndroidShutdown();
|
||||||
|
#endif
|
||||||
|
|
||||||
// This is more like an Event with self-reset when returning from Wait()
|
// This is more like an Event with self-reset when returning from Wait()
|
||||||
class Fence {
|
class Fence {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -21,20 +21,51 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#if XE_PLATFORM_ANDROID
|
#if XE_PLATFORM_ANDROID
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
|
||||||
#include "xenia/base/platform_android.h"
|
#include "xenia/base/main_android.h"
|
||||||
#include "xenia/base/string_util.h"
|
#include "xenia/base/string_util.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace threading {
|
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<decltype(android_pthread_getname_np_)>(
|
||||||
|
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 <typename _Rep, typename _Period>
|
template <typename _Rep, typename _Period>
|
||||||
inline timespec DurationToTimeSpec(
|
inline timespec DurationToTimeSpec(
|
||||||
std::chrono::duration<_Rep, _Period> duration) {
|
std::chrono::duration<_Rep, _Period> duration) {
|
||||||
|
@ -577,9 +608,9 @@ class PosixCondition<Thread> : public PosixConditionBase {
|
||||||
// pthread_getname_np was added in API 26 - below that, store the name in
|
// 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
|
// this object, which may be only modified through Xenia threading, but
|
||||||
// should be enough in most cases.
|
// should be enough in most cases.
|
||||||
if (xe::platform::android::api_level() >= 26) {
|
if (android_pthread_getname_np_) {
|
||||||
if (xe::platform::android::api_functions().api_26.pthread_getname_np(
|
if (android_pthread_getname_np_(thread_, result.data(),
|
||||||
thread_, result.data(), result.size() - 1) != 0) {
|
result.size() - 1) != 0) {
|
||||||
assert_always();
|
assert_always();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -608,7 +639,7 @@ class PosixCondition<Thread> : public PosixConditionBase {
|
||||||
|
|
||||||
#if XE_PLATFORM_ANDROID
|
#if XE_PLATFORM_ANDROID
|
||||||
void SetAndroidPreApi26Name(const std::string_view name) {
|
void SetAndroidPreApi26Name(const std::string_view name) {
|
||||||
if (xe::platform::android::api_level() >= 26) {
|
if (android_pthread_getname_np_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::lock_guard<std::mutex> lock(android_pre_api_26_name_mutex_);
|
std::lock_guard<std::mutex> 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) {
|
void set_name(const std::string_view name) {
|
||||||
pthread_setname_np(pthread_self(), std::string(name).c_str());
|
pthread_setname_np(pthread_self(), std::string(name).c_str());
|
||||||
#if XE_PLATFORM_ANDROID
|
#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);
|
current_thread_->condition().SetAndroidPreApi26Name(name);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -156,7 +156,7 @@ void* D3D12PrimitiveProcessor::RequestHostConvertedIndexBufferForCurrentFrame(
|
||||||
(coalign_for_simd ? XE_GPU_PRIMITIVE_PROCESSOR_SIMD_SIZE : 0),
|
(coalign_for_simd ? XE_GPU_PRIMITIVE_PROCESSOR_SIMD_SIZE : 0),
|
||||||
index_size, nullptr, nullptr, &gpu_address);
|
index_size, nullptr, nullptr, &gpu_address);
|
||||||
if (!mapping) {
|
if (!mapping) {
|
||||||
return false;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (coalign_for_simd) {
|
if (coalign_for_simd) {
|
||||||
ptrdiff_t coalignment_offset =
|
ptrdiff_t coalignment_offset =
|
||||||
|
|
|
@ -70,12 +70,11 @@ class PipelineCache {
|
||||||
|
|
||||||
// Retrieves the shader modification for the current state. The shader must
|
// Retrieves the shader modification for the current state. The shader must
|
||||||
// have microcode analyzed.
|
// have microcode analyzed.
|
||||||
DxbcShaderTranslator::Modification
|
DxbcShaderTranslator::Modification GetCurrentVertexShaderModification(
|
||||||
PipelineCache::GetCurrentVertexShaderModification(
|
|
||||||
const Shader& shader,
|
const Shader& shader,
|
||||||
Shader::HostVertexShaderType host_vertex_shader_type) const;
|
Shader::HostVertexShaderType host_vertex_shader_type) const;
|
||||||
DxbcShaderTranslator::Modification
|
DxbcShaderTranslator::Modification GetCurrentPixelShaderModification(
|
||||||
PipelineCache::GetCurrentPixelShaderModification(const Shader& shader) const;
|
const Shader& shader) const;
|
||||||
|
|
||||||
// If draw_util::IsRasterizationPotentiallyDone is false, the pixel shader
|
// If draw_util::IsRasterizationPotentiallyDone is false, the pixel shader
|
||||||
// MUST be made nullptr BEFORE calling this!
|
// MUST be made nullptr BEFORE calling this!
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -19,13 +19,6 @@
|
||||||
#include "xenia/gpu/xenos.h"
|
#include "xenia/gpu/xenos.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace ui {
|
|
||||||
class Loop;
|
|
||||||
class Window;
|
|
||||||
} // namespace ui
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
|
|
|
@ -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 <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "xenia/base/assert.h"
|
||||||
|
#include "xenia/ui/windowed_app_context_android.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
std::unique_ptr<Window> 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<AndroidWindowedAppContext&>(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<AndroidWindow>(app_context, title);
|
||||||
|
android_app_context.SetActivityWindow(window.get());
|
||||||
|
return std::move(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidWindow::~AndroidWindow() {
|
||||||
|
AndroidWindowedAppContext& android_app_context =
|
||||||
|
static_cast<AndroidWindowedAppContext&>(app_context());
|
||||||
|
if (android_app_context.GetActivityWindow() == this) {
|
||||||
|
android_app_context.SetActivityWindow(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<ui::MenuItem> MenuItem::Create(Type type,
|
||||||
|
const std::string& text,
|
||||||
|
const std::string& hotkey,
|
||||||
|
std::function<void()> callback) {
|
||||||
|
return std::make_unique<AndroidMenuItem>(type, text, hotkey, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace xe
|
|
@ -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<void()> 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_
|
|
@ -9,9 +9,12 @@
|
||||||
|
|
||||||
#include "xenia/ui/windowed_app_context_android.h"
|
#include "xenia/ui/windowed_app_context_android.h"
|
||||||
|
|
||||||
|
#include <android/configuration.h>
|
||||||
#include <android/native_activity.h>
|
#include <android/native_activity.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
|
#include "xenia/base/main_android.h"
|
||||||
#include "xenia/ui/windowed_app.h"
|
#include "xenia/ui/windowed_app.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -22,6 +25,8 @@ void AndroidWindowedAppContext::StartAppOnNativeActivityCreate(
|
||||||
[[maybe_unused]] size_t saved_state_size,
|
[[maybe_unused]] size_t saved_state_size,
|
||||||
std::unique_ptr<WindowedApp> (*app_creator)(
|
std::unique_ptr<WindowedApp> (*app_creator)(
|
||||||
WindowedAppContext& app_context)) {
|
WindowedAppContext& app_context)) {
|
||||||
|
// TODO(Triang3l): Pass the launch options from the Intent or the saved
|
||||||
|
// instance state.
|
||||||
AndroidWindowedAppContext* app_context =
|
AndroidWindowedAppContext* app_context =
|
||||||
new AndroidWindowedAppContext(activity);
|
new AndroidWindowedAppContext(activity);
|
||||||
// The pointer is now held by the Activity as its ANativeActivity::instance,
|
// The pointer is now held by the Activity as its ANativeActivity::instance,
|
||||||
|
@ -35,6 +40,8 @@ void AndroidWindowedAppContext::StartAppOnNativeActivityCreate(
|
||||||
AndroidWindowedAppContext::~AndroidWindowedAppContext() {
|
AndroidWindowedAppContext::~AndroidWindowedAppContext() {
|
||||||
// TODO(Triang3l): Unregister activity callbacks.
|
// TODO(Triang3l): Unregister activity callbacks.
|
||||||
activity_->instance = nullptr;
|
activity_->instance = nullptr;
|
||||||
|
|
||||||
|
xe::ShutdownAndroidAppFromMainThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidWindowedAppContext::NotifyUILoopOfPendingFunctions() {
|
void AndroidWindowedAppContext::NotifyUILoopOfPendingFunctions() {
|
||||||
|
@ -42,11 +49,21 @@ void AndroidWindowedAppContext::NotifyUILoopOfPendingFunctions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidWindowedAppContext::PlatformQuitFromUIThread() {
|
void AndroidWindowedAppContext::PlatformQuitFromUIThread() {
|
||||||
ANativeActivity_finish(activity);
|
ANativeActivity_finish(activity_);
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidWindowedAppContext::AndroidWindowedAppContext(ANativeActivity* activity)
|
AndroidWindowedAppContext::AndroidWindowedAppContext(ANativeActivity* activity)
|
||||||
: activity_(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;
|
activity_->instance = this;
|
||||||
// TODO(Triang3l): Register activity callbacks.
|
// TODO(Triang3l): Register activity callbacks.
|
||||||
}
|
}
|
||||||
|
@ -54,7 +71,7 @@ AndroidWindowedAppContext::AndroidWindowedAppContext(ANativeActivity* activity)
|
||||||
bool AndroidWindowedAppContext::InitializeApp(std::unique_ptr<WindowedApp> (
|
bool AndroidWindowedAppContext::InitializeApp(std::unique_ptr<WindowedApp> (
|
||||||
*app_creator)(WindowedAppContext& app_context)) {
|
*app_creator)(WindowedAppContext& app_context)) {
|
||||||
assert_null(app_);
|
assert_null(app_);
|
||||||
app_ = app_creator(this);
|
app_ = app_creator(*this);
|
||||||
if (!app_->OnInitialize()) {
|
if (!app_->OnInitialize()) {
|
||||||
app_->InvokeOnDestroy();
|
app_->InvokeOnDestroy();
|
||||||
app_.reset();
|
app_.reset();
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
|
class AndroidWindow;
|
||||||
class WindowedApp;
|
class WindowedApp;
|
||||||
|
|
||||||
class AndroidWindowedAppContext final : public WindowedAppContext {
|
class AndroidWindowedAppContext final : public WindowedAppContext {
|
||||||
|
@ -39,6 +40,13 @@ class AndroidWindowedAppContext final : public WindowedAppContext {
|
||||||
|
|
||||||
void PlatformQuitFromUIThread() override;
|
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:
|
private:
|
||||||
explicit AndroidWindowedAppContext(ANativeActivity* activity);
|
explicit AndroidWindowedAppContext(ANativeActivity* activity);
|
||||||
bool InitializeApp(std::unique_ptr<WindowedApp> (*app_creator)(
|
bool InitializeApp(std::unique_ptr<WindowedApp> (*app_creator)(
|
||||||
|
@ -50,6 +58,8 @@ class AndroidWindowedAppContext final : public WindowedAppContext {
|
||||||
ANativeActivity* activity_;
|
ANativeActivity* activity_;
|
||||||
std::unique_ptr<WindowedApp> app_;
|
std::unique_ptr<WindowedApp> app_;
|
||||||
|
|
||||||
|
AndroidWindow* activity_window_ = nullptr;
|
||||||
|
|
||||||
// TODO(Triang3l): The rest of the context, including quit handler (and the
|
// TODO(Triang3l): The rest of the context, including quit handler (and the
|
||||||
// destructor) calling `finish` on the activity, UI looper notification
|
// destructor) calling `finish` on the activity, UI looper notification
|
||||||
// posting, etc.
|
// posting, etc.
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue