Logging to with a ringbuffer. Much faster.

This commit is contained in:
Ben Vanik 2015-08-29 18:06:30 -07:00
parent 8dd59d07ac
commit b7203c2989
25 changed files with 387 additions and 312 deletions

1
.gitignore vendored
View File

@ -63,6 +63,7 @@ npm-debug.log
private/
*.trace
imgui.ini
*.log
# ==============================================================================
# Build system output

View File

@ -77,6 +77,8 @@ Have some spare time, know advanced C++, and want to write an emulator?
Contribute! There's a ton of work that needs to be done, a lot of which
is wide open greenfield fun.
For general rules and guidelines please see [CONTRIBUTING.md](CONTRIBUTING.md).
Fixes and optimizations are always welcome (please!), but in addition to
that there are some major work areas still untouched:

View File

@ -25,8 +25,8 @@ and set the 'Command' to `$(SolutionDir)$(TargetPath)` and the
'Working Directory' to `$(SolutionDir)..\..`. You can specify flags and
the file to run in the 'Command Arguments' field (or use `--flagfile=flags.txt`).
To redirect output, use the following command flags:
`--flagfile=$(SolutionDir)scratch\flags.txt 2>&1 1>$(SolutionDir)scratch\stdout.txt`
By default logs are written to a file with the name of the executable. You can
override this with `--log_file=log.txt`.
### Linux

View File

@ -44,8 +44,8 @@ std::unique_ptr<EmulatorWindow> EmulatorWindow::Create(Emulator* emulator) {
xe::Profiler::ThreadEnter("Win32 Loop");
if (!emulator_window->Initialize()) {
XEFATAL("Failed to initialize main window");
exit(1);
xe::FatalError("Failed to initialize main window");
return;
}
});

View File

