Merge branch 'linux'
This commit is contained in:
commit
5da59a95c9
42
.travis.yml
42
.travis.yml
|
@ -4,47 +4,52 @@
|
||||||
#sudo: false
|
#sudo: false
|
||||||
|
|
||||||
language: cpp
|
language: cpp
|
||||||
compiler:
|
|
||||||
- clang
|
|
||||||
# - gcc
|
|
||||||
|
|
||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
# - osx
|
# - osx
|
||||||
|
|
||||||
env:
|
|
||||||
- LINT=true
|
|
||||||
- BUILD=true CONFIG=Debug
|
|
||||||
- BUILD=true CONFIG=Release
|
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
include:
|
||||||
|
- compiler: clang
|
||||||
|
env: C_COMPILER=clang-3.9 CXX_COMPILER=clang++-3.9 LINT=true
|
||||||
|
- compiler: clang
|
||||||
|
env: C_COMPILER=clang-3.9 CXX_COMPILER=clang++-3.9 BUILD=true CONFIG=Debug
|
||||||
|
- compiler: clang
|
||||||
|
env: C_COMPILER=clang-3.9 CXX_COMPILER=clang++-3.9 BUILD=true CONFIG=Release
|
||||||
allow_failures:
|
allow_failures:
|
||||||
# LLVMGold.so is not installed correctly
|
# LLVMGold.so is not installed correctly
|
||||||
- env: BUILD=true CONFIG=Release
|
- env: C_COMPILER=clang-3.9 CXX_COMPILER=clang++-3.9 BUILD=true CONFIG=Release
|
||||||
|
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
sudo: required
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
sources:
|
||||||
# - ubuntu-toolchain-r-test
|
- ubuntu-toolchain-r-test
|
||||||
- llvm-toolchain-precise
|
- llvm-toolchain-trusty
|
||||||
packages:
|
packages:
|
||||||
- clang-3.8
|
- clang-3.9
|
||||||
- clang-format-3.8
|
- clang-format-3.9
|
||||||
|
#- g++-6
|
||||||
- libc++-dev
|
- libc++-dev
|
||||||
- python3
|
- python3
|
||||||
|
- libc++abi-dev
|
||||||
|
- libgtk-3-dev
|
||||||
|
- liblz4-dev
|
||||||
|
|
||||||
git:
|
git:
|
||||||
# We handle submodules ourselves in xenia-build setup.
|
# We handle submodules ourselves in xenia-build setup.
|
||||||
submodules: false
|
submodules: false
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- export CXX=clang++-3.8
|
- export CXX=$CXX_COMPILER
|
||||||
- export CC=clang-3.8
|
- export CC=$C_COMPILER
|
||||||
# Dump useful info.
|
# Dump useful info.
|
||||||
- $CXX --version
|
- $CXX --version
|
||||||
- python3 --version
|
- python3 --version
|
||||||
|
# Add Vulkan dependencies
|
||||||
|
- travis_retry wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_1.0.42.0+dfsg1-1ubuntu1~16.04.1_amd64.deb
|
||||||
|
- travis_retry 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
|
||||||
# Prepare environment (pull dependencies, build tools).
|
# Prepare environment (pull dependencies, build tools).
|
||||||
- travis_retry ./xenia-build setup
|
- travis_retry ./xenia-build setup
|
||||||
|
|
||||||
|
@ -59,9 +64,8 @@ script:
|
||||||
- if [[ $BUILD == true ]]; then ./xenia-build build --config=$CONFIG --target=xenia-cpu-ppc-tests; fi
|
- if [[ $BUILD == true ]]; then ./xenia-build build --config=$CONFIG --target=xenia-cpu-ppc-tests; fi
|
||||||
# - if [[ $BUILD == true ]]; then ./build/bin/Linux/$CONFIG/xenia-cpu-ppc-tests --log_file=stdout; fi
|
# - if [[ $BUILD == true ]]; then ./build/bin/Linux/$CONFIG/xenia-cpu-ppc-tests --log_file=stdout; fi
|
||||||
|
|
||||||
# TODO(DrChat): Enable builds in the future.
|
|
||||||
# Build all of xenia.
|
# Build all of xenia.
|
||||||
#- ./xenia-build build --config=debug
|
#- if [[ $BUILD == true ]]; then ./xenia-build build --config=$CONFIG; fi
|
||||||
# All tests (without haswell support).
|
# All tests (without haswell support).
|
||||||
#- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=false
|
#- ./xenia-build test --config=debug --no-build -- --enable_haswell_instructions=false
|
||||||
# All tests (with haswell support).
|
# All tests (with haswell support).
|
||||||
|
|
|
@ -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
|
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
|
## Running
|
||||||
|
|
||||||
To make life easier you can use `--flagfile=myflags.txt` to specify all
|
To make life easier you can use `--flagfile=myflags.txt` to specify all
|
||||||
|
|
21
premake5.lua
21
premake5.lua
|
@ -87,10 +87,30 @@ filter("platforms:Linux")
|
||||||
toolset("clang")
|
toolset("clang")
|
||||||
buildoptions({
|
buildoptions({
|
||||||
-- "-mlzcnt", -- (don't) Assume lzcnt is supported.
|
-- "-mlzcnt", -- (don't) Assume lzcnt is supported.
|
||||||
|
"`pkg-config --cflags gtk+-x11-3.0`"
|
||||||
})
|
})
|
||||||
links({
|
links({
|
||||||
"pthread",
|
"pthread",
|
||||||
|
"dl",
|
||||||
|
"lz4",
|
||||||
|
"X11",
|
||||||
|
"xcb",
|
||||||
|
"X11-xcb",
|
||||||
|
"GL",
|
||||||
|
"GLEW",
|
||||||
|
"vulkan",
|
||||||
|
"c++",
|
||||||
|
"c++abi"
|
||||||
})
|
})
|
||||||
|
linkoptions({
|
||||||
|
"`pkg-config --libs gtk+-3.0`",
|
||||||
|
})
|
||||||
|
disablewarnings({
|
||||||
|
"deprecated-register"
|
||||||
|
})
|
||||||
|
|
||||||
|
filter({"platforms:Linux", "kind:*App"})
|
||||||
|
linkgroups("On")
|
||||||
|
|
||||||
filter({"platforms:Linux", "language:C++", "toolset:gcc"})
|
filter({"platforms:Linux", "language:C++", "toolset:gcc"})
|
||||||
buildoptions({
|
buildoptions({
|
||||||
|
@ -105,7 +125,6 @@ filter({"platforms:Linux", "language:C++", "toolset:clang"})
|
||||||
"-stdlib=libc++",
|
"-stdlib=libc++",
|
||||||
})
|
})
|
||||||
links({
|
links({
|
||||||
"c++",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
filter("platforms:Windows")
|
filter("platforms:Windows")
|
||||||
|
|
|
@ -28,11 +28,11 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
|
using xe::ui::FileDropEvent;
|
||||||
using xe::ui::KeyEvent;
|
using xe::ui::KeyEvent;
|
||||||
using xe::ui::MenuItem;
|
using xe::ui::MenuItem;
|
||||||
using xe::ui::MouseEvent;
|
using xe::ui::MouseEvent;
|
||||||
using xe::ui::UIEvent;
|
using xe::ui::UIEvent;
|
||||||
using xe::ui::FileDropEvent;
|
|
||||||
|
|
||||||
const std::wstring kBaseTitle = L"xenia";
|
const std::wstring kBaseTitle = L"xenia";
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,15 @@ project("xenia-app")
|
||||||
targetname("xenia")
|
targetname("xenia")
|
||||||
language("C++")
|
language("C++")
|
||||||
links({
|
links({
|
||||||
|
"capstone",
|
||||||
"gflags",
|
"gflags",
|
||||||
|
"glslang-spirv",
|
||||||
"imgui",
|
"imgui",
|
||||||
|
"libavcodec",
|
||||||
|
"libavutil",
|
||||||
|
"snappy",
|
||||||
|
"spirv-tools",
|
||||||
|
"vulkan-loader",
|
||||||
"xenia-apu",
|
"xenia-apu",
|
||||||
"xenia-apu-nop",
|
"xenia-apu-nop",
|
||||||
"xenia-base",
|
"xenia-base",
|
||||||
|
@ -21,11 +28,15 @@ project("xenia-app")
|
||||||
"xenia-gpu-gl4",
|
"xenia-gpu-gl4",
|
||||||
"xenia-gpu-null",
|
"xenia-gpu-null",
|
||||||
"xenia-gpu-vulkan",
|
"xenia-gpu-vulkan",
|
||||||
|
"xenia-hid",
|
||||||
"xenia-hid-nop",
|
"xenia-hid-nop",
|
||||||
"xenia-kernel",
|
"xenia-kernel",
|
||||||
"xenia-ui",
|
"xenia-ui",
|
||||||
"xenia-ui-gl",
|
"xenia-ui-gl",
|
||||||
|
"xenia-ui-spirv",
|
||||||
|
"xenia-ui-vulkan",
|
||||||
"xenia-vfs",
|
"xenia-vfs",
|
||||||
|
"xxhash",
|
||||||
})
|
})
|
||||||
flags({
|
flags({
|
||||||
"WinMain", -- Use WinMain instead of main.
|
"WinMain", -- Use WinMain instead of main.
|
||||||
|
@ -40,6 +51,7 @@ project("xenia-app")
|
||||||
"xenia_main.cc",
|
"xenia_main.cc",
|
||||||
"../base/main_"..platform_suffix..".cc",
|
"../base/main_"..platform_suffix..".cc",
|
||||||
})
|
})
|
||||||
|
filter("platforms:Windows")
|
||||||
files({
|
files({
|
||||||
"main_resources.rc",
|
"main_resources.rc",
|
||||||
})
|
})
|
||||||
|
|
|
@ -149,17 +149,19 @@ int xenia_main(const std::vector<std::wstring>& args) {
|
||||||
// This will respond to debugging requests so we can open the debug UI.
|
// This will respond to debugging requests so we can open the debug UI.
|
||||||
std::unique_ptr<xe::debug::ui::DebugWindow> debug_window;
|
std::unique_ptr<xe::debug::ui::DebugWindow> debug_window;
|
||||||
if (FLAGS_debug) {
|
if (FLAGS_debug) {
|
||||||
emulator->processor()->set_debug_listener_request_handler([&](
|
emulator->processor()->set_debug_listener_request_handler(
|
||||||
xe::cpu::Processor* processor) {
|
[&](xe::cpu::Processor* processor) {
|
||||||
if (debug_window) {
|
if (debug_window) {
|
||||||
return debug_window.get();
|
return debug_window.get();
|
||||||
}
|
}
|
||||||
emulator_window->loop()->PostSynchronous([&]() {
|
emulator_window->loop()->PostSynchronous([&]() {
|
||||||
debug_window = xe::debug::ui::DebugWindow::Create(
|
debug_window = xe::debug::ui::DebugWindow::Create(
|
||||||
emulator.get(), emulator_window->loop());
|
emulator.get(), emulator_window->loop());
|
||||||
debug_window->window()->on_closed.AddListener([&](xe::ui::UIEvent* e) {
|
debug_window->window()->on_closed.AddListener(
|
||||||
|
[&](xe::ui::UIEvent* e) {
|
||||||
emulator->processor()->set_debug_listener(nullptr);
|
emulator->processor()->set_debug_listener(nullptr);
|
||||||
emulator_window->loop()->Post([&]() { debug_window.reset(); });
|
emulator_window->loop()->Post(
|
||||||
|
[&]() { debug_window.reset(); });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return debug_window.get();
|
return debug_window.get();
|
||||||
|
|
|
@ -53,8 +53,13 @@ XAudio2AudioDriver::XAudio2AudioDriver(Memory* memory,
|
||||||
XAudio2AudioDriver::~XAudio2AudioDriver() = default;
|
XAudio2AudioDriver::~XAudio2AudioDriver() = default;
|
||||||
|
|
||||||
const DWORD ChannelMasks[] = {
|
const DWORD ChannelMasks[] = {
|
||||||
0, 0, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY, 0,
|
0,
|
||||||
0, 0, SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT |
|
0,
|
||||||
|
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_CENTER | SPEAKER_FRONT_RIGHT |
|
||||||
SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
|
SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
|
||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,7 +29,8 @@ struct bf {
|
||||||
// For enum values, we strip them down to an underlying type.
|
// For enum values, we strip them down to an underlying type.
|
||||||
typedef
|
typedef
|
||||||
typename std::conditional<std::is_enum<T>::value, std::underlying_type<T>,
|
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 {
|
inline value_type mask() const {
|
||||||
return (((value_type)~0) >> (8 * sizeof(value_type) - n_bits)) << position;
|
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 "xenia/base/string.h"
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ftw.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -33,6 +35,124 @@ bool CreateFolder(const std::wstring& path) {
|
||||||
return mkdir(xe::to_string(path).c_str(), 0774);
|
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> ListFiles(const std::wstring& path) {
|
||||||
std::vector<FileInfo> result;
|
std::vector<FileInfo> result;
|
||||||
|
|
||||||
|
@ -43,18 +163,20 @@ std::vector<FileInfo> ListFiles(const std::wstring& path) {
|
||||||
|
|
||||||
while (auto ent = readdir(dir)) {
|
while (auto ent = readdir(dir)) {
|
||||||
FileInfo info;
|
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) {
|
if (ent->d_type == DT_DIR) {
|
||||||
info.type = FileInfo::Type::kDirectory;
|
info.type = FileInfo::Type::kDirectory;
|
||||||
info.total_size = 0;
|
info.total_size = 0;
|
||||||
} else {
|
} else {
|
||||||
info.type = FileInfo::Type::kFile;
|
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);
|
result.push_back(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
#define XENIA_BASE_MATH_H_
|
#define XENIA_BASE_MATH_H_
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
|
|
||||||
#if XE_ARCH_AMD64
|
#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::TogglePause() {}
|
||||||
void Profiler::set_window(ui::Window* window) {}
|
void Profiler::set_window(ui::Window* window) {}
|
||||||
void Profiler::Present() {}
|
void Profiler::Present() {}
|
||||||
|
void Profiler::Flip() {}
|
||||||
|
|
||||||
#endif // XE_OPTION_PROFILING
|
#endif // XE_OPTION_PROFILING
|
||||||
|
|
||||||
|
|
|
@ -259,17 +259,14 @@ class Win32SocketServer : public SocketServer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
accept_thread_ = xe::threading::Thread::Create(
|
accept_thread_ = xe::threading::Thread::Create({}, [this, port]() {
|
||||||
{},
|
|
||||||
[this, port]() {
|
|
||||||
xe::threading::set_name(std::string("xe::SocketServer localhost:") +
|
xe::threading::set_name(std::string("xe::SocketServer localhost:") +
|
||||||
std::to_string(port));
|
std::to_string(port));
|
||||||
while (socket_ != INVALID_SOCKET) {
|
while (socket_ != INVALID_SOCKET) {
|
||||||
sockaddr_in6 client_addr;
|
sockaddr_in6 client_addr;
|
||||||
int client_count = sizeof(client_addr);
|
int client_count = sizeof(client_addr);
|
||||||
SOCKET client_socket =
|
SOCKET client_socket = accept(
|
||||||
accept(socket_, reinterpret_cast<sockaddr*>(&client_addr),
|
socket_, reinterpret_cast<sockaddr*>(&client_addr), &client_count);
|
||||||
&client_count);
|
|
||||||
if (client_socket == INVALID_SOCKET) {
|
if (client_socket == INVALID_SOCKET) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
|
@ -13,9 +13,5 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace threading {
|
namespace threading {} // namespace threading
|
||||||
|
|
||||||
void MaybeYield() { pthread_yield(); }
|
|
||||||
|
|
||||||
} // namespace threading
|
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -21,6 +22,9 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace threading {
|
namespace threading {
|
||||||
|
|
||||||
|
// TODO(dougvj)
|
||||||
|
void EnableAffinityConfiguration() {}
|
||||||
|
|
||||||
// uint64_t ticks() { return mach_absolute_time(); }
|
// uint64_t ticks() { return mach_absolute_time(); }
|
||||||
|
|
||||||
uint32_t current_thread_system_id() {
|
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) {
|
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) {
|
void Sleep(std::chrono::microseconds duration) {
|
||||||
timespec rqtp = {time_t(duration.count() / 1000000),
|
timespec rqtp = {time_t(duration.count() / 1000000),
|
||||||
time_t(duration.count() % 1000)};
|
time_t(duration.count() % 1000)};
|
||||||
|
@ -42,11 +53,133 @@ void Sleep(std::chrono::microseconds duration) {
|
||||||
// TODO(benvanik): spin while rmtp >0?
|
// TODO(benvanik): spin while rmtp >0?
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
// TODO(dougvj) Not sure how to implement the equivalent of this on POSIX.
|
||||||
class PosixHandle : public T {
|
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();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FreeTlsHandle(TlsHandle handle) { return true; }
|
||||||
|
|
||||||
|
uintptr_t GetTlsValue(TlsHandle handle) {
|
||||||
|
assert_always();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetTlsValue(TlsHandle handle, uintptr_t value) {
|
||||||
|
assert_always();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(dougvj)
|
||||||
|
class PosixHighResolutionTimer : public HighResolutionTimer {
|
||||||
public:
|
public:
|
||||||
explicit PosixHandle(pthread_t handle) : handle_(handle) {}
|
PosixHighResolutionTimer(std::function<void()> callback)
|
||||||
~PosixHandle() override {}
|
: 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:
|
protected:
|
||||||
void* native_handle() const override {
|
void* native_handle() const override {
|
||||||
|
@ -56,13 +189,134 @@ class PosixHandle : public T {
|
||||||
pthread_t handle_;
|
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:
|
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;
|
~PosixThread() = default;
|
||||||
|
|
||||||
void set_name(std::string name) override {
|
void set_name(std::string name) override {
|
||||||
// TODO(DrChat)
|
pthread_setname_np(handle_, name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t system_id() const override { return 0; }
|
uint32_t system_id() const override { return 0; }
|
||||||
|
@ -141,5 +395,20 @@ std::unique_ptr<Thread> Thread::Create(CreationParameters params,
|
||||||
return std::unique_ptr<PosixThread>(new PosixThread(handle));
|
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 threading
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -600,78 +600,78 @@ static const vec128_t xmm_consts[] = {
|
||||||
/* XMMZero */ vec128f(0.0f),
|
/* XMMZero */ vec128f(0.0f),
|
||||||
/* XMMOne */ vec128f(1.0f),
|
/* XMMOne */ vec128f(1.0f),
|
||||||
/* XMMNegativeOne */ vec128f(-1.0f, -1.0f, -1.0f, -1.0f),
|
/* XMMNegativeOne */ vec128f(-1.0f, -1.0f, -1.0f, -1.0f),
|
||||||
/* XMMFFFF */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
|
/* XMMFFFF */
|
||||||
0xFFFFFFFFu),
|
vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||||
/* XMMMaskX16Y16 */ vec128i(0x0000FFFFu, 0xFFFF0000u, 0x00000000u,
|
/* XMMMaskX16Y16 */
|
||||||
0x00000000u),
|
vec128i(0x0000FFFFu, 0xFFFF0000u, 0x00000000u, 0x00000000u),
|
||||||
/* XMMFlipX16Y16 */ vec128i(0x00008000u, 0x00000000u, 0x00000000u,
|
/* XMMFlipX16Y16 */
|
||||||
0x00000000u),
|
vec128i(0x00008000u, 0x00000000u, 0x00000000u, 0x00000000u),
|
||||||
/* XMMFixX16Y16 */ vec128f(-32768.0f, 0.0f, 0.0f, 0.0f),
|
/* XMMFixX16Y16 */ vec128f(-32768.0f, 0.0f, 0.0f, 0.0f),
|
||||||
/* XMMNormalizeX16Y16 */ vec128f(
|
/* XMMNormalizeX16Y16 */
|
||||||
1.0f / 32767.0f, 1.0f / (32767.0f * 65536.0f), 0.0f, 0.0f),
|
vec128f(1.0f / 32767.0f, 1.0f / (32767.0f * 65536.0f), 0.0f, 0.0f),
|
||||||
/* XMM0001 */ vec128f(0.0f, 0.0f, 0.0f, 1.0f),
|
/* XMM0001 */ vec128f(0.0f, 0.0f, 0.0f, 1.0f),
|
||||||
/* XMM3301 */ vec128f(3.0f, 3.0f, 0.0f, 1.0f),
|
/* XMM3301 */ vec128f(3.0f, 3.0f, 0.0f, 1.0f),
|
||||||
/* XMM3333 */ vec128f(3.0f, 3.0f, 3.0f, 3.0f),
|
/* XMM3333 */ vec128f(3.0f, 3.0f, 3.0f, 3.0f),
|
||||||
/* XMMSignMaskPS */ vec128i(0x80000000u, 0x80000000u, 0x80000000u,
|
/* XMMSignMaskPS */
|
||||||
0x80000000u),
|
vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
|
||||||
/* XMMSignMaskPD */ vec128i(0x00000000u, 0x80000000u, 0x00000000u,
|
/* XMMSignMaskPD */
|
||||||
0x80000000u),
|
vec128i(0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u),
|
||||||
/* XMMAbsMaskPS */ vec128i(0x7FFFFFFFu, 0x7FFFFFFFu, 0x7FFFFFFFu,
|
/* XMMAbsMaskPS */
|
||||||
0x7FFFFFFFu),
|
vec128i(0x7FFFFFFFu, 0x7FFFFFFFu, 0x7FFFFFFFu, 0x7FFFFFFFu),
|
||||||
/* XMMAbsMaskPD */ vec128i(0xFFFFFFFFu, 0x7FFFFFFFu, 0xFFFFFFFFu,
|
/* XMMAbsMaskPD */
|
||||||
0x7FFFFFFFu),
|
vec128i(0xFFFFFFFFu, 0x7FFFFFFFu, 0xFFFFFFFFu, 0x7FFFFFFFu),
|
||||||
/* XMMByteSwapMask */ vec128i(0x00010203u, 0x04050607u, 0x08090A0Bu,
|
/* XMMByteSwapMask */
|
||||||
0x0C0D0E0Fu),
|
vec128i(0x00010203u, 0x04050607u, 0x08090A0Bu, 0x0C0D0E0Fu),
|
||||||
/* XMMByteOrderMask */ vec128i(0x01000302u, 0x05040706u, 0x09080B0Au,
|
/* XMMByteOrderMask */
|
||||||
0x0D0C0F0Eu),
|
vec128i(0x01000302u, 0x05040706u, 0x09080B0Au, 0x0D0C0F0Eu),
|
||||||
/* XMMPermuteControl15 */ vec128b(15),
|
/* XMMPermuteControl15 */ vec128b(15),
|
||||||
/* XMMPermuteByteMask */ vec128b(0x1F),
|
/* XMMPermuteByteMask */ vec128b(0x1F),
|
||||||
/* XMMPackD3DCOLORSat */ vec128i(0x404000FFu),
|
/* XMMPackD3DCOLORSat */ vec128i(0x404000FFu),
|
||||||
/* XMMPackD3DCOLOR */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
|
/* XMMPackD3DCOLOR */
|
||||||
0x0C000408u),
|
vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0x0C000408u),
|
||||||
/* XMMUnpackD3DCOLOR */ vec128i(0xFFFFFF0Eu, 0xFFFFFF0Du, 0xFFFFFF0Cu,
|
/* XMMUnpackD3DCOLOR */
|
||||||
0xFFFFFF0Fu),
|
vec128i(0xFFFFFF0Eu, 0xFFFFFF0Du, 0xFFFFFF0Cu, 0xFFFFFF0Fu),
|
||||||
/* XMMPackFLOAT16_2 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
|
/* XMMPackFLOAT16_2 */
|
||||||
0x01000302u),
|
vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0x01000302u),
|
||||||
/* XMMUnpackFLOAT16_2 */ vec128i(0x0D0C0F0Eu, 0xFFFFFFFFu, 0xFFFFFFFFu,
|
/* XMMUnpackFLOAT16_2 */
|
||||||
0xFFFFFFFFu),
|
vec128i(0x0D0C0F0Eu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||||
/* XMMPackFLOAT16_4 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0x05040706u,
|
/* XMMPackFLOAT16_4 */
|
||||||
0x01000302u),
|
vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0x05040706u, 0x01000302u),
|
||||||
/* XMMUnpackFLOAT16_4 */ vec128i(0x09080B0Au, 0x0D0C0F0Eu, 0xFFFFFFFFu,
|
/* XMMUnpackFLOAT16_4 */
|
||||||
0xFFFFFFFFu),
|
vec128i(0x09080B0Au, 0x0D0C0F0Eu, 0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||||
/* XMMPackSHORT_Min */ vec128i(0x403F8001u),
|
/* XMMPackSHORT_Min */ vec128i(0x403F8001u),
|
||||||
/* XMMPackSHORT_Max */ vec128i(0x40407FFFu),
|
/* XMMPackSHORT_Max */ vec128i(0x40407FFFu),
|
||||||
/* XMMPackSHORT_2 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu,
|
/* XMMPackSHORT_2 */
|
||||||
0x01000504u),
|
vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0x01000504u),
|
||||||
/* XMMPackSHORT_4 */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0x01000504u,
|
/* XMMPackSHORT_4 */
|
||||||
0x09080D0Cu),
|
vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0x01000504u, 0x09080D0Cu),
|
||||||
/* XMMUnpackSHORT_2 */ vec128i(0xFFFF0F0Eu, 0xFFFF0D0Cu, 0xFFFFFFFFu,
|
/* XMMUnpackSHORT_2 */
|
||||||
0xFFFFFFFFu),
|
vec128i(0xFFFF0F0Eu, 0xFFFF0D0Cu, 0xFFFFFFFFu, 0xFFFFFFFFu),
|
||||||
/* XMMUnpackSHORT_4 */ vec128i(0xFFFF0B0Au, 0xFFFF0908u, 0xFFFF0F0Eu,
|
/* XMMUnpackSHORT_4 */
|
||||||
0xFFFF0D0Cu),
|
vec128i(0xFFFF0B0Au, 0xFFFF0908u, 0xFFFF0F0Eu, 0xFFFF0D0Cu),
|
||||||
/* XMMOneOver255 */ vec128f(1.0f / 255.0f),
|
/* XMMOneOver255 */ vec128f(1.0f / 255.0f),
|
||||||
/* XMMMaskEvenPI16 */ vec128i(0x0000FFFFu, 0x0000FFFFu, 0x0000FFFFu,
|
/* XMMMaskEvenPI16 */
|
||||||
0x0000FFFFu),
|
vec128i(0x0000FFFFu, 0x0000FFFFu, 0x0000FFFFu, 0x0000FFFFu),
|
||||||
/* XMMShiftMaskEvenPI16 */ vec128i(0x0000000Fu, 0x0000000Fu, 0x0000000Fu,
|
/* XMMShiftMaskEvenPI16 */
|
||||||
0x0000000Fu),
|
vec128i(0x0000000Fu, 0x0000000Fu, 0x0000000Fu, 0x0000000Fu),
|
||||||
/* XMMShiftMaskPS */ vec128i(0x0000001Fu, 0x0000001Fu, 0x0000001Fu,
|
/* XMMShiftMaskPS */
|
||||||
0x0000001Fu),
|
vec128i(0x0000001Fu, 0x0000001Fu, 0x0000001Fu, 0x0000001Fu),
|
||||||
/* XMMShiftByteMask */ vec128i(0x000000FFu, 0x000000FFu, 0x000000FFu,
|
/* XMMShiftByteMask */
|
||||||
0x000000FFu),
|
vec128i(0x000000FFu, 0x000000FFu, 0x000000FFu, 0x000000FFu),
|
||||||
/* XMMSwapWordMask */ vec128i(0x03030303u, 0x03030303u, 0x03030303u,
|
/* XMMSwapWordMask */
|
||||||
0x03030303u),
|
vec128i(0x03030303u, 0x03030303u, 0x03030303u, 0x03030303u),
|
||||||
/* XMMUnsignedDwordMax */ vec128i(0xFFFFFFFFu, 0x00000000u, 0xFFFFFFFFu,
|
/* XMMUnsignedDwordMax */
|
||||||
0x00000000u),
|
vec128i(0xFFFFFFFFu, 0x00000000u, 0xFFFFFFFFu, 0x00000000u),
|
||||||
/* XMM255 */ vec128f(255.0f),
|
/* XMM255 */ vec128f(255.0f),
|
||||||
/* XMMPI32 */ vec128i(32),
|
/* XMMPI32 */ vec128i(32),
|
||||||
/* XMMSignMaskI8 */ vec128i(0x80808080u, 0x80808080u, 0x80808080u,
|
/* XMMSignMaskI8 */
|
||||||
0x80808080u),
|
vec128i(0x80808080u, 0x80808080u, 0x80808080u, 0x80808080u),
|
||||||
/* XMMSignMaskI16 */ vec128i(0x80008000u, 0x80008000u, 0x80008000u,
|
/* XMMSignMaskI16 */
|
||||||
0x80008000u),
|
vec128i(0x80008000u, 0x80008000u, 0x80008000u, 0x80008000u),
|
||||||
/* XMMSignMaskI32 */ vec128i(0x80000000u, 0x80000000u, 0x80000000u,
|
/* XMMSignMaskI32 */
|
||||||
0x80000000u),
|
vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
|
||||||
/* XMMSignMaskF32 */ vec128i(0x80000000u, 0x80000000u, 0x80000000u,
|
/* XMMSignMaskF32 */
|
||||||
0x80000000u),
|
vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
|
||||||
/* XMMShortMinPS */ vec128f(SHRT_MIN),
|
/* XMMShortMinPS */ vec128f(SHRT_MIN),
|
||||||
/* XMMShortMaxPS */ vec128f(SHRT_MAX),
|
/* XMMShortMaxPS */ vec128f(SHRT_MAX),
|
||||||
/* XMMIntMin */ vec128i(INT_MIN),
|
/* XMMIntMin */ vec128i(INT_MIN),
|
||||||
|
|
|
@ -2988,7 +2988,8 @@ EMITTER_OPCODE_TABLE(OPCODE_IS_NAN, IS_NAN_F32, IS_NAN_F64);
|
||||||
struct COMPARE_EQ_I8
|
struct COMPARE_EQ_I8
|
||||||
: Sequence<COMPARE_EQ_I8, I<OPCODE_COMPARE_EQ, I8Op, I8Op, I8Op>> {
|
: Sequence<COMPARE_EQ_I8, I<OPCODE_COMPARE_EQ, I8Op, I8Op, I8Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeCompareOp(e, i, [](X64Emitter& e, const Reg8& src1,
|
EmitCommutativeCompareOp(e, i,
|
||||||
|
[](X64Emitter& e, const Reg8& src1,
|
||||||
const Reg8& src2) { e.cmp(src1, src2); },
|
const Reg8& src2) { e.cmp(src1, src2); },
|
||||||
[](X64Emitter& e, const Reg8& src1,
|
[](X64Emitter& e, const Reg8& src1,
|
||||||
int32_t constant) { e.cmp(src1, constant); });
|
int32_t constant) { e.cmp(src1, constant); });
|
||||||
|
@ -2998,7 +2999,8 @@ struct COMPARE_EQ_I8
|
||||||
struct COMPARE_EQ_I16
|
struct COMPARE_EQ_I16
|
||||||
: Sequence<COMPARE_EQ_I16, I<OPCODE_COMPARE_EQ, I8Op, I16Op, I16Op>> {
|
: Sequence<COMPARE_EQ_I16, I<OPCODE_COMPARE_EQ, I8Op, I16Op, I16Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeCompareOp(e, i, [](X64Emitter& e, const Reg16& src1,
|
EmitCommutativeCompareOp(e, i,
|
||||||
|
[](X64Emitter& e, const Reg16& src1,
|
||||||
const Reg16& src2) { e.cmp(src1, src2); },
|
const Reg16& src2) { e.cmp(src1, src2); },
|
||||||
[](X64Emitter& e, const Reg16& src1,
|
[](X64Emitter& e, const Reg16& src1,
|
||||||
int32_t constant) { e.cmp(src1, constant); });
|
int32_t constant) { e.cmp(src1, constant); });
|
||||||
|
@ -3008,7 +3010,8 @@ struct COMPARE_EQ_I16
|
||||||
struct COMPARE_EQ_I32
|
struct COMPARE_EQ_I32
|
||||||
: Sequence<COMPARE_EQ_I32, I<OPCODE_COMPARE_EQ, I8Op, I32Op, I32Op>> {
|
: Sequence<COMPARE_EQ_I32, I<OPCODE_COMPARE_EQ, I8Op, I32Op, I32Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeCompareOp(e, i, [](X64Emitter& e, const Reg32& src1,
|
EmitCommutativeCompareOp(e, i,
|
||||||
|
[](X64Emitter& e, const Reg32& src1,
|
||||||
const Reg32& src2) { e.cmp(src1, src2); },
|
const Reg32& src2) { e.cmp(src1, src2); },
|
||||||
[](X64Emitter& e, const Reg32& src1,
|
[](X64Emitter& e, const Reg32& src1,
|
||||||
int32_t constant) { e.cmp(src1, constant); });
|
int32_t constant) { e.cmp(src1, constant); });
|
||||||
|
@ -3018,7 +3021,8 @@ struct COMPARE_EQ_I32
|
||||||
struct COMPARE_EQ_I64
|
struct COMPARE_EQ_I64
|
||||||
: Sequence<COMPARE_EQ_I64, I<OPCODE_COMPARE_EQ, I8Op, I64Op, I64Op>> {
|
: Sequence<COMPARE_EQ_I64, I<OPCODE_COMPARE_EQ, I8Op, I64Op, I64Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeCompareOp(e, i, [](X64Emitter& e, const Reg64& src1,
|
EmitCommutativeCompareOp(e, i,
|
||||||
|
[](X64Emitter& e, const Reg64& src1,
|
||||||
const Reg64& src2) { e.cmp(src1, src2); },
|
const Reg64& src2) { e.cmp(src1, src2); },
|
||||||
[](X64Emitter& e, const Reg64& src1,
|
[](X64Emitter& e, const Reg64& src1,
|
||||||
int32_t constant) { e.cmp(src1, constant); });
|
int32_t constant) { e.cmp(src1, constant); });
|
||||||
|
@ -3055,7 +3059,8 @@ EMITTER_OPCODE_TABLE(OPCODE_COMPARE_EQ, COMPARE_EQ_I8, COMPARE_EQ_I16,
|
||||||
struct COMPARE_NE_I8
|
struct COMPARE_NE_I8
|
||||||
: Sequence<COMPARE_NE_I8, I<OPCODE_COMPARE_NE, I8Op, I8Op, I8Op>> {
|
: Sequence<COMPARE_NE_I8, I<OPCODE_COMPARE_NE, I8Op, I8Op, I8Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeCompareOp(e, i, [](X64Emitter& e, const Reg8& src1,
|
EmitCommutativeCompareOp(e, i,
|
||||||
|
[](X64Emitter& e, const Reg8& src1,
|
||||||
const Reg8& src2) { e.cmp(src1, src2); },
|
const Reg8& src2) { e.cmp(src1, src2); },
|
||||||
[](X64Emitter& e, const Reg8& src1,
|
[](X64Emitter& e, const Reg8& src1,
|
||||||
int32_t constant) { e.cmp(src1, constant); });
|
int32_t constant) { e.cmp(src1, constant); });
|
||||||
|
@ -3065,7 +3070,8 @@ struct COMPARE_NE_I8
|
||||||
struct COMPARE_NE_I16
|
struct COMPARE_NE_I16
|
||||||
: Sequence<COMPARE_NE_I16, I<OPCODE_COMPARE_NE, I8Op, I16Op, I16Op>> {
|
: Sequence<COMPARE_NE_I16, I<OPCODE_COMPARE_NE, I8Op, I16Op, I16Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeCompareOp(e, i, [](X64Emitter& e, const Reg16& src1,
|
EmitCommutativeCompareOp(e, i,
|
||||||
|
[](X64Emitter& e, const Reg16& src1,
|
||||||
const Reg16& src2) { e.cmp(src1, src2); },
|
const Reg16& src2) { e.cmp(src1, src2); },
|
||||||
[](X64Emitter& e, const Reg16& src1,
|
[](X64Emitter& e, const Reg16& src1,
|
||||||
int32_t constant) { e.cmp(src1, constant); });
|
int32_t constant) { e.cmp(src1, constant); });
|
||||||
|
@ -3075,7 +3081,8 @@ struct COMPARE_NE_I16
|
||||||
struct COMPARE_NE_I32
|
struct COMPARE_NE_I32
|
||||||
: Sequence<COMPARE_NE_I32, I<OPCODE_COMPARE_NE, I8Op, I32Op, I32Op>> {
|
: Sequence<COMPARE_NE_I32, I<OPCODE_COMPARE_NE, I8Op, I32Op, I32Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeCompareOp(e, i, [](X64Emitter& e, const Reg32& src1,
|
EmitCommutativeCompareOp(e, i,
|
||||||
|
[](X64Emitter& e, const Reg32& src1,
|
||||||
const Reg32& src2) { e.cmp(src1, src2); },
|
const Reg32& src2) { e.cmp(src1, src2); },
|
||||||
[](X64Emitter& e, const Reg32& src1,
|
[](X64Emitter& e, const Reg32& src1,
|
||||||
int32_t constant) { e.cmp(src1, constant); });
|
int32_t constant) { e.cmp(src1, constant); });
|
||||||
|
@ -3085,7 +3092,8 @@ struct COMPARE_NE_I32
|
||||||
struct COMPARE_NE_I64
|
struct COMPARE_NE_I64
|
||||||
: Sequence<COMPARE_NE_I64, I<OPCODE_COMPARE_NE, I8Op, I64Op, I64Op>> {
|
: Sequence<COMPARE_NE_I64, I<OPCODE_COMPARE_NE, I8Op, I64Op, I64Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeCompareOp(e, i, [](X64Emitter& e, const Reg64& src1,
|
EmitCommutativeCompareOp(e, i,
|
||||||
|
[](X64Emitter& e, const Reg64& src1,
|
||||||
const Reg64& src2) { e.cmp(src1, src2); },
|
const Reg64& src2) { e.cmp(src1, src2); },
|
||||||
[](X64Emitter& e, const Reg64& src1,
|
[](X64Emitter& e, const Reg64& src1,
|
||||||
int32_t constant) { e.cmp(src1, constant); });
|
int32_t constant) { e.cmp(src1, constant); });
|
||||||
|
@ -3421,8 +3429,10 @@ EMITTER_OPCODE_TABLE(OPCODE_VECTOR_COMPARE_UGE, VECTOR_COMPARE_UGE_V128);
|
||||||
template <typename SEQ, typename REG, typename ARGS>
|
template <typename SEQ, typename REG, typename ARGS>
|
||||||
void EmitAddXX(X64Emitter& e, const ARGS& i) {
|
void EmitAddXX(X64Emitter& e, const ARGS& i) {
|
||||||
SEQ::EmitCommutativeBinaryOp(
|
SEQ::EmitCommutativeBinaryOp(
|
||||||
e, i, [](X64Emitter& e, const REG& dest_src,
|
e, i,
|
||||||
const REG& src) { e.add(dest_src, src); },
|
[](X64Emitter& e, const REG& dest_src, const REG& src) {
|
||||||
|
e.add(dest_src, src);
|
||||||
|
},
|
||||||
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
||||||
e.add(dest_src, constant);
|
e.add(dest_src, constant);
|
||||||
});
|
});
|
||||||
|
@ -3491,8 +3501,10 @@ void EmitAddCarryXX(X64Emitter& e, const ARGS& i) {
|
||||||
e.sahf();
|
e.sahf();
|
||||||
}
|
}
|
||||||
SEQ::EmitCommutativeBinaryOp(
|
SEQ::EmitCommutativeBinaryOp(
|
||||||
e, i, [](X64Emitter& e, const REG& dest_src,
|
e, i,
|
||||||
const REG& src) { e.adc(dest_src, src); },
|
[](X64Emitter& e, const REG& dest_src, const REG& src) {
|
||||||
|
e.adc(dest_src, src);
|
||||||
|
},
|
||||||
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
||||||
e.adc(dest_src, constant);
|
e.adc(dest_src, constant);
|
||||||
});
|
});
|
||||||
|
@ -3530,9 +3542,10 @@ EMITTER_OPCODE_TABLE(OPCODE_ADD_CARRY, ADD_CARRY_I8, ADD_CARRY_I16,
|
||||||
struct VECTOR_ADD
|
struct VECTOR_ADD
|
||||||
: Sequence<VECTOR_ADD, I<OPCODE_VECTOR_ADD, V128Op, V128Op, V128Op>> {
|
: Sequence<VECTOR_ADD, I<OPCODE_VECTOR_ADD, V128Op, V128Op, V128Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeBinaryXmmOp(e, i, [&i](X64Emitter& e, const Xmm& dest,
|
EmitCommutativeBinaryXmmOp(
|
||||||
Xmm src1, Xmm src2) {
|
e, i, [&i](X64Emitter& e, const Xmm& dest, Xmm src1, Xmm src2) {
|
||||||
const TypeName part_type = static_cast<TypeName>(i.instr->flags & 0xFF);
|
const TypeName part_type =
|
||||||
|
static_cast<TypeName>(i.instr->flags & 0xFF);
|
||||||
const uint32_t arithmetic_flags = i.instr->flags >> 8;
|
const uint32_t arithmetic_flags = i.instr->flags >> 8;
|
||||||
bool is_unsigned = !!(arithmetic_flags & ARITHMETIC_UNSIGNED);
|
bool is_unsigned = !!(arithmetic_flags & ARITHMETIC_UNSIGNED);
|
||||||
bool saturate = !!(arithmetic_flags & ARITHMETIC_SATURATE);
|
bool saturate = !!(arithmetic_flags & ARITHMETIC_SATURATE);
|
||||||
|
@ -3564,7 +3577,8 @@ struct VECTOR_ADD
|
||||||
case INT32_TYPE:
|
case INT32_TYPE:
|
||||||
if (saturate) {
|
if (saturate) {
|
||||||
if (is_unsigned) {
|
if (is_unsigned) {
|
||||||
// xmm0 is the only temp register that can be used by src1/src2.
|
// xmm0 is the only temp register that can be used by
|
||||||
|
// src1/src2.
|
||||||
e.vpaddd(e.xmm1, src1, src2);
|
e.vpaddd(e.xmm1, src1, src2);
|
||||||
|
|
||||||
// If result is smaller than either of the inputs, we've
|
// If result is smaller than either of the inputs, we've
|
||||||
|
@ -3586,17 +3600,18 @@ struct VECTOR_ADD
|
||||||
src2 = e.xmm1;
|
src2 = e.xmm1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// xmm0 is the only temp register that can be used by src1/src2.
|
// xmm0 is the only temp register that can be used by
|
||||||
|
// src1/src2.
|
||||||
e.vpaddd(dest, src1, src2);
|
e.vpaddd(dest, src1, src2);
|
||||||
|
|
||||||
// Overflow results if two inputs are the same sign and the result
|
// Overflow results if two inputs are the same sign and the
|
||||||
// isn't the same sign.
|
// result isn't the same sign. if ((s32b)(~(src1 ^ src2) &
|
||||||
// if ((s32b)(~(src1 ^ src2) & (src1 ^ res)) < 0) then overflowed
|
// (src1 ^ res)) < 0) then overflowed
|
||||||
// http://locklessinc.com/articles/sat_arithmetic/
|
// http://locklessinc.com/articles/sat_arithmetic/
|
||||||
e.vpxor(e.xmm1, src1, src2);
|
e.vpxor(e.xmm1, src1, src2);
|
||||||
|
|
||||||
// Move src1 to xmm0 in-case it was the same register as the dest.
|
// Move src1 to xmm0 in-case it was the same register as the
|
||||||
// This kills src2 if it's a constant.
|
// dest. This kills src2 if it's a constant.
|
||||||
if (src1 != e.xmm0) {
|
if (src1 != e.xmm0) {
|
||||||
e.vmovdqa(e.xmm0, src1);
|
e.vmovdqa(e.xmm0, src1);
|
||||||
src1 = e.xmm0;
|
src1 = e.xmm0;
|
||||||
|
@ -3609,11 +3624,13 @@ struct VECTOR_ADD
|
||||||
|
|
||||||
// Set any negative overflowed elements of src1 to INT_MIN
|
// Set any negative overflowed elements of src1 to INT_MIN
|
||||||
e.vpand(e.xmm2, src1, e.xmm1);
|
e.vpand(e.xmm2, src1, e.xmm1);
|
||||||
e.vblendvps(dest, dest, e.GetXmmConstPtr(XMMSignMaskI32), e.xmm2);
|
e.vblendvps(dest, dest, e.GetXmmConstPtr(XMMSignMaskI32),
|
||||||
|
e.xmm2);
|
||||||
|
|
||||||
// Set any positive overflowed elements of src1 to INT_MAX
|
// Set any positive overflowed elements of src1 to INT_MAX
|
||||||
e.vpandn(e.xmm2, src1, e.xmm1);
|
e.vpandn(e.xmm2, src1, e.xmm1);
|
||||||
e.vblendvps(dest, dest, e.GetXmmConstPtr(XMMAbsMaskPS), e.xmm2);
|
e.vblendvps(dest, dest, e.GetXmmConstPtr(XMMAbsMaskPS),
|
||||||
|
e.xmm2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
e.vpaddd(dest, src1, src2);
|
e.vpaddd(dest, src1, src2);
|
||||||
|
@ -3640,8 +3657,10 @@ EMITTER_OPCODE_TABLE(OPCODE_VECTOR_ADD, VECTOR_ADD);
|
||||||
template <typename SEQ, typename REG, typename ARGS>
|
template <typename SEQ, typename REG, typename ARGS>
|
||||||
void EmitSubXX(X64Emitter& e, const ARGS& i) {
|
void EmitSubXX(X64Emitter& e, const ARGS& i) {
|
||||||
SEQ::EmitAssociativeBinaryOp(
|
SEQ::EmitAssociativeBinaryOp(
|
||||||
e, i, [](X64Emitter& e, const REG& dest_src,
|
e, i,
|
||||||
const REG& src) { e.sub(dest_src, src); },
|
[](X64Emitter& e, const REG& dest_src, const REG& src) {
|
||||||
|
e.sub(dest_src, src);
|
||||||
|
},
|
||||||
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
||||||
e.sub(dest_src, constant);
|
e.sub(dest_src, constant);
|
||||||
});
|
});
|
||||||
|
@ -3693,9 +3712,10 @@ EMITTER_OPCODE_TABLE(OPCODE_SUB, SUB_I8, SUB_I16, SUB_I32, SUB_I64, SUB_F32,
|
||||||
struct VECTOR_SUB
|
struct VECTOR_SUB
|
||||||
: Sequence<VECTOR_SUB, I<OPCODE_VECTOR_SUB, V128Op, V128Op, V128Op>> {
|
: Sequence<VECTOR_SUB, I<OPCODE_VECTOR_SUB, V128Op, V128Op, V128Op>> {
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeBinaryXmmOp(e, i, [&i](X64Emitter& e, const Xmm& dest,
|
EmitCommutativeBinaryXmmOp(
|
||||||
Xmm src1, Xmm src2) {
|
e, i, [&i](X64Emitter& e, const Xmm& dest, Xmm src1, Xmm src2) {
|
||||||
const TypeName part_type = static_cast<TypeName>(i.instr->flags & 0xFF);
|
const TypeName part_type =
|
||||||
|
static_cast<TypeName>(i.instr->flags & 0xFF);
|
||||||
const uint32_t arithmetic_flags = i.instr->flags >> 8;
|
const uint32_t arithmetic_flags = i.instr->flags >> 8;
|
||||||
bool is_unsigned = !!(arithmetic_flags & ARITHMETIC_UNSIGNED);
|
bool is_unsigned = !!(arithmetic_flags & ARITHMETIC_UNSIGNED);
|
||||||
bool saturate = !!(arithmetic_flags & ARITHMETIC_SATURATE);
|
bool saturate = !!(arithmetic_flags & ARITHMETIC_SATURATE);
|
||||||
|
@ -3727,7 +3747,8 @@ struct VECTOR_SUB
|
||||||
case INT32_TYPE:
|
case INT32_TYPE:
|
||||||
if (saturate) {
|
if (saturate) {
|
||||||
if (is_unsigned) {
|
if (is_unsigned) {
|
||||||
// xmm0 is the only temp register that can be used by src1/src2.
|
// xmm0 is the only temp register that can be used by
|
||||||
|
// src1/src2.
|
||||||
e.vpsubd(e.xmm1, src1, src2);
|
e.vpsubd(e.xmm1, src1, src2);
|
||||||
|
|
||||||
// If result is greater than either of the inputs, we've
|
// If result is greater than either of the inputs, we've
|
||||||
|
@ -3749,18 +3770,19 @@ struct VECTOR_SUB
|
||||||
src2 = e.xmm1;
|
src2 = e.xmm1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// xmm0 is the only temp register that can be used by src1/src2.
|
// xmm0 is the only temp register that can be used by
|
||||||
|
// src1/src2.
|
||||||
e.vpsubd(dest, src1, src2);
|
e.vpsubd(dest, src1, src2);
|
||||||
|
|
||||||
// We can only overflow if the signs of the operands are opposite.
|
// We can only overflow if the signs of the operands are
|
||||||
// If signs are opposite and result sign isn't the same as src1's
|
// opposite. If signs are opposite and result sign isn't the
|
||||||
// sign, we've overflowed.
|
// same as src1's sign, we've overflowed. if ((s32b)((src1 ^
|
||||||
// if ((s32b)((src1 ^ src2) & (src1 ^ res)) < 0) then overflowed
|
// src2) & (src1 ^ res)) < 0) then overflowed
|
||||||
// http://locklessinc.com/articles/sat_arithmetic/
|
// http://locklessinc.com/articles/sat_arithmetic/
|
||||||
e.vpxor(e.xmm1, src1, src2);
|
e.vpxor(e.xmm1, src1, src2);
|
||||||
|
|
||||||
// Move src1 to xmm0 in-case it's the same register as the dest.
|
// Move src1 to xmm0 in-case it's the same register as the
|
||||||
// This kills src2 if it's a constant.
|
// dest. This kills src2 if it's a constant.
|
||||||
if (src1 != e.xmm0) {
|
if (src1 != e.xmm0) {
|
||||||
e.vmovdqa(e.xmm0, src1);
|
e.vmovdqa(e.xmm0, src1);
|
||||||
src1 = e.xmm0;
|
src1 = e.xmm0;
|
||||||
|
@ -3773,11 +3795,13 @@ struct VECTOR_SUB
|
||||||
|
|
||||||
// Set any negative overflowed elements of src1 to INT_MIN
|
// Set any negative overflowed elements of src1 to INT_MIN
|
||||||
e.vpand(e.xmm2, src1, e.xmm1);
|
e.vpand(e.xmm2, src1, e.xmm1);
|
||||||
e.vblendvps(dest, dest, e.GetXmmConstPtr(XMMSignMaskI32), e.xmm2);
|
e.vblendvps(dest, dest, e.GetXmmConstPtr(XMMSignMaskI32),
|
||||||
|
e.xmm2);
|
||||||
|
|
||||||
// Set any positive overflowed elements of src1 to INT_MAX
|
// Set any positive overflowed elements of src1 to INT_MAX
|
||||||
e.vpandn(e.xmm2, src1, e.xmm1);
|
e.vpandn(e.xmm2, src1, e.xmm1);
|
||||||
e.vblendvps(dest, dest, e.GetXmmConstPtr(XMMAbsMaskPS), e.xmm2);
|
e.vblendvps(dest, dest, e.GetXmmConstPtr(XMMAbsMaskPS),
|
||||||
|
e.xmm2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
e.vpsubd(dest, src1, src2);
|
e.vpsubd(dest, src1, src2);
|
||||||
|
@ -4469,8 +4493,10 @@ struct MUL_ADD_F32
|
||||||
|
|
||||||
// FMA extension
|
// FMA extension
|
||||||
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
||||||
EmitCommutativeBinaryXmmOp(e, i, [&i](X64Emitter& e, const Xmm& dest,
|
EmitCommutativeBinaryXmmOp(
|
||||||
const Xmm& src1, const Xmm& src2) {
|
e, i,
|
||||||
|
[&i](X64Emitter& e, const Xmm& dest, const Xmm& src1,
|
||||||
|
const Xmm& src2) {
|
||||||
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
||||||
if (i.src3.is_constant) {
|
if (i.src3.is_constant) {
|
||||||
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
||||||
|
@ -4526,8 +4552,10 @@ struct MUL_ADD_F64
|
||||||
|
|
||||||
// FMA extension
|
// FMA extension
|
||||||
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
||||||
EmitCommutativeBinaryXmmOp(e, i, [&i](X64Emitter& e, const Xmm& dest,
|
EmitCommutativeBinaryXmmOp(
|
||||||
const Xmm& src1, const Xmm& src2) {
|
e, i,
|
||||||
|
[&i](X64Emitter& e, const Xmm& dest, const Xmm& src1,
|
||||||
|
const Xmm& src2) {
|
||||||
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
||||||
if (i.src3.is_constant) {
|
if (i.src3.is_constant) {
|
||||||
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
||||||
|
@ -4589,8 +4617,10 @@ struct MUL_ADD_V128
|
||||||
// than vmul+vadd and it'd be nice to know why. Until we know, it's
|
// than vmul+vadd and it'd be nice to know why. Until we know, it's
|
||||||
// disabled so tests pass.
|
// disabled so tests pass.
|
||||||
if (false && e.IsFeatureEnabled(kX64EmitFMA)) {
|
if (false && e.IsFeatureEnabled(kX64EmitFMA)) {
|
||||||
EmitCommutativeBinaryXmmOp(e, i, [&i](X64Emitter& e, const Xmm& dest,
|
EmitCommutativeBinaryXmmOp(
|
||||||
const Xmm& src1, const Xmm& src2) {
|
e, i,
|
||||||
|
[&i](X64Emitter& e, const Xmm& dest, const Xmm& src1,
|
||||||
|
const Xmm& src2) {
|
||||||
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
||||||
if (i.src3.is_constant) {
|
if (i.src3.is_constant) {
|
||||||
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
||||||
|
@ -4660,8 +4690,10 @@ struct MUL_SUB_F32
|
||||||
|
|
||||||
// FMA extension
|
// FMA extension
|
||||||
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
||||||
EmitCommutativeBinaryXmmOp(e, i, [&i](X64Emitter& e, const Xmm& dest,
|
EmitCommutativeBinaryXmmOp(
|
||||||
const Xmm& src1, const Xmm& src2) {
|
e, i,
|
||||||
|
[&i](X64Emitter& e, const Xmm& dest, const Xmm& src1,
|
||||||
|
const Xmm& src2) {
|
||||||
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
||||||
if (i.src3.is_constant) {
|
if (i.src3.is_constant) {
|
||||||
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
||||||
|
@ -4717,8 +4749,10 @@ struct MUL_SUB_F64
|
||||||
|
|
||||||
// FMA extension
|
// FMA extension
|
||||||
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
||||||
EmitCommutativeBinaryXmmOp(e, i, [&i](X64Emitter& e, const Xmm& dest,
|
EmitCommutativeBinaryXmmOp(
|
||||||
const Xmm& src1, const Xmm& src2) {
|
e, i,
|
||||||
|
[&i](X64Emitter& e, const Xmm& dest, const Xmm& src1,
|
||||||
|
const Xmm& src2) {
|
||||||
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
||||||
if (i.src3.is_constant) {
|
if (i.src3.is_constant) {
|
||||||
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
||||||
|
@ -4778,8 +4812,10 @@ struct MUL_SUB_V128
|
||||||
|
|
||||||
// FMA extension
|
// FMA extension
|
||||||
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
if (e.IsFeatureEnabled(kX64EmitFMA)) {
|
||||||
EmitCommutativeBinaryXmmOp(e, i, [&i](X64Emitter& e, const Xmm& dest,
|
EmitCommutativeBinaryXmmOp(
|
||||||
const Xmm& src1, const Xmm& src2) {
|
e, i,
|
||||||
|
[&i](X64Emitter& e, const Xmm& dest, const Xmm& src1,
|
||||||
|
const Xmm& src2) {
|
||||||
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
Xmm src3 = i.src3.is_constant ? e.xmm1 : i.src3;
|
||||||
if (i.src3.is_constant) {
|
if (i.src3.is_constant) {
|
||||||
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
e.LoadConstantXmm(e.xmm1, i.src3.constant());
|
||||||
|
@ -5098,8 +5134,10 @@ EMITTER_OPCODE_TABLE(OPCODE_DOT_PRODUCT_4, DOT_PRODUCT_4_V128);
|
||||||
template <typename SEQ, typename REG, typename ARGS>
|
template <typename SEQ, typename REG, typename ARGS>
|
||||||
void EmitAndXX(X64Emitter& e, const ARGS& i) {
|
void EmitAndXX(X64Emitter& e, const ARGS& i) {
|
||||||
SEQ::EmitCommutativeBinaryOp(
|
SEQ::EmitCommutativeBinaryOp(
|
||||||
e, i, [](X64Emitter& e, const REG& dest_src,
|
e, i,
|
||||||
const REG& src) { e.and_(dest_src, src); },
|
[](X64Emitter& e, const REG& dest_src, const REG& src) {
|
||||||
|
e.and_(dest_src, src);
|
||||||
|
},
|
||||||
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
||||||
e.and_(dest_src, constant);
|
e.and_(dest_src, constant);
|
||||||
});
|
});
|
||||||
|
@ -5141,8 +5179,10 @@ EMITTER_OPCODE_TABLE(OPCODE_AND, AND_I8, AND_I16, AND_I32, AND_I64, AND_V128);
|
||||||
template <typename SEQ, typename REG, typename ARGS>
|
template <typename SEQ, typename REG, typename ARGS>
|
||||||
void EmitOrXX(X64Emitter& e, const ARGS& i) {
|
void EmitOrXX(X64Emitter& e, const ARGS& i) {
|
||||||
SEQ::EmitCommutativeBinaryOp(
|
SEQ::EmitCommutativeBinaryOp(
|
||||||
e, i, [](X64Emitter& e, const REG& dest_src,
|
e, i,
|
||||||
const REG& src) { e.or_(dest_src, src); },
|
[](X64Emitter& e, const REG& dest_src, const REG& src) {
|
||||||
|
e.or_(dest_src, src);
|
||||||
|
},
|
||||||
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
||||||
e.or_(dest_src, constant);
|
e.or_(dest_src, constant);
|
||||||
});
|
});
|
||||||
|
@ -5184,8 +5224,10 @@ EMITTER_OPCODE_TABLE(OPCODE_OR, OR_I8, OR_I16, OR_I32, OR_I64, OR_V128);
|
||||||
template <typename SEQ, typename REG, typename ARGS>
|
template <typename SEQ, typename REG, typename ARGS>
|
||||||
void EmitXorXX(X64Emitter& e, const ARGS& i) {
|
void EmitXorXX(X64Emitter& e, const ARGS& i) {
|
||||||
SEQ::EmitCommutativeBinaryOp(
|
SEQ::EmitCommutativeBinaryOp(
|
||||||
e, i, [](X64Emitter& e, const REG& dest_src,
|
e, i,
|
||||||
const REG& src) { e.xor_(dest_src, src); },
|
[](X64Emitter& e, const REG& dest_src, const REG& src) {
|
||||||
|
e.xor_(dest_src, src);
|
||||||
|
},
|
||||||
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
[](X64Emitter& e, const REG& dest_src, int32_t constant) {
|
||||||
e.xor_(dest_src, constant);
|
e.xor_(dest_src, constant);
|
||||||
});
|
});
|
||||||
|
@ -6209,9 +6251,11 @@ struct VECTOR_AVERAGE
|
||||||
return _mm_load_si128(reinterpret_cast<__m128i*>(value));
|
return _mm_load_si128(reinterpret_cast<__m128i*>(value));
|
||||||
}
|
}
|
||||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||||
EmitCommutativeBinaryXmmOp(e, i, [&i](X64Emitter& e, const Xmm& dest,
|
EmitCommutativeBinaryXmmOp(
|
||||||
const Xmm& src1, const Xmm& src2) {
|
e, i,
|
||||||
const TypeName part_type = static_cast<TypeName>(i.instr->flags & 0xFF);
|
[&i](X64Emitter& e, const Xmm& dest, const Xmm& src1, const Xmm& src2) {
|
||||||
|
const TypeName part_type =
|
||||||
|
static_cast<TypeName>(i.instr->flags & 0xFF);
|
||||||
const uint32_t arithmetic_flags = i.instr->flags >> 8;
|
const uint32_t arithmetic_flags = i.instr->flags >> 8;
|
||||||
bool is_unsigned = !!(arithmetic_flags & ARITHMETIC_UNSIGNED);
|
bool is_unsigned = !!(arithmetic_flags & ARITHMETIC_UNSIGNED);
|
||||||
switch (part_type) {
|
switch (part_type) {
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#else
|
#else
|
||||||
#include <llvm/ADT/BitVector.h>
|
#include <llvm/ADT/BitVector.h>
|
||||||
|
#include <cmath>
|
||||||
#endif // XE_COMPILER_MSVC
|
#endif // XE_COMPILER_MSVC
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
|
@ -420,7 +420,7 @@ bool RegisterAllocationPass::SpillOneRegister(HIRBuilder* builder, Block* block,
|
||||||
auto new_value = builder->LoadLocal(spill_value->local_slot);
|
auto new_value = builder->LoadLocal(spill_value->local_slot);
|
||||||
auto spill_load = builder->last_instr();
|
auto spill_load = builder->last_instr();
|
||||||
spill_load->MoveBefore(next_use->instr);
|
spill_load->MoveBefore(next_use->instr);
|
||||||
// Note: implicit first use added.
|
// Note: implicit first use added.
|
||||||
|
|
||||||
#if ASSERT_NO_CYCLES
|
#if ASSERT_NO_CYCLES
|
||||||
builder->AssertNoCycles();
|
builder->AssertNoCycles();
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#else
|
#else
|
||||||
#include <llvm/ADT/BitVector.h>
|
#include <llvm/ADT/BitVector.h>
|
||||||
|
#include <cmath>
|
||||||
#endif // XE_COMPILER_MSVC
|
#endif // XE_COMPILER_MSVC
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
|
|
@ -1963,7 +1963,10 @@ Value* HIRBuilder::CountLeadingZeros(Value* value) {
|
||||||
|
|
||||||
if (value->IsConstantZero()) {
|
if (value->IsConstantZero()) {
|
||||||
static const uint8_t zeros[] = {
|
static const uint8_t zeros[] = {
|
||||||
8, 16, 32, 64,
|
8,
|
||||||
|
16,
|
||||||
|
32,
|
||||||
|
64,
|
||||||
};
|
};
|
||||||
assert_true(value->type <= INT64_TYPE);
|
assert_true(value->type <= INT64_TYPE);
|
||||||
return LoadConstantUint8(zeros[value->type]);
|
return LoadConstantUint8(zeros[value->type]);
|
||||||
|
|
|
@ -15,7 +15,10 @@ namespace hir {
|
||||||
|
|
||||||
#define DEFINE_OPCODE(num, name, sig, flags) \
|
#define DEFINE_OPCODE(num, name, sig, flags) \
|
||||||
const OpcodeInfo num##_info = { \
|
const OpcodeInfo num##_info = { \
|
||||||
flags, sig, name, num, \
|
flags, \
|
||||||
|
sig, \
|
||||||
|
name, \
|
||||||
|
num, \
|
||||||
};
|
};
|
||||||
#include "xenia/cpu/hir/opcodes.inl"
|
#include "xenia/cpu/hir/opcodes.inl"
|
||||||
#undef DEFINE_OPCODE
|
#undef DEFINE_OPCODE
|
||||||
|
|
|
@ -87,7 +87,6 @@ enum PackType : uint16_t {
|
||||||
PACK_TYPE_16_IN_32 = 7,
|
PACK_TYPE_16_IN_32 = 7,
|
||||||
|
|
||||||
PACK_TYPE_MODE = 0x000F, // just to get the mode
|
PACK_TYPE_MODE = 0x000F, // just to get the mode
|
||||||
|
|
||||||
// Unpack to low or high parts.
|
// Unpack to low or high parts.
|
||||||
PACK_TYPE_TO_LO = 0 << 12,
|
PACK_TYPE_TO_LO = 0 << 12,
|
||||||
PACK_TYPE_TO_HI = 1 << 12,
|
PACK_TYPE_TO_HI = 1 << 12,
|
||||||
|
|
|
@ -52,7 +52,12 @@ bool MMIOHandler::RegisterRange(uint32_t virtual_address, uint32_t mask,
|
||||||
MMIOReadCallback read_callback,
|
MMIOReadCallback read_callback,
|
||||||
MMIOWriteCallback write_callback) {
|
MMIOWriteCallback write_callback) {
|
||||||
mapped_ranges_.push_back({
|
mapped_ranges_.push_back({
|
||||||
virtual_address, mask, size, context, read_callback, write_callback,
|
virtual_address,
|
||||||
|
mask,
|
||||||
|
size,
|
||||||
|
context,
|
||||||
|
read_callback,
|
||||||
|
write_callback,
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ int InstrEmit_branch(PPCHIRBuilder& f, const char* src, uint64_t cia,
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} // namespace ppc
|
||||||
|
|
||||||
int InstrEmit_bx(PPCHIRBuilder& f, const InstrData& i) {
|
int InstrEmit_bx(PPCHIRBuilder& f, const InstrData& i) {
|
||||||
// if AA then
|
// if AA then
|
||||||
|
@ -799,6 +799,6 @@ void RegisterEmitCategoryControl() {
|
||||||
XEREGISTERINSTR(mtmsrd);
|
XEREGISTERINSTR(mtmsrd);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ppc
|
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
} // namespace xe
|
||||||
|
|
|
@ -341,14 +341,16 @@ std::vector<BlockInfo> PPCScanner::FindBlocks(GuestFunction* function) {
|
||||||
if (ends_block) {
|
if (ends_block) {
|
||||||
in_block = false;
|
in_block = false;
|
||||||
block_map[block_start] = {
|
block_map[block_start] = {
|
||||||
block_start, address,
|
block_start,
|
||||||
|
address,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_block) {
|
if (in_block) {
|
||||||
block_map[block_start] = {
|
block_map[block_start] = {
|
||||||
block_start, end_address,
|
block_start,
|
||||||
|
end_address,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@ using xe::cpu::ppc::PPCContext;
|
||||||
|
|
||||||
TEST_CASE("ADD_I8", "[instr]") {
|
TEST_CASE("ADD_I8", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Add(b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Add(b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -90,7 +91,8 @@ TEST_CASE("ADD_I8", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("ADD_I16", "[instr]") {
|
TEST_CASE("ADD_I16", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Add(b.Truncate(LoadGPR(b, 4), INT16_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Add(b.Truncate(LoadGPR(b, 4), INT16_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT16_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT16_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -162,7 +164,8 @@ TEST_CASE("ADD_I16", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("ADD_I32", "[instr]") {
|
TEST_CASE("ADD_I32", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Add(b.Truncate(LoadGPR(b, 4), INT32_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Add(b.Truncate(LoadGPR(b, 4), INT32_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT32_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT32_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -304,7 +307,8 @@ TEST_CASE("ADD_I64", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("ADD_F32", "[instr]") {
|
TEST_CASE("ADD_F32", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreFPR(b, 3, b.Convert(b.Add(b.Convert(LoadFPR(b, 4), FLOAT32_TYPE),
|
StoreFPR(b, 3,
|
||||||
|
b.Convert(b.Add(b.Convert(LoadFPR(b, 4), FLOAT32_TYPE),
|
||||||
b.Convert(LoadFPR(b, 5), FLOAT32_TYPE)),
|
b.Convert(LoadFPR(b, 5), FLOAT32_TYPE)),
|
||||||
FLOAT64_TYPE));
|
FLOAT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
|
|
@ -19,9 +19,10 @@ using xe::cpu::ppc::PPCContext;
|
||||||
|
|
||||||
TEST_CASE("EXTRACT_INT8", "[instr]") {
|
TEST_CASE("EXTRACT_INT8", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Extract(LoadVR(b, 4),
|
StoreGPR(
|
||||||
b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
b, 3,
|
||||||
INT8_TYPE),
|
b.ZeroExtend(b.Extract(LoadVR(b, 4),
|
||||||
|
b.Truncate(LoadGPR(b, 4), INT8_TYPE), INT8_TYPE),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -42,8 +43,9 @@ TEST_CASE("EXTRACT_INT8", "[instr]") {
|
||||||
TEST_CASE("EXTRACT_INT8_CONSTANT", "[instr]") {
|
TEST_CASE("EXTRACT_INT8_CONSTANT", "[instr]") {
|
||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
TestFunction([i](HIRBuilder& b) {
|
TestFunction([i](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Extract(LoadVR(b, 4), b.LoadConstantInt8(i),
|
StoreGPR(b, 3,
|
||||||
INT8_TYPE),
|
b.ZeroExtend(
|
||||||
|
b.Extract(LoadVR(b, 4), b.LoadConstantInt8(i), INT8_TYPE),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
})
|
})
|
||||||
|
@ -62,8 +64,9 @@ TEST_CASE("EXTRACT_INT8_CONSTANT", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("EXTRACT_INT16", "[instr]") {
|
TEST_CASE("EXTRACT_INT16", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Extract(LoadVR(b, 4),
|
StoreGPR(b, 3,
|
||||||
b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
b.ZeroExtend(
|
||||||
|
b.Extract(LoadVR(b, 4), b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
||||||
INT16_TYPE),
|
INT16_TYPE),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -85,8 +88,9 @@ TEST_CASE("EXTRACT_INT16", "[instr]") {
|
||||||
TEST_CASE("EXTRACT_INT16_CONSTANT", "[instr]") {
|
TEST_CASE("EXTRACT_INT16_CONSTANT", "[instr]") {
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
TestFunction([i](HIRBuilder& b) {
|
TestFunction([i](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Extract(LoadVR(b, 4), b.LoadConstantInt8(i),
|
StoreGPR(b, 3,
|
||||||
INT16_TYPE),
|
b.ZeroExtend(
|
||||||
|
b.Extract(LoadVR(b, 4), b.LoadConstantInt8(i), INT16_TYPE),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
})
|
})
|
||||||
|
@ -104,8 +108,9 @@ TEST_CASE("EXTRACT_INT16_CONSTANT", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("EXTRACT_INT32", "[instr]") {
|
TEST_CASE("EXTRACT_INT32", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Extract(LoadVR(b, 4),
|
StoreGPR(b, 3,
|
||||||
b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
b.ZeroExtend(
|
||||||
|
b.Extract(LoadVR(b, 4), b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
||||||
INT32_TYPE),
|
INT32_TYPE),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -126,8 +131,9 @@ TEST_CASE("EXTRACT_INT32", "[instr]") {
|
||||||
TEST_CASE("EXTRACT_INT32_CONSTANT", "[instr]") {
|
TEST_CASE("EXTRACT_INT32_CONSTANT", "[instr]") {
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
TestFunction([i](HIRBuilder& b) {
|
TestFunction([i](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Extract(LoadVR(b, 4), b.LoadConstantInt8(i),
|
StoreGPR(b, 3,
|
||||||
INT32_TYPE),
|
b.ZeroExtend(
|
||||||
|
b.Extract(LoadVR(b, 4), b.LoadConstantInt8(i), INT32_TYPE),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
})
|
})
|
||||||
|
|
|
@ -20,7 +20,8 @@ using xe::cpu::ppc::PPCContext;
|
||||||
TEST_CASE("INSERT_INT8", "[instr]") {
|
TEST_CASE("INSERT_INT8", "[instr]") {
|
||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
TestFunction test([i](HIRBuilder& b) {
|
TestFunction test([i](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.Insert(LoadVR(b, 4), b.LoadConstantInt32(i),
|
StoreVR(b, 3,
|
||||||
|
b.Insert(LoadVR(b, 4), b.LoadConstantInt32(i),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)));
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -44,7 +45,8 @@ TEST_CASE("INSERT_INT8", "[instr]") {
|
||||||
TEST_CASE("INSERT_INT16", "[instr]") {
|
TEST_CASE("INSERT_INT16", "[instr]") {
|
||||||
for (int i = 0; i < 8; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
TestFunction test([i](HIRBuilder& b) {
|
TestFunction test([i](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.Insert(LoadVR(b, 4), b.LoadConstantInt32(i),
|
StoreVR(b, 3,
|
||||||
|
b.Insert(LoadVR(b, 4), b.LoadConstantInt32(i),
|
||||||
b.Truncate(LoadGPR(b, 5), INT16_TYPE)));
|
b.Truncate(LoadGPR(b, 5), INT16_TYPE)));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -66,7 +68,8 @@ TEST_CASE("INSERT_INT16", "[instr]") {
|
||||||
TEST_CASE("INSERT_INT32", "[instr]") {
|
TEST_CASE("INSERT_INT32", "[instr]") {
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
TestFunction test([i](HIRBuilder& b) {
|
TestFunction test([i](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.Insert(LoadVR(b, 4), b.LoadConstantInt32(i),
|
StoreVR(b, 3,
|
||||||
|
b.Insert(LoadVR(b, 4), b.LoadConstantInt32(i),
|
||||||
b.Truncate(LoadGPR(b, 5), INT32_TYPE)));
|
b.Truncate(LoadGPR(b, 5), INT32_TYPE)));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,8 +19,9 @@ TEST_CASE("PERMUTE_V128_BY_INT32_CONSTANT", "[instr]") {
|
||||||
{
|
{
|
||||||
uint32_t mask = MakePermuteMask(0, 0, 0, 1, 0, 2, 0, 3);
|
uint32_t mask = MakePermuteMask(0, 0, 0, 1, 0, 2, 0, 3);
|
||||||
TestFunction([mask](HIRBuilder& b) {
|
TestFunction([mask](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.Permute(b.LoadConstantUint32(mask), LoadVR(b, 4),
|
StoreVR(b, 3,
|
||||||
LoadVR(b, 5), INT32_TYPE));
|
b.Permute(b.LoadConstantUint32(mask), LoadVR(b, 4), LoadVR(b, 5),
|
||||||
|
INT32_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
})
|
})
|
||||||
.Run(
|
.Run(
|
||||||
|
@ -36,8 +37,9 @@ TEST_CASE("PERMUTE_V128_BY_INT32_CONSTANT", "[instr]") {
|
||||||
{
|
{
|
||||||
uint32_t mask = MakePermuteMask(1, 0, 1, 1, 1, 2, 1, 3);
|
uint32_t mask = MakePermuteMask(1, 0, 1, 1, 1, 2, 1, 3);
|
||||||
TestFunction([mask](HIRBuilder& b) {
|
TestFunction([mask](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.Permute(b.LoadConstantUint32(mask), LoadVR(b, 4),
|
StoreVR(b, 3,
|
||||||
LoadVR(b, 5), INT32_TYPE));
|
b.Permute(b.LoadConstantUint32(mask), LoadVR(b, 4), LoadVR(b, 5),
|
||||||
|
INT32_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
})
|
})
|
||||||
.Run(
|
.Run(
|
||||||
|
@ -53,8 +55,9 @@ TEST_CASE("PERMUTE_V128_BY_INT32_CONSTANT", "[instr]") {
|
||||||
{
|
{
|
||||||
uint32_t mask = MakePermuteMask(0, 3, 0, 2, 0, 1, 0, 0);
|
uint32_t mask = MakePermuteMask(0, 3, 0, 2, 0, 1, 0, 0);
|
||||||
TestFunction([mask](HIRBuilder& b) {
|
TestFunction([mask](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.Permute(b.LoadConstantUint32(mask), LoadVR(b, 4),
|
StoreVR(b, 3,
|
||||||
LoadVR(b, 5), INT32_TYPE));
|
b.Permute(b.LoadConstantUint32(mask), LoadVR(b, 4), LoadVR(b, 5),
|
||||||
|
INT32_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
})
|
})
|
||||||
.Run(
|
.Run(
|
||||||
|
@ -70,8 +73,9 @@ TEST_CASE("PERMUTE_V128_BY_INT32_CONSTANT", "[instr]") {
|
||||||
{
|
{
|
||||||
uint32_t mask = MakePermuteMask(1, 3, 1, 2, 1, 1, 1, 0);
|
uint32_t mask = MakePermuteMask(1, 3, 1, 2, 1, 1, 1, 0);
|
||||||
TestFunction([mask](HIRBuilder& b) {
|
TestFunction([mask](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.Permute(b.LoadConstantUint32(mask), LoadVR(b, 4),
|
StoreVR(b, 3,
|
||||||
LoadVR(b, 5), INT32_TYPE));
|
b.Permute(b.LoadConstantUint32(mask), LoadVR(b, 4), LoadVR(b, 5),
|
||||||
|
INT32_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
})
|
})
|
||||||
.Run(
|
.Run(
|
||||||
|
|
|
@ -20,8 +20,8 @@ namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
namespace sandbox {
|
namespace sandbox {
|
||||||
|
|
||||||
using xe::cpu::ppc::PPCContext;
|
|
||||||
using xe::cpu::Runtime;
|
using xe::cpu::Runtime;
|
||||||
|
using xe::cpu::ppc::PPCContext;
|
||||||
|
|
||||||
// TODO(benvanik): simple memory? move more into core?
|
// TODO(benvanik): simple memory? move more into core?
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ using xe::cpu::ppc::PPCContext;
|
||||||
|
|
||||||
TEST_CASE("SHA_I8", "[instr]") {
|
TEST_CASE("SHA_I8", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Sha(b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Sha(b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -70,7 +71,8 @@ TEST_CASE("SHA_I8", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("SHA_I16", "[instr]") {
|
TEST_CASE("SHA_I16", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Sha(b.Truncate(LoadGPR(b, 4), INT16_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Sha(b.Truncate(LoadGPR(b, 4), INT16_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -124,7 +126,8 @@ TEST_CASE("SHA_I16", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("SHA_I32", "[instr]") {
|
TEST_CASE("SHA_I32", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Sha(b.Truncate(LoadGPR(b, 4), INT32_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Sha(b.Truncate(LoadGPR(b, 4), INT32_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -178,7 +181,8 @@ TEST_CASE("SHA_I32", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("SHA_I64", "[instr]") {
|
TEST_CASE("SHA_I64", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.Sha(b.Truncate(LoadGPR(b, 4), INT64_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.Sha(b.Truncate(LoadGPR(b, 4), INT64_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)));
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,7 +16,8 @@ using xe::cpu::ppc::PPCContext;
|
||||||
|
|
||||||
TEST_CASE("SHL_I8", "[instr]") {
|
TEST_CASE("SHL_I8", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Shl(b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Shl(b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -70,7 +71,8 @@ TEST_CASE("SHL_I8", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("SHL_I16", "[instr]") {
|
TEST_CASE("SHL_I16", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Shl(b.Truncate(LoadGPR(b, 4), INT16_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Shl(b.Truncate(LoadGPR(b, 4), INT16_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -124,7 +126,8 @@ TEST_CASE("SHL_I16", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("SHL_I32", "[instr]") {
|
TEST_CASE("SHL_I32", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Shl(b.Truncate(LoadGPR(b, 4), INT32_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Shl(b.Truncate(LoadGPR(b, 4), INT32_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -178,7 +181,8 @@ TEST_CASE("SHL_I32", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("SHL_I64", "[instr]") {
|
TEST_CASE("SHL_I64", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.Shl(b.Truncate(LoadGPR(b, 4), INT64_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.Shl(b.Truncate(LoadGPR(b, 4), INT64_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)));
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,8 @@ using xe::cpu::ppc::PPCContext;
|
||||||
|
|
||||||
TEST_CASE("SHR_I8", "[instr]") {
|
TEST_CASE("SHR_I8", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Shr(b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Shr(b.Truncate(LoadGPR(b, 4), INT8_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -71,7 +72,8 @@ TEST_CASE("SHR_I8", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("SHR_I16", "[instr]") {
|
TEST_CASE("SHR_I16", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Shr(b.Truncate(LoadGPR(b, 4), INT16_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Shr(b.Truncate(LoadGPR(b, 4), INT16_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -125,7 +127,8 @@ TEST_CASE("SHR_I16", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("SHR_I32", "[instr]") {
|
TEST_CASE("SHR_I32", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.ZeroExtend(b.Shr(b.Truncate(LoadGPR(b, 4), INT32_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.ZeroExtend(b.Shr(b.Truncate(LoadGPR(b, 4), INT32_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)),
|
||||||
INT64_TYPE));
|
INT64_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -179,7 +182,8 @@ TEST_CASE("SHR_I32", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("SHR_I64", "[instr]") {
|
TEST_CASE("SHR_I64", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreGPR(b, 3, b.Shr(b.Truncate(LoadGPR(b, 4), INT64_TYPE),
|
StoreGPR(b, 3,
|
||||||
|
b.Shr(b.Truncate(LoadGPR(b, 4), INT64_TYPE),
|
||||||
b.Truncate(LoadGPR(b, 5), INT8_TYPE)));
|
b.Truncate(LoadGPR(b, 5), INT8_TYPE)));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -47,7 +47,8 @@ TEST_CASE("VECTOR_ADD_I8", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_ADD_I8_SAT_SIGNED", "[instr]") {
|
TEST_CASE("VECTOR_ADD_I8_SAT_SIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT8_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT8_TYPE,
|
||||||
ARITHMETIC_SATURATE));
|
ARITHMETIC_SATURATE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -73,7 +74,8 @@ TEST_CASE("VECTOR_ADD_I8_SAT_SIGNED", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_ADD_I8_SAT_UNSIGNED", "[instr]") {
|
TEST_CASE("VECTOR_ADD_I8_SAT_UNSIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT8_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT8_TYPE,
|
||||||
ARITHMETIC_SATURATE | ARITHMETIC_UNSIGNED));
|
ARITHMETIC_SATURATE | ARITHMETIC_UNSIGNED));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -124,7 +126,8 @@ TEST_CASE("VECTOR_ADD_I16", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_ADD_I16_SAT_SIGNED", "[instr]") {
|
TEST_CASE("VECTOR_ADD_I16_SAT_SIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE,
|
||||||
ARITHMETIC_SATURATE));
|
ARITHMETIC_SATURATE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -150,7 +153,8 @@ TEST_CASE("VECTOR_ADD_I16_SAT_SIGNED", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_ADD_I16_SAT_UNSIGNED", "[instr]") {
|
TEST_CASE("VECTOR_ADD_I16_SAT_UNSIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE,
|
||||||
ARITHMETIC_SATURATE | ARITHMETIC_UNSIGNED));
|
ARITHMETIC_SATURATE | ARITHMETIC_UNSIGNED));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -201,7 +205,8 @@ TEST_CASE("VECTOR_ADD_I32", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_ADD_I32_SAT_SIGNED", "[instr]") {
|
TEST_CASE("VECTOR_ADD_I32_SAT_SIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT32_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT32_TYPE,
|
||||||
ARITHMETIC_SATURATE));
|
ARITHMETIC_SATURATE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -236,7 +241,8 @@ TEST_CASE("VECTOR_ADD_I32_SAT_SIGNED", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_ADD_I32_SAT_UNSIGNED", "[instr]") {
|
TEST_CASE("VECTOR_ADD_I32_SAT_UNSIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT32_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorAdd(LoadVR(b, 4), LoadVR(b, 5), INT32_TYPE,
|
||||||
ARITHMETIC_SATURATE | ARITHMETIC_UNSIGNED));
|
ARITHMETIC_SATURATE | ARITHMETIC_UNSIGNED));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,7 +39,8 @@ TEST_CASE("VECTOR_MAX_I8_SIGNED", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_MAX_I8_UNSIGNED", "[instr]") {
|
TEST_CASE("VECTOR_MAX_I8_UNSIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorMax(LoadVR(b, 4), LoadVR(b, 5), INT8_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorMax(LoadVR(b, 4), LoadVR(b, 5), INT8_TYPE,
|
||||||
ARITHMETIC_UNSIGNED));
|
ARITHMETIC_UNSIGNED));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -75,7 +76,8 @@ TEST_CASE("VECTOR_MAX_I16_SIGNED", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_MAX_I16_UNSIGNED", "[instr]") {
|
TEST_CASE("VECTOR_MAX_I16_UNSIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorMax(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorMax(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE,
|
||||||
ARITHMETIC_UNSIGNED));
|
ARITHMETIC_UNSIGNED));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -108,7 +110,8 @@ TEST_CASE("VECTOR_MAX_I32_SIGNED", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_MAX_I32_UNSIGNED", "[instr]") {
|
TEST_CASE("VECTOR_MAX_I32_UNSIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorMax(LoadVR(b, 4), LoadVR(b, 5), INT32_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorMax(LoadVR(b, 4), LoadVR(b, 5), INT32_TYPE,
|
||||||
ARITHMETIC_UNSIGNED));
|
ARITHMETIC_UNSIGNED));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,7 +39,8 @@ TEST_CASE("VECTOR_MIN_I8_SIGNED", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_MIN_I8_UNSIGNED", "[instr]") {
|
TEST_CASE("VECTOR_MIN_I8_UNSIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorMin(LoadVR(b, 4), LoadVR(b, 5), INT8_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorMin(LoadVR(b, 4), LoadVR(b, 5), INT8_TYPE,
|
||||||
ARITHMETIC_UNSIGNED));
|
ARITHMETIC_UNSIGNED));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -75,7 +76,8 @@ TEST_CASE("VECTOR_MIN_I16_SIGNED", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_MIN_I16_UNSIGNED", "[instr]") {
|
TEST_CASE("VECTOR_MIN_I16_UNSIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorMin(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorMin(LoadVR(b, 4), LoadVR(b, 5), INT16_TYPE,
|
||||||
ARITHMETIC_UNSIGNED));
|
ARITHMETIC_UNSIGNED));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -108,7 +110,8 @@ TEST_CASE("VECTOR_MIN_I32_SIGNED", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_MIN_I32_UNSIGNED", "[instr]") {
|
TEST_CASE("VECTOR_MIN_I32_UNSIGNED", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorMin(LoadVR(b, 4), LoadVR(b, 5), INT32_TYPE,
|
StoreVR(b, 3,
|
||||||
|
b.VectorMin(LoadVR(b, 4), LoadVR(b, 5), INT32_TYPE,
|
||||||
ARITHMETIC_UNSIGNED));
|
ARITHMETIC_UNSIGNED));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,8 +37,10 @@ TEST_CASE("VECTOR_SHA_I8", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_SHA_I8_CONSTANT", "[instr]") {
|
TEST_CASE("VECTOR_SHA_I8_CONSTANT", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorSha(LoadVR(b, 4), b.LoadConstantVec128(vec128b(
|
StoreVR(
|
||||||
0, 1, 2, 8, 4, 4, 6, 7, 8, 9,
|
b, 3,
|
||||||
|
b.VectorSha(LoadVR(b, 4),
|
||||||
|
b.LoadConstantVec128(vec128b(0, 1, 2, 8, 4, 4, 6, 7, 8, 9,
|
||||||
10, 11, 12, 13, 14, 15)),
|
10, 11, 12, 13, 14, 15)),
|
||||||
INT8_TYPE));
|
INT8_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -76,8 +78,10 @@ TEST_CASE("VECTOR_SHA_I16", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_SHA_I16_CONSTANT", "[instr]") {
|
TEST_CASE("VECTOR_SHA_I16_CONSTANT", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorSha(LoadVR(b, 4), b.LoadConstantVec128(vec128s(
|
StoreVR(
|
||||||
0, 1, 8, 15, 15, 8, 1, 16)),
|
b, 3,
|
||||||
|
b.VectorSha(LoadVR(b, 4),
|
||||||
|
b.LoadConstantVec128(vec128s(0, 1, 8, 15, 15, 8, 1, 16)),
|
||||||
INT16_TYPE));
|
INT16_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -122,11 +126,13 @@ TEST_CASE("VECTOR_SHA_I32", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_SHA_I32_CONSTANT", "[instr]") {
|
TEST_CASE("VECTOR_SHA_I32_CONSTANT", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorSha(LoadVR(b, 4),
|
StoreVR(
|
||||||
b.LoadConstantVec128(vec128i(0, 1, 16, 31)),
|
b, 3,
|
||||||
|
b.VectorSha(LoadVR(b, 4), b.LoadConstantVec128(vec128i(0, 1, 16, 31)),
|
||||||
INT32_TYPE));
|
INT32_TYPE));
|
||||||
StoreVR(b, 4, b.VectorSha(LoadVR(b, 5),
|
StoreVR(
|
||||||
b.LoadConstantVec128(vec128i(31, 16, 1, 32)),
|
b, 4,
|
||||||
|
b.VectorSha(LoadVR(b, 5), b.LoadConstantVec128(vec128i(31, 16, 1, 32)),
|
||||||
INT32_TYPE));
|
INT32_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,8 +37,10 @@ TEST_CASE("VECTOR_SHL_I8", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_SHL_I8_CONSTANT", "[instr]") {
|
TEST_CASE("VECTOR_SHL_I8_CONSTANT", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorShl(LoadVR(b, 4), b.LoadConstantVec128(vec128b(
|
StoreVR(
|
||||||
0, 1, 2, 8, 4, 4, 6, 7, 8, 9,
|
b, 3,
|
||||||
|
b.VectorShl(LoadVR(b, 4),
|
||||||
|
b.LoadConstantVec128(vec128b(0, 1, 2, 8, 4, 4, 6, 7, 8, 9,
|
||||||
10, 11, 12, 13, 14, 15)),
|
10, 11, 12, 13, 14, 15)),
|
||||||
INT8_TYPE));
|
INT8_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -76,8 +78,10 @@ TEST_CASE("VECTOR_SHL_I16", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_SHL_I16_CONSTANT", "[instr]") {
|
TEST_CASE("VECTOR_SHL_I16_CONSTANT", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorShl(LoadVR(b, 4), b.LoadConstantVec128(vec128s(
|
StoreVR(
|
||||||
0, 1, 8, 15, 15, 8, 1, 16)),
|
b, 3,
|
||||||
|
b.VectorShl(LoadVR(b, 4),
|
||||||
|
b.LoadConstantVec128(vec128s(0, 1, 8, 15, 15, 8, 1, 16)),
|
||||||
INT16_TYPE));
|
INT16_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -122,11 +126,13 @@ TEST_CASE("VECTOR_SHL_I32", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_SHL_I32_CONSTANT", "[instr]") {
|
TEST_CASE("VECTOR_SHL_I32_CONSTANT", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorShl(LoadVR(b, 4),
|
StoreVR(
|
||||||
b.LoadConstantVec128(vec128i(0, 1, 16, 31)),
|
b, 3,
|
||||||
|
b.VectorShl(LoadVR(b, 4), b.LoadConstantVec128(vec128i(0, 1, 16, 31)),
|
||||||
INT32_TYPE));
|
INT32_TYPE));
|
||||||
StoreVR(b, 4, b.VectorShl(LoadVR(b, 5),
|
StoreVR(
|
||||||
b.LoadConstantVec128(vec128i(31, 16, 1, 32)),
|
b, 4,
|
||||||
|
b.VectorShl(LoadVR(b, 5), b.LoadConstantVec128(vec128i(31, 16, 1, 32)),
|
||||||
INT32_TYPE));
|
INT32_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,8 +37,10 @@ TEST_CASE("VECTOR_SHR_I8", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_SHR_I8_CONSTANT", "[instr]") {
|
TEST_CASE("VECTOR_SHR_I8_CONSTANT", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorShr(LoadVR(b, 4), b.LoadConstantVec128(vec128b(
|
StoreVR(
|
||||||
0, 1, 2, 8, 4, 4, 6, 7, 8, 9,
|
b, 3,
|
||||||
|
b.VectorShr(LoadVR(b, 4),
|
||||||
|
b.LoadConstantVec128(vec128b(0, 1, 2, 8, 4, 4, 6, 7, 8, 9,
|
||||||
10, 11, 12, 13, 14, 15)),
|
10, 11, 12, 13, 14, 15)),
|
||||||
INT8_TYPE));
|
INT8_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
|
@ -76,8 +78,10 @@ TEST_CASE("VECTOR_SHR_I16", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_SHR_I16_CONSTANT", "[instr]") {
|
TEST_CASE("VECTOR_SHR_I16_CONSTANT", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorShr(LoadVR(b, 4), b.LoadConstantVec128(vec128s(
|
StoreVR(
|
||||||
0, 1, 8, 15, 15, 8, 1, 16)),
|
b, 3,
|
||||||
|
b.VectorShr(LoadVR(b, 4),
|
||||||
|
b.LoadConstantVec128(vec128s(0, 1, 8, 15, 15, 8, 1, 16)),
|
||||||
INT16_TYPE));
|
INT16_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
@ -122,11 +126,13 @@ TEST_CASE("VECTOR_SHR_I32", "[instr]") {
|
||||||
|
|
||||||
TEST_CASE("VECTOR_SHR_I32_CONSTANT", "[instr]") {
|
TEST_CASE("VECTOR_SHR_I32_CONSTANT", "[instr]") {
|
||||||
TestFunction test([](HIRBuilder& b) {
|
TestFunction test([](HIRBuilder& b) {
|
||||||
StoreVR(b, 3, b.VectorShr(LoadVR(b, 4),
|
StoreVR(
|
||||||
b.LoadConstantVec128(vec128i(0, 1, 16, 31)),
|
b, 3,
|
||||||
|
b.VectorShr(LoadVR(b, 4), b.LoadConstantVec128(vec128i(0, 1, 16, 31)),
|
||||||
INT32_TYPE));
|
INT32_TYPE));
|
||||||
StoreVR(b, 4, b.VectorShr(LoadVR(b, 5),
|
StoreVR(
|
||||||
b.LoadConstantVec128(vec128i(31, 16, 1, 32)),
|
b, 4,
|
||||||
|
b.VectorShr(LoadVR(b, 5), b.LoadConstantVec128(vec128i(31, 16, 1, 32)),
|
||||||
INT32_TYPE));
|
INT32_TYPE));
|
||||||
b.Return();
|
b.Return();
|
||||||
});
|
});
|
||||||
|
|
|
@ -395,7 +395,10 @@ void DebugWindow::DrawSourcePane() {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (function->is_guest()) {
|
if (function->is_guest()) {
|
||||||
const char* kSourceDisplayModes[] = {
|
const char* kSourceDisplayModes[] = {
|
||||||
"PPC", "PPC+HIR+x64", "PPC+HIR (opt)+x64", "PPC+x64",
|
"PPC",
|
||||||
|
"PPC+HIR+x64",
|
||||||
|
"PPC+HIR (opt)+x64",
|
||||||
|
"PPC+x64",
|
||||||
};
|
};
|
||||||
ImGui::PushItemWidth(90);
|
ImGui::PushItemWidth(90);
|
||||||
ImGui::Combo("##display_mode", &state_.source_display_mode,
|
ImGui::Combo("##display_mode", &state_.source_display_mode,
|
||||||
|
@ -1338,7 +1341,8 @@ void DebugWindow::DrawBreakpointsPane() {
|
||||||
function->MapGuestAddressToMachineCode(
|
function->MapGuestAddressToMachineCode(
|
||||||
breakpoint->guest_address()));
|
breakpoint->guest_address()));
|
||||||
} else {
|
} else {
|
||||||
NavigateToFunction(function, function->MapMachineCodeToGuestAddress(
|
NavigateToFunction(function,
|
||||||
|
function->MapMachineCodeToGuestAddress(
|
||||||
breakpoint->host_address()),
|
breakpoint->host_address()),
|
||||||
breakpoint->host_address());
|
breakpoint->host_address());
|
||||||
}
|
}
|
||||||
|
|
|
@ -782,7 +782,9 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRenderTargets() {
|
||||||
GLuint color_targets[4] = {kAnyTarget, kAnyTarget, kAnyTarget, kAnyTarget};
|
GLuint color_targets[4] = {kAnyTarget, kAnyTarget, kAnyTarget, kAnyTarget};
|
||||||
if (enable_mode == ModeControl::kColorDepth) {
|
if (enable_mode == ModeControl::kColorDepth) {
|
||||||
uint32_t color_info[4] = {
|
uint32_t color_info[4] = {
|
||||||
regs.rb_color_info, regs.rb_color1_info, regs.rb_color2_info,
|
regs.rb_color_info,
|
||||||
|
regs.rb_color1_info,
|
||||||
|
regs.rb_color2_info,
|
||||||
regs.rb_color3_info,
|
regs.rb_color3_info,
|
||||||
};
|
};
|
||||||
// A2XX_RB_COLOR_MASK_WRITE_* == D3DRS_COLORWRITEENABLE
|
// A2XX_RB_COLOR_MASK_WRITE_* == D3DRS_COLORWRITEENABLE
|
||||||
|
@ -1099,7 +1101,9 @@ GL4CommandProcessor::UpdateStatus GL4CommandProcessor::UpdateRasterizerState(
|
||||||
}
|
}
|
||||||
|
|
||||||
static const GLenum kFillModes[3] = {
|
static const GLenum kFillModes[3] = {
|
||||||
GL_POINT, GL_LINE, GL_FILL,
|
GL_POINT,
|
||||||
|
GL_LINE,
|
||||||
|
GL_FILL,
|
||||||
};
|
};
|
||||||
bool poly_mode = ((regs.pa_su_sc_mode_cntl >> 3) & 0x3) != 0;
|
bool poly_mode = ((regs.pa_su_sc_mode_cntl >> 3) & 0x3) != 0;
|
||||||
if (poly_mode) {
|
if (poly_mode) {
|
||||||
|
@ -1590,7 +1594,8 @@ bool GL4CommandProcessor::IssueCopy() {
|
||||||
if (copy_src_select <= 3 || color_clear_enabled) {
|
if (copy_src_select <= 3 || color_clear_enabled) {
|
||||||
// Source from a color target.
|
// Source from a color target.
|
||||||
uint32_t color_info[4] = {
|
uint32_t color_info[4] = {
|
||||||
regs[XE_GPU_REG_RB_COLOR_INFO].u32, regs[XE_GPU_REG_RB_COLOR1_INFO].u32,
|
regs[XE_GPU_REG_RB_COLOR_INFO].u32,
|
||||||
|
regs[XE_GPU_REG_RB_COLOR1_INFO].u32,
|
||||||
regs[XE_GPU_REG_RB_COLOR2_INFO].u32,
|
regs[XE_GPU_REG_RB_COLOR2_INFO].u32,
|
||||||
regs[XE_GPU_REG_RB_COLOR3_INFO].u32,
|
regs[XE_GPU_REG_RB_COLOR3_INFO].u32,
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,8 +11,10 @@
|
||||||
#define XENIA_GPU_GL4_SHADER_CACHE_H_
|
#define XENIA_GPU_GL4_SHADER_CACHE_H_
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/gpu/xenos.h"
|
#include "xenia/gpu/xenos.h"
|
||||||
|
|
||||||
|
|
|
@ -940,7 +940,8 @@ bool TextureCache::UploadTexture2D(GLuint texture,
|
||||||
texture_info.size_2d.logical_height);
|
texture_info.size_2d.logical_height);
|
||||||
y++, output_base_offset += host_info.size_2d.output_pitch) {
|
y++, output_base_offset += host_info.size_2d.output_pitch) {
|
||||||
auto input_base_offset = TextureInfo::TiledOffset2DOuter(
|
auto input_base_offset = TextureInfo::TiledOffset2DOuter(
|
||||||
offset_y + y, (texture_info.size_2d.input_width /
|
offset_y + y,
|
||||||
|
(texture_info.size_2d.input_width /
|
||||||
texture_info.format_info()->block_width),
|
texture_info.format_info()->block_width),
|
||||||
bpp);
|
bpp);
|
||||||
for (uint32_t x = 0, output_offset = output_base_offset;
|
for (uint32_t x = 0, output_offset = output_base_offset;
|
||||||
|
@ -1048,7 +1049,8 @@ bool TextureCache::UploadTextureCube(GLuint texture,
|
||||||
y < texture_info.size_cube.block_height;
|
y < texture_info.size_cube.block_height;
|
||||||
y++, output_base_offset += host_info.size_cube.output_pitch) {
|
y++, output_base_offset += host_info.size_cube.output_pitch) {
|
||||||
auto input_base_offset = TextureInfo::TiledOffset2DOuter(
|
auto input_base_offset = TextureInfo::TiledOffset2DOuter(
|
||||||
offset_y + y, (texture_info.size_cube.input_width /
|
offset_y + y,
|
||||||
|
(texture_info.size_cube.input_width /
|
||||||
texture_info.format_info()->block_width),
|
texture_info.format_info()->block_width),
|
||||||
bpp);
|
bpp);
|
||||||
for (uint32_t x = 0, output_offset = output_base_offset;
|
for (uint32_t x = 0, output_offset = output_base_offset;
|
||||||
|
|
|
@ -23,7 +23,8 @@ const RegisterInfo* RegisterFile::GetRegisterInfo(uint32_t index) {
|
||||||
#define XE_GPU_REGISTER(index, type, name) \
|
#define XE_GPU_REGISTER(index, type, name) \
|
||||||
case index: { \
|
case index: { \
|
||||||
static const RegisterInfo reg_info = { \
|
static const RegisterInfo reg_info = { \
|
||||||
RegisterInfo::Type::type, #name, \
|
RegisterInfo::Type::type, \
|
||||||
|
#name, \
|
||||||
}; \
|
}; \
|
||||||
return ®_info; \
|
return ®_info; \
|
||||||
}
|
}
|
||||||
|
|
|
@ -356,7 +356,10 @@ void ParsedVertexFetchInstruction::Disassemble(StringBuffer* out) const {
|
||||||
|
|
||||||
void ParsedTextureFetchInstruction::Disassemble(StringBuffer* out) const {
|
void ParsedTextureFetchInstruction::Disassemble(StringBuffer* out) const {
|
||||||
static const char* kTextureFilterNames[] = {
|
static const char* kTextureFilterNames[] = {
|
||||||
"point", "linear", "BASEMAP", "keep",
|
"point",
|
||||||
|
"linear",
|
||||||
|
"BASEMAP",
|
||||||
|
"keep",
|
||||||
};
|
};
|
||||||
static const char* kAnisoFilterNames[] = {
|
static const char* kAnisoFilterNames[] = {
|
||||||
"disabled", "max1to1", "max2to1", "max4to1",
|
"disabled", "max1to1", "max2to1", "max4to1",
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/gpu/spirv/passes/control_flow_analysis_pass.h"
|
#include "xenia/gpu/spirv/passes/control_flow_analysis_pass.h"
|
||||||
|
@ -337,8 +338,7 @@ void SpirvShaderTranslator::StartTranslation() {
|
||||||
registers_ptr_,
|
registers_ptr_,
|
||||||
std::vector<Id>({b.makeUintConstant(0)}));
|
std::vector<Id>({b.makeUintConstant(0)}));
|
||||||
auto r0 = b.createLoad(r0_ptr);
|
auto r0 = b.createLoad(r0_ptr);
|
||||||
r0 = b.createCompositeInsert(vertex_idx, r0, vec4_float_type_,
|
r0 = b.createCompositeInsert(vertex_idx, r0, vec4_float_type_, 0);
|
||||||
std::vector<uint32_t>({0}));
|
|
||||||
b.createStore(r0, r0_ptr);
|
b.createStore(r0, r0_ptr);
|
||||||
} else {
|
} else {
|
||||||
// Pixel inputs from vertex shader.
|
// Pixel inputs from vertex shader.
|
||||||
|
|
|
@ -950,8 +950,7 @@ void TraceViewer::DrawVertexFetcher(Shader* shader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::Columns(1);
|
ImGui::Columns(1);
|
||||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (vertex_count - display_end) *
|
||||||
(vertex_count - display_end) *
|
|
||||||
ImGui::GetTextLineHeight());
|
ImGui::GetTextLineHeight());
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
@ -971,10 +970,14 @@ static const char* kStencilFuncNames[] = {
|
||||||
"Decrement and Clamp",
|
"Decrement and Clamp",
|
||||||
};
|
};
|
||||||
static const char* kIndexFormatNames[] = {
|
static const char* kIndexFormatNames[] = {
|
||||||
"uint16", "uint32",
|
"uint16",
|
||||||
|
"uint32",
|
||||||
};
|
};
|
||||||
static const char* kEndiannessNames[] = {
|
static const char* kEndiannessNames[] = {
|
||||||
"unspecified endianness", "8-in-16", "8-in-32", "16-in-32",
|
"unspecified endianness",
|
||||||
|
"8-in-16",
|
||||||
|
"8-in-32",
|
||||||
|
"16-in-32",
|
||||||
};
|
};
|
||||||
static const char* kColorFormatNames[] = {
|
static const char* kColorFormatNames[] = {
|
||||||
/* 0 */ "k_8_8_8_8",
|
/* 0 */ "k_8_8_8_8",
|
||||||
|
@ -995,7 +998,8 @@ static const char* kColorFormatNames[] = {
|
||||||
/* 15 */ "k_32_32_FLOAT",
|
/* 15 */ "k_32_32_FLOAT",
|
||||||
};
|
};
|
||||||
static const char* kDepthFormatNames[] = {
|
static const char* kDepthFormatNames[] = {
|
||||||
"kD24S8", "kD24FS8",
|
"kD24S8",
|
||||||
|
"kD24FS8",
|
||||||
};
|
};
|
||||||
|
|
||||||
void ProgressBar(float frac, float width, float height = 0,
|
void ProgressBar(float frac, float width, float height = 0,
|
||||||
|
@ -1185,7 +1189,9 @@ void TraceViewer::DrawStateUI() {
|
||||||
uint32_t surface_pitch = surface_info & 0x3FFF;
|
uint32_t surface_pitch = surface_info & 0x3FFF;
|
||||||
auto surface_msaa = (surface_info >> 16) & 0x3;
|
auto surface_msaa = (surface_info >> 16) & 0x3;
|
||||||
static const char* kMsaaNames[] = {
|
static const char* kMsaaNames[] = {
|
||||||
"1X", "2X", "4X",
|
"1X",
|
||||||
|
"2X",
|
||||||
|
"4X",
|
||||||
};
|
};
|
||||||
ImGui::BulletText("Surface Pitch: %d", surface_pitch);
|
ImGui::BulletText("Surface Pitch: %d", surface_pitch);
|
||||||
ImGui::BulletText("Surface HI-Z Pitch: %d", surface_hiz);
|
ImGui::BulletText("Surface HI-Z Pitch: %d", surface_hiz);
|
||||||
|
@ -1270,7 +1276,9 @@ void TraceViewer::DrawStateUI() {
|
||||||
ImGui::BulletText("Front-face: counter-clockwise");
|
ImGui::BulletText("Front-face: counter-clockwise");
|
||||||
}
|
}
|
||||||
static const char* kFillModeNames[3] = {
|
static const char* kFillModeNames[3] = {
|
||||||
"point", "line", "fill",
|
"point",
|
||||||
|
"line",
|
||||||
|
"fill",
|
||||||
};
|
};
|
||||||
bool poly_mode = ((pa_su_sc_mode_cntl >> 3) & 0x3) != 0;
|
bool poly_mode = ((pa_su_sc_mode_cntl >> 3) & 0x3) != 0;
|
||||||
if (poly_mode) {
|
if (poly_mode) {
|
||||||
|
|
|
@ -72,7 +72,9 @@ void TraceWriter::WritePrimaryBufferStart(uint32_t base_ptr, uint32_t count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PrimaryBufferStartCommand cmd = {
|
PrimaryBufferStartCommand cmd = {
|
||||||
TraceCommandType::kPrimaryBufferStart, base_ptr, 0,
|
TraceCommandType::kPrimaryBufferStart,
|
||||||
|
base_ptr,
|
||||||
|
0,
|
||||||
};
|
};
|
||||||
fwrite(&cmd, 1, sizeof(cmd), file_);
|
fwrite(&cmd, 1, sizeof(cmd), file_);
|
||||||
}
|
}
|
||||||
|
@ -92,7 +94,9 @@ void TraceWriter::WriteIndirectBufferStart(uint32_t base_ptr, uint32_t count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IndirectBufferStartCommand cmd = {
|
IndirectBufferStartCommand cmd = {
|
||||||
TraceCommandType::kIndirectBufferStart, base_ptr, 0,
|
TraceCommandType::kIndirectBufferStart,
|
||||||
|
base_ptr,
|
||||||
|
0,
|
||||||
};
|
};
|
||||||
fwrite(&cmd, 1, sizeof(cmd), file_);
|
fwrite(&cmd, 1, sizeof(cmd), file_);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +116,9 @@ void TraceWriter::WritePacketStart(uint32_t base_ptr, uint32_t count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PacketStartCommand cmd = {
|
PacketStartCommand cmd = {
|
||||||
TraceCommandType::kPacketStart, base_ptr, count,
|
TraceCommandType::kPacketStart,
|
||||||
|
base_ptr,
|
||||||
|
count,
|
||||||
};
|
};
|
||||||
fwrite(&cmd, 1, sizeof(cmd), file_);
|
fwrite(&cmd, 1, sizeof(cmd), file_);
|
||||||
fwrite(membase_ + base_ptr, 4, count, file_);
|
fwrite(membase_ + base_ptr, 4, count, file_);
|
||||||
|
@ -220,7 +226,8 @@ void TraceWriter::WriteEvent(EventCommand::Type event_type) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
EventCommand cmd = {
|
EventCommand cmd = {
|
||||||
TraceCommandType::kEvent, event_type,
|
TraceCommandType::kEvent,
|
||||||
|
event_type,
|
||||||
};
|
};
|
||||||
fwrite(&cmd, 1, sizeof(cmd), file_);
|
fwrite(&cmd, 1, sizeof(cmd), file_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1206,7 +1206,9 @@ PipelineCache::UpdateStatus PipelineCache::UpdateRasterizationState(
|
||||||
// Vulkan only supports both matching.
|
// Vulkan only supports both matching.
|
||||||
assert_true(front_poly_mode == back_poly_mode);
|
assert_true(front_poly_mode == back_poly_mode);
|
||||||
static const VkPolygonMode kFillModes[3] = {
|
static const VkPolygonMode kFillModes[3] = {
|
||||||
VK_POLYGON_MODE_POINT, VK_POLYGON_MODE_LINE, VK_POLYGON_MODE_FILL,
|
VK_POLYGON_MODE_POINT,
|
||||||
|
VK_POLYGON_MODE_LINE,
|
||||||
|
VK_POLYGON_MODE_FILL,
|
||||||
};
|
};
|
||||||
state_info.polygonMode = kFillModes[front_poly_mode];
|
state_info.polygonMode = kFillModes[front_poly_mode];
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -223,7 +223,9 @@ CachedTileView::CachedTileView(ui::vulkan::VulkanDevice* device,
|
||||||
image_view_info.format = image_info.format;
|
image_view_info.format = image_info.format;
|
||||||
// TODO(benvanik): manipulate? may not be able to when attached.
|
// TODO(benvanik): manipulate? may not be able to when attached.
|
||||||
image_view_info.components = {
|
image_view_info.components = {
|
||||||
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
|
VK_COMPONENT_SWIZZLE_R,
|
||||||
|
VK_COMPONENT_SWIZZLE_G,
|
||||||
|
VK_COMPONENT_SWIZZLE_B,
|
||||||
VK_COMPONENT_SWIZZLE_A,
|
VK_COMPONENT_SWIZZLE_A,
|
||||||
};
|
};
|
||||||
image_view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
image_view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||||
|
@ -766,7 +768,9 @@ bool RenderCache::ParseConfiguration(RenderConfiguration* config) {
|
||||||
// Color attachment configuration.
|
// Color attachment configuration.
|
||||||
if (config->mode_control == ModeControl::kColorDepth) {
|
if (config->mode_control == ModeControl::kColorDepth) {
|
||||||
reg::RB_COLOR_INFO color_info[4] = {
|
reg::RB_COLOR_INFO color_info[4] = {
|
||||||
regs.rb_color_info, regs.rb_color1_info, regs.rb_color2_info,
|
regs.rb_color_info,
|
||||||
|
regs.rb_color1_info,
|
||||||
|
regs.rb_color2_info,
|
||||||
regs.rb_color3_info,
|
regs.rb_color3_info,
|
||||||
};
|
};
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
@ -783,6 +787,9 @@ bool RenderCache::ParseConfiguration(RenderConfiguration* config) {
|
||||||
case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown:
|
case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown:
|
||||||
config->color[i].format = ColorRenderTargetFormat::k_2_10_10_10_FLOAT;
|
config->color[i].format = ColorRenderTargetFormat::k_2_10_10_10_FLOAT;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
// The rest are good
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -863,7 +870,7 @@ bool RenderCache::ConfigureRenderPass(VkCommandBuffer command_buffer,
|
||||||
color_key.edram_format = static_cast<uint16_t>(config->color[i].format);
|
color_key.edram_format = static_cast<uint16_t>(config->color[i].format);
|
||||||
target_color_attachments[i] =
|
target_color_attachments[i] =
|
||||||
FindOrCreateTileView(command_buffer, color_key);
|
FindOrCreateTileView(command_buffer, color_key);
|
||||||
if (!target_color_attachments) {
|
if (!target_color_attachments[i]) {
|
||||||
XELOGE("Failed to get tile view for color attachment");
|
XELOGE("Failed to get tile view for color attachment");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -926,6 +933,9 @@ CachedTileView* RenderCache::FindTileView(uint32_t base, uint32_t pitch,
|
||||||
case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown:
|
case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown:
|
||||||
format = uint32_t(ColorRenderTargetFormat::k_2_10_10_10_FLOAT);
|
format = uint32_t(ColorRenderTargetFormat::k_2_10_10_10_FLOAT);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
// Other types as-is.
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1165,6 +1175,9 @@ void RenderCache::BlitToImage(VkCommandBuffer command_buffer,
|
||||||
case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown:
|
case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown:
|
||||||
format = uint32_t(ColorRenderTargetFormat::k_2_10_10_10_FLOAT);
|
format = uint32_t(ColorRenderTargetFormat::k_2_10_10_10_FLOAT);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
// Rest are OK
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1278,6 +1291,9 @@ void RenderCache::ClearEDRAMColor(VkCommandBuffer command_buffer,
|
||||||
case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown:
|
case ColorRenderTargetFormat::k_2_10_10_10_FLOAT_unknown:
|
||||||
format = ColorRenderTargetFormat::k_2_10_10_10_FLOAT;
|
format = ColorRenderTargetFormat::k_2_10_10_10_FLOAT;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
// Rest are OK
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t tile_width = num_samples == MsaaSamples::k4X ? 40 : 80;
|
uint32_t tile_width = num_samples == MsaaSamples::k4X ? 40 : 80;
|
||||||
|
|
|
@ -84,7 +84,7 @@ class CachedTileView {
|
||||||
}
|
}
|
||||||
|
|
||||||
VkExtent2D GetSize() const {
|
VkExtent2D GetSize() const {
|
||||||
return {key.tile_width * 80ul, key.tile_height * 16ul};
|
return {key.tile_width * 80u, key.tile_height * 16u};
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -1116,8 +1116,10 @@ void TextureCache::WritebackTexture(Texture* texture) {
|
||||||
auto command_buffer = wb_command_pool_->AcquireEntry();
|
auto command_buffer = wb_command_pool_->AcquireEntry();
|
||||||
|
|
||||||
VkCommandBufferBeginInfo begin_info = {
|
VkCommandBufferBeginInfo begin_info = {
|
||||||
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr,
|
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||||
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, nullptr,
|
nullptr,
|
||||||
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||||
|
nullptr,
|
||||||
};
|
};
|
||||||
vkBeginCommandBuffer(command_buffer, &begin_info);
|
vkBeginCommandBuffer(command_buffer, &begin_info);
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,9 @@ bool VulkanCommandProcessor::SetupContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
VkEventCreateInfo info = {
|
VkEventCreateInfo info = {
|
||||||
VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, nullptr, 0,
|
VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
};
|
};
|
||||||
|
|
||||||
status = vkCreateEvent(*device_, &info, nullptr,
|
status = vkCreateEvent(*device_, &info, nullptr,
|
||||||
|
@ -469,7 +471,8 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr,
|
||||||
nullptr, 1, &barrier);
|
nullptr, 1, &barrier);
|
||||||
|
|
||||||
VkRect2D src_rect = {
|
VkRect2D src_rect = {
|
||||||
{0, 0}, {frontbuffer_width, frontbuffer_height},
|
{0, 0},
|
||||||
|
{frontbuffer_width, frontbuffer_height},
|
||||||
};
|
};
|
||||||
blitter_->BlitTexture2D(
|
blitter_->BlitTexture2D(
|
||||||
copy_commands, current_batch_fence_,
|
copy_commands, current_batch_fence_,
|
||||||
|
@ -1032,7 +1035,8 @@ bool VulkanCommandProcessor::IssueCopy() {
|
||||||
if (is_color_source) {
|
if (is_color_source) {
|
||||||
// Source from a color target.
|
// Source from a color target.
|
||||||
uint32_t color_info[4] = {
|
uint32_t color_info[4] = {
|
||||||
regs[XE_GPU_REG_RB_COLOR_INFO].u32, regs[XE_GPU_REG_RB_COLOR1_INFO].u32,
|
regs[XE_GPU_REG_RB_COLOR_INFO].u32,
|
||||||
|
regs[XE_GPU_REG_RB_COLOR1_INFO].u32,
|
||||||
regs[XE_GPU_REG_RB_COLOR2_INFO].u32,
|
regs[XE_GPU_REG_RB_COLOR2_INFO].u32,
|
||||||
regs[XE_GPU_REG_RB_COLOR3_INFO].u32,
|
regs[XE_GPU_REG_RB_COLOR3_INFO].u32,
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,8 +27,8 @@ namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
namespace vulkan {
|
namespace vulkan {
|
||||||
|
|
||||||
using xe::ui::vulkan::CheckResult;
|
|
||||||
using xe::ui::RawImage;
|
using xe::ui::RawImage;
|
||||||
|
using xe::ui::vulkan::CheckResult;
|
||||||
|
|
||||||
VulkanGraphicsSystem::VulkanGraphicsSystem() {}
|
VulkanGraphicsSystem::VulkanGraphicsSystem() {}
|
||||||
VulkanGraphicsSystem::~VulkanGraphicsSystem() = default;
|
VulkanGraphicsSystem::~VulkanGraphicsSystem() = default;
|
||||||
|
@ -50,7 +50,8 @@ X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
|
|
||||||
// Create our own command pool we can use for captures.
|
// Create our own command pool we can use for captures.
|
||||||
VkCommandPoolCreateInfo create_info = {
|
VkCommandPoolCreateInfo create_info = {
|
||||||
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, nullptr,
|
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
|
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
|
||||||
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||||
device_->queue_family_index(),
|
device_->queue_family_index(),
|
||||||
|
@ -93,8 +94,10 @@ std::unique_ptr<RawImage> VulkanGraphicsSystem::Capture() {
|
||||||
}
|
}
|
||||||
|
|
||||||
VkCommandBufferBeginInfo begin_info = {
|
VkCommandBufferBeginInfo begin_info = {
|
||||||
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr,
|
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||||
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, nullptr,
|
nullptr,
|
||||||
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||||
|
nullptr,
|
||||||
};
|
};
|
||||||
vkBeginCommandBuffer(cmd, &begin_info);
|
vkBeginCommandBuffer(cmd, &begin_info);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* 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/clock.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/main.h"
|
#include "xenia/base/main.h"
|
||||||
#include "xenia/base/platform_win.h"
|
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
#include "xenia/hid/input_system.h"
|
#include "xenia/hid/input_system.h"
|
||||||
#include "xenia/ui/gl/gl_provider.h"
|
#include "xenia/ui/gl/gl_provider.h"
|
||||||
|
@ -117,8 +116,8 @@ int hid_demo_main(const std::vector<std::wstring>& args) {
|
||||||
auto& io = window->imgui_drawer()->GetIO();
|
auto& io = window->imgui_drawer()->GetIO();
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(-1, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(-1, 0));
|
||||||
ImGui::Begin("main_window", nullptr, ImGuiWindowFlags_NoMove |
|
ImGui::Begin("main_window", nullptr,
|
||||||
ImGuiWindowFlags_NoResize |
|
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize |
|
||||||
ImGuiWindowFlags_NoTitleBar |
|
ImGuiWindowFlags_NoTitleBar |
|
||||||
ImGuiWindowFlags_NoScrollbar |
|
ImGuiWindowFlags_NoScrollbar |
|
||||||
ImGuiWindowFlags_NoSavedSettings);
|
ImGuiWindowFlags_NoSavedSettings);
|
||||||
|
|
|
@ -213,9 +213,8 @@ std::vector<object_ref<XObject>> ObjectTable::GetAllObjects() {
|
||||||
|
|
||||||
for (uint32_t slot = 0; slot < table_capacity_; slot++) {
|
for (uint32_t slot = 0; slot < table_capacity_; slot++) {
|
||||||
auto& entry = table_[slot];
|
auto& entry = table_[slot];
|
||||||
if (entry.object &&
|
if (entry.object && std::find(results.begin(), results.end(),
|
||||||
std::find(results.begin(), results.end(), entry.object) ==
|
entry.object) == results.end()) {
|
||||||
results.end()) {
|
|
||||||
entry.object->Retain();
|
entry.object->Retain();
|
||||||
results.push_back(object_ref<XObject>(entry.object));
|
results.push_back(object_ref<XObject>(entry.object));
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,7 +474,9 @@ xe::cpu::Export* RegisterExport(R (*fn)(Ps&...), const char* name,
|
||||||
static void Trampoline(PPCContext* ppc_context) {
|
static void Trampoline(PPCContext* ppc_context) {
|
||||||
++export_entry->function_data.call_count;
|
++export_entry->function_data.call_count;
|
||||||
Param::Init init = {
|
Param::Init init = {
|
||||||
ppc_context, sizeof...(Ps), 0,
|
ppc_context,
|
||||||
|
sizeof...(Ps),
|
||||||
|
0,
|
||||||
};
|
};
|
||||||
auto params = std::make_tuple<Ps...>(Ps(init)...);
|
auto params = std::make_tuple<Ps...>(Ps(init)...);
|
||||||
if (export_entry->tags & xe::cpu::ExportTag::kLog &&
|
if (export_entry->tags & xe::cpu::ExportTag::kLog &&
|
||||||
|
@ -507,7 +509,8 @@ xe::cpu::Export* RegisterExport(void (*fn)(Ps&...), const char* name,
|
||||||
static void Trampoline(PPCContext* ppc_context) {
|
static void Trampoline(PPCContext* ppc_context) {
|
||||||
++export_entry->function_data.call_count;
|
++export_entry->function_data.call_count;
|
||||||
Param::Init init = {
|
Param::Init init = {
|
||||||
ppc_context, sizeof...(Ps),
|
ppc_context,
|
||||||
|
sizeof...(Ps),
|
||||||
};
|
};
|
||||||
auto params = std::make_tuple<Ps...>(Ps(init)...);
|
auto params = std::make_tuple<Ps...>(Ps(init)...);
|
||||||
if (export_entry->tags & xe::cpu::ExportTag::kLog &&
|
if (export_entry->tags & xe::cpu::ExportTag::kLog &&
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
|
|
||||||
namespace xe {} // namespace xe
|
namespace xe {} // namespace xe
|
||||||
|
|
||||||
typedef struct { int reserved; } xe_xex2_options_t;
|
typedef struct {
|
||||||
|
int reserved;
|
||||||
|
} xe_xex2_options_t;
|
||||||
|
|
||||||
struct xe_xex2;
|
struct xe_xex2;
|
||||||
typedef struct xe_xex2* xe_xex2_ref;
|
typedef struct xe_xex2* xe_xex2_ref;
|
||||||
|
|
|
@ -24,7 +24,7 @@ dword_result_t XamAvatarInitialize(
|
||||||
lpdword_t function_ptrs, // 20b, 5 pointers
|
lpdword_t function_ptrs, // 20b, 5 pointers
|
||||||
lpunknown_t unk5, // ptr in data segment
|
lpunknown_t unk5, // ptr in data segment
|
||||||
dword_t unk6 // flags - 0x00300000, 0x30, etc
|
dword_t unk6 // flags - 0x00300000, 0x30, etc
|
||||||
) {
|
) {
|
||||||
// Negative to fail. Game should immediately call XamAvatarShutdown.
|
// Negative to fail. Game should immediately call XamAvatarShutdown.
|
||||||
return ~0u;
|
return ~0u;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,6 @@ DECLARE_XBDM_EXPORT(DmFindPdbSignature, ExportTag::kStub | ExportTag::kDebug);
|
||||||
void RegisterMiscExports(xe::cpu::ExportResolver* export_resolver,
|
void RegisterMiscExports(xe::cpu::ExportResolver* export_resolver,
|
||||||
KernelState* kernel_state) {}
|
KernelState* kernel_state) {}
|
||||||
|
|
||||||
} // namespace xboxkrnl
|
} // namespace xbdm
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/apu/audio_system.h"
|
#include "xenia/apu/audio_system.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/emulator.h"
|
#include "xenia/emulator.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
#include "third_party/crypto/TinySHA1.hpp"
|
#include "third_party/crypto/TinySHA1.hpp"
|
||||||
#include "third_party/crypto/des/des.cpp"
|
#include "third_party/crypto/des/des.cpp"
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/base/debugging.h"
|
#include "xenia/base/debugging.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
#include "xenia/kernel/xthread.h"
|
#include "xenia/kernel/xthread.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/memory.h"
|
#include "xenia/base/memory.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
|
@ -19,6 +18,7 @@
|
||||||
#include "xenia/kernel/xiocompletion.h"
|
#include "xenia/kernel/xiocompletion.h"
|
||||||
#include "xenia/kernel/xthread.h"
|
#include "xenia/kernel/xthread.h"
|
||||||
#include "xenia/vfs/device.h"
|
#include "xenia/vfs/device.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
#include "xenia/kernel/xthread.h"
|
#include "xenia/kernel/xthread.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/cpu/processor.h"
|
#include "xenia/cpu/processor.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
|
@ -15,6 +14,7 @@
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/util/xex2.h"
|
#include "xenia/kernel/util/xex2.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
|
@ -15,6 +14,7 @@
|
||||||
#include "xenia/kernel/xobject.h"
|
#include "xenia/kernel/xobject.h"
|
||||||
#include "xenia/kernel/xsemaphore.h"
|
#include "xenia/kernel/xsemaphore.h"
|
||||||
#include "xenia/kernel/xthread.h"
|
#include "xenia/kernel/xthread.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
|
@ -559,13 +559,12 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
|
||||||
|
|
||||||
if (!is_wide) {
|
if (!is_wide) {
|
||||||
length = 0;
|
length = 0;
|
||||||
for (auto s = (const uint8_t *)str; cap > 0 && *s; ++s, cap--) {
|
for (auto s = (const uint8_t*)str; cap > 0 && *s; ++s, cap--) {
|
||||||
length++;
|
length++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
length = 0;
|
length = 0;
|
||||||
for (auto s = (const uint16_t *)str; cap > 0 && *s;
|
for (auto s = (const uint16_t*)str; cap > 0 && *s; ++s, cap--) {
|
||||||
++s, cap--) {
|
|
||||||
length++;
|
length++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xenia/xbox.h"
|
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/kernel/kernel_state.h"
|
#include "xenia/kernel/kernel_state.h"
|
||||||
#include "xenia/kernel/util/shim_utils.h"
|
#include "xenia/kernel/util/shim_utils.h"
|
||||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||||
|
#include "xenia/xbox.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
|
@ -257,7 +257,7 @@ dword_result_t VdInitializeScalerCommandBuffer(
|
||||||
lpvoid_t dest_ptr, // Points to the first 80000000h where the memcpy
|
lpvoid_t dest_ptr, // Points to the first 80000000h where the memcpy
|
||||||
// sources from.
|
// sources from.
|
||||||
dword_t dest_count // Count in words.
|
dword_t dest_count // Count in words.
|
||||||
) {
|
) {
|
||||||
// We could fake the commands here, but I'm not sure the game checks for
|
// We could fake the commands here, but I'm not sure the game checks for
|
||||||
// anything but success (non-zero ret).
|
// anything but success (non-zero ret).
|
||||||
// For now, we just fill it with NOPs.
|
// For now, we just fill it with NOPs.
|
||||||
|
|
|
@ -209,39 +209,57 @@ static const struct {
|
||||||
} map_info[] = {
|
} map_info[] = {
|
||||||
// (1024mb) - virtual 4k pages
|
// (1024mb) - virtual 4k pages
|
||||||
{
|
{
|
||||||
0x00000000, 0x3FFFFFFF, 0x0000000000000000ull,
|
0x00000000,
|
||||||
|
0x3FFFFFFF,
|
||||||
|
0x0000000000000000ull,
|
||||||
},
|
},
|
||||||
// (1024mb) - virtual 64k pages (cont)
|
// (1024mb) - virtual 64k pages (cont)
|
||||||
{
|
{
|
||||||
0x40000000, 0x7EFFFFFF, 0x0000000040000000ull,
|
0x40000000,
|
||||||
|
0x7EFFFFFF,
|
||||||
|
0x0000000040000000ull,
|
||||||
},
|
},
|
||||||
// (16mb) - GPU writeback + 15mb of XPS?
|
// (16mb) - GPU writeback + 15mb of XPS?
|
||||||
{
|
{
|
||||||
0x7F000000, 0x7FFFFFFF, 0x0000000100000000ull,
|
0x7F000000,
|
||||||
|
0x7FFFFFFF,
|
||||||
|
0x0000000100000000ull,
|
||||||
},
|
},
|
||||||
// (256mb) - xex 64k pages
|
// (256mb) - xex 64k pages
|
||||||
{
|
{
|
||||||
0x80000000, 0x8FFFFFFF, 0x0000000080000000ull,
|
0x80000000,
|
||||||
|
0x8FFFFFFF,
|
||||||
|
0x0000000080000000ull,
|
||||||
},
|
},
|
||||||
// (256mb) - xex 4k pages
|
// (256mb) - xex 4k pages
|
||||||
{
|
{
|
||||||
0x90000000, 0x9FFFFFFF, 0x0000000080000000ull,
|
0x90000000,
|
||||||
|
0x9FFFFFFF,
|
||||||
|
0x0000000080000000ull,
|
||||||
},
|
},
|
||||||
// (512mb) - physical 64k pages
|
// (512mb) - physical 64k pages
|
||||||
{
|
{
|
||||||
0xA0000000, 0xBFFFFFFF, 0x0000000100000000ull,
|
0xA0000000,
|
||||||
|
0xBFFFFFFF,
|
||||||
|
0x0000000100000000ull,
|
||||||
},
|
},
|
||||||
// - physical 16mb pages
|
// - physical 16mb pages
|
||||||
{
|
{
|
||||||
0xC0000000, 0xDFFFFFFF, 0x0000000100000000ull,
|
0xC0000000,
|
||||||
|
0xDFFFFFFF,
|
||||||
|
0x0000000100000000ull,
|
||||||
},
|
},
|
||||||
// - physical 4k pages
|
// - physical 4k pages
|
||||||
{
|
{
|
||||||
0xE0000000, 0xFFFFFFFF, 0x0000000100000000ull,
|
0xE0000000,
|
||||||
|
0xFFFFFFFF,
|
||||||
|
0x0000000100000000ull,
|
||||||
},
|
},
|
||||||
// - physical raw
|
// - physical raw
|
||||||
{
|
{
|
||||||
0x100000000, 0x11FFFFFFF, 0x0000000100000000ull,
|
0x100000000,
|
||||||
|
0x11FFFFFFF,
|
||||||
|
0x0000000100000000ull,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
int Memory::MapViews(uint8_t* mapping_base) {
|
int Memory::MapViews(uint8_t* mapping_base) {
|
||||||
|
|
|
@ -179,5 +179,5 @@ bool apiscanner_loader::ExtractImports(const void* addr, const size_t length,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // tools
|
} // namespace tools
|
||||||
} // xe
|
} // namespace xe
|
||||||
|
|
|
@ -56,5 +56,5 @@ class apiscanner_loader {
|
||||||
bool ExtractImports(const void* addr, const size_t length, title& info);
|
bool ExtractImports(const void* addr, const size_t length, title& info);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // tools
|
} // namespace tools
|
||||||
} // xe
|
} // namespace xe
|
||||||
|
|
|
@ -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.
|
// required.
|
||||||
typedef struct WGLEWContextStruct WGLEWContext;
|
typedef struct WGLEWContextStruct WGLEWContext;
|
||||||
extern "C" WGLEWContext* wglewGetContext();
|
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_
|
#endif // XENIA_UI_GL_GL_H_
|
||||||
|
|
|
@ -17,14 +17,10 @@
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/math.h"
|
#include "xenia/base/math.h"
|
||||||
#include "xenia/base/platform_win.h"
|
|
||||||
#include "xenia/base/profiling.h"
|
#include "xenia/base/profiling.h"
|
||||||
#include "xenia/ui/gl/gl_immediate_drawer.h"
|
#include "xenia/ui/gl/gl_immediate_drawer.h"
|
||||||
#include "xenia/ui/window.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,
|
DEFINE_bool(thread_safe_gl, false,
|
||||||
"Only allow one GL context to be active at a time.");
|
"Only allow one GL context to be active at a time.");
|
||||||
|
|
||||||
|
@ -43,14 +39,9 @@ namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace gl {
|
namespace gl {
|
||||||
|
|
||||||
static std::recursive_mutex global_gl_mutex_;
|
std::recursive_mutex GLContext::global_gl_mutex_;
|
||||||
|
|
||||||
thread_local GLEWContext* tls_glew_context_ = nullptr;
|
void GLContext::FatalGLError(std::string error) {
|
||||||
thread_local WGLEWContext* tls_wglew_context_ = nullptr;
|
|
||||||
extern "C" GLEWContext* glewGetContext() { return tls_glew_context_; }
|
|
||||||
extern "C" WGLEWContext* wglewGetContext() { return tls_wglew_context_; }
|
|
||||||
|
|
||||||
void FatalGLError(std::string error) {
|
|
||||||
xe::FatalError(
|
xe::FatalError(
|
||||||
error +
|
error +
|
||||||
"\nEnsure you have the latest drivers for your GPU and that it supports "
|
"\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.");
|
"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)
|
GLContext::GLContext(GraphicsProvider* provider, Window* target_window)
|
||||||
: GraphicsContext(provider, target_window) {
|
: GraphicsContext(provider, target_window) {}
|
||||||
glew_context_.reset(new GLEWContext());
|
|
||||||
wglew_context_.reset(new WGLEWContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
GLContext::~GLContext() {
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLContext::AssertExtensionsPresent() {
|
void GLContext::AssertExtensionsPresent() {
|
||||||
if (!MakeCurrent()) {
|
if (!MakeCurrent()) {
|
||||||
|
@ -436,40 +214,6 @@ ImmediateDrawer* GLContext::immediate_drawer() {
|
||||||
return immediate_drawer_.get();
|
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() {
|
bool GLContext::WasLost() {
|
||||||
if (!robust_access_supported_) {
|
if (!robust_access_supported_) {
|
||||||
// Can't determine if we lost the context.
|
// Can't determine if we lost the context.
|
||||||
|
@ -484,7 +228,7 @@ bool GLContext::WasLost() {
|
||||||
if (status != GL_NO_ERROR) {
|
if (status != GL_NO_ERROR) {
|
||||||
// Graphics card reset.
|
// Graphics card reset.
|
||||||
XELOGE("============= TDR detected on context %p! Context %s =============",
|
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;
|
context_lost_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -492,24 +236,6 @@ bool GLContext::WasLost() {
|
||||||
return false;
|
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() {
|
std::unique_ptr<RawImage> GLContext::Capture() {
|
||||||
GraphicsContextLock lock(this);
|
GraphicsContextLock lock(this);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include "xenia/ui/gl/blitter.h"
|
#include "xenia/ui/gl/blitter.h"
|
||||||
#include "xenia/ui/gl/gl.h"
|
#include "xenia/ui/gl/gl.h"
|
||||||
|
@ -20,9 +21,13 @@
|
||||||
|
|
||||||
DECLARE_bool(thread_safe_gl);
|
DECLARE_bool(thread_safe_gl);
|
||||||
|
|
||||||
// TODO(benvanik): hide Win32 stuff.
|
DECLARE_bool(disable_gl_context_reset);
|
||||||
typedef struct HDC__* HDC;
|
|
||||||
typedef struct HGLRC__* HGLRC;
|
DECLARE_bool(random_clear_color);
|
||||||
|
|
||||||
|
DECLARE_bool(gl_debug);
|
||||||
|
DECLARE_bool(gl_debug_output);
|
||||||
|
DECLARE_bool(gl_debug_output_synchronous);
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
@ -37,18 +42,33 @@ class GLContext : public GraphicsContext {
|
||||||
|
|
||||||
ImmediateDrawer* immediate_drawer() override;
|
ImmediateDrawer* immediate_drawer() override;
|
||||||
|
|
||||||
bool is_current() override;
|
virtual bool is_current() override = 0;
|
||||||
bool MakeCurrent() override;
|
virtual bool MakeCurrent() override = 0;
|
||||||
void ClearCurrent() override;
|
virtual void ClearCurrent() override = 0;
|
||||||
bool WasLost() override;
|
bool WasLost() override;
|
||||||
|
|
||||||
void BeginSwap() override;
|
virtual void BeginSwap() override = 0;
|
||||||
void EndSwap() override;
|
virtual void EndSwap() override = 0;
|
||||||
|
|
||||||
std::unique_ptr<RawImage> Capture() override;
|
std::unique_ptr<RawImage> Capture() override;
|
||||||
|
|
||||||
Blitter* blitter() { return &blitter_; }
|
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:
|
private:
|
||||||
friend class GLProvider;
|
friend class GLProvider;
|
||||||
|
|
||||||
|
@ -59,31 +79,11 @@ class GLContext : public GraphicsContext {
|
||||||
GLContext* parent_context);
|
GLContext* parent_context);
|
||||||
|
|
||||||
private:
|
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,
|
static void GLAPIENTRY DebugMessageThunk(GLenum source, GLenum type,
|
||||||
GLuint id, GLenum severity,
|
GLuint id, GLenum severity,
|
||||||
GLsizei length,
|
GLsizei length,
|
||||||
const GLchar* message,
|
const GLchar* message,
|
||||||
GLvoid* user_param);
|
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
|
} // 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_
|
|
@ -143,7 +143,8 @@ void ImGuiDrawer::SetupFont() {
|
||||||
font_config.OversampleH = font_config.OversampleV = 1;
|
font_config.OversampleH = font_config.OversampleV = 1;
|
||||||
font_config.PixelSnapH = true;
|
font_config.PixelSnapH = true;
|
||||||
static const ImWchar font_glyph_ranges[] = {
|
static const ImWchar font_glyph_ranges[] = {
|
||||||
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
0x0020,
|
||||||
|
0x00FF, // Basic Latin + Latin Supplement
|
||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
io.Fonts->AddFontFromMemoryCompressedBase85TTF(
|
io.Fonts->AddFontFromMemoryCompressedBase85TTF(
|
||||||
|
|
|
@ -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 <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/ui/ui_event.h"
|
#include "xenia/ui/ui_event.h"
|
||||||
|
|
|
@ -256,7 +256,10 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
|
||||||
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
|
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
|
||||||
|
|
||||||
VkRect2D scissor = {
|
VkRect2D scissor = {
|
||||||
dst_offset.x, dst_offset.y, dst_extents.width, dst_extents.height,
|
dst_offset.x,
|
||||||
|
dst_offset.y,
|
||||||
|
dst_extents.width,
|
||||||
|
dst_extents.height,
|
||||||
};
|
};
|
||||||
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
||||||
|
|
||||||
|
@ -305,7 +308,10 @@ void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
|
||||||
&vtx_constants);
|
&vtx_constants);
|
||||||
|
|
||||||
PixPushConstants pix_constants = {
|
PixPushConstants pix_constants = {
|
||||||
0, 0, 0, swap_channels ? 1 : 0,
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
swap_channels ? 1 : 0,
|
||||||
};
|
};
|
||||||
vkCmdPushConstants(command_buffer, pipeline_layout_,
|
vkCmdPushConstants(command_buffer, pipeline_layout_,
|
||||||
VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(VtxPushConstants),
|
VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(VtxPushConstants),
|
||||||
|
@ -540,7 +546,8 @@ VkPipeline Blitter::CreatePipeline(VkRenderPass render_pass,
|
||||||
dynamic_state_info.pNext = nullptr;
|
dynamic_state_info.pNext = nullptr;
|
||||||
dynamic_state_info.flags = 0;
|
dynamic_state_info.flags = 0;
|
||||||
VkDynamicState dynamic_states[] = {
|
VkDynamicState dynamic_states[] = {
|
||||||
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
|
VK_DYNAMIC_STATE_VIEWPORT,
|
||||||
|
VK_DYNAMIC_STATE_SCISSOR,
|
||||||
};
|
};
|
||||||
dynamic_state_info.dynamicStateCount =
|
dynamic_state_info.dynamicStateCount =
|
||||||
static_cast<uint32_t>(xe::countof(dynamic_states));
|
static_cast<uint32_t>(xe::countof(dynamic_states));
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
#if XE_PLATFORM_WIN32
|
#if XE_PLATFORM_WIN32
|
||||||
#define VK_USE_PLATFORM_WIN32_KHR 1
|
#define VK_USE_PLATFORM_WIN32_KHR 1
|
||||||
|
#elif XE_PLATFORM_LINUX
|
||||||
|
#define VK_USE_PLATFORM_XCB_KHR 1
|
||||||
#else
|
#else
|
||||||
#error Platform not yet supported.
|
#error Platform not yet supported.
|
||||||
#endif // XE_PLATFORM_WIN32
|
#endif // XE_PLATFORM_WIN32
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* 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/vulkan/vulkan_util.h"
|
||||||
#include "xenia/ui/window.h"
|
#include "xenia/ui/window.h"
|
||||||
|
|
||||||
|
#if XE_PLATFORM_LINUX
|
||||||
|
#include "xenia/ui/window_gtk.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace ui {
|
namespace ui {
|
||||||
namespace vulkan {
|
namespace vulkan {
|
||||||
|
@ -61,6 +65,29 @@ bool VulkanContext::Initialize() {
|
||||||
auto err = vkCreateWin32SurfaceKHR(*provider->instance(), &create_info,
|
auto err = vkCreateWin32SurfaceKHR(*provider->instance(), &create_info,
|
||||||
nullptr, &surface);
|
nullptr, &surface);
|
||||||
CheckResult(err, "vkCreateWin32SurfaceKHR");
|
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
|
#else
|
||||||
#error Platform not yet implemented.
|
#error Platform not yet implemented.
|
||||||
#endif // XE_PLATFORM_WIN32
|
#endif // XE_PLATFORM_WIN32
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -12,6 +12,7 @@
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <climits>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
|
@ -224,7 +224,9 @@ class VulkanImmediateTexture : public ImmediateTexture {
|
||||||
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
view_info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
view_info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
view_info.components = {
|
view_info.components = {
|
||||||
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
|
VK_COMPONENT_SWIZZLE_R,
|
||||||
|
VK_COMPONENT_SWIZZLE_G,
|
||||||
|
VK_COMPONENT_SWIZZLE_B,
|
||||||
VK_COMPONENT_SWIZZLE_A,
|
VK_COMPONENT_SWIZZLE_A,
|
||||||
};
|
};
|
||||||
view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||||
|
@ -597,7 +599,8 @@ VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext* graphics_context)
|
||||||
dynamic_state_info.pNext = nullptr;
|
dynamic_state_info.pNext = nullptr;
|
||||||
dynamic_state_info.flags = 0;
|
dynamic_state_info.flags = 0;
|
||||||
VkDynamicState dynamic_states[] = {
|
VkDynamicState dynamic_states[] = {
|
||||||
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
|
VK_DYNAMIC_STATE_VIEWPORT,
|
||||||
|
VK_DYNAMIC_STATE_SCISSOR,
|
||||||
};
|
};
|
||||||
dynamic_state_info.dynamicStateCount =
|
dynamic_state_info.dynamicStateCount =
|
||||||
static_cast<uint32_t>(xe::countof(dynamic_states));
|
static_cast<uint32_t>(xe::countof(dynamic_states));
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* 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/vulkan/vulkan_util.h"
|
||||||
#include "xenia/ui/window.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
|
#define VK_API_VERSION VK_API_VERSION_1_0
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
|
@ -385,6 +389,29 @@ bool VulkanInstance::QueryDevices(Window* any_target_window) {
|
||||||
create_info.hwnd = static_cast<HWND>(any_target_window->native_handle());
|
create_info.hwnd = static_cast<HWND>(any_target_window->native_handle());
|
||||||
err = vkCreateWin32SurfaceKHR(handle, &create_info, nullptr, &any_surface);
|
err = vkCreateWin32SurfaceKHR(handle, &create_info, nullptr, &any_surface);
|
||||||
CheckResult(err, "vkCreateWin32SurfaceKHR");
|
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
|
#else
|
||||||
#error Platform not yet implemented.
|
#error Platform not yet implemented.
|
||||||
#endif // XE_PLATFORM_WIN32
|
#endif // XE_PLATFORM_WIN32
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include "xenia/base/clock.h"
|
#include "xenia/base/clock.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
#include "xenia/base/main.h"
|
#include "xenia/base/main.h"
|
||||||
#include "xenia/base/platform_win.h"
|
|
||||||
#include "xenia/base/profiling.h"
|
#include "xenia/base/profiling.h"
|
||||||
#include "xenia/base/threading.h"
|
#include "xenia/base/threading.h"
|
||||||
#include "xenia/ui/graphics_provider.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();
|
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;
|
Instruction* constant = 0;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -772,7 +772,7 @@ Id Builder::findCompositeConstant(Op typeClass, std::vector<Id>& comps) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comments in header
|
// 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;
|
Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite;
|
||||||
assert(typeId);
|
assert(typeId);
|
||||||
|
@ -1022,7 +1022,7 @@ Id Builder::createLoad(Id lValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comments in header
|
// 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.
|
// Figure out the final resulting type.
|
||||||
spv::Id typeId = getTypeId(base);
|
spv::Id typeId = getTypeId(base);
|
||||||
|
@ -1089,7 +1089,7 @@ Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned i
|
||||||
return insert->getResultId();
|
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);
|
Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert);
|
||||||
insert->addIdOperand(object);
|
insert->addIdOperand(object);
|
||||||
|
@ -1210,7 +1210,7 @@ Id Builder::createOp(Op opCode, Id typeId, const std::vector<Id>& operands)
|
||||||
return op->getResultId();
|
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);
|
Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall);
|
||||||
op->addIdOperand(function->getId());
|
op->addIdOperand(function->getId());
|
||||||
|
@ -1647,7 +1647,7 @@ Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, b
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpCompositeConstruct
|
// 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()));
|
assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && getNumTypeConstituents(typeId) == (int)constituents.size()));
|
||||||
|
|
||||||
|
@ -1848,7 +1848,7 @@ void Builder::If::makeEndIf()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Comments in header
|
// 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)
|
std::vector<Block*>& segmentBlocks)
|
||||||
{
|
{
|
||||||
Function& function = buildPoint->getParent();
|
Function& function = buildPoint->getParent();
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue