diff --git a/.travis.yml b/.travis.yml index bda8138c1..241431eaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,18 +22,20 @@ matrix: # LLVMGold.so is not installed correctly - env: BUILD=true CONFIG=Release -dist: trusty sudo: required addons: apt: sources: # - ubuntu-toolchain-r-test - - llvm-toolchain-precise + - llvm-toolchain-trusty packages: - clang-3.8 - clang-format-3.8 - libc++-dev - python3 + - libc++abi-dev + - libgtk-3-dev + - liblz4-dev git: # We handle submodules ourselves in xenia-build setup. @@ -44,6 +46,10 @@ before_script: - export CC=clang-3.8 # Dump useful info. - $CXX --version + # Add Vulkan dependencies + - wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb + - wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan-dev_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb + - sudo dpkg -i libvulkan1_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb libvulkan-dev_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb - python3 --version # Prepare environment (pull dependencies, build tools). - travis_retry ./xenia-build setup diff --git a/docs/building.md b/docs/building.md index 3da01799b..842a53156 100644 --- a/docs/building.md +++ b/docs/building.md @@ -52,6 +52,14 @@ sudo -E apt-get -yq update &>> ~/apt-get-update.log sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install clang-3.8 clang-format-3.8 ``` +You will also need some development libraries. To get them on an ubuntu system: +``` +sudo apt-get install libgtk-3-dev libpthread-stubs0-dev liblz4-dev libglew-dev libx11-dev +``` + +In addition, you will need the latest OpenGL libraries and drivers for your hardware. Intel and the open source +drivers are not supported as they do not yet support OpenGL 4.5. + ## Running To make life easier you can use `--flagfile=myflags.txt` to specify all diff --git a/premake5.lua b/premake5.lua index 593f0503d..184a04c90 100644 --- a/premake5.lua +++ b/premake5.lua @@ -87,10 +87,27 @@ filter("platforms:Linux") toolset("clang") buildoptions({ -- "-mlzcnt", -- (don't) Assume lzcnt is supported. + "`pkg-config --cflags gtk+-x11-3.0`" }) links({ "pthread", + "dl", + "lz4", + "X11", + "xcb", + "X11-xcb", + "GL", + "GLEW", + "vulkan", + "c++", + "c++abi" }) + linkoptions({ + "`pkg-config --libs gtk+-3.0`", + }) + +filter({"platforms:Linux", "kind:*App"}) + linkgroups("On") filter({"platforms:Linux", "language:C++", "toolset:gcc"}) buildoptions({ @@ -105,7 +122,6 @@ filter({"platforms:Linux", "language:C++", "toolset:clang"}) "-stdlib=libc++", }) links({ - "c++", }) filter("platforms:Windows") diff --git a/src/xenia/app/premake5.lua b/src/xenia/app/premake5.lua index 26b30fc5b..5803ea4f9 100644 --- a/src/xenia/app/premake5.lua +++ b/src/xenia/app/premake5.lua @@ -8,8 +8,15 @@ project("xenia-app") targetname("xenia") language("C++") links({ + "capstone", "gflags", + "glslang-spirv", "imgui", + "libavcodec", + "libavutil", + "snappy", + "spirv-tools", + "vulkan-loader", "xenia-apu", "xenia-apu-nop", "xenia-base", @@ -21,11 +28,15 @@ project("xenia-app") "xenia-gpu-gl4", "xenia-gpu-null", "xenia-gpu-vulkan", + "xenia-hid", "xenia-hid-nop", "xenia-kernel", "xenia-ui", "xenia-ui-gl", + "xenia-ui-spirv", + "xenia-ui-vulkan", "xenia-vfs", + "xxhash", }) flags({ "WinMain", -- Use WinMain instead of main. @@ -40,9 +51,10 @@ project("xenia-app") "xenia_main.cc", "../base/main_"..platform_suffix..".cc", }) - files({ - "main_resources.rc", - }) + filter("platforms:Windows") + files({ + "main_resources.rc", + }) resincludedirs({ project_root, }) diff --git a/src/xenia/base/bit_field.h b/src/xenia/base/bit_field.h index a089775c9..034f43d9e 100644 --- a/src/xenia/base/bit_field.h +++ b/src/xenia/base/bit_field.h @@ -29,7 +29,8 @@ struct bf { // For enum values, we strip them down to an underlying type. typedef typename std::conditional::value, std::underlying_type, - std::identity>::type::type value_type; + std::remove_reference>::type::type + value_type; inline value_type mask() const { return (((value_type)~0) >> (8 * sizeof(value_type) - n_bits)) << position; } @@ -39,4 +40,4 @@ struct bf { } // namespace xe -#endif // XENIA_BASE_BIT_FIELD_H_ \ No newline at end of file +#endif // XENIA_BASE_BIT_FIELD_H_ diff --git a/src/xenia/base/exception_handler_linux.cc b/src/xenia/base/exception_handler_linux.cc new file mode 100644 index 000000000..bc656a15d --- /dev/null +++ b/src/xenia/base/exception_handler_linux.cc @@ -0,0 +1,35 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/exception_handler.h" + +#include "xenia/base/assert.h" +#include "xenia/base/math.h" +#include "xenia/base/platform_linux.h" + +namespace xe { + +// This can be as large as needed, but isn't often needed. +// As we will be sometimes firing many exceptions we want to avoid having to +// scan the table too much or invoke many custom handlers. +constexpr size_t kMaxHandlerCount = 8; + +// All custom handlers, left-aligned and null terminated. +// Executed in order. +std::pair handlers_[kMaxHandlerCount]; + +void ExceptionHandler::Install(Handler fn, void* data) { + // TODO(dougvj) stub +} + +void ExceptionHandler::Uninstall(Handler fn, void* data) { + // TODO(dougvj) stub +} + +} // namespace xe diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index b9125e319..a948717cc 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -12,6 +12,8 @@ #include "xenia/base/string.h" #include +#include +#include #include #include #include @@ -33,6 +35,124 @@ bool CreateFolder(const std::wstring& path) { return mkdir(xe::to_string(path).c_str(), 0774); } +static int removeCallback(const char* fpath, const struct stat* sb, + int typeflag, struct FTW* ftwbuf) { + int rv = remove(fpath); + return rv; +} + +bool DeleteFolder(const std::wstring& path) { + return nftw(xe::to_string(path).c_str(), removeCallback, 64, + FTW_DEPTH | FTW_PHYS) == 0 + ? true + : false; +} + +static uint64_t convertUnixtimeToWinFiletime(time_t unixtime) { + // Linux uses number of seconds since 1/1/1970, and Windows uses + // number of nanoseconds since 1/1/1601 + // so we convert linux time to nanoseconds and then add the number of + // nanoseconds from 1601 to 1970 + // see https://msdn.microsoft.com/en-us/library/ms724228 + uint64_t filetime = filetime = (unixtime * 10000000) + 116444736000000000; + return filetime; +} + +bool IsFolder(const std::wstring& path) { + struct stat st; + if (stat(xe::to_string(path).c_str(), &st) == 0) { + if (S_ISDIR(st.st_mode)) return true; + } + return false; +} + +bool CreateFile(const std::wstring& path) { + int file = creat(xe::to_string(path).c_str(), 0774); + if (file >= 0) { + close(file); + return true; + } + return false; +} + +bool DeleteFile(const std::wstring& path) { + return (xe::to_string(path).c_str()) == 0 ? true : false; +} + +class PosixFileHandle : public FileHandle { + public: + PosixFileHandle(std::wstring path, int handle) + : FileHandle(std::move(path)), handle_(handle) {} + ~PosixFileHandle() override { + close(handle_); + handle_ = -1; + } + bool Read(size_t file_offset, void* buffer, size_t buffer_length, + size_t* out_bytes_read) override { + ssize_t out = pread(handle_, buffer, buffer_length, file_offset); + *out_bytes_read = out; + return out >= 0 ? true : false; + } + bool Write(size_t file_offset, const void* buffer, size_t buffer_length, + size_t* out_bytes_written) override { + ssize_t out = pwrite(handle_, buffer, buffer_length, file_offset); + *out_bytes_written = out; + return out >= 0 ? true : false; + } + void Flush() override { fsync(handle_); } + + private: + int handle_ = -1; +}; + +std::unique_ptr FileHandle::OpenExisting(std::wstring path, + uint32_t desired_access) { + int open_access; + if (desired_access & FileAccess::kGenericRead) { + open_access |= O_RDONLY; + } + if (desired_access & FileAccess::kGenericWrite) { + open_access |= O_WRONLY; + } + if (desired_access & FileAccess::kGenericExecute) { + open_access |= O_RDONLY; + } + if (desired_access & FileAccess::kGenericAll) { + open_access |= O_RDWR; + } + if (desired_access & FileAccess::kFileReadData) { + open_access |= O_RDONLY; + } + if (desired_access & FileAccess::kFileWriteData) { + open_access |= O_WRONLY; + } + if (desired_access & FileAccess::kFileAppendData) { + open_access |= O_APPEND; + } + int handle = open(xe::to_string(path).c_str(), open_access); + if (handle == -1) { + // TODO(benvanik): pick correct response. + return nullptr; + } + return std::make_unique(path, handle); +} + +bool GetInfo(const std::wstring& path, FileInfo* out_info) { + struct stat st; + if (stat(xe::to_string(path).c_str(), &st) == 0) { + if (S_ISDIR(st.st_mode)) { + out_info->type = FileInfo::Type::kDirectory; + } else { + out_info->type = FileInfo::Type::kFile; + } + out_info->create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime); + out_info->access_timestamp = convertUnixtimeToWinFiletime(st.st_atime); + out_info->write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime); + return true; + } + return false; +} + std::vector ListFiles(const std::wstring& path) { std::vector result; @@ -43,18 +163,20 @@ std::vector ListFiles(const std::wstring& path) { while (auto ent = readdir(dir)) { FileInfo info; + + info.name = xe::to_wstring(ent->d_name); + struct stat st; + stat((xe::to_string(path) + xe::to_string(info.name)).c_str(), &st); + info.create_timestamp = convertUnixtimeToWinFiletime(st.st_ctime); + info.access_timestamp = convertUnixtimeToWinFiletime(st.st_atime); + info.write_timestamp = convertUnixtimeToWinFiletime(st.st_mtime); if (ent->d_type == DT_DIR) { info.type = FileInfo::Type::kDirectory; info.total_size = 0; } else { info.type = FileInfo::Type::kFile; - info.total_size = 0; // TODO(DrChat): Find a way to get this + info.total_size = st.st_size; } - - info.create_timestamp = 0; - info.access_timestamp = 0; - info.write_timestamp = 0; - info.name = xe::to_wstring(ent->d_name); result.push_back(info); } @@ -62,4 +184,4 @@ std::vector ListFiles(const std::wstring& path) { } } // namespace filesystem -} // namespace xe \ No newline at end of file +} // namespace xe diff --git a/src/xenia/base/math.h b/src/xenia/base/math.h index 539a98b3c..d2c58ee3a 100644 --- a/src/xenia/base/math.h +++ b/src/xenia/base/math.h @@ -11,10 +11,10 @@ #define XENIA_BASE_MATH_H_ #include +#include #include #include #include - #include "xenia/base/platform.h" #if XE_ARCH_AMD64 diff --git a/src/xenia/base/platform_linux.cc b/src/xenia/base/platform_linux.cc new file mode 100644 index 000000000..b90c6f78e --- /dev/null +++ b/src/xenia/base/platform_linux.cc @@ -0,0 +1,21 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/platform_linux.h" +#include +#include + +namespace xe { + +void LaunchBrowser(const char* url) { + auto cmd = std::string("xdg-open " + std::string(url)); + system(cmd.c_str()); +} + +} // namespace xe diff --git a/src/xenia/base/platform_linux.h b/src/xenia/base/platform_linux.h new file mode 100644 index 000000000..345be94ab --- /dev/null +++ b/src/xenia/base/platform_linux.h @@ -0,0 +1,32 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_BASE_PLATFORM_X11_H_ +#define XENIA_BASE_PLATFORM_X11_H_ + +// NOTE: if you're including this file it means you are explicitly depending +// on Linux headers. Including this file outside of linux platform specific +// source code will break portability + +#include "xenia/base/platform.h" + +// Xlib/Xcb is used only for GLX/Vulkan interaction, the window management +// and input events are done with gtk/gdk +#include +#include +#include +#include +#include + +// Used for window management. Gtk is for GUI and wigets, gdk is for lower +// level events like key presses, mouse events, window handles, etc +#include +#include + +#endif // XENIA_BASE_PLATFORM_X11_H_ diff --git a/src/xenia/base/profiling.cc b/src/xenia/base/profiling.cc index 69d4e090e..da069b6f5 100644 --- a/src/xenia/base/profiling.cc +++ b/src/xenia/base/profiling.cc @@ -268,6 +268,7 @@ void Profiler::ToggleDisplay() {} void Profiler::TogglePause() {} void Profiler::set_window(ui::Window* window) {} void Profiler::Present() {} +void Profiler::Flip() {} #endif // XE_OPTION_PROFILING diff --git a/src/xenia/base/threading.h b/src/xenia/base/threading.h index 62fb5e815..480b76207 100644 --- a/src/xenia/base/threading.h +++ b/src/xenia/base/threading.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xenia/base/threading_linux.cc b/src/xenia/base/threading_linux.cc index 6f4a97584..3535f3011 100644 --- a/src/xenia/base/threading_linux.cc +++ b/src/xenia/base/threading_linux.cc @@ -13,9 +13,5 @@ #include namespace xe { -namespace threading { - -void MaybeYield() { pthread_yield(); } - -} // namespace threading +namespace threading {} // namespace threading } // namespace xe diff --git a/src/xenia/base/threading_posix.cc b/src/xenia/base/threading_posix.cc index 3089f11b8..c8d6f6654 100644 --- a/src/xenia/base/threading_posix.cc +++ b/src/xenia/base/threading_posix.cc @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -21,6 +22,9 @@ namespace xe { namespace threading { +// TODO(dougvj) +void EnableAffinityConfiguration() {} + // uint64_t ticks() { return mach_absolute_time(); } uint32_t current_thread_system_id() { @@ -32,9 +36,16 @@ void set_name(const std::string& name) { } void set_name(std::thread::native_handle_type handle, const std::string& name) { - pthread_setname_np(pthread_self(), name.c_str()); + pthread_setname_np(handle, name.c_str()); } +void MaybeYield() { + pthread_yield(); + __sync_synchronize(); +} + +void SyncMemory() { __sync_synchronize(); } + void Sleep(std::chrono::microseconds duration) { timespec rqtp = {time_t(duration.count() / 1000000), time_t(duration.count() % 1000)}; @@ -42,11 +53,124 @@ void Sleep(std::chrono::microseconds duration) { // TODO(benvanik): spin while rmtp >0? } -template -class PosixHandle : public T { +// TODO(dougvj) Not sure how to implement the equivalent of this on POSIX. +SleepResult AlertableSleep(std::chrono::microseconds duration) { + sleep(duration.count() / 1000); + return SleepResult::kSuccess; +} + +// TODO(dougvj) We can probably wrap this with pthread_key_t but the type of +// TlsHandle probably needs to be refactored +TlsHandle AllocateTlsHandle() { assert_always(); } + +bool FreeTlsHandle(TlsHandle handle) { return true; } + +uintptr_t GetTlsValue(TlsHandle handle) { assert_always(); } + +bool SetTlsValue(TlsHandle handle, uintptr_t value) { assert_always(); } + +// TODO(dougvj) +class PosixHighResolutionTimer : public HighResolutionTimer { public: - explicit PosixHandle(pthread_t handle) : handle_(handle) {} - ~PosixHandle() override {} + PosixHighResolutionTimer(std::function callback) + : callback_(callback) {} + ~PosixHighResolutionTimer() override {} + + bool Initialize(std::chrono::milliseconds period) { + assert_always(); + return false; + } + + private: + std::function callback_; +}; + +std::unique_ptr HighResolutionTimer::CreateRepeating( + std::chrono::milliseconds period, std::function callback) { + auto timer = std::make_unique(std::move(callback)); + if (!timer->Initialize(period)) { + return nullptr; + } + return std::unique_ptr(timer.release()); +} + +// TODO(dougvj) There really is no native POSIX handle for a single wait/signal +// construct pthreads is at a lower level with more handles for such a mechanism +// This simple wrapper class could function as our handle, but probably needs +// some more functionality +class PosixCondition { + public: + PosixCondition() : signal_(false) { + pthread_mutex_init(&mutex_, NULL); + pthread_cond_init(&cond_, NULL); + } + + ~PosixCondition() { + pthread_mutex_destroy(&mutex_); + pthread_cond_destroy(&cond_); + } + + void Signal() { + pthread_mutex_lock(&mutex_); + signal_ = true; + pthread_cond_broadcast(&cond_); + pthread_mutex_unlock(&mutex_); + } + + void Reset() { + pthread_mutex_lock(&mutex_); + signal_ = false; + pthread_mutex_unlock(&mutex_); + } + + bool Wait(unsigned int timeout_ms) { + // Assume 0 means no timeout, not instant timeout + if (timeout_ms == 0) { + Wait(); + } + struct timespec time_to_wait; + struct timeval now; + gettimeofday(&now, NULL); + + // Add the number of seconds we want to wait to the current time + time_to_wait.tv_sec = now.tv_sec + (timeout_ms / 1000); + // Add the number of nanoseconds we want to wait to the current nanosecond + // stride + long nsec = (now.tv_usec + (timeout_ms % 1000)) * 1000; + // If we overflowed the nanosecond count then we add a second + time_to_wait.tv_sec += nsec / 1000000000UL; + // We only add nanoseconds within the 1 second stride + time_to_wait.tv_nsec = nsec % 1000000000UL; + pthread_mutex_lock(&mutex_); + while (!signal_) { + int status = pthread_cond_timedwait(&cond_, &mutex_, &time_to_wait); + if (status == ETIMEDOUT) return false; // We timed out + } + pthread_mutex_unlock(&mutex_); + return true; // We didn't time out + } + + bool Wait() { + pthread_mutex_lock(&mutex_); + while (!signal_) { + pthread_cond_wait(&cond_, &mutex_); + } + pthread_mutex_unlock(&mutex_); + return true; // Did not time out; + } + + private: + bool signal_; + pthread_cond_t cond_; + pthread_mutex_t mutex_; +}; + +// Native posix thread handle +template +class PosixThreadHandle : public T { + public: + explicit PosixThreadHandle(pthread_t handle) : handle_(handle) {} + ~PosixThreadHandle() override {} protected: void* native_handle() const override { @@ -56,13 +180,134 @@ class PosixHandle : public T { pthread_t handle_; }; -class PosixThread : public PosixHandle { +// This is wraps a condition object as our handle because posix has no single +// native handle for higher level concurrency constructs such as semaphores +template +class PosixConditionHandle : public T { public: - explicit PosixThread(pthread_t handle) : PosixHandle(handle) {} + ~PosixConditionHandle() override {} + + protected: + void* native_handle() const override { + return reinterpret_cast(const_cast(&handle_)); + } + + PosixCondition handle_; +}; + +// TODO(dougvj) +WaitResult Wait(WaitHandle* wait_handle, bool is_alertable, + std::chrono::milliseconds timeout) { + assert_always(); + return WaitResult::kFailed; +} + +// TODO(dougvj) +WaitResult SignalAndWait(WaitHandle* wait_handle_to_signal, + WaitHandle* wait_handle_to_wait_on, bool is_alertable, + std::chrono::milliseconds timeout) { + assert_always(); + return WaitResult::kFailed; +} + +// TODO(dougvj) +std::pair WaitMultiple(WaitHandle* wait_handles[], + size_t wait_handle_count, + bool wait_all, bool is_alertable, + std::chrono::milliseconds timeout) { + assert_always(); + return std::pair(WaitResult::kFailed, 0); +} + +// TODO(dougvj) +class PosixEvent : public PosixConditionHandle { + public: + PosixEvent(bool initial_state, int auto_reset) { assert_always(); } + ~PosixEvent() override = default; + void Set() override { assert_always(); } + void Reset() override { assert_always(); } + void Pulse() override { assert_always(); } + + private: + PosixCondition condition_; +}; + +std::unique_ptr Event::CreateManualResetEvent(bool initial_state) { + return std::make_unique(PosixEvent(initial_state, false)); +} + +std::unique_ptr Event::CreateAutoResetEvent(bool initial_state) { + return std::make_unique(PosixEvent(initial_state, true)); +} + +// TODO(dougvj) +class PosixSemaphore : public PosixConditionHandle { + public: + PosixSemaphore(int initial_count, int maximum_count) { assert_always(); } + ~PosixSemaphore() override = default; + bool Release(int release_count, int* out_previous_count) override { + assert_always(); + return false; + } +}; + +std::unique_ptr Semaphore::Create(int initial_count, + int maximum_count) { + return std::make_unique(initial_count, maximum_count); +} + +// TODO(dougvj) +class PosixMutant : public PosixConditionHandle { + public: + PosixMutant(bool initial_owner) { assert_always(); } + ~PosixMutant() = default; + bool Release() override { + assert_always(); + return false; + } +}; + +std::unique_ptr Mutant::Create(bool initial_owner) { + return std::make_unique(initial_owner); +} + +// TODO(dougvj) +class PosixTimer : public PosixConditionHandle { + public: + PosixTimer(bool manual_reset) { assert_always(); } + ~PosixTimer() = default; + bool SetOnce(std::chrono::nanoseconds due_time, + std::function opt_callback) override { + assert_always(); + return false; + } + bool SetRepeating(std::chrono::nanoseconds due_time, + std::chrono::milliseconds period, + std::function opt_callback) override { + assert_always(); + return false; + } + bool Cancel() override { + assert_always(); + return false; + } +}; + +std::unique_ptr Timer::CreateManualResetTimer() { + return std::make_unique(true); +} + +std::unique_ptr Timer::CreateSynchronizationTimer() { + return std::make_unique(false); +} + +class PosixThread : public PosixThreadHandle { + public: + explicit PosixThread(pthread_t handle) : PosixThreadHandle(handle) {} ~PosixThread() = default; void set_name(std::string name) override { - // TODO(DrChat) + pthread_setname_np(handle_, name.c_str()); } uint32_t system_id() const override { return 0; } @@ -141,5 +386,20 @@ std::unique_ptr Thread::Create(CreationParameters params, return std::unique_ptr(new PosixThread(handle)); } +Thread* Thread::GetCurrentThread() { + if (current_thread_) { + return current_thread_.get(); + } + + pthread_t handle = pthread_self(); + + current_thread_ = std::make_unique(handle); + return current_thread_.get(); +} + +void Thread::Exit(int exit_code) { + pthread_exit(reinterpret_cast(exit_code)); +} + } // namespace threading } // namespace xe diff --git a/src/xenia/cpu/compiler/passes/data_flow_analysis_pass.cc b/src/xenia/cpu/compiler/passes/data_flow_analysis_pass.cc index a858e4329..622af656b 100644 --- a/src/xenia/cpu/compiler/passes/data_flow_analysis_pass.cc +++ b/src/xenia/cpu/compiler/passes/data_flow_analysis_pass.cc @@ -24,6 +24,7 @@ #pragma warning(pop) #else #include +#include #endif // XE_COMPILER_MSVC namespace xe { diff --git a/src/xenia/cpu/compiler/passes/value_reduction_pass.cc b/src/xenia/cpu/compiler/passes/value_reduction_pass.cc index 94a218219..02ec9e757 100644 --- a/src/xenia/cpu/compiler/passes/value_reduction_pass.cc +++ b/src/xenia/cpu/compiler/passes/value_reduction_pass.cc @@ -23,6 +23,7 @@ #pragma warning(pop) #else #include +#include #endif // XE_COMPILER_MSVC namespace xe { diff --git a/src/xenia/gpu/gl4/gl4_shader_cache.cc b/src/xenia/gpu/gl4/gl4_shader_cache.cc index 033ed1b5e..714de3e1d 100644 --- a/src/xenia/gpu/gl4/gl4_shader_cache.cc +++ b/src/xenia/gpu/gl4/gl4_shader_cache.cc @@ -184,4 +184,4 @@ GL4Shader* GL4ShaderCache::FindCachedShader(ShaderType shader_type, } // namespace gl4 } // namespace gpu -} // namespace xe \ No newline at end of file +} // namespace xe diff --git a/src/xenia/gpu/gl4/gl4_shader_cache.h b/src/xenia/gpu/gl4/gl4_shader_cache.h index 7bac4c084..9c5c77cb2 100644 --- a/src/xenia/gpu/gl4/gl4_shader_cache.h +++ b/src/xenia/gpu/gl4/gl4_shader_cache.h @@ -11,8 +11,10 @@ #define XENIA_GPU_GL4_SHADER_CACHE_H_ #include +#include #include #include +#include #include "xenia/gpu/xenos.h" @@ -57,4 +59,4 @@ class GL4ShaderCache { } // namespace gpu } // namespace xe -#endif // XENIA_GPU_GL4_SHADER_CACHE_H_ \ No newline at end of file +#endif // XENIA_GPU_GL4_SHADER_CACHE_H_ diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index bfccc052c..0b24a47c4 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2017 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -13,6 +13,7 @@ #include #include +#include #include "xenia/base/logging.h" #include "xenia/gpu/spirv/passes/control_flow_analysis_pass.h" @@ -337,8 +338,7 @@ void SpirvShaderTranslator::StartTranslation() { registers_ptr_, std::vector({b.makeUintConstant(0)})); auto r0 = b.createLoad(r0_ptr); - r0 = b.createCompositeInsert(vertex_idx, r0, vec4_float_type_, - std::vector({0})); + r0 = b.createCompositeInsert(vertex_idx, r0, vec4_float_type_, 0); b.createStore(r0, r0_ptr); } else { // Pixel inputs from vertex shader. diff --git a/src/xenia/gpu/vulkan/render_cache.cc b/src/xenia/gpu/vulkan/render_cache.cc index 19879fa53..3c556128c 100644 --- a/src/xenia/gpu/vulkan/render_cache.cc +++ b/src/xenia/gpu/vulkan/render_cache.cc @@ -763,6 +763,9 @@ bool RenderCache::ParseConfiguration(RenderConfiguration* config) { case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown: config->color[i].format = ColorRenderTargetFormat::k_2_10_10_10_FLOAT; break; + default: + // The rest are good + break; } } } else { @@ -843,7 +846,7 @@ bool RenderCache::ConfigureRenderPass(VkCommandBuffer command_buffer, color_key.edram_format = static_cast(config->color[i].format); target_color_attachments[i] = FindOrCreateTileView(command_buffer, color_key); - if (!target_color_attachments) { + if (!target_color_attachments[i]) { XELOGE("Failed to get tile view for color attachment"); return false; } @@ -906,6 +909,9 @@ CachedTileView* RenderCache::FindTileView(uint32_t base, uint32_t pitch, case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown: format = uint32_t(ColorRenderTargetFormat::k_2_10_10_10_FLOAT); break; + default: + // Other types as-is. + break; } } @@ -1145,6 +1151,9 @@ void RenderCache::BlitToImage(VkCommandBuffer command_buffer, case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown: format = uint32_t(ColorRenderTargetFormat::k_2_10_10_10_FLOAT); break; + default: + // Rest are OK + break; } } @@ -1258,6 +1267,9 @@ void RenderCache::ClearEDRAMColor(VkCommandBuffer command_buffer, case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown: format = ColorRenderTargetFormat::k_2_10_10_10_FLOAT; break; + default: + // Rest are OK + break; } uint32_t tile_width = num_samples == MsaaSamples::k4X ? 40 : 80; diff --git a/src/xenia/gpu/vulkan/render_cache.h b/src/xenia/gpu/vulkan/render_cache.h index e6074a119..6a53048b0 100644 --- a/src/xenia/gpu/vulkan/render_cache.h +++ b/src/xenia/gpu/vulkan/render_cache.h @@ -84,7 +84,7 @@ class CachedTileView { } VkExtent2D GetSize() const { - return {key.tile_width * 80ul, key.tile_height * 16ul}; + return {key.tile_width * 80u, key.tile_height * 16u}; } private: diff --git a/src/xenia/hid/hid_demo.cc b/src/xenia/hid/hid_demo.cc index 0f11a4922..5ccf0bf75 100644 --- a/src/xenia/hid/hid_demo.cc +++ b/src/xenia/hid/hid_demo.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2015 Ben Vanik. All rights reserved. * + * Copyright 2017 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -15,7 +15,6 @@ #include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/base/main.h" -#include "xenia/base/platform_win.h" #include "xenia/base/threading.h" #include "xenia/hid/input_system.h" #include "xenia/ui/gl/gl_provider.h" diff --git a/src/xenia/ui/file_picker_gtk.cc b/src/xenia/ui/file_picker_gtk.cc new file mode 100644 index 000000000..dce28772d --- /dev/null +++ b/src/xenia/ui/file_picker_gtk.cc @@ -0,0 +1,70 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/file_picker.h" + +#include +#include +#include +#include "xenia/base/assert.h" +#include "xenia/base/platform_linux.h" + +namespace xe { +namespace ui { + +class GtkFilePicker : public FilePicker { + public: + GtkFilePicker(); + ~GtkFilePicker() override; + + bool Show(void* parent_window_handle) override; + + private: +}; + +std::unique_ptr FilePicker::Create() { + return std::make_unique(); +} + +GtkFilePicker::GtkFilePicker() = default; + +GtkFilePicker::~GtkFilePicker() = default; + +bool GtkFilePicker::Show(void* parent_window_handle) { + // TODO(benvanik): FileSaveDialog. + assert_true(mode() == Mode::kOpen); + // TODO(benvanik): folder dialogs. + assert_true(type() == Type::kFile); + GtkWidget* dialog; + gint res; + + dialog = gtk_file_chooser_dialog_new( + "Open File", (GtkWindow*)parent_window_handle, + GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", + GTK_RESPONSE_ACCEPT, NULL); + + res = gtk_dialog_run(GTK_DIALOG(dialog)); + char* filename; + if (res == GTK_RESPONSE_ACCEPT) { + GtkFileChooser* chooser = GTK_FILE_CHOOSER(dialog); + filename = gtk_file_chooser_get_filename(chooser); + std::vector selected_files; + std::wstring_convert> converter; + std::wstring ws_filename = converter.from_bytes(filename); + selected_files.push_back(ws_filename); + set_selected_files(selected_files); + gtk_widget_destroy(dialog); + return true; + } + gtk_widget_destroy(dialog); + return false; +} + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/gl/gl.h b/src/xenia/ui/gl/gl.h index c2e35a907..796248803 100644 --- a/src/xenia/ui/gl/gl.h +++ b/src/xenia/ui/gl/gl.h @@ -23,6 +23,10 @@ extern "C" GLEWContext* glewGetContext(); // required. typedef struct WGLEWContextStruct WGLEWContext; extern "C" WGLEWContext* wglewGetContext(); -#endif // XE_PLATFORM_WIN32 +#elif XE_PLATFORM_LINUX +typedef struct GLXEWContextStruct GLXEWContext; +extern "C" GLXEWContext* glxewGetContext(); + +#endif #endif // XENIA_UI_GL_GL_H_ diff --git a/src/xenia/ui/gl/gl_context.cc b/src/xenia/ui/gl/gl_context.cc index c908aaf40..a1c94ef9e 100644 --- a/src/xenia/ui/gl/gl_context.cc +++ b/src/xenia/ui/gl/gl_context.cc @@ -17,14 +17,10 @@ #include "xenia/base/assert.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" -#include "xenia/base/platform_win.h" #include "xenia/base/profiling.h" #include "xenia/ui/gl/gl_immediate_drawer.h" #include "xenia/ui/window.h" -// TODO(benvanik): move win32 code to _win? -#include "third_party/GL/wglew.h" - DEFINE_bool(thread_safe_gl, false, "Only allow one GL context to be active at a time."); @@ -43,14 +39,9 @@ namespace xe { namespace ui { namespace gl { -static std::recursive_mutex global_gl_mutex_; +std::recursive_mutex GLContext::global_gl_mutex_; -thread_local GLEWContext* tls_glew_context_ = nullptr; -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) { +void GLContext::FatalGLError(std::string error) { xe::FatalError( error + "\nEnsure you have the latest drivers for your GPU and that it supports " @@ -58,223 +49,10 @@ void FatalGLError(std::string error) { "of supported GPUs."); } -std::unique_ptr GLContext::Create(GraphicsProvider* provider, - Window* target_window, - GLContext* share_context) { - auto context = - std::unique_ptr(new GLContext(provider, target_window)); - if (!context->Initialize(share_context)) { - return nullptr; - } - context->AssertExtensionsPresent(); - return context; -} - GLContext::GLContext(GraphicsProvider* provider, Window* target_window) - : GraphicsContext(provider, target_window) { - glew_context_.reset(new GLEWContext()); - wglew_context_.reset(new WGLEWContext()); -} + : GraphicsContext(provider, target_window) {} -GLContext::~GLContext() { - MakeCurrent(); - blitter_.Shutdown(); - immediate_drawer_.reset(); - ClearCurrent(); - if (glrc_) { - wglDeleteContext(glrc_); - } - if (dc_) { - ReleaseDC(HWND(target_window_->native_handle()), dc_); - } -} - -bool GLContext::Initialize(GLContext* share_context) { - dc_ = GetDC(HWND(target_window_->native_handle())); - - PIXELFORMATDESCRIPTOR pfd = {0}; - pfd.nSize = sizeof(pfd); - pfd.nVersion = 1; - pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; - pfd.iPixelType = PFD_TYPE_RGBA; - pfd.cColorBits = 32; - pfd.cDepthBits = 32; - pfd.iLayerType = PFD_MAIN_PLANE; - int pixel_format = ChoosePixelFormat(dc_, &pfd); - if (!pixel_format) { - FatalGLError("Unable to choose pixel format."); - return false; - } - if (!SetPixelFormat(dc_, pixel_format, &pfd)) { - FatalGLError("Unable to set pixel format."); - return false; - } - - HGLRC temp_context = wglCreateContext(dc_); - if (!temp_context) { - FatalGLError("Unable to create temporary GL context."); - return false; - } - wglMakeCurrent(dc_, temp_context); - - tls_glew_context_ = glew_context_.get(); - tls_wglew_context_ = wglew_context_.get(); - if (glewInit() != GLEW_OK) { - FatalGLError("Unable to initialize GLEW."); - return false; - } - if (wglewInit() != GLEW_OK) { - FatalGLError("Unable to initialize WGLEW."); - return false; - } - - if (!WGLEW_ARB_create_context) { - FatalGLError("WGL_ARG_create_context not supported by GL ICD."); - return false; - } - - if (GLEW_ARB_robustness) { - robust_access_supported_ = true; - } - - int context_flags = 0; - if (FLAGS_gl_debug) { - context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; - } - if (robust_access_supported_) { - context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; - } - - int attrib_list[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, - 4, - WGL_CONTEXT_MINOR_VERSION_ARB, - 5, - WGL_CONTEXT_FLAGS_ARB, - context_flags, - WGL_CONTEXT_PROFILE_MASK_ARB, - WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - robust_access_supported_ ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0, - 0}; - - glrc_ = wglCreateContextAttribsARB( - dc_, share_context ? share_context->glrc_ : nullptr, attrib_list); - wglMakeCurrent(nullptr, nullptr); - wglDeleteContext(temp_context); - if (!glrc_) { - FatalGLError("Unable to create real GL context."); - return false; - } - - if (!MakeCurrent()) { - FatalGLError("Could not make real GL context current."); - return false; - } - - XELOGI("Successfully created OpenGL context:"); - XELOGI(" GL_VENDOR: %s", glGetString(GL_VENDOR)); - XELOGI(" GL_VERSION: %s", glGetString(GL_VERSION)); - XELOGI(" GL_RENDERER: %s", glGetString(GL_RENDERER)); - XELOGI(" GL_SHADING_LANGUAGE_VERSION: %s", - glGetString(GL_SHADING_LANGUAGE_VERSION)); - - while (glGetError()) { - // Clearing errors. - } - - SetupDebugging(); - - if (!blitter_.Initialize()) { - FatalGLError("Unable to initialize blitter."); - ClearCurrent(); - return false; - } - - immediate_drawer_ = std::make_unique(this); - - ClearCurrent(); - - return true; -} - -std::unique_ptr GLContext::CreateOffscreen( - GraphicsProvider* provider, GLContext* parent_context) { - assert_not_null(parent_context->glrc_); - - HGLRC new_glrc = nullptr; - { - GraphicsContextLock context_lock(parent_context); - - int context_flags = 0; - if (FLAGS_gl_debug) { - context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; - } - - bool robust_access_supported = parent_context->robust_access_supported_; - if (robust_access_supported) { - context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; - } - - int attrib_list[] = { - WGL_CONTEXT_MAJOR_VERSION_ARB, - 4, - WGL_CONTEXT_MINOR_VERSION_ARB, - 5, - WGL_CONTEXT_FLAGS_ARB, - context_flags, - WGL_CONTEXT_PROFILE_MASK_ARB, - WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, - robust_access_supported ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0, - 0}; - new_glrc = wglCreateContextAttribsARB(parent_context->dc_, - parent_context->glrc_, attrib_list); - if (!new_glrc) { - FatalGLError("Could not create shared context."); - return nullptr; - } - } - - auto new_context = std::unique_ptr( - new GLContext(provider, parent_context->target_window_)); - new_context->glrc_ = new_glrc; - new_context->dc_ = - GetDC(HWND(parent_context->target_window_->native_handle())); - new_context->robust_access_supported_ = - parent_context->robust_access_supported_; - if (!new_context->MakeCurrent()) { - FatalGLError("Could not make new GL context current."); - return nullptr; - } - if (!glGetString(GL_EXTENSIONS)) { - new_context->ClearCurrent(); - FatalGLError("New GL context did not have extensions."); - return nullptr; - } - - if (glewInit() != GLEW_OK) { - new_context->ClearCurrent(); - FatalGLError("Unable to initialize GLEW on shared context."); - return nullptr; - } - if (wglewInit() != GLEW_OK) { - new_context->ClearCurrent(); - FatalGLError("Unable to initialize WGLEW on shared context."); - return nullptr; - } - - new_context->SetupDebugging(); - - if (!new_context->blitter_.Initialize()) { - FatalGLError("Unable to initialize blitter on shared context."); - return nullptr; - } - - new_context->ClearCurrent(); - - return new_context; -} +GLContext::~GLContext() {} void GLContext::AssertExtensionsPresent() { if (!MakeCurrent()) { @@ -436,40 +214,6 @@ ImmediateDrawer* GLContext::immediate_drawer() { return immediate_drawer_.get(); } -bool GLContext::is_current() { - return tls_glew_context_ == glew_context_.get(); -} - -bool GLContext::MakeCurrent() { - SCOPE_profile_cpu_f("gpu"); - if (FLAGS_thread_safe_gl) { - global_gl_mutex_.lock(); - } - - if (!wglMakeCurrent(dc_, glrc_)) { - if (FLAGS_thread_safe_gl) { - global_gl_mutex_.unlock(); - } - FatalGLError("Unable to make GL context current."); - return false; - } - tls_glew_context_ = glew_context_.get(); - tls_wglew_context_ = wglew_context_.get(); - return true; -} - -void GLContext::ClearCurrent() { - if (!FLAGS_disable_gl_context_reset) { - wglMakeCurrent(nullptr, nullptr); - } - tls_glew_context_ = nullptr; - tls_wglew_context_ = nullptr; - - if (FLAGS_thread_safe_gl) { - global_gl_mutex_.unlock(); - } -} - bool GLContext::WasLost() { if (!robust_access_supported_) { // Can't determine if we lost the context. @@ -484,7 +228,7 @@ bool GLContext::WasLost() { if (status != GL_NO_ERROR) { // Graphics card reset. XELOGE("============= TDR detected on context %p! Context %s =============", - glrc_, status == GL_GUILTY_CONTEXT_RESET ? "guilty" : "innocent"); + handle(), status == GL_GUILTY_CONTEXT_RESET ? "guilty" : "innocent"); context_lost_ = true; return true; } @@ -492,24 +236,6 @@ bool GLContext::WasLost() { return false; } -void GLContext::BeginSwap() { - SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLContext::BeginSwap"); - float clear_color[] = {238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.0f}; - if (FLAGS_random_clear_color) { - clear_color[0] = - rand() / static_cast(RAND_MAX); // NOLINT(runtime/threadsafe_fn) - clear_color[1] = 1.0f; - clear_color[2] = 0.0f; - clear_color[3] = 1.0f; - } - glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color); -} - -void GLContext::EndSwap() { - SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLContext::EndSwap"); - SwapBuffers(dc_); -} - std::unique_ptr GLContext::Capture() { GraphicsContextLock lock(this); diff --git a/src/xenia/ui/gl/gl_context.h b/src/xenia/ui/gl/gl_context.h index a60e2d4ed..d71e209b2 100644 --- a/src/xenia/ui/gl/gl_context.h +++ b/src/xenia/ui/gl/gl_context.h @@ -13,6 +13,7 @@ #include #include +#include #include "xenia/ui/gl/blitter.h" #include "xenia/ui/gl/gl.h" @@ -20,9 +21,13 @@ DECLARE_bool(thread_safe_gl); -// TODO(benvanik): hide Win32 stuff. -typedef struct HDC__* HDC; -typedef struct HGLRC__* HGLRC; +DECLARE_bool(disable_gl_context_reset); + +DECLARE_bool(random_clear_color); + +DECLARE_bool(gl_debug); +DECLARE_bool(gl_debug_output); +DECLARE_bool(gl_debug_output_synchronous); namespace xe { namespace ui { @@ -37,18 +42,33 @@ class GLContext : public GraphicsContext { ImmediateDrawer* immediate_drawer() override; - bool is_current() override; - bool MakeCurrent() override; - void ClearCurrent() override; + virtual bool is_current() override = 0; + virtual bool MakeCurrent() override = 0; + virtual void ClearCurrent() override = 0; bool WasLost() override; - void BeginSwap() override; - void EndSwap() override; - + virtual void BeginSwap() override = 0; + virtual void EndSwap() override = 0; std::unique_ptr Capture() override; Blitter* blitter() { return &blitter_; } + protected: + Blitter blitter_; + std::unique_ptr immediate_drawer_; + + static std::recursive_mutex global_gl_mutex_; + bool context_lost_ = false; + bool robust_access_supported_ = false; + static void FatalGLError(std::string error); + virtual bool Initialize(GLContext* share_context) = 0; + virtual void* handle() = 0; + GLContext(GraphicsProvider* provider, Window* target_window); + void SetupDebugging(); + void AssertExtensionsPresent(); + void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, + GLsizei length, const GLchar* message); + private: friend class GLProvider; @@ -59,31 +79,11 @@ class GLContext : public GraphicsContext { GLContext* parent_context); private: - GLContext(GraphicsProvider* provider, Window* target_window); - - bool Initialize(GLContext* share_context); - void AssertExtensionsPresent(); - - void SetupDebugging(); - void DebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, - GLsizei length, const GLchar* message); static void GLAPIENTRY DebugMessageThunk(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* user_param); - - HDC dc_ = nullptr; - HGLRC glrc_ = nullptr; - - std::unique_ptr glew_context_; - std::unique_ptr wglew_context_; - - Blitter blitter_; - std::unique_ptr immediate_drawer_; - - bool context_lost_ = false; - bool robust_access_supported_ = false; }; } // namespace gl diff --git a/src/xenia/ui/gl/gl_context_win.cc b/src/xenia/ui/gl/gl_context_win.cc new file mode 100644 index 000000000..0ca66146c --- /dev/null +++ b/src/xenia/ui/gl/gl_context_win.cc @@ -0,0 +1,315 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/gl/gl_context_win.h" + +#include + +#include +#include + +#include "xenia/base/assert.h" +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/base/platform_win.h" +#include "xenia/base/profiling.h" +#include "xenia/ui/gl/gl_immediate_drawer.h" +#include "xenia/ui/window.h" + +#include "third_party/GL/wglew.h" + +namespace xe { +namespace ui { +namespace gl { + +thread_local GLEWContext* tls_glew_context_ = nullptr; +thread_local WGLEWContext* tls_wglew_context_ = nullptr; +extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; } +extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; } + +std::unique_ptr GLContext::Create(GraphicsProvider* provider, + Window* target_window, + GLContext* share_context) { + auto context = + std::unique_ptr(new WGLContext(provider, target_window)); + if (!context->Initialize(share_context)) { + return nullptr; + } + context->AssertExtensionsPresent(); + return context; +} + +std::unique_ptr GLContext::CreateOffscreen( + GraphicsProvider* provider, GLContext* parent_context) { + return WGLContext::CreateOffscreen(provider, + static_cast(parent_context)); +} + +WGLContext::WGLContext(GraphicsProvider* provider, Window* target_window) + : GLContext(provider, target_window) { + glew_context_.reset(new GLEWContext()); + wglew_context_.reset(new WGLEWContext()); +} + +WGLContext::~WGLContext() { + MakeCurrent(); + blitter_.Shutdown(); + immediate_drawer_.reset(); + ClearCurrent(); + if (glrc_) { + wglDeleteContext(glrc_); + } + if (dc_) { + ReleaseDC(HWND(target_window_->native_handle()), dc_); + } +} + +bool WGLContext::Initialize(GLContext* share_context_) { + WGLContext* share_context = static_cast(share_context_); + dc_ = GetDC(HWND(target_window_->native_handle())); + + PIXELFORMATDESCRIPTOR pfd = {0}; + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 32; + pfd.cDepthBits = 32; + pfd.iLayerType = PFD_MAIN_PLANE; + int pixel_format = ChoosePixelFormat(dc_, &pfd); + if (!pixel_format) { + FatalGLError("Unable to choose pixel format."); + return false; + } + if (!SetPixelFormat(dc_, pixel_format, &pfd)) { + FatalGLError("Unable to set pixel format."); + return false; + } + + HGLRC temp_context = wglCreateContext(dc_); + if (!temp_context) { + FatalGLError("Unable to create temporary GL context."); + return false; + } + wglMakeCurrent(dc_, temp_context); + + tls_glew_context_ = glew_context_.get(); + tls_wglew_context_ = wglew_context_.get(); + if (glewInit() != GLEW_OK) { + FatalGLError("Unable to initialize GLEW."); + return false; + } + if (wglewInit() != GLEW_OK) { + FatalGLError("Unable to initialize WGLEW."); + return false; + } + + if (!WGLEW_ARB_create_context) { + FatalGLError("WGL_ARG_create_context not supported by GL ICD."); + return false; + } + + if (GLEW_ARB_robustness) { + robust_access_supported_ = true; + } + + int context_flags = 0; + if (FLAGS_gl_debug) { + context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; + } + if (robust_access_supported_) { + context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } + + int attrib_list[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, + 4, + WGL_CONTEXT_MINOR_VERSION_ARB, + 5, + WGL_CONTEXT_FLAGS_ARB, + context_flags, + WGL_CONTEXT_PROFILE_MASK_ARB, + WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + robust_access_supported_ ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0, + 0}; + + glrc_ = wglCreateContextAttribsARB( + dc_, share_context ? share_context->glrc_ : nullptr, attrib_list); + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(temp_context); + if (!glrc_) { + FatalGLError("Unable to create real GL context."); + return false; + } + + if (!MakeCurrent()) { + FatalGLError("Could not make real GL context current."); + return false; + } + + XELOGI("Successfully created OpenGL context:"); + XELOGI(" GL_VENDOR: %s", glGetString(GL_VENDOR)); + XELOGI(" GL_VERSION: %s", glGetString(GL_VERSION)); + XELOGI(" GL_RENDERER: %s", glGetString(GL_RENDERER)); + XELOGI(" GL_SHADING_LANGUAGE_VERSION: %s", + glGetString(GL_SHADING_LANGUAGE_VERSION)); + + while (glGetError()) { + // Clearing errors. + } + + SetupDebugging(); + + if (!blitter_.Initialize()) { + FatalGLError("Unable to initialize blitter."); + ClearCurrent(); + return false; + } + + immediate_drawer_ = std::make_unique(this); + + ClearCurrent(); + + return true; +} + +std::unique_ptr WGLContext::CreateOffscreen( + GraphicsProvider* provider, WGLContext* parent_context) { + assert_not_null(parent_context->glrc_); + + HGLRC new_glrc = nullptr; + { + GraphicsContextLock context_lock(parent_context); + + int context_flags = 0; + if (FLAGS_gl_debug) { + context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB; + } + + bool robust_access_supported = parent_context->robust_access_supported_; + if (robust_access_supported) { + context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } + + int attrib_list[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, + 4, + WGL_CONTEXT_MINOR_VERSION_ARB, + 5, + WGL_CONTEXT_FLAGS_ARB, + context_flags, + WGL_CONTEXT_PROFILE_MASK_ARB, + WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + robust_access_supported ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0, + 0}; + new_glrc = wglCreateContextAttribsARB(parent_context->dc_, + parent_context->glrc_, attrib_list); + if (!new_glrc) { + FatalGLError("Could not create shared context."); + return nullptr; + } + } + + auto new_context = std::unique_ptr( + new WGLContext(provider, parent_context->target_window_)); + new_context->glrc_ = new_glrc; + new_context->dc_ = + GetDC(HWND(parent_context->target_window_->native_handle())); + new_context->robust_access_supported_ = + parent_context->robust_access_supported_; + if (!new_context->MakeCurrent()) { + FatalGLError("Could not make new GL context current."); + return nullptr; + } + if (!glGetString(GL_EXTENSIONS)) { + new_context->ClearCurrent(); + FatalGLError("New GL context did not have extensions."); + return nullptr; + } + + if (glewInit() != GLEW_OK) { + new_context->ClearCurrent(); + FatalGLError("Unable to initialize GLEW on shared context."); + return nullptr; + } + if (wglewInit() != GLEW_OK) { + new_context->ClearCurrent(); + FatalGLError("Unable to initialize WGLEW on shared context."); + return nullptr; + } + + new_context->SetupDebugging(); + + if (!new_context->blitter_.Initialize()) { + FatalGLError("Unable to initialize blitter on shared context."); + return nullptr; + } + + new_context->ClearCurrent(); + + return new_context; +} + +bool WGLContext::is_current() { + return tls_glew_context_ == glew_context_.get(); +} + +bool WGLContext::MakeCurrent() { + SCOPE_profile_cpu_f("gpu"); + if (FLAGS_thread_safe_gl) { + global_gl_mutex_.lock(); + } + + if (!wglMakeCurrent(dc_, glrc_)) { + if (FLAGS_thread_safe_gl) { + global_gl_mutex_.unlock(); + } + FatalGLError("Unable to make GL context current."); + return false; + } + tls_glew_context_ = glew_context_.get(); + tls_wglew_context_ = wglew_context_.get(); + return true; +} + +void WGLContext::ClearCurrent() { + if (!FLAGS_disable_gl_context_reset) { + wglMakeCurrent(nullptr, nullptr); + } + tls_glew_context_ = nullptr; + tls_wglew_context_ = nullptr; + + if (FLAGS_thread_safe_gl) { + global_gl_mutex_.unlock(); + } +} + +void WGLContext::BeginSwap() { + SCOPE_profile_cpu_i("gpu", "xe::ui::gl::WGLContext::BeginSwap"); + float clear_color[] = {238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.0f}; + if (FLAGS_random_clear_color) { + clear_color[0] = + rand() / static_cast(RAND_MAX); // NOLINT(runtime/threadsafe_fn) + clear_color[1] = 1.0f; + clear_color[2] = 0.0f; + clear_color[3] = 1.0f; + } + glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color); +} + +void WGLContext::EndSwap() { + SCOPE_profile_cpu_i("gpu", "xe::ui::gl::WGLContext::EndSwap"); + SwapBuffers(dc_); +} + +} // namespace gl +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/gl/gl_context_win.h b/src/xenia/ui/gl/gl_context_win.h new file mode 100644 index 000000000..772de4d3e --- /dev/null +++ b/src/xenia/ui/gl/gl_context_win.h @@ -0,0 +1,64 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_GL_WGL_CONTEXT_H_ +#define XENIA_UI_GL_WGL_CONTEXT_H_ + +#include + +#include + +#include "xenia/ui/gl/blitter.h" +#include "xenia/ui/gl/gl.h" +#include "xenia/ui/gl/gl_context.h" +#include "xenia/ui/graphics_context.h" + +typedef struct HDC__* HDC; +typedef struct HGLRC__* HGLRC; + +namespace xe { +namespace ui { +namespace gl { + +class GLImmediateDrawer; +class GLProvider; + +class WGLContext : public GLContext { + public: + ~WGLContext() override; + + bool is_current() override; + bool MakeCurrent() override; + void ClearCurrent() override; + + void BeginSwap() override; + void EndSwap() override; + + protected: + friend class GLContext; + WGLContext(GraphicsProvider* provider, Window* target_window); + static std::unique_ptr CreateOffscreen( + GraphicsProvider* provider, WGLContext* parent_context); + + bool Initialize(GLContext* share_context) override; + void* handle() override { return glrc_; } + + private: + HDC dc_ = nullptr; + HGLRC glrc_ = nullptr; + + std::unique_ptr glew_context_; + std::unique_ptr wglew_context_; +}; + +} // namespace gl +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_GL_GL_CONTEXT_H_ diff --git a/src/xenia/ui/gl/gl_context_x11.cc b/src/xenia/ui/gl/gl_context_x11.cc new file mode 100644 index 000000000..fc4b3176b --- /dev/null +++ b/src/xenia/ui/gl/gl_context_x11.cc @@ -0,0 +1,323 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/gl/gl_context_x11.h" + +#include + +#include +#include +#include + +#include "third_party/GL/glxew.h" +#include "xenia/base/assert.h" +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/base/platform_linux.h" +#include "xenia/base/profiling.h" +#include "xenia/ui/gl/gl_immediate_drawer.h" +#include "xenia/ui/window.h" + +namespace xe { +namespace ui { +namespace gl { + +thread_local GLEWContext* tls_glew_context_ = nullptr; +thread_local GLXEWContext* tls_glxew_context_ = nullptr; +extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; } +extern "C" GLXEWContext* glxewGetContext() { return tls_glxew_context_; } + +std::unique_ptr GLContext::Create(GraphicsProvider* provider, + Window* target_window, + GLContext* share_context) { + auto context = + std::unique_ptr(new GLXContext(provider, target_window)); + if (!context->Initialize(share_context)) { + return nullptr; + } + context->AssertExtensionsPresent(); + return context; +} + +std::unique_ptr GLContext::CreateOffscreen( + GraphicsProvider* provider, GLContext* parent_context) { + return GLXContext::CreateOffscreen(provider, + static_cast(parent_context)); +} + +GLXContext::GLXContext(GraphicsProvider* provider, Window* target_window) + : GLContext(provider, target_window) { + glew_context_.reset(new GLEWContext()); + glxew_context_.reset(new GLXEWContext()); +} + +GLXContext::~GLXContext() { + MakeCurrent(); + blitter_.Shutdown(); + immediate_drawer_.reset(); + ClearCurrent(); + if (glx_context_) { + glXDestroyContext(disp_, glx_context_); + } + if (draw_area_) { + gtk_widget_destroy(draw_area_); + } +} + +bool GLXContext::Initialize(GLContext* share_context) { + GtkWidget* window = GTK_WIDGET(target_window_->native_handle()); + GtkWidget* draw_area = gtk_drawing_area_new(); + int32_t width; + int32_t height; + gtk_window_get_size(GTK_WINDOW(window), &width, &height); + gtk_widget_set_size_request(draw_area, width, height); + gtk_container_add(GTK_CONTAINER(window), draw_area); + GdkVisual* visual = gdk_screen_get_system_visual(gdk_screen_get_default()); + + GdkDisplay* gdk_display = gtk_widget_get_display(window); + Display* display = gdk_x11_display_get_xdisplay(gdk_display); + disp_ = display; + ::Window root = gdk_x11_get_default_root_xwindow(); + static int vis_attrib_list[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, + GLX_DOUBLEBUFFER, None}; + XVisualInfo* vi = glXChooseVisual(display, 0, vis_attrib_list); + if (vi == NULL) { + FatalGLError("No matching visuals for X display"); + return false; + } + + cmap_ = XCreateColormap(display, root, vi->visual, AllocNone); + + ::GLXContext temp_context = glXCreateContext(display, vi, NULL, GL_TRUE); + if (!temp_context) { + FatalGLError("Unable to create temporary GLX context"); + return false; + } + xid_ = GDK_WINDOW_XID(gtk_widget_get_window(window)); + glXMakeCurrent(display, xid_, temp_context); + + tls_glew_context_ = glew_context_.get(); + tls_glxew_context_ = glxew_context_.get(); + if (glewInit() != GLEW_OK) { + FatalGLError("Unable to initialize GLEW."); + return false; + } + if (glxewInit() != GLEW_OK) { + FatalGLError("Unable to initialize GLXEW."); + return false; + } + + if (!GLXEW_ARB_create_context) { + FatalGLError("GLX_ARB_create_context not supported by GL ICD."); + return false; + } + + if (GLEW_ARB_robustness) { + robust_access_supported_ = true; + } + + int context_flags = 0; + if (FLAGS_gl_debug) { + context_flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + } + if (robust_access_supported_) { + context_flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } + + int attrib_list[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, + 4, + GLX_CONTEXT_MINOR_VERSION_ARB, + 5, + GLX_CONTEXT_FLAGS_ARB, + context_flags, + GLX_CONTEXT_PROFILE_MASK_ARB, + GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + robust_access_supported_ ? GLX_LOSE_CONTEXT_ON_RESET_ARB : 0, + 0}; + GLXContext* share_context_glx = static_cast(share_context); + glx_context_ = glXCreateContextAttribsARB( + display, nullptr, + share_context ? share_context_glx->glx_context_ : nullptr, True, + attrib_list); + glXMakeCurrent(display, 0, nullptr); + glXDestroyContext(display, temp_context); + if (!glx_context_) { + FatalGLError("Unable to create real GL context."); + return false; + } + + if (!MakeCurrent()) { + FatalGLError("Could not make real GL context current."); + return false; + } + + XELOGI("Successfully created OpenGL context:"); + XELOGI(" GL_VENDOR: %s", glGetString(GL_VENDOR)); + XELOGI(" GL_VERSION: %s", glGetString(GL_VERSION)); + XELOGI(" GL_RENDERER: %s", glGetString(GL_RENDERER)); + XELOGI(" GL_SHADING_LANGUAGE_VERSION: %s", + glGetString(GL_SHADING_LANGUAGE_VERSION)); + + while (glGetError()) { + // Clearing errors. + } + + SetupDebugging(); + + if (!blitter_.Initialize()) { + FatalGLError("Unable to initialize blitter."); + ClearCurrent(); + return false; + } + + immediate_drawer_ = std::make_unique(this); + + ClearCurrent(); + + return true; +} + +std::unique_ptr GLXContext::CreateOffscreen( + GraphicsProvider* provider, GLXContext* parent_context) { + assert_not_null(parent_context->glx_context_); + + ::GLXContext new_glrc; + { + GraphicsContextLock context_lock(parent_context); + + int context_flags = 0; + if (FLAGS_gl_debug) { + context_flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + } + + bool robust_access_supported = parent_context->robust_access_supported_; + if (robust_access_supported) { + context_flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } + + int attrib_list[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, + 4, + GLX_CONTEXT_MINOR_VERSION_ARB, + 5, + GLX_CONTEXT_FLAGS_ARB, + context_flags, + GLX_CONTEXT_PROFILE_MASK_ARB, + GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + robust_access_supported ? GLX_LOSE_CONTEXT_ON_RESET_ARB : 0, + 0}; + new_glrc = glXCreateContextAttribsARB(parent_context->disp_, nullptr, + parent_context->glx_context_, True, + attrib_list); + if (!new_glrc) { + FatalGLError("Could not create shared context."); + return nullptr; + } + } + + auto new_context = std::unique_ptr( + new GLXContext(provider, parent_context->target_window_)); + new_context->glx_context_ = new_glrc; + new_context->window_ = parent_context->window_; + new_context->draw_area_ = parent_context->draw_area_; + new_context->disp_ = parent_context->disp_; + new_context->xid_ = parent_context->xid_; + new_context->robust_access_supported_ = + parent_context->robust_access_supported_; + if (!new_context->MakeCurrent()) { + FatalGLError("Could not make new GL context current."); + return nullptr; + } + if (!glGetString(GL_EXTENSIONS)) { + new_context->ClearCurrent(); + FatalGLError("New GL context did not have extensions."); + return nullptr; + } + + if (glewInit() != GLEW_OK) { + new_context->ClearCurrent(); + FatalGLError("Unable to initialize GLEW on shared context."); + return nullptr; + } + if (glxewInit() != GLEW_OK) { + new_context->ClearCurrent(); + FatalGLError("Unable to initialize GLXEW on shared context."); + return nullptr; + } + + new_context->SetupDebugging(); + + if (!new_context->blitter_.Initialize()) { + FatalGLError("Unable to initialize blitter on shared context."); + return nullptr; + } + + new_context->ClearCurrent(); + + return new_context; +} + +bool GLXContext::is_current() { + return tls_glew_context_ == glew_context_.get(); +} + +bool GLXContext::MakeCurrent() { + SCOPE_profile_cpu_f("gpu"); + if (FLAGS_thread_safe_gl) { + global_gl_mutex_.lock(); + } + + if (!glXMakeCurrent(disp_, xid_, glx_context_)) { + if (FLAGS_thread_safe_gl) { + global_gl_mutex_.unlock(); + } + FatalGLError("Unable to make GL context current."); + return false; + } + tls_glew_context_ = glew_context_.get(); + tls_glxew_context_ = glxew_context_.get(); + return true; +} + +void GLXContext::ClearCurrent() { + if (!FLAGS_disable_gl_context_reset) { + glXMakeCurrent(disp_, 0, nullptr); + } + tls_glew_context_ = nullptr; + tls_glxew_context_ = nullptr; + + if (FLAGS_thread_safe_gl) { + global_gl_mutex_.unlock(); + } +} + +void GLXContext::BeginSwap() { + SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLXContext::BeginSwap"); + float clear_color[] = {238 / 255.0f, 238 / 255.0f, 238 / 255.0f, 1.0f}; + if (FLAGS_random_clear_color) { + clear_color[0] = + rand() / static_cast(RAND_MAX); // NOLINT(runtime/threadsafe_fn) + clear_color[1] = 1.0f; + clear_color[2] = 0.0f; + clear_color[3] = 1.0f; + } + glClearNamedFramebufferfv(0, GL_COLOR, 0, clear_color); +} + +void GLXContext::EndSwap() { + SCOPE_profile_cpu_i("gpu", "xe::ui::gl::GLXContext::EndSwap"); + glXSwapBuffers(disp_, xid_); +} + +} // namespace gl +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/gl/gl_context_x11.h b/src/xenia/ui/gl/gl_context_x11.h new file mode 100644 index 000000000..6eea7fdb7 --- /dev/null +++ b/src/xenia/ui/gl/gl_context_x11.h @@ -0,0 +1,69 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_GL_GLX_CONTEXT_H_ +#define XENIA_UI_GL_GLX_CONTEXT_H_ + +#include + +#include + +#include "third_party/GL/glxew.h" +#include "xenia/base/platform_linux.h" +#include "xenia/ui/gl/blitter.h" +#include "xenia/ui/gl/gl.h" +#include "xenia/ui/gl/gl_context.h" +#include "xenia/ui/graphics_context.h" + +DECLARE_bool(thread_safe_gl); + +namespace xe { +namespace ui { +namespace gl { + +class GLImmediateDrawer; +class GLProvider; + +class GLXContext : public GLContext { + public: + ~GLXContext() override; + + bool is_current() override; + + bool MakeCurrent() override; + void ClearCurrent() override; + + void BeginSwap() override; + void EndSwap() override; + + protected: + static std::unique_ptr CreateOffscreen( + GraphicsProvider* provider, GLXContext* parent_context); + + bool Initialize(GLContext* share_context) override; + void* handle() override { return glx_context_; } + + private: + friend class GLContext; + GLXContext(GraphicsProvider* provider, Window* target_window); + std::unique_ptr glew_context_; + std::unique_ptr glxew_context_; + ::GLXContext glx_context_; + GtkWidget* window_; + GtkWidget* draw_area_; + Colormap cmap_; + Display* disp_; + int xid_; +}; + +} // namespace gl +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_GL_GL_CONTEXT_H_ diff --git a/src/xenia/ui/loop_gtk.cc b/src/xenia/ui/loop_gtk.cc new file mode 100644 index 000000000..8beb967e5 --- /dev/null +++ b/src/xenia/ui/loop_gtk.cc @@ -0,0 +1,78 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/ui/loop_gtk.h" + +#include "xenia/base/assert.h" + +namespace xe { +namespace ui { + +class PostedFn { + public: + explicit PostedFn(std::function fn) : fn_(std::move(fn)) {} + void Call() { fn_(); } + + private: + std::function fn_; +}; + +std::unique_ptr Loop::Create() { return std::make_unique(); } + +GTKLoop::GTKLoop() : thread_id_() { + gtk_init(nullptr, nullptr); + xe::threading::Fence init_fence; + thread_ = std::thread([&init_fence, this]() { + xe::threading::set_name("GTK Loop"); + + thread_id_ = std::this_thread::get_id(); + init_fence.Signal(); + + ThreadMain(); + + quit_fence_.Signal(); + }); + init_fence.Wait(); +} + +GTKLoop::~GTKLoop() { + Quit(); + thread_.join(); +} + +void GTKLoop::ThreadMain() { gtk_main(); } + +bool GTKLoop::is_on_loop_thread() { + return thread_id_ == std::this_thread::get_id(); +} + +gboolean _posted_fn_thunk(gpointer posted_fn) { + PostedFn* Fn = reinterpret_cast(posted_fn); + Fn->Call(); + return G_SOURCE_REMOVE; +} + +void GTKLoop::Post(std::function fn) { + assert_true(thread_id_ != std::thread::id()); + gdk_threads_add_idle(_posted_fn_thunk, + reinterpret_cast(new PostedFn(std::move(fn)))); +} + +void GTKLoop::PostDelayed(std::function fn, uint64_t delay_millis) { + gdk_threads_add_timeout( + delay_millis, _posted_fn_thunk, + reinterpret_cast(new PostedFn(std::move(fn)))); +} + +void GTKLoop::Quit() { assert_true(thread_id_ != std::thread::id()); } + +void GTKLoop::AwaitQuit() { quit_fence_.Wait(); } + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/loop_gtk.h b/src/xenia/ui/loop_gtk.h new file mode 100644 index 000000000..4e24d8915 --- /dev/null +++ b/src/xenia/ui/loop_gtk.h @@ -0,0 +1,50 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_LOOP_GTK_H_ +#define XENIA_UI_LOOP_GTK_H_ + +#include +#include +#include + +#include "xenia/base/platform_linux.h" +#include "xenia/base/threading.h" +#include "xenia/ui/loop.h" + +namespace xe { +namespace ui { + +class GTKWindow; + +class GTKLoop : public Loop { + public: + GTKLoop(); + ~GTKLoop() override; + + bool is_on_loop_thread() override; + + void Post(std::function fn) override; + void PostDelayed(std::function fn, uint64_t delay_millis) override; + + void Quit() override; + void AwaitQuit() override; + + private: + void ThreadMain(); + + std::thread::id thread_id_; + std::thread thread_; + xe::threading::Fence quit_fence_; +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_LOOP_GTK_H_ diff --git a/src/xenia/ui/menu_item.h b/src/xenia/ui/menu_item.h index 4cdc5cca2..ca45a5b8b 100644 --- a/src/xenia/ui/menu_item.h +++ b/src/xenia/ui/menu_item.h @@ -12,6 +12,7 @@ #include #include +#include #include #include "xenia/ui/ui_event.h" diff --git a/src/xenia/ui/vulkan/vulkan.h b/src/xenia/ui/vulkan/vulkan.h index 7a7e64f10..38a4400f7 100644 --- a/src/xenia/ui/vulkan/vulkan.h +++ b/src/xenia/ui/vulkan/vulkan.h @@ -16,6 +16,8 @@ #if XE_PLATFORM_WIN32 #define VK_USE_PLATFORM_WIN32_KHR 1 +#elif XE_PLATFORM_LINUX +#define VK_USE_PLATFORM_XCB_KHR 1 #else #error Platform not yet supported. #endif // XE_PLATFORM_WIN32 diff --git a/src/xenia/ui/vulkan/vulkan_context.cc b/src/xenia/ui/vulkan/vulkan_context.cc index 381fb0ab7..da3d323be 100644 --- a/src/xenia/ui/vulkan/vulkan_context.cc +++ b/src/xenia/ui/vulkan/vulkan_context.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2017 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -25,6 +25,10 @@ #include "xenia/ui/vulkan/vulkan_util.h" #include "xenia/ui/window.h" +#if XE_PLATFORM_LINUX +#include "xenia/ui/window_gtk.h" +#endif + namespace xe { namespace ui { namespace vulkan { @@ -61,6 +65,29 @@ bool VulkanContext::Initialize() { auto err = vkCreateWin32SurfaceKHR(*provider->instance(), &create_info, nullptr, &surface); CheckResult(err, "vkCreateWin32SurfaceKHR"); +#elif XE_PLATFORM_LINUX +#ifdef GDK_WINDOWING_X11 + GtkWidget* window_handle = + static_cast(target_window_->native_handle()); + GdkDisplay* gdk_display = gtk_widget_get_display(window_handle); + assert(GDK_IS_X11_DISPLAY(gdk_display)); + xcb_connection_t* connection = + XGetXCBConnection(gdk_x11_display_get_xdisplay(gdk_display)); + xcb_window_t window = + gdk_x11_window_get_xid(gtk_widget_get_window(window_handle)); + VkXcbSurfaceCreateInfoKHR create_info; + create_info.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; + create_info.pNext = nullptr; + create_info.flags = 0; + create_info.connection = static_cast( + target_window_->native_platform_handle()); + create_info.window = static_cast(window); + auto err = vkCreateXcbSurfaceKHR(*provider->instance(), &create_info, + nullptr, &surface); + CheckResult(err, "vkCreateXcbSurfaceKHR"); +#else +#error Unsupported GDK Backend on Linux. +#endif // GDK_WINDOWING_X11 #else #error Platform not yet implemented. #endif // XE_PLATFORM_WIN32 diff --git a/src/xenia/ui/vulkan/vulkan_device.cc b/src/xenia/ui/vulkan/vulkan_device.cc index 166a18b01..b8ded460e 100644 --- a/src/xenia/ui/vulkan/vulkan_device.cc +++ b/src/xenia/ui/vulkan/vulkan_device.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2017 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -12,6 +12,7 @@ #include #include +#include #include #include diff --git a/src/xenia/ui/vulkan/vulkan_instance.cc b/src/xenia/ui/vulkan/vulkan_instance.cc index cb7a3ee91..3c51dfa1a 100644 --- a/src/xenia/ui/vulkan/vulkan_instance.cc +++ b/src/xenia/ui/vulkan/vulkan_instance.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2016 Ben Vanik. All rights reserved. * + * Copyright 2017 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -26,6 +26,10 @@ #include "xenia/ui/vulkan/vulkan_util.h" #include "xenia/ui/window.h" +#if XE_PLATFORM_LINUX +#include "xenia/ui/window_gtk.h" +#endif + #define VK_API_VERSION VK_API_VERSION_1_0 namespace xe { @@ -385,6 +389,29 @@ bool VulkanInstance::QueryDevices(Window* any_target_window) { create_info.hwnd = static_cast(any_target_window->native_handle()); err = vkCreateWin32SurfaceKHR(handle, &create_info, nullptr, &any_surface); CheckResult(err, "vkCreateWin32SurfaceKHR"); +#elif XE_PLATFORM_LINUX +#ifdef GDK_WINDOWING_X11 + GtkWidget* window_handle = + static_cast(any_target_window->native_handle()); + GdkDisplay* gdk_display = gtk_widget_get_display(window_handle); + assert(GDK_IS_X11_DISPLAY(gdk_display)); + xcb_connection_t* connection = + XGetXCBConnection(gdk_x11_display_get_xdisplay(gdk_display)); + xcb_window_t window = + gdk_x11_window_get_xid(gtk_widget_get_window(window_handle)); + VkXcbSurfaceCreateInfoKHR create_info; + create_info.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; + create_info.pNext = nullptr; + create_info.flags = 0; + create_info.connection = static_cast( + any_target_window->native_platform_handle()); + create_info.window = static_cast(window); + auto err = + vkCreateXcbSurfaceKHR(handle, &create_info, nullptr, &any_surface); + CheckResult(err, "vkCreateXcbSurfaceKHR"); +#else +#error Unsupported GDK Backend on Linux. +#endif // GDK_WINDOWING_X11 #else #error Platform not yet implemented. #endif // XE_PLATFORM_WIN32 diff --git a/src/xenia/ui/window_demo.cc b/src/xenia/ui/window_demo.cc index 454864a92..13b3689af 100644 --- a/src/xenia/ui/window_demo.cc +++ b/src/xenia/ui/window_demo.cc @@ -15,7 +15,6 @@ #include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/base/main.h" -#include "xenia/base/platform_win.h" #include "xenia/base/profiling.h" #include "xenia/base/threading.h" #include "xenia/ui/graphics_provider.h" diff --git a/src/xenia/ui/window_gtk.cc b/src/xenia/ui/window_gtk.cc new file mode 100644 index 000000000..2e94f6ddc --- /dev/null +++ b/src/xenia/ui/window_gtk.cc @@ -0,0 +1,451 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + +#include "xenia/base/assert.h" +#include "xenia/base/logging.h" +#include "xenia/base/platform_linux.h" +#include "xenia/ui/window_gtk.h" + +namespace xe { +namespace ui { + +class FnWrapper { + public: + explicit FnWrapper(std::function fn) : fn_(std::move(fn)) {} + void Call() { fn_(); } + + private: + std::function fn_; +}; + +std::unique_ptr Window::Create(Loop* loop, const std::wstring& title) { + return std::make_unique(loop, title); +} + +GTKWindow::GTKWindow(Loop* loop, const std::wstring& title) + : Window(loop, title) {} + +GTKWindow::~GTKWindow() { + OnDestroy(); + if (window_) { + gtk_widget_destroy(window_); + window_ = nullptr; + } +} + +bool GTKWindow::Initialize() { return OnCreate(); } + +void gtk_event_handler_(GtkWidget* widget, GdkEvent* event, gpointer data) { + GTKWindow* window = reinterpret_cast(data); + switch (event->type) { + case GDK_OWNER_CHANGE: + window->HandleWindowOwnerChange(&(event->owner_change)); + break; + case GDK_VISIBILITY_NOTIFY: + window->HandleWindowVisibility(&(event->visibility)); + break; + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + window->HandleKeyboard(&(event->key)); + break; + case GDK_SCROLL: + case GDK_BUTTON_PRESS: + case GDK_MOTION_NOTIFY: + window->HandleMouse(&(event->any)); + break; + case GDK_FOCUS_CHANGE: + window->HandleWindowFocus(&(event->focus_change)); + break; + case GDK_CONFIGURE: + window->HandleWindowResize(&(event->configure)); + default: + // Do nothing + return; + } +} + +void GTKWindow::Create() { + window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window_), (gchar*)title_.c_str()); + gtk_window_set_default_size(GTK_WINDOW(window_), 1280, 720); + gtk_widget_show_all(window_); + g_signal_connect(G_OBJECT(window_), "destroy", G_CALLBACK(gtk_main_quit), + NULL); + g_signal_connect(G_OBJECT(window_), "event", G_CALLBACK(gtk_event_handler_), + reinterpret_cast(this)); +} + +bool GTKWindow::OnCreate() { + loop()->PostSynchronous([this]() { this->Create(); }); + return super::OnCreate(); +} + +void GTKWindow::OnDestroy() { super::OnDestroy(); } + +void GTKWindow::OnClose() { + if (!closing_ && window_) { + closing_ = true; + gtk_widget_destroy(window_); + window_ = nullptr; + } + super::OnClose(); +} + +bool GTKWindow::set_title(const std::wstring& title) { + if (!super::set_title(title)) { + return false; + } + gtk_window_set_title(GTK_WINDOW(window_), (gchar*)title.c_str()); + return true; +} + +bool GTKWindow::SetIcon(const void* buffer, size_t size) { + // TODO(dougvj) Set icon after changin buffer to the correct format. (the + // call is gtk_window_set_icon) + return false; +} + +bool GTKWindow::is_fullscreen() const { return fullscreen_; } + +void GTKWindow::ToggleFullscreen(bool fullscreen) { + if (fullscreen == is_fullscreen()) { + return; + } + + fullscreen_ = fullscreen; + + if (fullscreen) { + gtk_window_fullscreen(GTK_WINDOW(window_)); + } else { + gtk_window_unfullscreen(GTK_WINDOW(window_)); + } +} + +bool GTKWindow::is_bordered() const { + return gtk_window_get_decorated(GTK_WINDOW(window_)); +} + +void GTKWindow::set_bordered(bool enabled) { + if (is_fullscreen()) { + // Don't screw with the borders if we're fullscreen. + return; + } + + gtk_window_set_decorated(GTK_WINDOW(window_), enabled); +} + +void GTKWindow::set_cursor_visible(bool value) { + if (is_cursor_visible_ == value) { + return; + } + if (value) { + // TODO(dougvj) Show and hide cursor + } else { + } +} + +void GTKWindow::set_focus(bool value) { + if (has_focus_ == value) { + return; + } + if (window_) { + if (value) { + gtk_window_activate_focus(GTK_WINDOW(window_)); + } else { + // TODO(dougvj) Check to see if we need to do something here to unset + // the focus. + } + } else { + has_focus_ = value; + } +} + +void GTKWindow::Resize(int32_t width, int32_t height) { + gtk_window_resize(GTK_WINDOW(window_), width, height); +} + +void GTKWindow::Resize(int32_t left, int32_t top, int32_t right, + int32_t bottom) { + // TODO(dougvj) Verify that this is the desired behavior from this call + gtk_window_move(GTK_WINDOW(window_), left, top); + gtk_window_resize(GTK_WINDOW(window_), left - right, top - bottom); +} + +void GTKWindow::OnResize(UIEvent* e) { + int32_t width; + int32_t height; + gtk_window_get_size(GTK_WINDOW(window_), &width, &height); + if (width != width_ || height != height_) { + width_ = width; + height_ = height; + Layout(); + } + super::OnResize(e); +} + +void GTKWindow::Invalidate() { + super::Invalidate(); + // TODO(dougvj) I am not sure what this is supposed to do +} + +void GTKWindow::Close() { + if (closing_) { + return; + } + closing_ = true; + Close(); + OnClose(); +} + +void GTKWindow::OnMainMenuChange() { + // We need to store the old handle for detachment + static GtkWidget* box = nullptr; + auto main_menu = reinterpret_cast(main_menu_.get()); + if (main_menu && !is_fullscreen()) { + if (box) gtk_widget_destroy(box); + GtkWidget* menu = main_menu->handle(); + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); + gtk_box_pack_start(GTK_BOX(box), menu, FALSE, FALSE, 3); + gtk_container_add(GTK_CONTAINER(window_), box); + } +} + +bool GTKWindow::HandleWindowOwnerChange(GdkEventOwnerChange* event) { + if (event->type == GDK_OWNER_CHANGE) { + if (event->reason == GDK_OWNER_CHANGE_DESTROY) { + OnDestroy(); + } else if (event->reason == GDK_OWNER_CHANGE_CLOSE) { + closing_ = true; + Close(); + OnClose(); + } + return true; + } + return false; +} + +bool GTKWindow::HandleWindowResize(GdkEventConfigure* event) { + if (event->type == GDK_CONFIGURE) { + auto e = UIEvent(this); + OnResize(&e); + return true; + } + return false; +} + +bool GTKWindow::HandleWindowVisibility(GdkEventVisibility* event) { + // TODO(dougvj) The gdk docs say that this is deprecated because modern window + // managers composite everything and nothing is truly hidden. + if (event->type == GDK_VISIBILITY_NOTIFY) { + if (event->state == GDK_VISIBILITY_UNOBSCURED) { + auto e = UIEvent(this); + OnVisible(&e); + } else { + auto e = UIEvent(this); + OnHidden(&e); + } + return true; + } + return false; +} + +bool GTKWindow::HandleWindowFocus(GdkEventFocus* event) { + if (event->type == GDK_FOCUS_CHANGE) { + if (!event->in) { + has_focus_ = false; + auto e = UIEvent(this); + OnLostFocus(&e); + } else { + has_focus_ = true; + auto e = UIEvent(this); + OnGotFocus(&e); + } + return true; + } + return false; +} + +bool GTKWindow::HandleMouse(GdkEventAny* event) { + MouseEvent::Button button = MouseEvent::Button::kNone; + int32_t dx = 0; + int32_t dy = 0; + int32_t x = 0; + int32_t y = 0; + switch (event->type) { + default: + // Double click/etc? + return true; + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: { + GdkEventButton* e = reinterpret_cast(event); + switch (e->button) { + case 1: + button = MouseEvent::Button::kLeft; + break; + case 3: + button = MouseEvent::Button::kRight; + break; + case 2: + button = MouseEvent::Button::kMiddle; + break; + case 4: + button = MouseEvent::Button::kX1; + break; + case 5: + button = MouseEvent::Button::kX2; + break; + } + x = e->x; + y = e->y; + break; + } + case GDK_MOTION_NOTIFY: { + GdkEventMotion* e = reinterpret_cast(event); + x = e->x; + y = e->y; + break; + } + case GDK_SCROLL: { + GdkEventScroll* e = reinterpret_cast(event); + x = e->x; + y = e->y; + dx = e->delta_x; + dy = e->delta_y; + break; + } + } + + auto e = MouseEvent(this, button, x, y, dx, dy); + switch (event->type) { + case GDK_BUTTON_PRESS: + OnMouseDown(&e); + break; + case GDK_BUTTON_RELEASE: + OnMouseUp(&e); + break; + case GDK_MOTION_NOTIFY: + OnMouseMove(&e); + break; + case GDK_SCROLL: + OnMouseWheel(&e); + break; + default: + return false; + } + return e.is_handled(); +} + +bool GTKWindow::HandleKeyboard(GdkEventKey* event) { + unsigned int modifiers = event->state; + bool shift_pressed = modifiers & GDK_SHIFT_MASK; + bool ctrl_pressed = modifiers & GDK_CONTROL_MASK; + bool alt_pressed = modifiers & GDK_META_MASK; + bool super_pressed = modifiers & GDK_SUPER_MASK; + auto e = + KeyEvent(this, event->hardware_keycode, 1, event->type == GDK_KEY_RELEASE, + shift_pressed, ctrl_pressed, alt_pressed, super_pressed); + switch (event->type) { + case GDK_KEY_PRESS: + OnKeyDown(&e); + break; + case GDK_KEY_RELEASE: + OnKeyUp(&e); + break; + // TODO(dougvj) GDK doesn't have a KEY CHAR event, so we will have to + // figure out its equivalent here to call OnKeyChar(&e); + default: + return false; + } + return e.is_handled(); +} + +std::unique_ptr MenuItem::Create(Type type, + const std::wstring& text, + const std::wstring& hotkey, + std::function callback) { + return std::make_unique(type, text, hotkey, callback); +} + +static void _menu_activate_callback(GtkWidget* menu, gpointer data) { + auto fn = reinterpret_cast(data); + fn->Call(); + delete fn; +} + +GTKMenuItem::GTKMenuItem(Type type, const std::wstring& text, + const std::wstring& hotkey, + std::function callback) + : MenuItem(type, text, hotkey, std::move(callback)) { + switch (type) { + case MenuItem::Type::kNormal: + default: + menu_ = gtk_menu_bar_new(); + break; + case MenuItem::Type::kPopup: + menu_ = gtk_menu_item_new_with_label((gchar*)xe::to_string(text).c_str()); + break; + case MenuItem::Type::kSeparator: + menu_ = gtk_separator_menu_item_new(); + break; + case MenuItem::Type::kString: + auto full_name = text; + if (!hotkey.empty()) { + full_name += L"\t" + hotkey; + } + menu_ = gtk_menu_item_new_with_label( + (gchar*)xe::to_string(full_name).c_str()); + break; + } + if (GTK_IS_MENU_ITEM(menu_)) + g_signal_connect(menu_, "activate", G_CALLBACK(_menu_activate_callback), + (gpointer) new FnWrapper(callback)); +} + +GTKMenuItem::~GTKMenuItem() { + if (menu_) { + } +} + +void GTKMenuItem::OnChildAdded(MenuItem* generic_child_item) { + auto child_item = static_cast(generic_child_item); + switch (child_item->type()) { + case MenuItem::Type::kNormal: + // Nothing special. + break; + case MenuItem::Type::kPopup: + if (GTK_IS_MENU_ITEM(menu_)) { + assert(gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_)) == nullptr); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_), child_item->handle()); + } else { + gtk_menu_shell_append(GTK_MENU_SHELL(menu_), child_item->handle()); + } + break; + case MenuItem::Type::kSeparator: + case MenuItem::Type::kString: + assert(GTK_IS_MENU_ITEM(menu_)); + // Get sub menu and if it doesn't exist create it + GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_)); + if (submenu == nullptr) { + submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_), submenu); + } + gtk_menu_shell_append(GTK_MENU_SHELL(submenu), child_item->handle()); + break; + } +} + +// TODO(dougvj) +void GTKMenuItem::OnChildRemoved(MenuItem* generic_child_item) { + assert_always(); +} + +} // namespace ui +} // namespace xe diff --git a/src/xenia/ui/window_gtk.h b/src/xenia/ui/window_gtk.h new file mode 100644 index 000000000..b19248e18 --- /dev/null +++ b/src/xenia/ui/window_gtk.h @@ -0,0 +1,102 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2016 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_UI_WINDOW_GTK_H_ +#define XENIA_UI_WINDOW_GTK_H_ + +#include +#include + +#include "xenia/base/platform_linux.h" +#include "xenia/ui/menu_item.h" +#include "xenia/ui/window.h" + +namespace xe { +namespace ui { + +class GTKWindow : public Window { + using super = Window; + + public: + GTKWindow(Loop* loop, const std::wstring& title); + ~GTKWindow() override; + + NativePlatformHandle native_platform_handle() const override { + return nullptr; + } + NativeWindowHandle native_handle() const override { return window_; } + + bool set_title(const std::wstring& title) override; + + bool SetIcon(const void* buffer, size_t size) override; + + bool is_fullscreen() const override; + void ToggleFullscreen(bool fullscreen) override; + + bool is_bordered() const override; + void set_bordered(bool enabled) override; + + void set_cursor_visible(bool value) override; + void set_focus(bool value) override; + + void Resize(int32_t width, int32_t height) override; + void Resize(int32_t left, int32_t top, int32_t right, + int32_t bottom) override; + + bool Initialize() override; + void Invalidate() override; + void Close() override; + + protected: + bool OnCreate() override; + void OnMainMenuChange() override; + void OnDestroy() override; + void OnClose() override; + + void OnResize(UIEvent* e) override; + + private: + void Create(); + GtkWidget* window_; + + friend void gtk_event_handler_(GtkWidget*, GdkEvent*, gpointer); + bool HandleMouse(GdkEventAny* event); + bool HandleKeyboard(GdkEventKey* event); + bool HandleWindowResize(GdkEventConfigure* event); + bool HandleWindowFocus(GdkEventFocus* event); + bool HandleWindowVisibility(GdkEventVisibility* event); + bool HandleWindowOwnerChange(GdkEventOwnerChange* event); + + bool closing_ = false; + bool fullscreen_ = false; +}; + +class GTKMenuItem : public MenuItem { + public: + GTKMenuItem(Type type, const std::wstring& text, const std::wstring& hotkey, + std::function callback); + ~GTKMenuItem() override; + + GtkWidget* handle() { return menu_; } + using MenuItem::OnSelected; + + protected: + void OnChildAdded(MenuItem* child_item) override; + void OnChildRemoved(MenuItem* child_item) override; + GTKMenuItem* parent_ = nullptr; + GTKMenuItem* child_ = nullptr; + + private: + GtkWidget* menu_ = nullptr; +}; + +} // namespace ui +} // namespace xe + +#endif // XENIA_UI_WINDOW_WIN_H_ diff --git a/third_party/glslang-spirv/SpvBuilder.cpp b/third_party/glslang-spirv/SpvBuilder.cpp index 13a6c946a..36bededc5 100644 --- a/third_party/glslang-spirv/SpvBuilder.cpp +++ b/third_party/glslang-spirv/SpvBuilder.cpp @@ -77,7 +77,7 @@ Id Builder::import(const char* name) { Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport); import->addStringOperand(name); - + imports.push_back(std::unique_ptr(import)); return import->getResultId(); } @@ -244,7 +244,7 @@ Id Builder::makeStructResultType(Id type0, Id type1) type = groupedTypes[OpTypeStruct][t]; if (type->getNumOperands() != 2) continue; - if (type->getIdOperand(0) != type0 || + if (type->getIdOperand(0) != type0 || type->getIdOperand(1) != type1) continue; return type->getResultId(); @@ -626,7 +626,7 @@ Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, bool Builder::isConstantOpCode(Op opcode) const { switch (opcode) { - case OpUndef: + case OpUndef: case OpConstantTrue: case OpConstantFalse: case OpConstant: @@ -743,7 +743,7 @@ Id Builder::makeDoubleConstant(double d, bool specConstant) return c->getResultId(); } -Id Builder::findCompositeConstant(Op typeClass, std::vector& comps) const +Id Builder::findCompositeConstant(Op typeClass, const std::vector& comps) const { Instruction* constant = 0; bool found = false; @@ -772,7 +772,7 @@ Id Builder::findCompositeConstant(Op typeClass, std::vector& comps) const } // Comments in header -Id Builder::makeCompositeConstant(Id typeId, std::vector& members, bool specConstant) +Id Builder::makeCompositeConstant(Id typeId, const std::vector& members, bool specConstant) { Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite; assert(typeId); @@ -1022,7 +1022,7 @@ Id Builder::createLoad(Id lValue) } // Comments in header -Id Builder::createAccessChain(StorageClass storageClass, Id base, std::vector& offsets) +Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector& offsets) { // Figure out the final resulting type. spv::Id typeId = getTypeId(base); @@ -1089,7 +1089,7 @@ Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned i return insert->getResultId(); } -Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, std::vector& indexes) +Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector& indexes) { Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); insert->addIdOperand(object); @@ -1210,7 +1210,7 @@ Id Builder::createOp(Op opCode, Id typeId, const std::vector& operands) return op->getResultId(); } -Id Builder::createFunctionCall(spv::Function* function, std::vector& args) +Id Builder::createFunctionCall(spv::Function* function, const std::vector& args) { Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall); op->addIdOperand(function->getId()); @@ -1647,7 +1647,7 @@ Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, b } // OpCompositeConstruct -Id Builder::createCompositeConstruct(Id typeId, std::vector& constituents) +Id Builder::createCompositeConstruct(Id typeId, const std::vector& constituents) { assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && getNumTypeConstituents(typeId) == (int)constituents.size())); @@ -1848,7 +1848,7 @@ void Builder::If::makeEndIf() } // Comments in header -void Builder::makeSwitch(Id selector, int numSegments, std::vector& caseValues, std::vector& valueIndexToSegment, int defaultSegment, +void Builder::makeSwitch(Id selector, int numSegments, const std::vector& caseValues, const std::vector& valueIndexToSegment, int defaultSegment, std::vector& segmentBlocks) { Function& function = buildPoint->getParent(); diff --git a/third_party/glslang-spirv/SpvBuilder.h b/third_party/glslang-spirv/SpvBuilder.h index 7eae4fe91..28f846578 100644 --- a/third_party/glslang-spirv/SpvBuilder.h +++ b/third_party/glslang-spirv/SpvBuilder.h @@ -192,7 +192,7 @@ public: Id makeDoubleConstant(double d, bool specConstant = false); // Turn the array of constants into a proper spv constant of the requested type. - Id makeCompositeConstant(Id type, std::vector& comps, bool specConst = false); + Id makeCompositeConstant(Id type, const std::vector& comps, bool specConst = false); // Methods for adding information outside the CFG. Instruction* addEntryPoint(ExecutionModel, Function*, const char* name); @@ -240,7 +240,7 @@ public: Id createLoad(Id lValue); // Create an OpAccessChain instruction - Id createAccessChain(StorageClass, Id base, std::vector& offsets); + Id createAccessChain(StorageClass, Id base, const std::vector& offsets); // Create an OpArrayLength instruction Id createArrayLength(Id base, unsigned int member); @@ -249,7 +249,7 @@ public: Id createCompositeExtract(Id composite, Id typeId, unsigned index); Id createCompositeExtract(Id composite, Id typeId, std::vector& indexes); Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); - Id createCompositeInsert(Id object, Id composite, Id typeId, std::vector& indexes); + Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector& indexes); Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex); Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex); @@ -263,7 +263,7 @@ public: Id createBinOp(Op, Id typeId, Id operand1, Id operand2); Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); Id createOp(Op, Id typeId, const std::vector& operands); - Id createFunctionCall(spv::Function*, std::vector&); + Id createFunctionCall(spv::Function*, const std::vector&); // Take an rvalue (source) and a set of channels to extract from it to // make a new rvalue, which is returned. @@ -296,7 +296,7 @@ public: // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'. // The type of the created vector is a vector of components of the same type as the scalar. // - // Note: One of the arguments will change, with the result coming back that way rather than + // Note: One of the arguments will change, with the result coming back that way rather than // through the return value. void promoteScalar(Decoration precision, Id& left, Id& right); @@ -341,7 +341,7 @@ public: Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */); // OpCompositeConstruct - Id createCompositeConstruct(Id typeId, std::vector& constituents); + Id createCompositeConstruct(Id typeId, const std::vector& constituents); // vector or scalar constructor Id createConstructor(Decoration precision, const std::vector& sources, Id resultTypeId); @@ -383,7 +383,7 @@ public: // Returns the right set of basic blocks to start each code segment with, so that the caller's // recursion stack can hold the memory for it. // - void makeSwitch(Id condition, int numSegments, std::vector& caseValues, std::vector& valueToSegment, int defaultSegment, + void makeSwitch(Id condition, int numSegments, const std::vector& caseValues, const std::vector& valueToSegment, int defaultSegment, std::vector& segmentBB); // return argument // Add a branch to the innermost switch's merge block. @@ -525,7 +525,7 @@ public: Id makeIntConstant(Id typeId, unsigned value, bool specConstant); Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) const; Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) const; - Id findCompositeConstant(Op typeClass, std::vector& comps) const; + Id findCompositeConstant(Op typeClass, const std::vector& comps) const; Id collapseAccessChain(); void transferAccessChainSwizzle(bool dynamic); void simplifyAccessChainSwizzle(); diff --git a/third_party/vulkan/loader/premake5.lua b/third_party/vulkan/loader/premake5.lua index 15d05f44c..e653b278b 100644 --- a/third_party/vulkan/loader/premake5.lua +++ b/third_party/vulkan/loader/premake5.lua @@ -30,6 +30,9 @@ project("vulkan-loader") removefiles("dirent_on_windows.c") filter("platforms:Linux") defines({ + "VK_USE_PLATFORM_XCB_KHR", [[SYSCONFDIR="\"/etc\""]], + [[FALLBACK_CONFIG_DIRS="\"/etc/xdg\""]], [[DATADIR="\"/usr/share\""]], + [[FALLBACK_DATA_DIRS="\"/usr/share:/usr/local/share\""]], }) diff --git a/tools/build/scripts/platform_files.lua b/tools/build/scripts/platform_files.lua index b6d2e9ecc..4c887115e 100644 --- a/tools/build/scripts/platform_files.lua +++ b/tools/build/scripts/platform_files.lua @@ -13,6 +13,8 @@ local function match_platform_files(base_path, base_match) removefiles({base_path.."/".."**_test.cc"}) removefiles({base_path.."/".."**_posix.h", base_path.."/".."**_posix.cc"}) removefiles({base_path.."/".."**_linux.h", base_path.."/".."**_linux.cc"}) + removefiles({base_path.."/".."**_x11.h", base_path.."/".."**_x11.cc"}) + removefiles({base_path.."/".."**_gtk.h", base_path.."/".."**_gtk.cc"}) removefiles({base_path.."/".."**_mac.h", base_path.."/".."**_mac.cc"}) removefiles({base_path.."/".."**_win.h", base_path.."/".."**_win.cc"}) filter("platforms:Windows") @@ -26,6 +28,10 @@ local function match_platform_files(base_path, base_match) base_path.."/"..base_match.."_posix.cc", base_path.."/"..base_match.."_linux.h", base_path.."/"..base_match.."_linux.cc", + base_path.."/"..base_match.."_x11.h", + base_path.."/"..base_match.."_x11.cc", + base_path.."/"..base_match.."_gtk.h", + base_path.."/"..base_match.."_gtk.cc", }) filter({}) end diff --git a/xb b/xb new file mode 120000 index 000000000..e841366c6 --- /dev/null +++ b/xb @@ -0,0 +1 @@ +xenia-build \ No newline at end of file