@ -20,7 +20,7 @@
extern "C" {
#include "libavutil/log.h"
}
} // extern "C"
// As with normal Microsoft, there are like twelve different ways to access
// the audio APIs. Early games use XMA*() methods almost exclusively to touch
@ -55,9 +55,7 @@ XmaDecoder::XmaDecoder(cpu::Processor* processor)
XmaDecoder::~XmaDecoder() = default;
void av_log_callback(void* avcl, int level, const char* fmt, va_list va) {
StringBuffer buff;
buff.AppendVarargs(fmt, va);
xe::log_line('i', "libav: %s", buff.GetString());
xe::LogLineVarargs('A', fmt, va);
}
X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {

View File

@ -34,12 +34,12 @@ std::string CanonicalizePath(const std::string& original_path) {
pos_n = std::string::npos;
break;
case 1:
// Duplicate separators
// Duplicate separators.
path.erase(pos, 1);
pos_n -= 1;
break;
case 2:
// Potential marker for current directory
// Potential marker for current directory.
if (path[pos + 1] == '.') {
path.erase(pos, 2);
pos_n -= 2;
@ -48,10 +48,10 @@ std::string CanonicalizePath(const std::string& original_path) {
}
break;
case 3:
// Potential marker for parent directory
// Potential marker for parent directory.
if (path[pos + 1] == '.' && path[pos + 2] == '.') {
if (path_breaks.empty()) {
// Ensure we don't override the device name
// Ensure we don't override the device name.
std::string::size_type loc(path.find_first_of(':'));
auto req(pos + 3);
if (loc == std::string::npos || loc > req) {
@ -66,7 +66,7 @@ std::string CanonicalizePath(const std::string& original_path) {
auto last_diff((pos + 3) - last);
path.erase(last, last_diff);
pos_n = last;
// Also remove path reference
// Also remove path reference.
path_breaks.erase(path_breaks.end() - 1);
}
} else {
@ -82,12 +82,12 @@ std::string CanonicalizePath(const std::string& original_path) {
pos = pos_n;
}
// Remove trailing seperator
// Remove trailing seperator.
if (!path.empty() && path.back() == path_sep) {
path.erase(path.size() - 1);
}
// Final sanity check for dead paths
// Final sanity check for dead paths.
if ((path.size() == 1 && (path[0] == '.' || path[0] == path_sep)) ||
(path.size() == 2 && path[0] == '.' && path[1] == '.')) {
return "";
@ -96,6 +96,16 @@ std::string CanonicalizePath(const std::string& original_path) {
return path;
}
bool CreateParentFolder(const std::wstring& path) {
auto fixed_path = xe::fix_path_separators(path, '/');
auto base_path = xe::find_base_path(fixed_path, '/');
if (!PathExists(base_path)) {
return CreateFolder(base_path);
} else {
return true;
}
}
WildcardFlags WildcardFlags::FIRST(true, false);
WildcardFlags WildcardFlags::LAST(false, true);

View File

@ -24,6 +24,7 @@ std::string CanonicalizePath(const std::string& original_path);
bool PathExists(const std::wstring& path);
bool CreateParentFolder(const std::wstring& path);
bool CreateFolder(const std::wstring& path);
bool DeleteFolder(const std::wstring& path);
bool IsFolder(const std::wstring& path);

View File

@ -11,12 +11,15 @@
#include <gflags/gflags.h>
#include <atomic>
#include <cstdarg>
#include <mutex>
#include <vector>
#include "xenia/base/filesystem.h"
#include "xenia/base/main.h"
#include "xenia/base/math.h"
#include "xenia/base/ring_buffer.h"
#include "xenia/base/threading.h"
// For MessageBox:
@ -25,91 +28,150 @@
#include "xenia/base/platform_win.h"
#endif // XE_PLATFORM_WIN32
DEFINE_bool(fast_stdout, false,
"Don't lock around stdout/stderr. May introduce weirdness.");
DEFINE_bool(flush_stdout, true, "Flush stdout after each log line.");
DEFINE_string(log_file, "",
"Logs are written to the given file instead of the default.");
DEFINE_bool(flush_log, true, "Flush log file after each log line batch.");
namespace xe {
std::mutex log_lock;
thread_local std::vector<char> log_format_buffer_(64 * 1024);
thread_local std::vector<char> log_buffer(16 * 1024);
class Logger {
public:
Logger() : ring_buffer_(buffer_, kBufferSize), running_(true) {
flush_event_ = xe::threading::Event::CreateAutoResetEvent(false);
write_thread_ =
xe::threading::Thread::Create({}, [this]() { WriteThread(); });
write_thread_->set_name("xe::FileLogSink Writer");
}
void format_log_line(char* buffer, size_t buffer_capacity,
const char level_char, const char* fmt, va_list args) {
char* buffer_ptr;
buffer_ptr = buffer;
*(buffer_ptr++) = level_char;
*(buffer_ptr++) = '>';
*(buffer_ptr++) = ' ';
buffer_ptr +=
std::snprintf(buffer_ptr, buffer_capacity - (buffer_ptr - buffer), "%.4X",
xe::threading::current_thread_id());
*(buffer_ptr++) = ' ';
~Logger() {
running_ = false;
flush_event_->Set();
xe::threading::Wait(write_thread_.get(), true);
fflush(file_);
fclose(file_);
}
// Scribble args into the print buffer.
size_t remaining_capacity = buffer_capacity - (buffer_ptr - buffer) - 3;
size_t chars_written = vsnprintf(buffer_ptr, remaining_capacity, fmt, args);
if (chars_written >= remaining_capacity) {
buffer_ptr += remaining_capacity - 1;
void Initialize(const std::wstring& app_name) {
if (!FLAGS_log_file.empty()) {
auto file_path = xe::to_wstring(FLAGS_log_file.c_str());
xe::filesystem::CreateParentFolder(file_path);
file_ = xe::filesystem::OpenFile(file_path, "wt");
} else {
buffer_ptr += chars_written;
}
// Add a trailing newline.
if (buffer_ptr[-1] != '\n') {
buffer_ptr[0] = '\n';
buffer_ptr[1] = 0;
auto file_path = app_name + L".log";
file_ = xe::filesystem::OpenFile(file_path, "wt");
}
}
void log_line(const char level_char, const char* fmt, ...) {
// SCOPE_profile_cpu_i("emu", "log_line");
void AppendLine(uint32_t thread_id, const char level_char, const char* buffer,
size_t buffer_length) {
LogLine line;
line.thread_id = thread_id;
line.level_char = level_char;
line.buffer_length = buffer_length;
while (true) {
mutex_.lock();
if (ring_buffer_.write_count() < sizeof(line) + buffer_length) {
// Buffer is full. Stall.
mutex_.unlock();
xe::threading::MaybeYield();
continue;
}
ring_buffer_.Write(&line, sizeof(LogLine));
ring_buffer_.Write(buffer, buffer_length);
mutex_.unlock();
break;
}
flush_event_->Set();
}
private:
static const size_t kBufferSize = 32 * 1024 * 1024;
struct LogLine {
uint32_t thread_id;
char level_char;
size_t buffer_length;
};
void WriteThread() {
while (running_) {
mutex_.lock();
bool did_write = false;
while (!ring_buffer_.empty()) {
did_write = true;
LogLine line;
ring_buffer_.Read(&line, sizeof(line));
ring_buffer_.Read(log_format_buffer_.data(), line.buffer_length);
const char prefix[3] = {line.level_char, '>', ' '};
fwrite(prefix, 1, sizeof(prefix), file_);
fwrite(log_format_buffer_.data(), 1, line.buffer_length, file_);
if (log_format_buffer_[line.buffer_length - 1] != '\n') {
const char suffix[1] = {'\n'};
fwrite(suffix, 1, sizeof(suffix), file_);
}
}
mutex_.unlock();
if (did_write) {
if (FLAGS_flush_log) {
fflush(file_);
}
}
xe::threading::Wait(flush_event_.get(), true);
}
}
FILE* file_ = nullptr;
uint8_t buffer_[kBufferSize];
RingBuffer ring_buffer_;
std::mutex mutex_;
std::atomic<bool> running_;
std::unique_ptr<xe::threading::Event> flush_event_;
std::unique_ptr<xe::threading::Thread> write_thread_;
};
Logger logger_;
void InitializeLogging(const std::wstring& app_name) {
logger_.Initialize(app_name);
}
void LogLineFormat(const char level_char, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
format_log_line(log_buffer.data(), log_buffer.capacity(), level_char, fmt,
args);
size_t chars_written = vsnprintf(log_format_buffer_.data(),
log_format_buffer_.capacity(), fmt, args);
va_end(args);
if (!FLAGS_fast_stdout) {
log_lock.lock();
}
#if 0 // defined(OutputDebugString)
OutputDebugStringA(log_buffer.data());
#else
fprintf(stdout, "%s", log_buffer.data());
if (FLAGS_flush_stdout) {
fflush(stdout);
}
#endif // OutputDebugString
if (!FLAGS_fast_stdout) {
log_lock.unlock();
}
logger_.AppendLine(xe::threading::current_thread_id(), level_char,
log_format_buffer_.data(), chars_written);
}
void handle_fatal(const char* fmt, ...) {
void LogLineVarargs(const char level_char, const char* fmt, va_list args) {
size_t chars_written = vsnprintf(log_format_buffer_.data(),
log_format_buffer_.capacity(), fmt, args);
logger_.AppendLine(xe::threading::current_thread_id(), level_char,
log_format_buffer_.data(), chars_written);
}
void LogLine(const char level_char, const std::string& str) {
logger_.AppendLine(xe::threading::current_thread_id(), level_char,
str.c_str(), str.length());
}
void FatalError(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
format_log_line(log_buffer.data(), log_buffer.capacity(), 'X', fmt, args);
LogLineVarargs('X', fmt, args);
va_end(args);
if (!FLAGS_fast_stdout) {
log_lock.lock();
}
#if defined(OutputDebugString)
OutputDebugStringA(log_buffer.data());
#else
fprintf(stderr, "%s", log_buffer.data());
fflush(stderr);
#endif // OutputDebugString
if (!FLAGS_fast_stdout) {
log_lock.unlock();
}
#if XE_PLATFORM_WIN32
if (!xe::has_console_attached()) {
MessageBoxA(NULL, log_buffer.data(), "Xenia Error",
va_start(args, fmt);
vsnprintf(log_format_buffer_.data(), log_format_buffer_.capacity(), fmt,
args);
va_end(args);
MessageBoxA(NULL, log_format_buffer_.data(), "Xenia Error",
MB_OK | MB_ICONERROR | MB_APPLMODAL | MB_SETFOREGROUND);
}
#endif // WIN32
@ -117,4 +179,6 @@ void handle_fatal(const char* fmt, ...) {
exit(1);
}
void FatalError(const std::string& str) { FatalError(str.c_str()); }
} // namespace xe

View File

@ -11,86 +11,47 @@
#define XENIA_BASE_LOGGING_H_
#include <cstdint>
#include <string>
#include "xenia/base/string.h"
namespace xe {
#define XE_OPTION_ENABLE_LOGGING 1
#define XE_OPTION_LOG_ERROR 1
#define XE_OPTION_LOG_WARNING 1
#define XE_OPTION_LOG_INFO 1
#define XE_OPTION_LOG_DEBUG 1
#define XE_OPTION_LOG_CPU 1
#define XE_OPTION_LOG_APU 1
#define XE_OPTION_LOG_GPU 1
#define XE_OPTION_LOG_KERNEL 1
#define XE_OPTION_LOG_FS 1
#define XE_EMPTY_MACRO \
do { \
} while (false)
// Initializes the logging system and any outputs requested.
// Must be called on startup.
void InitializeLogging(const std::wstring& app_name);
void log_line(const char level_char, const char* fmt, ...);
void handle_fatal(const char* fmt, ...);
// Appends a line to the log with printf-style formatting.
void LogLineFormat(const char level_char, const char* fmt, ...);
void LogLineVarargs(const char level_char, const char* fmt, va_list args);
// Appends a line to the log.
void LogLine(const char level_char, const std::string& str);
// Logs a fatal error with printf-style formatting and aborts the program.
void FatalError(const char* fmt, ...);
// Logs a fatal error and aborts the program.
void FatalError(const std::string& str);
#if XE_OPTION_ENABLE_LOGGING
#define XELOGCORE(level, fmt, ...) xe::log_line(level, fmt, ##__VA_ARGS__)
#define XELOGCORE(level, fmt, ...) xe::LogLineFormat(level, fmt, ##__VA_ARGS__)
#else
#define XELOGCORE(level, fmt, ...) XE_EMPTY_MACRO
#define XELOGCORE(level, fmt, ...) \
do { \
} while (false)
#endif // ENABLE_LOGGING
#define XEFATAL(fmt, ...) \
do { \
xe::handle_fatal(fmt, ##__VA_ARGS__); \
} while (false)
#if XE_OPTION_LOG_ERROR
#define XELOGE(fmt, ...) XELOGCORE('!', fmt, ##__VA_ARGS__)
#else
#define XELOGE(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_WARNING
#define XELOGW(fmt, ...) XELOGCORE('w', fmt, ##__VA_ARGS__)
#else
#define XELOGW(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_INFO
#define XELOGI(fmt, ...) XELOGCORE('i', fmt, ##__VA_ARGS__)
#else
#define XELOGI(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_DEBUG
#define XELOGD(fmt, ...) XELOGCORE('d', fmt, ##__VA_ARGS__)
#else
#define XELOGD(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_CPU
#define XELOGCPU(fmt, ...) XELOGCORE('C', fmt, ##__VA_ARGS__)
#else
#define XELOGCPU(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_APU
#define XELOGAPU(fmt, ...) XELOGCORE('A', fmt, ##__VA_ARGS__)
#else
#define XELOGAPU(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_GPU
#define XELOGGPU(fmt, ...) XELOGCORE('G', fmt, ##__VA_ARGS__)
#else
#define XELOGGPU(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_KERNEL
#define XELOGKERNEL(fmt, ...) XELOGCORE('K', fmt, ##__VA_ARGS__)
#else
#define XELOGKERNEL(fmt, ...) XE_EMPTY_MACRO
#endif
#if XE_OPTION_LOG_FS
#define XELOGFS(fmt, ...) XELOGCORE('F', fmt, ##__VA_ARGS__)
#else
#define XELOGFS(fmt, ...) XE_EMPTY_MACRO
#endif
} // namespace xe

View File

@ -11,6 +11,7 @@
#include <gflags/gflags.h>
#include "xenia/base/logging.h"
#include "xenia/base/string.h"
namespace xe {
@ -32,6 +33,9 @@ extern "C" int main(int argc, char** argv) {
args.push_back(xe::to_wstring(argv[n]));
}
// Initialize logging. Needs parsed FLAGS.
xe::InitializeLogging(entry_info.name);
// Call app-provided entry point.
int result = entry_info.entry_point(args);

View File

@ -13,6 +13,7 @@
#include <gflags/gflags.h>
#include <io.h>
#include "xenia/base/logging.h"
#include "xenia/base/platform_win.h"
#include "xenia/base/string.h"
@ -84,6 +85,9 @@ int Main() {
// NOTE: this may fail if COM has already been initialized - that's OK.
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
// Initialize logging. Needs parsed FLAGS.
xe::InitializeLogging(entry_info.name);
// Call app-provided entry point.
int result = entry_info.entry_point(args);

View File

@ -15,7 +15,7 @@
namespace xe {
RingBuffer::RingBuffer(uint8_t* buffer, size_t capacity)
: buffer_(buffer), capacity_(capacity), read_offset_(0), write_offset_(0) {}
: buffer_(buffer), capacity_(capacity) {}
size_t RingBuffer::Read(uint8_t* buffer, size_t count) {
count = std::min(count, capacity_);
@ -37,7 +37,7 @@ size_t RingBuffer::Read(uint8_t* buffer, size_t count) {
return count;
}
size_t RingBuffer::Write(uint8_t* buffer, size_t count) {
size_t RingBuffer::Write(const uint8_t* buffer, size_t count) {
count = std::min(count, capacity_);
if (!count) {
return 0;

View File

@ -20,14 +20,13 @@ class RingBuffer {
public:
RingBuffer(uint8_t* buffer, size_t capacity);
size_t Read(uint8_t* buffer, size_t count);
size_t Write(uint8_t* buffer, size_t count);
uint8_t* buffer() const { return buffer_; }
size_t capacity() const { return capacity_; }
bool empty() const { return read_offset_ == write_offset_; }
uint8_t* buffer() { return buffer_; }
size_t capacity() { return capacity_; }
size_t read_offset() { return read_offset_; }
size_t read_count() {
size_t read_offset() const { return read_offset_; }
void set_read_offset(size_t offset) { read_offset_ = offset % capacity_; }
size_t read_count() const {
if (read_offset_ == write_offset_) {
return 0;
} else if (read_offset_ < write_offset_) {
@ -37,8 +36,9 @@ class RingBuffer {
}
}
size_t write_offset() { return write_offset_; }
size_t write_count() {
size_t write_offset() const { return write_offset_; }
void set_write_offset(size_t offset) { write_offset_ = offset % capacity_; }
size_t write_count() const {
if (read_offset_ == write_offset_) {
return capacity_;
} else if (write_offset_ < read_offset_) {
@ -48,15 +48,23 @@ class RingBuffer {
}
}
void set_read_offset(size_t offset) { read_offset_ = offset % capacity_; }
size_t Read(uint8_t* buffer, size_t count);
template <typename T>
size_t Read(T* buffer, size_t count) {
return Read(reinterpret_cast<uint8_t*>(buffer), count);
}
void set_write_offset(size_t offset) { write_offset_ = offset % capacity_; }
size_t Write(const uint8_t* buffer, size_t count);
template <typename T>
size_t Write(const T* buffer, size_t count) {
return Write(reinterpret_cast<const uint8_t*>(buffer), count);
}
private:
uint8_t* buffer_;
size_t capacity_;
size_t read_offset_;
size_t write_offset_;
uint8_t* buffer_ = nullptr;
size_t capacity_ = 0;
size_t read_offset_ = 0;
size_t write_offset_ = 0;
};
} // namespace xe

View File

@ -155,14 +155,14 @@ std::string fix_path_separators(const std::string& source, char new_sep) {
return dest;
}
std::string find_name_from_path(const std::string& path) {
std::string find_name_from_path(const std::string& path, char sep) {
std::string name(path);
if (!path.empty()) {
std::string::size_type from(std::string::npos);
if (path.back() == '\\') {
if (path.back() == sep) {
from = path.size() - 2;
}
auto pos(path.find_last_of('\\', from));
auto pos(path.find_last_of(sep, from));
if (pos != std::string::npos) {
if (from == std::string::npos) {
name = path.substr(pos + 1);
@ -175,14 +175,14 @@ std::string find_name_from_path(const std::string& path) {
return name;
}
std::wstring find_name_from_path(const std::wstring& path) {
std::wstring find_name_from_path(const std::wstring& path, wchar_t sep) {
std::wstring name(path);
if (!path.empty()) {
std::wstring::size_type from(std::wstring::npos);
if (path.back() == '\\') {
if (path.back() == sep) {
from = path.size() - 2;
}
auto pos(path.find_last_of('\\', from));
auto pos(path.find_last_of(sep, from));
if (pos != std::wstring::npos) {
if (from == std::wstring::npos) {
name = path.substr(pos + 1);
@ -195,12 +195,12 @@ std::wstring find_name_from_path(const std::wstring& path) {
return name;
}
std::string find_base_path(const std::string& path) {
auto last_slash = path.find_last_of('\\');
std::string find_base_path(const std::string& path, char sep) {
auto last_slash = path.find_last_of(sep);
if (last_slash == std::string::npos) {
return path;
} else if (last_slash == path.length() - 1) {
auto prev_slash = path.find_last_of('\\', last_slash - 1);
auto prev_slash = path.find_last_of(sep, last_slash - 1);
if (prev_slash == std::string::npos) {
return "";
} else {
@ -211,12 +211,12 @@ std::string find_base_path(const std::string& path) {
}
}
std::wstring find_base_path(const std::wstring& path) {
auto last_slash = path.find_last_of('\\');
std::wstring find_base_path(const std::wstring& path, wchar_t sep) {
auto last_slash = path.find_last_of(sep);
if (last_slash == std::wstring::npos) {
return path;
} else if (last_slash == path.length() - 1) {
auto prev_slash = path.find_last_of('\\', last_slash - 1);
auto prev_slash = path.find_last_of(sep, last_slash - 1);
if (prev_slash == std::wstring::npos) {
return L"";
} else {

View File

@ -45,12 +45,16 @@ std::string fix_path_separators(const std::string& source,
char new_sep = xe::kPathSeparator);
// Find the top directory name or filename from a path.
std::string find_name_from_path(const std::string& path);
std::wstring find_name_from_path(const std::wstring& path);
std::string find_name_from_path(const std::string& path,
char sep = xe::kPathSeparator);
std::wstring find_name_from_path(const std::wstring& path,
wchar_t sep = xe::kPathSeparator);
// Get parent path of the given directory or filename.
std::string find_base_path(const std::string& path);
std::wstring find_base_path(const std::wstring& path);
std::string find_base_path(const std::string& path,
char sep = xe::kPathSeparator);
std::wstring find_base_path(const std::wstring& path,
wchar_t sep = xe::kPathSeparator);
} // namespace xe

View File

@ -75,7 +75,7 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
}
if (!cpu_.has(Xbyak::util::Cpu::tAVX)) {
XEFATAL(
xe::FatalError(
"Your CPU is too old to support Xenia. See the FAQ for system "
"requirements at http://xenia.jp");
return;

View File

@ -22,7 +22,9 @@
#if 0
#define LOGPPC(fmt, ...) XELOGCORE('p', fmt, ##__VA_ARGS__)
#else
#define LOGPPC(fmt, ...) XE_EMPTY_MACRO
#define LOGPPC(fmt, ...) \
do { \
} while (false)
#endif
namespace xe {

View File

@ -46,8 +46,8 @@ std::unique_ptr<Application> Application::Create() {
xe::Profiler::ThreadEnter("Win32 Loop");
if (!app->Initialize()) {
XEFATAL("Failed to initialize application");
exit(1);
xe::FatalError("Failed to initialize application");
return;
}
fence.Signal();

View File

@ -228,11 +228,11 @@ X_STATUS Emulator::LaunchDiscImage(std::wstring path) {
// Register the disc image in the virtual filesystem.
auto device = std::make_unique<vfs::DiscImageDevice>(mount_path, path);
if (!device->Initialize()) {
XELOGE("Unable to mount disc image");
xe::FatalError("Unable to mount disc image; file not found or corrupt.");
return X_STATUS_NO_SUCH_FILE;
}
if (!file_system_->RegisterDevice(std::move(device))) {
XELOGE("Unable to register disc image");
xe::FatalError("Unable to register disc image.");
return X_STATUS_NO_SUCH_FILE;
}
@ -250,11 +250,12 @@ X_STATUS Emulator::LaunchStfsContainer(std::wstring path) {
// Register the container in the virtual filesystem.
auto device = std::make_unique<vfs::StfsContainerDevice>(mount_path, path);
if (!device->Initialize()) {
XELOGE("Unable to mount STFS container");
xe::FatalError(
"Unable to mount STFS container; file not found or corrupt.");
return X_STATUS_NO_SUCH_FILE;
}
if (!file_system_->RegisterDevice(std::move(device))) {
XELOGE("Unable to register STFS container");
xe::FatalError("Unable to register STFS container.");
return X_STATUS_NO_SUCH_FILE;
}

View File

@ -172,7 +172,7 @@ void CommandProcessor::ClearCaches() {
void CommandProcessor::WorkerThreadMain() {
context_->MakeCurrent();
if (!SetupGL()) {
XEFATAL("Unable to setup command processor GL state");
xe::FatalError("Unable to setup command processor GL state");
return;
}

View File

@ -88,7 +88,7 @@ X_STATUS GL4GraphicsSystem::Setup(cpu::Processor* processor,
processor_context->ClearCurrent();
});
if (!processor_context) {
XEFATAL(
xe::FatalError(
"Unable to initialize GL context. Xenia requires OpenGL 4.5. Ensure "
"you have the latest drivers for your GPU and that it supports OpenGL "
"4.5. See http://xenia.jp/faq/ for more information.");

View File

@ -2197,8 +2197,8 @@ int trace_viewer_main(const std::vector<std::wstring>& args) {
loop->PostSynchronous([&window]() {
xe::threading::set_name("Win32 Loop");
if (!window->Initialize()) {
XEFATAL("Failed to initialize main window");
exit(1);
FatalError("Failed to initialize main window");
return;
}
});
window->on_closed.AddListener([&loop](xe::ui::UIEvent* e) {

View File

@ -76,8 +76,8 @@ void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) {
}
thread->SetActiveCpu(cpu);
// XELOGGPU("Dispatching GPU interrupt at %.8X w/ mode %d on cpu %d",
// interrupt_callback_, source, cpu);
XELOGGPU("Dispatching GPU interrupt at %.8X w/ mode %d on cpu %d",
interrupt_callback_, source, cpu);
uint64_t args[] = {source, interrupt_callback_data_};
processor_->Execute(thread->thread_state(), interrupt_callback_, args,

View File

@ -276,7 +276,7 @@ X_STATUS XUserModule::Launch(uint32_t flags) {
// We know this is the 'main thread'.
char thread_name[32];
snprintf(thread_name, xe::countof(thread_name), "Main XThread %04X",
std::snprintf(thread_name, xe::countof(thread_name), "Main XThread %04X",
thread->handle());
thread->set_name(thread_name);
@ -308,24 +308,31 @@ void XUserModule::Dump() {
return;
}
StringBuffer sb;
xe::cpu::ExportResolver* export_resolver =
kernel_state_->emulator()->export_resolver();
auto header = xex_header();
// XEX header.
printf("Module %s:\n", path_.c_str());
printf(" Module Flags: %.8X\n", (uint32_t)header->module_flags);
sb.AppendFormat("Module %s:\n", path_.c_str());
sb.AppendFormat(" Module Flags: %.8X\n", (uint32_t)header->module_flags);
// Security header
auto security_info = xex_module()->xex_security_info();
printf("Security Header:\n");
printf(" Image Flags: %.8X\n", (uint32_t)security_info->image_flags);
printf(" Load Address: %.8X\n", (uint32_t)security_info->load_address);
printf(" Image Size: %.8X\n", (uint32_t)security_info->image_size);
printf(" Export Table: %.8X\n", (uint32_t)security_info->export_table);
sb.AppendFormat("Security Header:\n");
sb.AppendFormat(" Image Flags: %.8X\n",
(uint32_t)security_info->image_flags);
sb.AppendFormat(" Load Address: %.8X\n",
(uint32_t)security_info->load_address);
sb.AppendFormat(" Image Size: %.8X\n",
(uint32_t)security_info->image_size);
sb.AppendFormat(" Export Table: %.8X\n",
(uint32_t)security_info->export_table);
// Optional headers
printf("Optional Header Count: %d\n", (uint32_t)header->header_count);
sb.AppendFormat("Optional Header Count: %d\n",
(uint32_t)header->header_count);
for (uint32_t i = 0; i < header->header_count; i++) {
auto& opt_header = header->headers[i];
@ -335,7 +342,7 @@ void XUserModule::Dump() {
reinterpret_cast<const uint8_t*>(header) + opt_header.offset;
switch (opt_header.key) {
case XEX_HEADER_RESOURCE_INFO: {
printf(" XEX_HEADER_RESOURCE_INFO:\n");
sb.AppendFormat(" XEX_HEADER_RESOURCE_INFO:\n");
auto opt_resource_info =
reinterpret_cast<const xex2_opt_resource_info*>(opt_header_ptr);
@ -348,35 +355,37 @@ void XUserModule::Dump() {
std::memcpy(name, res.name, sizeof(res.name));
name[8] = 0;
printf(" %-8s %.8X-%.8X, %db\n", name, (uint32_t)res.address,
(uint32_t)res.address + (uint32_t)res.size,
(uint32_t)res.size);
sb.AppendFormat(
" %-8s %.8X-%.8X, %db\n", name, (uint32_t)res.address,
(uint32_t)res.address + (uint32_t)res.size, (uint32_t)res.size);
}
} break;
case XEX_HEADER_FILE_FORMAT_INFO: {
printf(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n");
sb.AppendFormat(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n");
} break;
case XEX_HEADER_DELTA_PATCH_DESCRIPTOR: {
printf(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n");
sb.AppendFormat(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n");
} break;
case XEX_HEADER_BOUNDING_PATH: {
auto opt_bound_path =
reinterpret_cast<const xex2_opt_bound_path*>(opt_header_ptr);
printf(" XEX_HEADER_BOUNDING_PATH: %s\n", opt_bound_path->path);
sb.AppendFormat(" XEX_HEADER_BOUNDING_PATH: %s\n",
opt_bound_path->path);
} break;
case XEX_HEADER_ORIGINAL_BASE_ADDRESS: {
printf(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n",
sb.AppendFormat(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n",
(uint32_t)opt_header.value);
} break;
case XEX_HEADER_ENTRY_POINT: {
printf(" XEX_HEADER_ENTRY_POINT: %.8X\n", (uint32_t)opt_header.value);
sb.AppendFormat(" XEX_HEADER_ENTRY_POINT: %.8X\n",
(uint32_t)opt_header.value);
} break;
case XEX_HEADER_IMAGE_BASE_ADDRESS: {
printf(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n",
sb.AppendFormat(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n",
(uint32_t)opt_header.value);
} break;
case XEX_HEADER_IMPORT_LIBRARIES: {
printf(" XEX_HEADER_IMPORT_LIBRARIES:\n");
sb.AppendFormat(" XEX_HEADER_IMPORT_LIBRARIES:\n");
auto opt_import_libraries =
reinterpret_cast<const xex2_opt_import_libraries*>(opt_header_ptr);
@ -408,38 +417,41 @@ void XUserModule::Dump() {
auto library = reinterpret_cast<const xex2_import_library*>(
libraries + library_offset);
auto name = string_table[library->name_index];
printf(" %s - %d imports\n", name, (uint16_t)library->count);
sb.AppendFormat(" %s - %d imports\n", name,
(uint16_t)library->count);
// Manually byteswap these because of the bitfields.
xex2_version version, version_min;
version.value = xe::byte_swap<uint32_t>(library->version.value);
version_min.value =
xe::byte_swap<uint32_t>(library->version_min.value);
printf(" Version: %d.%d.%d.%d\n", version.major, version.minor,
version.build, version.qfe);
printf(" Min Version: %d.%d.%d.%d\n", version_min.major,
version_min.minor, version_min.build, version_min.qfe);
sb.AppendFormat(" Version: %d.%d.%d.%d\n", version.major,
version.minor, version.build, version.qfe);
sb.AppendFormat(" Min Version: %d.%d.%d.%d\n", version_min.major,
version_min.minor, version_min.build,
version_min.qfe);
library_offset += library->size;
}
} break;
case XEX_HEADER_CHECKSUM_TIMESTAMP: {
printf(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n");
sb.AppendFormat(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n");
} break;
case XEX_HEADER_ORIGINAL_PE_NAME: {
auto opt_pe_name =
reinterpret_cast<const xex2_opt_original_pe_name*>(opt_header_ptr);
printf(" XEX_HEADER_ORIGINAL_PE_NAME: %s\n", opt_pe_name->name);
sb.AppendFormat(" XEX_HEADER_ORIGINAL_PE_NAME: %s\n",
opt_pe_name->name);
} break;
case XEX_HEADER_STATIC_LIBRARIES: {
printf(" XEX_HEADER_STATIC_LIBRARIES:\n");
sb.AppendFormat(" XEX_HEADER_STATIC_LIBRARIES:\n");
auto opt_static_libraries =
reinterpret_cast<const xex2_opt_static_libraries*>(opt_header_ptr);
uint32_t count = (opt_static_libraries->size - 4) / 0x10;
for (uint32_t l = 0; l < count; l++) {
auto& library = opt_static_libraries->libraries[l];
printf(" %-8s : %d.%d.%d.%d\n", library.name,
sb.AppendFormat(" %-8s : %d.%d.%d.%d\n", library.name,
static_cast<uint16_t>(library.version_major),
static_cast<uint16_t>(library.version_minor),
static_cast<uint16_t>(library.version_build),
@ -447,77 +459,77 @@ void XUserModule::Dump() {
}
} break;
case XEX_HEADER_TLS_INFO: {
printf(" XEX_HEADER_TLS_INFO:\n");
sb.AppendFormat(" XEX_HEADER_TLS_INFO:\n");
auto opt_tls_info =
reinterpret_cast<const xex2_opt_tls_info*>(opt_header_ptr);
printf(" Slot Count: %d\n",
sb.AppendFormat(" Slot Count: %d\n",
static_cast<uint32_t>(opt_tls_info->slot_count));
printf(" Raw Data Address: %.8X\n",
sb.AppendFormat(" Raw Data Address: %.8X\n",
static_cast<uint32_t>(opt_tls_info->raw_data_address));
printf(" Data Size: %d\n",
sb.AppendFormat(" Data Size: %d\n",
static_cast<uint32_t>(opt_tls_info->data_size));
printf(" Raw Data Size: %d\n",
sb.AppendFormat(" Raw Data Size: %d\n",
static_cast<uint32_t>(opt_tls_info->raw_data_size));
} break;
case XEX_HEADER_DEFAULT_STACK_SIZE: {
printf(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n",
sb.AppendFormat(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n",
static_cast<uint32_t>(opt_header.value));
} break;
case XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: {
printf(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n",
sb.AppendFormat(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n",
static_cast<uint32_t>(opt_header.value));
} break;
case XEX_HEADER_DEFAULT_HEAP_SIZE: {
printf(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n",
sb.AppendFormat(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n",
static_cast<uint32_t>(opt_header.value));
} break;
case XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS: {
printf(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n");
sb.AppendFormat(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n");
} break;
case XEX_HEADER_SYSTEM_FLAGS: {
printf(" XEX_HEADER_SYSTEM_FLAGS: %.8X\n",
sb.AppendFormat(" XEX_HEADER_SYSTEM_FLAGS: %.8X\n",
static_cast<uint32_t>(opt_header.value));
} break;
case XEX_HEADER_EXECUTION_INFO: {
printf(" XEX_HEADER_EXECUTION_INFO:\n");
sb.AppendFormat(" XEX_HEADER_EXECUTION_INFO:\n");
auto opt_exec_info =
reinterpret_cast<const xex2_opt_execution_info*>(opt_header_ptr);
printf(" Media ID: %.8X\n",
sb.AppendFormat(" Media ID: %.8X\n",
static_cast<uint32_t>(opt_exec_info->media_id));
printf(" Title ID: %.8X\n",
sb.AppendFormat(" Title ID: %.8X\n",
static_cast<uint32_t>(opt_exec_info->title_id));
printf(" Savegame ID: %.8X\n",
sb.AppendFormat(" Savegame ID: %.8X\n",
static_cast<uint32_t>(opt_exec_info->title_id));
printf(" Disc Number / Total: %d / %d\n", opt_exec_info->disc_number,
opt_exec_info->disc_count);
sb.AppendFormat(" Disc Number / Total: %d / %d\n",
opt_exec_info->disc_number, opt_exec_info->disc_count);
} break;
case XEX_HEADER_TITLE_WORKSPACE_SIZE: {
printf(" XEX_HEADER_TITLE_WORKSPACE_SIZE: %d\n",
sb.AppendFormat(" XEX_HEADER_TITLE_WORKSPACE_SIZE: %d\n",
uint32_t(opt_header.value));
} break;
case XEX_HEADER_GAME_RATINGS: {
printf(" XEX_HEADER_GAME_RATINGS (TODO):\n");
sb.AppendFormat(" XEX_HEADER_GAME_RATINGS (TODO):\n");
} break;
case XEX_HEADER_LAN_KEY: {
printf(" XEX_HEADER_LAN_KEY (TODO):\n");
sb.AppendFormat(" XEX_HEADER_LAN_KEY (TODO):\n");
} break;
case XEX_HEADER_XBOX360_LOGO: {
printf(" XEX_HEADER_XBOX360_LOGO (TODO):\n");
sb.AppendFormat(" XEX_HEADER_XBOX360_LOGO (TODO):\n");
} break;
case XEX_HEADER_MULTIDISC_MEDIA_IDS: {
printf(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n");
sb.AppendFormat(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n");
} break;
case XEX_HEADER_ALTERNATE_TITLE_IDS: {
printf(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n");
sb.AppendFormat(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n");
} break;
case XEX_HEADER_ADDITIONAL_TITLE_MEMORY: {
printf(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY: %d\n",
sb.AppendFormat(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY: %d\n",
uint32_t(opt_header.value));
} break;
case XEX_HEADER_EXPORTS_BY_NAME: {
printf(" XEX_HEADER_EXPORTS_BY_NAME:\n");
sb.AppendFormat(" XEX_HEADER_EXPORTS_BY_NAME:\n");
auto dir =
reinterpret_cast<const xex2_opt_data_directory*>(opt_header_ptr);
@ -539,16 +551,16 @@ void XUserModule::Dump() {
auto name = reinterpret_cast<const char*>(e_base + name_table[n]);
uint16_t ordinal = ordinal_table[n];
uint32_t addr = exe_address + function_table[ordinal];
printf(" %-28s - %.3X - %.8X\n", name, ordinal, addr);
sb.AppendFormat(" %-28s - %.3X - %.8X\n", name, ordinal, addr);
}
} break;
default: {
printf(" Unknown Header %.8X\n", (uint32_t)opt_header.key);
sb.AppendFormat(" Unknown Header %.8X\n", (uint32_t)opt_header.key);
} break;
}
}
printf("Sections:\n");
sb.AppendFormat("Sections:\n");
for (uint32_t i = 0, page = 0; i < security_info->page_descriptor_count;
i++) {
// Manually byteswap the bitfield data.
@ -574,8 +586,8 @@ void XUserModule::Dump() {
uint32_t start_address = security_info->load_address + (page * page_size);
uint32_t end_address = start_address + (page_descriptor.size * page_size);
printf(" %3u %s %3u pages %.8X - %.8X (%d bytes)\n", page, type,
page_descriptor.size, start_address, end_address,
sb.AppendFormat(" %3u %s %3u pages %.8X - %.8X (%d bytes)\n", page,
type, page_descriptor.size, start_address, end_address,
page_descriptor.size * page_size);
page += page_descriptor.size;
}
@ -584,7 +596,7 @@ void XUserModule::Dump() {
// TODO(benvanik): figure out a way to remove dependency on old xex header.
auto old_header = xe_xex2_get_header(xex_module()->xex());
printf("Imports:\n");
sb.AppendFormat("Imports:\n");
for (size_t n = 0; n < old_header->import_library_count; n++) {
const xe_xex2_import_library_t* library = &old_header->import_libraries[n];
@ -592,14 +604,14 @@ void XUserModule::Dump() {
size_t import_info_count;
if (!xe_xex2_get_import_infos(xex_module()->xex(), library, &import_infos,
&import_info_count)) {
printf(" %s - %lld imports\n", library->name, import_info_count);
printf(" Version: %d.%d.%d.%d\n", library->version.major,
sb.AppendFormat(" %s - %lld imports\n", library->name, import_info_count);
sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->version.major,
library->version.minor, library->version.build,
library->version.qfe);
printf(" Min Version: %d.%d.%d.%d\n", library->min_version.major,
library->min_version.minor, library->min_version.build,
library->min_version.qfe);
printf("\n");
sb.AppendFormat(" Min Version: %d.%d.%d.%d\n",
library->min_version.major, library->min_version.minor,
library->min_version.build, library->min_version.qfe);
sb.AppendFormat("\n");
// Counts.
int known_count = 0;
@ -642,14 +654,14 @@ void XUserModule::Dump() {
}
}
float total_count = static_cast<float>(import_info_count) / 100.0f;
printf(" Total: %4llu\n", import_info_count);
printf(" Known: %3d%% (%d known, %d unknown)\n",
sb.AppendFormat(" Total: %4llu\n", import_info_count);
sb.AppendFormat(" Known: %3d%% (%d known, %d unknown)\n",
static_cast<int>(known_count / total_count), known_count,
unknown_count);
printf(" Implemented: %3d%% (%d implemented, %d unimplemented)\n",
static_cast<int>(impl_count / total_count), impl_count,
unimpl_count);
printf("\n");
sb.AppendFormat(
" Implemented: %3d%% (%d implemented, %d unimplemented)\n",
static_cast<int>(impl_count / total_count), impl_count, unimpl_count);
sb.AppendFormat("\n");
// Listing.
for (size_t m = 0; m < import_info_count; m++) {
@ -674,18 +686,22 @@ void XUserModule::Dump() {
}
if (kernel_export &&
kernel_export->type == cpu::Export::Type::kVariable) {
printf(" V %.8X %.3X (%3d) %s %s\n", info->value_address,
info->ordinal, info->ordinal, implemented ? " " : "!!", name);
sb.AppendFormat(" V %.8X %.3X (%3d) %s %s\n",
info->value_address, info->ordinal, info->ordinal,
implemented ? " " : "!!", name);
} else if (info->thunk_address) {
printf(" F %.8X %.8X %.3X (%3d) %s %s\n", info->value_address,
info->thunk_address, info->ordinal, info->ordinal,
sb.AppendFormat(" F %.8X %.8X %.3X (%3d) %s %s\n",
info->value_address, info->thunk_address,
info->ordinal, info->ordinal,
implemented ? " " : "!!", name);
}
}
}
printf("\n");
sb.AppendFormat("\n");
}
XELOGI(sb.GetString());
}
} // namespace kernel

View File

@ -51,6 +51,14 @@ thread_local WGLEWContext* tls_wglew_context_ = nullptr;
extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; }
extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; }
void FatalGLError(std::string error) {
xe::FatalError(
error +
"\nEnsure you have the latest drivers for your GPU and that it supports "
"OpenGL 4.5. See http://xenia.jp/faq/ for more information and a list"
"of supported GPUs.");
}
std::unique_ptr<GLContext> GLContext::Create(Window* target_window) {
auto context = std::unique_ptr<GLContext>(new GLContext(target_window));
if (!context->Initialize(target_window)) {
@ -98,17 +106,17 @@ bool GLContext::Initialize(Window* target_window) {
pfd.iLayerType = PFD_MAIN_PLANE;
int pixel_format = ChoosePixelFormat(dc_, &pfd);
if (!pixel_format) {
XELOGE("Unable to choose pixel format");
FatalGLError("Unable to choose pixel format.");
return false;
}
if (!SetPixelFormat(dc_, pixel_format, &pfd)) {
XELOGE("Unable to set pixel format");
FatalGLError("Unable to set pixel format.");
return false;
}
HGLRC temp_context = wglCreateContext(dc_);
if (!temp_context) {
XELOGE("Unable to create temporary GL context");
FatalGLError("Unable to create temporary GL context.");
return false;
}
wglMakeCurrent(dc_, temp_context);
@ -116,16 +124,16 @@ bool GLContext::Initialize(Window* target_window) {
tls_glew_context_ = glew_context_.get();
tls_wglew_context_ = wglew_context_.get();
if (glewInit() != GLEW_OK) {
XELOGE("Unable to initialize GLEW");
FatalGLError("Unable to initialize GLEW.");
return false;
}
if (wglewInit() != GLEW_OK) {
XELOGE("Unable to initialize WGLEW");
FatalGLError("Unable to initialize WGLEW.");
return false;
}
if (!WGLEW_ARB_create_context) {
XELOGE("WGL_ARG_create_context not supported by GL ICD");
FatalGLError("WGL_ARG_create_context not supported by GL ICD.");
return false;
}
@ -148,12 +156,12 @@ bool GLContext::Initialize(Window* target_window) {
wglMakeCurrent(nullptr, nullptr);
wglDeleteContext(temp_context);
if (!glrc_) {
XELOGE("Unable to create real GL context");
FatalGLError("Unable to create real GL context.");
return false;
}
if (!MakeCurrent()) {
XELOGE("Could not make real GL context current");
FatalGLError("Could not make real GL context current.");
return false;
}
@ -171,7 +179,7 @@ bool GLContext::Initialize(Window* target_window) {
SetupDebugging();
if (!blitter_.Initialize()) {
XELOGE("Unable to initialize blitter");
FatalGLError("Unable to initialize blitter.");
ClearCurrent();
return false;
}
@ -204,7 +212,7 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
0};
new_glrc = wglCreateContextAttribsARB(dc_, glrc_, attrib_list);
if (!new_glrc) {
XELOGE("Could not create shared context");
FatalGLError("Could not create shared context.");
return nullptr;
}
}
@ -212,30 +220,30 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
auto new_context =
std::unique_ptr<GLContext>(new GLContext(target_window_, new_glrc));
if (!new_context->MakeCurrent()) {
XELOGE("Could not make new GL context current");
FatalGLError("Could not make new GL context current.");
return nullptr;
}
if (!glGetString(GL_EXTENSIONS)) {
new_context->ClearCurrent();
XELOGE("New GL context did not have extensions");
FatalGLError("New GL context did not have extensions.");
return nullptr;
}
if (glewInit() != GLEW_OK) {
new_context->ClearCurrent();
XELOGE("Unable to initialize GLEW");
FatalGLError("Unable to initialize GLEW on shared context.");
return nullptr;
}
if (wglewInit() != GLEW_OK) {
new_context->ClearCurrent();
XELOGE("Unable to initialize WGLEW");
FatalGLError("Unable to initialize WGLEW on shared context.");
return nullptr;
}
SetupDebugging();
if (!new_context->blitter_.Initialize()) {
XELOGE("Unable to initialize blitter");
FatalGLError("Unable to initialize blitter on shared context.");
return nullptr;
}
@ -244,18 +252,9 @@ std::unique_ptr<GraphicsContext> GLContext::CreateShared() {
return std::unique_ptr<GraphicsContext>(new_context.release());
}
void FatalGLError(std::string error) {
XEFATAL(
(error +
"\nEnsure you have the latest drivers for your GPU and that it supports "
"OpenGL 4.5. See http://xenia.jp/faq/ for more information and a list"
"of supported GPUs.")
.c_str());
}
void GLContext::AssertExtensionsPresent() {
if (!MakeCurrent()) {
XEFATAL("Unable to make GL context current");
FatalGLError("Unable to make GL context current.");
return;
}
@ -425,7 +424,7 @@ bool GLContext::MakeCurrent() {
if (FLAGS_thread_safe_gl) {
global_gl_mutex_.unlock();
}
XELOGE("Unable to make GL context current");
FatalGLError("Unable to make GL context current.");
return false;
}
tls_glew_context_ = glew_context_.get();