summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml10
-rw-r--r--docs/building.md8
-rw-r--r--premake5.lua18
-rw-r--r--src/xenia/app/premake5.lua18
-rw-r--r--src/xenia/base/bit_field.h5
-rw-r--r--src/xenia/base/exception_handler_linux.cc35
-rw-r--r--src/xenia/base/filesystem_posix.cc136
-rw-r--r--src/xenia/base/math.h2
-rw-r--r--src/xenia/base/platform_linux.cc21
-rw-r--r--src/xenia/base/platform_linux.h32
-rw-r--r--src/xenia/base/profiling.cc1
-rw-r--r--src/xenia/base/threading.h1
-rw-r--r--src/xenia/base/threading_linux.cc6
-rw-r--r--src/xenia/base/threading_posix.cc274
-rw-r--r--src/xenia/cpu/compiler/passes/data_flow_analysis_pass.cc1
-rw-r--r--src/xenia/cpu/compiler/passes/value_reduction_pass.cc1
-rw-r--r--src/xenia/gpu/gl4/gl4_shader_cache.cc2
-rw-r--r--src/xenia/gpu/gl4/gl4_shader_cache.h4
-rw-r--r--src/xenia/gpu/spirv_shader_translator.cc6
-rw-r--r--src/xenia/gpu/vulkan/render_cache.cc14
-rw-r--r--src/xenia/gpu/vulkan/render_cache.h2
-rw-r--r--src/xenia/hid/hid_demo.cc3
-rw-r--r--src/xenia/ui/file_picker_gtk.cc70
-rw-r--r--src/xenia/ui/gl/gl.h6
-rw-r--r--src/xenia/ui/gl/gl_context.cc284
-rw-r--r--src/xenia/ui/gl/gl_context.h58
-rw-r--r--src/xenia/ui/gl/gl_context_win.cc315
-rw-r--r--src/xenia/ui/gl/gl_context_win.h64
-rw-r--r--src/xenia/ui/gl/gl_context_x11.cc323
-rw-r--r--src/xenia/ui/gl/gl_context_x11.h69
-rw-r--r--src/xenia/ui/loop_gtk.cc78
-rw-r--r--src/xenia/ui/loop_gtk.h50
-rw-r--r--src/xenia/ui/menu_item.h1
-rw-r--r--src/xenia/ui/vulkan/vulkan.h2
-rw-r--r--src/xenia/ui/vulkan/vulkan_context.cc29
-rw-r--r--src/xenia/ui/vulkan/vulkan_device.cc3
-rw-r--r--src/xenia/ui/vulkan/vulkan_instance.cc29
-rw-r--r--src/xenia/ui/window_demo.cc1
-rw-r--r--src/xenia/ui/window_gtk.cc451
-rw-r--r--src/xenia/ui/window_gtk.h102
-rw-r--r--third_party/glslang-spirv/SpvBuilder.cpp20
-rw-r--r--third_party/glslang-spirv/SpvBuilder.h16
-rw-r--r--third_party/vulkan/loader/premake5.lua3
-rw-r--r--tools/build/scripts/platform_files.lua6
l---------xb1
45 files changed, 2213 insertions, 368 deletions
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,11 +87,28 @@ 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({
"--std=c++11",
@@ -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<std::is_enum<T>::value, std::underlying_type<T>,
- std::identity<T>>::type::type value_type;
+ std::remove_reference<T>>::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<ExceptionHandler::Handler, void*> 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 <dirent.h>
+#include <fcntl.h>
+#include <ftw.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -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> 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<PosixFileHandle>(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<FileInfo> ListFiles(const std::wstring& path) {
std::vector<FileInfo> result;
@@ -43,18 +163,20 @@ std::vector<FileInfo> 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<FileInfo> 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 <algorithm>
+#include <cmath>
#include <cstdint>
#include <cstring>
#include <type_traits>
-
#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 <stdlib.h>
+#include <string>
+
+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 <X11/Xlib-xcb.h>
+#include <X11/Xlib.h>
+#include <X11/Xos.h>
+#include <X11/Xutil.h>
+#include <xcb/xcb.h>
+
+// 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 <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#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 <climits>
#include <condition_variable>
#include <cstdint>
+#include <functional>
#include <memory>
#include <mutex>
#include <string>
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 <time.h>
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 <pthread.h>
#include <sys/syscall.h>
+#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
@@ -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?
}
+// 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:
+ PosixHighResolutionTimer(std::function<void()> callback)
+ : callback_(callback) {}
+ ~PosixHighResolutionTimer() override {}
+
+ bool Initialize(std::chrono::milliseconds period) {
+ assert_always();
+ return false;
+ }
+
+ private:
+ std::function<void()> callback_;
+};
+
+std::unique_ptr<HighResolutionTimer> HighResolutionTimer::CreateRepeating(
+ std::chrono::milliseconds period, std::function<void()> callback) {
+ auto timer = std::make_unique<PosixHighResolutionTimer>(std::move(callback));
+ if (!timer->Initialize(period)) {
+ return nullptr;
+ }
+ return std::unique_ptr<HighResolutionTimer>(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 <typename T>
-class PosixHandle : public T {
+class PosixThreadHandle : public T {
public:
- explicit PosixHandle(pthread_t handle) : handle_(handle) {}
- ~PosixHandle() override {}
+ 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<Thread> {
+// 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 <typename T>
+class PosixConditionHandle : public T {
+ public:
+ ~PosixConditionHandle() override {}
+
+ protected:
+ void* native_handle() const override {
+ return reinterpret_cast<void*>(const_cast<PosixCondition*>(&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<WaitResult, size_t> 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, size_t>(WaitResult::kFailed, 0);
+}
+
+// TODO(dougvj)
+class PosixEvent : public PosixConditionHandle<Event> {
+ 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> Event::CreateManualResetEvent(bool initial_state) {
+ return std::make_unique<PosixEvent>(PosixEvent(initial_state, false));
+}
+
+std::unique_ptr<Event> Event::CreateAutoResetEvent(bool initial_state) {
+ return std::make_unique<PosixEvent>(PosixEvent(initial_state, true));
+}
+
+// TODO(dougvj)
+class PosixSemaphore : public PosixConditionHandle<Semaphore> {
+ 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> Semaphore::Create(int initial_count,
+ int maximum_count) {
+ return std::make_unique<PosixSemaphore>(initial_count, maximum_count);
+}
+
+// TODO(dougvj)
+class PosixMutant : public PosixConditionHandle<Mutant> {
+ public:
+ PosixMutant(bool initial_owner) { assert_always(); }
+ ~PosixMutant() = default;
+ bool Release() override {
+ assert_always();
+ return false;
+ }
+};
+
+std::unique_ptr<Mutant> Mutant::Create(bool initial_owner) {
+ return std::make_unique<PosixMutant>(initial_owner);
+}
+
+// TODO(dougvj)
+class PosixTimer : public PosixConditionHandle<Timer> {
+ public:
+ PosixTimer(bool manual_reset) { assert_always(); }
+ ~PosixTimer() = default;
+ bool SetOnce(std::chrono::nanoseconds due_time,
+ std::function<void()> opt_callback) override {
+ assert_always();
+ return false;
+ }
+ bool SetRepeating(std::chrono::nanoseconds due_time,
+ std::chrono::milliseconds period,
+ std::function<void()> opt_callback) override {
+ assert_always();
+ return false;
+ }
+ bool Cancel() override {
+ assert_always();
+ return false;
+ }
+};
+
+std::unique_ptr<Timer> Timer::CreateManualResetTimer() {
+ return std::make_unique<PosixTimer>(true);
+}
+
+std::unique_ptr<Timer> Timer::CreateSynchronizationTimer() {
+ return std::make_unique<PosixTimer>(false);
+}
+
+class PosixThread : public PosixThreadHandle<Thread> {
public:
- explicit PosixThread(pthread_t handle) : PosixHandle(handle) {}
+ 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> Thread::Create(CreationParameters params,
return std::unique_ptr<PosixThread>(new PosixThread(handle));
}
+Thread* Thread::GetCurrentThread() {
+ if (current_thread_) {
+ return current_thread_.get();
+ }
+
+ pthread_t handle = pthread_self();
+
+ current_thread_ = std::make_unique<PosixThread>(handle);
+ return current_thread_.get();
+}
+
+void Thread::Exit(int exit_code) {
+ pthread_exit(reinterpret_cast<void*>(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 <llvm/ADT/BitVector.h>
+#include <cmath>
#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 <llvm/ADT/BitVector.h>
+#include <cmath>
#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 <cstdint>
+#include <cstring>
#include <memory>
#include <unordered_map>
+#include <vector>
#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 <cfloat>
#include <cstring>
+#include <vector>
#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<Id>({b.makeUintConstant(0)}));
auto r0 = b.createLoad(r0_ptr);
- r0 = b.createCompositeInsert(vertex_idx, r0, vec4_float_type_,
- std::vector<uint32_t>({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<uint16_t>(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 <codecvt>
+#include <locale>
+#include <string>
+#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> FilePicker::Create() {
+ return std::make_unique<GtkFilePicker>();
+}
+
+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<std::wstring> selected_files;
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> 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_;
-
-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::recursive_mutex GLContext::global_gl_mutex_;
-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> GLContext::Create(GraphicsProvider* provider,
- Window* target_window,
- GLContext* share_context) {
- auto context =
- std::unique_ptr<GLContext>(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());
-}
-
-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;
- }
+ : GraphicsContext(provider, target_window) {}
- immediate_drawer_ = std::make_unique<GLImmediateDrawer>(this);
-
- ClearCurrent();
-
- return true;
-}
-
-std::unique_ptr<GLContext> 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<GLContext>(
- 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<float>(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<RawImage> 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 <gflags/gflags.h>
#include <memory>
+#include <mutex>
#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<RawImage> Capture() override;
Blitter* blitter() { return &blitter_; }
+ protected:
+ Blitter blitter_;
+ std::unique_ptr<GLImmediateDrawer> 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<GLEWContext> glew_context_;
- std::unique_ptr<WGLEWContext> wglew_context_;
-
- Blitter blitter_;
- std::unique_ptr<GLImmediateDrawer> 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 <gflags/gflags.h>
+
+#include <mutex>
+#include <string>
+
+#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> GLContext::Create(GraphicsProvider* provider,
+ Window* target_window,
+ GLContext* share_context) {
+ auto context =
+ std::unique_ptr<GLContext>(new WGLContext(provider, target_window));
+ if (!context->Initialize(share_context)) {
+ return nullptr;
+ }
+ context->AssertExtensionsPresent();
+ return context;
+}
+
+std::unique_ptr<GLContext> GLContext::CreateOffscreen(
+ GraphicsProvider* provider, GLContext* parent_context) {
+ return WGLContext::CreateOffscreen(provider,
+ static_cast<WGLContext*>(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<WGLContext*>(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<GLImmediateDrawer>(this);
+
+ ClearCurrent();
+
+ return true;
+}
+
+std::unique_ptr<WGLContext> 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<WGLContext>(
+ 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<float>(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 <gflags/gflags.h>
+
+#include <memory>
+
+#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<WGLContext> 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<GLEWContext> glew_context_;
+ std::unique_ptr<WGLEWContext> 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 <gflags/gflags.h>
+
+#include <gdk/gdkx.h>
+#include <mutex>
+#include <string>
+
+#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> GLContext::Create(GraphicsProvider* provider,
+ Window* target_window,
+ GLContext* share_context) {
+ auto context =
+ std::unique_ptr<GLContext>(new GLXContext(provider, target_window));
+ if (!context->Initialize(share_context)) {
+ return nullptr;
+ }
+ context->AssertExtensionsPresent();
+ return context;
+}
+
+std::unique_ptr<GLContext> GLContext::CreateOffscreen(
+ GraphicsProvider* provider, GLContext* parent_context) {
+ return GLXContext::CreateOffscreen(provider,
+ static_cast<GLXContext*>(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<GLXContext*>(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<GLImmediateDrawer>(this);
+
+ ClearCurrent();
+
+ return true;
+}
+
+std::unique_ptr<GLXContext> 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<GLXContext>(
+ 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<float>(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 <gflags/gflags.h>
+
+#include <memory>
+
+#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<GLXContext> 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<GLEWContext> glew_context_;
+ std::unique_ptr<GLXEWContext> 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<void()> fn) : fn_(std::move(fn)) {}
+ void Call() { fn_(); }
+
+ private:
+ std::function<void()> fn_;
+};
+
+std::unique_ptr<Loop> Loop::Create() { return std::make_unique<GTKLoop>(); }
+
+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<PostedFn*>(posted_fn);
+ Fn->Call();
+ return G_SOURCE_REMOVE;
+}
+
+void GTKLoop::Post(std::function<void()> fn) {
+ assert_true(thread_id_ != std::thread::id());
+ gdk_threads_add_idle(_posted_fn_thunk,
+ reinterpret_cast<gpointer>(new PostedFn(std::move(fn))));
+}
+
+void GTKLoop::PostDelayed(std::function<void()> fn, uint64_t delay_millis) {
+ gdk_threads_add_timeout(
+ delay_millis, _posted_fn_thunk,
+ reinterpret_cast<gpointer>(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 <list>
+#include <mutex>
+#include <thread>
+
+#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<void()> fn) override;
+ void PostDelayed(std::function<void()> 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 <functional>
#include <memory>
+#include <string>
#include <vector>
#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<GtkWidget*>(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<xcb_connection_t*>(
+ target_window_->native_platform_handle());
+ create_info.window = static_cast<xcb_window_t>(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 <gflags/gflags.h>
#include <cinttypes>
+#include <climits>
#include <mutex>
#include <string>
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<HWND>(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<GtkWidget*>(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<xcb_connection_t*>(
+ any_target_window->native_platform_handle());
+ create_info.window = static_cast<xcb_window_t>(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 <string>
+
+#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<void()> fn) : fn_(std::move(fn)) {}
+ void Call() { fn_(); }
+
+ private:
+ std::function<void()> fn_;
+};
+
+std::unique_ptr<Window> Window::Create(Loop* loop, const std::wstring& title) {
+ return std::make_unique<GTKWindow>(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<GTKWindow*>(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<gpointer>(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<GTKMenuItem*>(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<GdkEventButton*>(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<GdkEventMotion*>(event);
+ x = e->x;
+ y = e->y;
+ break;
+ }
+ case GDK_SCROLL: {
+ GdkEventScroll* e = reinterpret_cast<GdkEventScroll*>(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<ui::MenuItem> MenuItem::Create(Type type,
+ const std::wstring& text,
+ const std::wstring& hotkey,
+ std::function<void()> callback) {
+ return std::make_unique<GTKMenuItem>(type, text, hotkey, callback);
+}
+
+static void _menu_activate_callback(GtkWidget* menu, gpointer data) {
+ auto fn = reinterpret_cast<FnWrapper*>(data);
+ fn->Call();
+ delete fn;
+}
+
+GTKMenuItem::GTKMenuItem(Type type, const std::wstring& text,
+ const std::wstring& hotkey,
+ std::function<void()> 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<GTKMenuItem*>(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 <memory>
+#include <string>
+
+#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<void()> 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<Instruction>(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<Id>& comps) const
+Id Builder::findCompositeConstant(Op typeClass, const std::vector<Id>& comps) const
{
Instruction* constant = 0;
bool found = false;
@@ -772,7 +772,7 @@ Id Builder::findCompositeConstant(Op typeClass, std::vector<Id>& comps) const
}
// Comments in header
-Id Builder::makeCompositeConstant(Id typeId, std::vector<Id>& members, bool specConstant)
+Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& 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<Id>& offsets)
+Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector<Id>& 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<unsigned>& indexes)
+Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& 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<Id>& operands)
return op->getResultId();
}
-Id Builder::createFunctionCall(spv::Function* function, std::vector<spv::Id>& args)
+Id Builder::createFunctionCall(spv::Function* function, const std::vector<spv::Id>& 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<Id>& constituents)
+Id Builder::createCompositeConstruct(Id typeId, const std::vector<Id>& 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<int>& caseValues, std::vector<int>& valueIndexToSegment, int defaultSegment,
+void Builder::makeSwitch(Id selector, int numSegments, const std::vector<int>& caseValues, const std::vector<int>& valueIndexToSegment, int defaultSegment,
std::vector<Block*>& 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<Id>& comps, bool specConst = false);
+ Id makeCompositeConstant(Id type, const std::vector<Id>& 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<Id>& offsets);
+ Id createAccessChain(StorageClass, Id base, const std::vector<Id>& 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<unsigned>& indexes);
Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index);
- Id createCompositeInsert(Id object, Id composite, Id typeId, std::vector<unsigned>& indexes);
+ Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector<unsigned>& 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<Id>& operands);
- Id createFunctionCall(spv::Function*, std::vector<spv::Id>&);
+ Id createFunctionCall(spv::Function*, const std::vector<spv::Id>&);
// 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<Id>& constituents);
+ Id createCompositeConstruct(Id typeId, const std::vector<Id>& constituents);
// vector or scalar constructor
Id createConstructor(Decoration precision, const std::vector<Id>& 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<int>& caseValues, std::vector<int>& valueToSegment, int defaultSegment,
+ void makeSwitch(Id condition, int numSegments, const std::vector<int>& caseValues, const std::vector<int>& valueToSegment, int defaultSegment,
std::vector<Block*>& 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<Id>& comps) const;
+ Id findCompositeConstant(Op typeClass, const std::vector<Id>& 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