Merge branch 'linux' of git://github.com/dougvj/xenia into linux
# Conflicts: # .travis.yml
This commit is contained in:
commit
02e37be4e5
10
.travis.yml
10
.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
|
||||
|
|
|
@ -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
|
||||
|
|
18
premake5.lua
18
premake5.lua
|
@ -87,10 +87,27 @@ filter("platforms:Linux")
|
|||
toolset("clang")
|
||||
buildoptions({
|
||||
-- "-mlzcnt", -- (don't) Assume lzcnt is supported.
|
||||
"`pkg-config --cflags gtk+-x11-3.0`"
|
||||
})
|
||||
links({
|
||||
"pthread",
|
||||
"dl",
|
||||
"lz4",
|
||||
"X11",
|
||||
"xcb",
|
||||
"X11-xcb",
|
||||
"GL",
|
||||
"GLEW",
|
||||
"vulkan",
|
||||
"c++",
|
||||
"c++abi"
|
||||
})
|
||||
linkoptions({
|
||||
"`pkg-config --libs gtk+-3.0`",
|
||||
})
|
||||
|
||||
filter({"platforms:Linux", "kind:*App"})
|
||||
linkgroups("On")
|
||||
|
||||
filter({"platforms:Linux", "language:C++", "toolset:gcc"})
|
||||
buildoptions({
|
||||
|
@ -105,7 +122,6 @@ filter({"platforms:Linux", "language:C++", "toolset:clang"})
|
|||
"-stdlib=libc++",
|
||||
})
|
||||
links({
|
||||
"c++",
|
||||
})
|
||||
|
||||
filter("platforms:Windows")
|
||||
|
|
|
@ -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,6 +51,7 @@ project("xenia-app")
|
|||
"xenia_main.cc",
|
||||
"../base/main_"..platform_suffix..".cc",
|
||||
})
|
||||
filter("platforms:Windows")
|
||||
files({
|
||||
"main_resources.rc",
|
||||
})
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <climits>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
|
|
@ -13,9 +13,5 @@
|
|||
#include <time.h>
|
||||
|
||||
namespace xe {
|
||||
namespace threading {
|
||||
|
||||
void MaybeYield() { pthread_yield(); }
|
||||
|
||||
} // namespace threading
|
||||
namespace threading {} // namespace threading
|
||||
} // namespace xe
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class PosixHandle : public T {
|
||||
// TODO(dougvj) Not sure how to implement the equivalent of this on POSIX.
|
||||
SleepResult AlertableSleep(std::chrono::microseconds duration) {
|
||||
sleep(duration.count() / 1000);
|
||||
return SleepResult::kSuccess;
|
||||
}
|
||||
|
||||
// TODO(dougvj) We can probably wrap this with pthread_key_t but the type of
|
||||
// TlsHandle probably needs to be refactored
|
||||
TlsHandle AllocateTlsHandle() { assert_always(); }
|
||||
|
||||
bool FreeTlsHandle(TlsHandle handle) { return true; }
|
||||
|
||||
uintptr_t GetTlsValue(TlsHandle handle) { assert_always(); }
|
||||
|
||||
bool SetTlsValue(TlsHandle handle, uintptr_t value) { assert_always(); }
|
||||
|
||||
// TODO(dougvj)
|
||||
class PosixHighResolutionTimer : public HighResolutionTimer {
|
||||
public:
|
||||
explicit PosixHandle(pthread_t handle) : handle_(handle) {}
|
||||
~PosixHandle() override {}
|
||||
PosixHighResolutionTimer(std::function<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 PosixThreadHandle : public T {
|
||||
public:
|
||||
explicit PosixThreadHandle(pthread_t handle) : handle_(handle) {}
|
||||
~PosixThreadHandle() override {}
|
||||
|
||||
protected:
|
||||
void* native_handle() const override {
|
||||
|
@ -56,13 +180,134 @@ class PosixHandle : public T {
|
|||
pthread_t handle_;
|
||||
};
|
||||
|
||||
class PosixThread : public PosixHandle<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:
|
||||
explicit PosixThread(pthread_t handle) : PosixHandle(handle) {}
|
||||
~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) : 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
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#pragma warning(pop)
|
||||
#else
|
||||
#include <llvm/ADT/BitVector.h>
|
||||
#include <cmath>
|
||||
#endif // XE_COMPILER_MSVC
|
||||
|
||||
namespace xe {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#pragma warning(pop)
|
||||
#else
|
||||
#include <llvm/ADT/BitVector.h>
|
||||
#include <cmath>
|
||||
#endif // XE_COMPILER_MSVC
|
||||
|
||||
namespace xe {
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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_
|
||||
|
|
|
@ -17,14 +17,10 @@
|
|||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/platform_win.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/ui/gl/gl_immediate_drawer.h"
|
||||
#include "xenia/ui/window.h"
|
||||
|
||||
// TODO(benvanik): move win32 code to _win?
|
||||
#include "third_party/GL/wglew.h"
|
||||
|
||||
DEFINE_bool(thread_safe_gl, false,
|
||||
"Only allow one GL context to be active at a time.");
|
||||
|
||||
|
@ -43,14 +39,9 @@ namespace xe {
|
|||
namespace ui {
|
||||
namespace gl {
|
||||
|
||||
static std::recursive_mutex global_gl_mutex_;
|
||||
std::recursive_mutex GLContext::global_gl_mutex_;
|
||||
|
||||
thread_local GLEWContext* tls_glew_context_ = nullptr;
|
||||
thread_local WGLEWContext* tls_wglew_context_ = nullptr;
|
||||
extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; }
|
||||
extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; }
|
||||
|
||||
void FatalGLError(std::string error) {
|
||||
void GLContext::FatalGLError(std::string error) {
|
||||
xe::FatalError(
|
||||
error +
|
||||
"\nEnsure you have the latest drivers for your GPU and that it supports "
|
||||
|
@ -58,223 +49,10 @@ void FatalGLError(std::string error) {
|
|||
"of supported GPUs.");
|
||||
}
|
||||
|
||||
std::unique_ptr<GLContext> 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());
|
||||
}
|
||||
: GraphicsContext(provider, target_window) {}
|
||||
|
||||
GLContext::~GLContext() {
|
||||
MakeCurrent();
|
||||
blitter_.Shutdown();
|
||||
immediate_drawer_.reset();
|
||||
ClearCurrent();
|
||||
if (glrc_) {
|
||||
wglDeleteContext(glrc_);
|
||||
}
|
||||
if (dc_) {
|
||||
ReleaseDC(HWND(target_window_->native_handle()), dc_);
|
||||
}
|
||||
}
|
||||
|
||||
bool GLContext::Initialize(GLContext* share_context) {
|
||||
dc_ = GetDC(HWND(target_window_->native_handle()));
|
||||
|
||||
PIXELFORMATDESCRIPTOR pfd = {0};
|
||||
pfd.nSize = sizeof(pfd);
|
||||
pfd.nVersion = 1;
|
||||
pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
|
||||
pfd.iPixelType = PFD_TYPE_RGBA;
|
||||
pfd.cColorBits = 32;
|
||||
pfd.cDepthBits = 32;
|
||||
pfd.iLayerType = PFD_MAIN_PLANE;
|
||||
int pixel_format = ChoosePixelFormat(dc_, &pfd);
|
||||
if (!pixel_format) {
|
||||
FatalGLError("Unable to choose pixel format.");
|
||||
return false;
|
||||
}
|
||||
if (!SetPixelFormat(dc_, pixel_format, &pfd)) {
|
||||
FatalGLError("Unable to set pixel format.");
|
||||
return false;
|
||||
}
|
||||
|
||||
HGLRC temp_context = wglCreateContext(dc_);
|
||||
if (!temp_context) {
|
||||
FatalGLError("Unable to create temporary GL context.");
|
||||
return false;
|
||||
}
|
||||
wglMakeCurrent(dc_, temp_context);
|
||||
|
||||
tls_glew_context_ = glew_context_.get();
|
||||
tls_wglew_context_ = wglew_context_.get();
|
||||
if (glewInit() != GLEW_OK) {
|
||||
FatalGLError("Unable to initialize GLEW.");
|
||||
return false;
|
||||
}
|
||||
if (wglewInit() != GLEW_OK) {
|
||||
FatalGLError("Unable to initialize WGLEW.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WGLEW_ARB_create_context) {
|
||||
FatalGLError("WGL_ARG_create_context not supported by GL ICD.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GLEW_ARB_robustness) {
|
||||
robust_access_supported_ = true;
|
||||
}
|
||||
|
||||
int context_flags = 0;
|
||||
if (FLAGS_gl_debug) {
|
||||
context_flags |= WGL_CONTEXT_DEBUG_BIT_ARB;
|
||||
}
|
||||
if (robust_access_supported_) {
|
||||
context_flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;
|
||||
}
|
||||
|
||||
int attrib_list[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB,
|
||||
4,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB,
|
||||
5,
|
||||
WGL_CONTEXT_FLAGS_ARB,
|
||||
context_flags,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB,
|
||||
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
|
||||
robust_access_supported_ ? WGL_LOSE_CONTEXT_ON_RESET_ARB : 0,
|
||||
0};
|
||||
|
||||
glrc_ = wglCreateContextAttribsARB(
|
||||
dc_, share_context ? share_context->glrc_ : nullptr, attrib_list);
|
||||
wglMakeCurrent(nullptr, nullptr);
|
||||
wglDeleteContext(temp_context);
|
||||
if (!glrc_) {
|
||||
FatalGLError("Unable to create real GL context.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MakeCurrent()) {
|
||||
FatalGLError("Could not make real GL context current.");
|
||||
return false;
|
||||
}
|
||||
|
||||
XELOGI("Successfully created OpenGL context:");
|
||||
XELOGI(" GL_VENDOR: %s", glGetString(GL_VENDOR));
|
||||
XELOGI(" GL_VERSION: %s", glGetString(GL_VERSION));
|
||||
XELOGI(" GL_RENDERER: %s", glGetString(GL_RENDERER));
|
||||
XELOGI(" GL_SHADING_LANGUAGE_VERSION: %s",
|
||||
glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
|
||||
while (glGetError()) {
|
||||
// Clearing errors.
|
||||
}
|
||||
|
||||
SetupDebugging();
|
||||
|
||||
if (!blitter_.Initialize()) {
|
||||
FatalGLError("Unable to initialize blitter.");
|
||||
ClearCurrent();
|
||||
return false;
|
||||
}
|
||||
|
||||
immediate_drawer_ = std::make_unique<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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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_
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/ui/ui_event.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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
@ -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();
|
||||
|
|
|
@ -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\""]],
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue