Compare commits
188 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8340bcb07a | |||
| ea84a6588b | |||
| babaf8b91e | |||
| 3fbc60f7d4 | |||
| 662fe8cc95 | |||
| 695b4c1f06 | |||
| d366823949 | |||
| 33aa2f0b62 | |||
| 917ee472d4 | |||
| 2f7d621fc8 | |||
| 754cdea435 | |||
| 5f07f78c23 | |||
| b22f3d7a55 | |||
| 1344f15efd | |||
| f7803927d9 | |||
| 2ac773385f | |||
| 477c6fdedd | |||
| 78101a9e57 | |||
| d05071221e | |||
| 7d3a528871 | |||
| cc4bc41cf4 | |||
| 5fffef74a2 | |||
| ddbcbd0f1c | |||
| b60d3a3dae | |||
| 74c248150b | |||
| e77d5cf970 | |||
| d32a30bbeb | |||
| 868180eacd | |||
| c247ebad0c | |||
| 6a5ce475b8 | |||
| 3628a8593f | |||
| 58c42ad9c0 | |||
| fb52cbb8b2 | |||
| de465cb941 | |||
| dd5791a2cc | |||
| 380f90b097 | |||
| 86a34fbb45 | |||
| 5d87ef86b5 | |||
| df7d52b177 | |||
| f5addbf751 | |||
| a8c0dd649e | |||
| bbb3bdf008 | |||
| 6d2dcbd164 | |||
| be6b5922dd | |||
| 59be9dc36e | |||
| bd0d87ecaf | |||
| 61ca9df45c | |||
| 222c0e0fc5 | |||
| 12ecba7d49 | |||
| 214ea1e0f6 | |||
| 0870862898 | |||
| c58738807e | |||
| 223f17ac7e | |||
| c435b328c7 | |||
| 3792a85084 | |||
| 0d1cb8c878 | |||
| 16d7023a92 | |||
| 358afc045d | |||
| 0fbb8b9a18 | |||
| 9ef00b4a12 | |||
| 916851bd4a | |||
| 9bc0ec586a | |||
| f4eab09557 | |||
| f2980d57a1 | |||
| dbc9bdfe02 | |||
| 9e0b881b2b | |||
| 946ba3be29 | |||
| 0064976c43 | |||
| 4bdb6674eb | |||
| c7f7d7ef5b | |||
| 2c19fdf092 | |||
| 8fc9862614 | |||
| 55d84414ee | |||
| 8c0bdd71d9 | |||
| 3c9126d91f | |||
| 8b4836a12f | |||
| b2a7eee1ec | |||
| ed21bb309f | |||
| 7b3e4f5e3d | |||
| 30b3af0d12 | |||
| 541cb4062b | |||
| 4e9160104a | |||
| 86fa379c78 | |||
| 8733505d0a | |||
| 242611aa46 | |||
| baa5a261a5 | |||
| 3730b9d1da | |||
| 0d35363ea0 | |||
| 75504b3f5e | |||
| 4ca98e53a6 | |||
| 300a36d3d6 | |||
| 60c7a508a7 | |||
| 1fa18757fc | |||
| fbb7186e66 | |||
| 0961a43997 | |||
| 7037504dcf | |||
| 2385029eab | |||
| eed55a446c | |||
| bbcb6b6851 | |||
| b95630d84a | |||
| af1d3c2aa6 | |||
| ec3e5c547f | |||
| 963a87fed5 | |||
| 173d05b54f | |||
| 865bb47462 | |||
| 3e7ed8a831 | |||
| 3a49a77c07 | |||
| 6782b22637 | |||
| 4600094829 | |||
| daaa83b9ca | |||
| 3288050680 | |||
| b8c29ae957 | |||
| 47bacc0ef0 | |||
| 3d0dced4bd | |||
| 30bba09fed | |||
| 31b07f2c5c | |||
| f7063bb57b | |||
| e4ef85b6e0 | |||
| b7c2088489 | |||
| 59cd0a9c7f | |||
| af107df0b4 | |||
| 0e7e40f2cb | |||
| 0b788f5b43 | |||
| db5c2500c2 | |||
| a1496eb1f1 | |||
| 4aa89132d7 | |||
| 9b8eb089b1 | |||
| c1f08ce58d | |||
| 09a0d046f4 | |||
| 0741698f13 | |||
| c2ac05f734 | |||
| f8f5f9f418 | |||
| 7e6d6b45d4 | |||
| 361e80f7dc | |||
| 395b4bfa45 | |||
| ce2b790d8f | |||
| 1ffc1e82b0 | |||
| 8520ca7ef7 | |||
| 89fba1c385 | |||
| 00a0d37455 | |||
| dbb2251f93 | |||
| 7abf755a57 | |||
| 055f0e2e4a | |||
| 49f4da3016 | |||
| bf234dc668 | |||
| 11bd65c2b5 | |||
| e0d1ac676e | |||
| 494cbac78b | |||
| 0d46a8e786 | |||
| 626b9f93c4 | |||
| 53f9defb07 | |||
| 4bf8d1e41f | |||
| 936c817bd8 | |||
| 9c9495621c | |||
| d53673c6f7 | |||
| 882e949f80 | |||
| e6849a59a2 | |||
| 6918e265ec | |||
| e9f293f522 | |||
| 5fc36d64b6 | |||
| 95e6d78689 | |||
| f4a666345a | |||
| 7a5c88a8d1 | |||
| 0de0dded53 | |||
| 5e58cf6079 | |||
| b0737d1c90 | |||
| 779ed75a19 | |||
| dbf1573e89 | |||
| 96fd8556f2 | |||
| 8f324dfc0a | |||
| 34a6085cc0 | |||
| b3efa75206 | |||
| b90a7dc66a | |||
| 200557c036 | |||
| 472cb3b1c2 | |||
| d8ee6a2edb | |||
| ddcacb8258 | |||
| a8e1754fa4 | |||
| 5db45c3699 | |||
| 89dcafbe41 | |||
| 3d05e61f7e | |||
| 5146f3ec47 | |||
| 170a95f5df | |||
| c661578906 | |||
| f20e0c2898 | |||
| 64b2ae3cd5 | |||
| a11d66f52d | |||
| 304a4a1b61 |
@@ -59,6 +59,7 @@ rpcs3/git-version.h
|
||||
|
||||
# Visual Studio Files
|
||||
.vs/*
|
||||
*.ipch
|
||||
*.vspx
|
||||
*.psess
|
||||
*.VC.*
|
||||
|
||||
+6
-6
@@ -8,13 +8,13 @@
|
||||
[submodule "llvm"]
|
||||
path = llvm
|
||||
url = https://github.com/llvm-mirror/llvm
|
||||
branch = release_40
|
||||
branch = release_50
|
||||
[submodule "GSL"]
|
||||
path = 3rdparty/GSL
|
||||
url = https://github.com/Microsoft/GSL.git
|
||||
[submodule "libpng"]
|
||||
path = 3rdparty/libpng
|
||||
url = https://github.com/RPCS3/libpng
|
||||
url = https://github.com/Zangetsu38/libpng
|
||||
ignore = dirty
|
||||
[submodule "Vulkan/glslang"]
|
||||
path = Vulkan/glslang
|
||||
@@ -25,9 +25,6 @@
|
||||
[submodule "Utilities/yaml-cpp"]
|
||||
path = Utilities/yaml-cpp
|
||||
url = https://github.com/RPCS3/yaml-cpp
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/RPCS3/pugixml
|
||||
[submodule "3rdparty/cereal"]
|
||||
path = 3rdparty/cereal
|
||||
url = https://github.com/USCiLab/cereal.git
|
||||
@@ -36,8 +33,11 @@
|
||||
url = https://github.com/madler/zlib
|
||||
[submodule "3rdparty/hidapi"]
|
||||
path = 3rdparty/hidapi
|
||||
url = https://github.com/RPCS3/hidapi
|
||||
url = https://github.com/Zangetsu38/hidapi
|
||||
branch = master
|
||||
[submodule "3rdparty/Optional"]
|
||||
path = 3rdparty/Optional
|
||||
url = https://github.com/akrzemi1/Optional.git
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/zeux/pugixml
|
||||
|
||||
+8
-8
@@ -54,7 +54,7 @@ before_script:
|
||||
- git submodule update --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal 3rdparty/hidapi 3rdparty/Optional Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
- mkdir build
|
||||
- cd build
|
||||
- export CMAKE_PREFIX_PATH=~/Qt/5.9.1/gcc_64/lib/cmake
|
||||
- export CMAKE_PREFIX_PATH=~/Qt/5.9.2/gcc_64/lib/cmake
|
||||
- if [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH";
|
||||
else
|
||||
@@ -64,20 +64,20 @@ before_script:
|
||||
- make -j 3
|
||||
- # AppImage generation
|
||||
- if [ -n "$UPLOAD_URL" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$CC" = "clang-4.0" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export LD_LIBRARY_PATH=~/Qt/5.9.1/gcc_64/lib;
|
||||
export LD_LIBRARY_PATH=~/Qt/5.9.2/gcc_64/lib;
|
||||
make DESTDIR=appdir install ; find appdir/ ;
|
||||
find ../bin ;
|
||||
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" ;
|
||||
chmod a+x linuxdeployqt*.AppImage ;
|
||||
export PATH=~/Qt/5.9.1/gcc_64/bin/:${PATH} ;
|
||||
export PATH=~/Qt/5.9.2/gcc_64/bin/:${PATH} ;
|
||||
./linuxdeployqt*.AppImage --appimage-extract ;
|
||||
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs ;
|
||||
mkdir ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||
mkdir ./appdir/usr/plugins/imageformats/ ;
|
||||
cp ~/Qt/5.9.1/gcc_64/lib/libQt5Svg.so.5 ./appdir/usr/lib/ ;
|
||||
cp ~/Qt/5.9.1/gcc_64/plugins/xcbglintegrations/* ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||
cp ~/Qt/5.9.1/gcc_64/plugins/imageformats/* ./appdir/usr/plugins/imageformats/ ;
|
||||
cp ~/Qt/5.9.1/gcc_64/plugins/platforms/* ./appdir/usr/plugins/platforms/ ;
|
||||
cp ~/Qt/5.9.2/gcc_64/lib/libQt5Svg.so.5 ./appdir/usr/lib/ ;
|
||||
cp ~/Qt/5.9.2/gcc_64/plugins/xcbglintegrations/* ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||
cp ~/Qt/5.9.2/gcc_64/plugins/imageformats/* ./appdir/usr/plugins/imageformats/ ;
|
||||
cp ~/Qt/5.9.2/gcc_64/plugins/platforms/* ./appdir/usr/plugins/platforms/ ;
|
||||
export PATH=${TRAVIS_BUILD_DIR}/build/squashfs-root/usr/bin/:${PATH} ;
|
||||
./squashfs-root/usr/bin/appimagetool ${TRAVIS_BUILD_DIR}/build/appdir ;
|
||||
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq ;
|
||||
@@ -115,7 +115,7 @@ addons:
|
||||
- libstdc++-5-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
# We need to install qt 5.9.1 manually because the version trusty provides is too old.
|
||||
# We need to install qt 5.9.2 manually because the version trusty provides is too old.
|
||||
#- qtbase5-dev
|
||||
- libudev-dev
|
||||
- libevdev-dev
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/hidapi updated: ca39ce8939...4c2750112e
Vendored
+1
-1
Submodule 3rdparty/libpng updated: 1dcba4d6eb...bbbcaf1122
Vendored
+1
-1
Submodule 3rdparty/pugixml updated: f205aaf6e1...257fbb4e1b
+1
-1
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
option(WITH_GDB "WITH_GDB" OFF)
|
||||
option(WITHOUT_LLVM "WITHOUT_LLVM" OFF)
|
||||
|
||||
@@ -20,19 +20,19 @@ If you want to contribute please take a look at the [Coding Style](https://githu
|
||||
## Dependencies
|
||||
|
||||
### Windows
|
||||
* [Visual Studio 2015](https://www.visualstudio.com/vs/older-downloads/)
|
||||
* [Visual C++ Redistributable Packages for Visual Studio 2015](http://www.microsoft.com/en-us/download/details.aspx?id=48145)
|
||||
* [Visual Studio 2017](https://www.visualstudio.com/en/downloads/)
|
||||
* [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572)
|
||||
* [Cmake 3.1.0+](https://www.cmake.org/download/) (required; add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
|
||||
* [Qt 5.7+](https://www.qt.io/download-open-source/) (required; add QTDIR `<QtInstallFolder>\5.7\msvc2015_64\` environment variable if you do not want to use the Visual Studio Qt Plugin)
|
||||
* [Qt 5.9.2](https://www.qt.io/download-open-source/) (required; add QTDIR `<QtInstallFolder>\5.9.1\msvc2015_64\` environment variable if you do not want to use the Visual Studio Qt Plugin)
|
||||
* [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2015) (optional; see above)
|
||||
|
||||
### Linux
|
||||
* [Qt 5.7+](https://www.qt.io/download-open-source/)
|
||||
* GCC 5.1+ or Clang 3.5.0+ ([not GCC 6.1](https://github.com/RPCS3/rpcs3/issues/1691))
|
||||
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libasound2-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default`
|
||||
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default`
|
||||
* Arch: `sudo pacman -S glew openal cmake llvm qt5-base`
|
||||
* Fedora: `sudo dnf install cmake qt5-devel vulkan-devel glew glew-devel`
|
||||
* Fedora: `sudo dnf install cmake glew glew-devel libatomic libudev-devel openal-devel qt5-devel vulkan-devel`
|
||||
**If you have a NVIDIA GPU, you may need to install the libglvnd package.**
|
||||
|
||||
### MacOS
|
||||
@@ -47,15 +47,23 @@ To initialize the repository don't forget to execute `git submodule update --ini
|
||||
### Configuring Qt
|
||||
|
||||
*If you're using Visual Studio 2017 without Qt plugin support (or simply dont want to use it):*
|
||||
1) Add `QTDIR` environment variable and set it to `<QtInstallFolder>\5.7\msvc2015_64\` </br>
|
||||
1) Add `QTDIR` environment variable and set it to `<QtInstallFolder>\5.9.2\msvc2017_64\` </br>
|
||||
|
||||
|
||||
Open `rpcs3.sln`
|
||||
|
||||
|
||||
*If you wish to use the Visual Studio plugin for Qt:* </br>
|
||||
1) Go to the Qt5 menu and edit Qt5 options. Add the path to your Qt installation with compiler e.g. `C:\Qt\5.7\msvc2015_64`. </br>
|
||||
1) Go to the Qt5 menu and edit Qt5 options. Add the path to your Qt installation with compiler e.g. `C:\Qt\5.9.2\msvc2015_64`. </br>
|
||||
2) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
### Building the projects
|
||||
1) Build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*. </br>
|
||||
2) Press *BUILD* > *Build Solution* or *Rebuild Solution*. </br>
|
||||
You may want to download precompiled [LLVM lib](https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRY2k3Q2Yya05lcm8) and extract to root rpcs3 folder (which contains `rpcs3.sln`), as well as download and extract [additional libs](https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRaHYxV3VOS2d0a0U) to `lib\%CONFIGURATION%-x64\` to speed up compilation time (unoptimised/debug libs are currently not available precompiled).
|
||||
|
||||
If you're not using precompiled libs, build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*. </br></br>
|
||||
|
||||
`Build > Build Solution`</br>
|
||||
|
||||
|
||||
|
||||
## Building on Linux & Mac OS:
|
||||
|
||||
+13
-3
@@ -118,7 +118,7 @@ namespace cfg
|
||||
bool m_value;
|
||||
|
||||
public:
|
||||
const bool def;
|
||||
bool def;
|
||||
|
||||
_bool(node* owner, const std::string& name, bool def = false)
|
||||
: _base(type::_bool, owner, name)
|
||||
@@ -150,6 +150,11 @@ namespace cfg
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void set(const bool& value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
};
|
||||
|
||||
// Value node with fixed set of possible values, each maps to an enum value of type T.
|
||||
@@ -217,7 +222,7 @@ namespace cfg
|
||||
int_type m_value;
|
||||
|
||||
public:
|
||||
const int_type def;
|
||||
int_type def;
|
||||
|
||||
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
||||
: _base(type::_int, owner, name)
|
||||
@@ -253,6 +258,11 @@ namespace cfg
|
||||
return false;
|
||||
}
|
||||
|
||||
void set(const s64& value)
|
||||
{
|
||||
m_value = static_cast<int_type>(value);
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
return make_int_range(Min, Max);
|
||||
@@ -271,7 +281,7 @@ namespace cfg
|
||||
std::string m_value;
|
||||
|
||||
public:
|
||||
const std::string def;
|
||||
std::string def;
|
||||
|
||||
string(node* owner, const std::string& name, const std::string& def = {})
|
||||
: _base(type::string, owner, name)
|
||||
|
||||
+28
-3
@@ -75,10 +75,25 @@ static time_t to_time(const FILETIME& ft)
|
||||
|
||||
static FILETIME from_time(s64 _time)
|
||||
{
|
||||
const ullong wtime = (_time + 11644473600ULL) * 10000000ULL;
|
||||
FILETIME result;
|
||||
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
||||
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
||||
|
||||
if (_time <= -11644473600ll)
|
||||
{
|
||||
result.dwLowDateTime = 0;
|
||||
result.dwHighDateTime = 0;
|
||||
}
|
||||
else if (_time > INT64_MAX / 10000000ll - 11644473600ll)
|
||||
{
|
||||
result.dwLowDateTime = 0xffffffff;
|
||||
result.dwHighDateTime = 0x7fffffff;
|
||||
}
|
||||
else
|
||||
{
|
||||
const ullong wtime = (_time + 11644473600ull) * 10000000ull;
|
||||
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
||||
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -96,6 +111,8 @@ static fs::error to_error(DWORD e)
|
||||
case ERROR_INVALID_NAME: return fs::error::inval;
|
||||
case ERROR_SHARING_VIOLATION: return fs::error::acces;
|
||||
case ERROR_DIR_NOT_EMPTY: return fs::error::notempty;
|
||||
case ERROR_NOT_READY: return fs::error::noent;
|
||||
//case ERROR_INVALID_PARAMETER: return fs::error::inval;
|
||||
default: fmt::throw_exception("Unknown Win32 error: %u.", e);
|
||||
}
|
||||
}
|
||||
@@ -106,6 +123,7 @@ static fs::error to_error(DWORD e)
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/file.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
@@ -937,6 +955,13 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (test(mode & fs::unshare) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
|
||||
{
|
||||
g_tls_error = errno == EWOULDBLOCK ? fs::error::acces : to_error(errno);
|
||||
::close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
class unix_file final : public file_base, public get_native_handle
|
||||
{
|
||||
const int m_fd;
|
||||
|
||||
+7
-7
@@ -422,7 +422,7 @@ namespace fs
|
||||
|
||||
class iterator
|
||||
{
|
||||
dir* m_parent;
|
||||
const dir* m_parent;
|
||||
dir_entry m_entry;
|
||||
|
||||
public:
|
||||
@@ -432,7 +432,7 @@ namespace fs
|
||||
from_current
|
||||
};
|
||||
|
||||
iterator(dir* parent, mode mode_ = mode::from_first)
|
||||
iterator(const dir* parent, mode mode_ = mode::from_first)
|
||||
: m_parent(parent)
|
||||
{
|
||||
if (!m_parent)
|
||||
@@ -458,7 +458,7 @@ namespace fs
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
*this = { m_parent, mode::from_current };
|
||||
*this = {m_parent, mode::from_current};
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -468,14 +468,14 @@ namespace fs
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin()
|
||||
iterator begin() const
|
||||
{
|
||||
return{ m_dir ? this : nullptr };
|
||||
return {m_dir ? this : nullptr};
|
||||
}
|
||||
|
||||
iterator end()
|
||||
iterator end() const
|
||||
{
|
||||
return{ nullptr };
|
||||
return {nullptr};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+10
-2
@@ -30,6 +30,8 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "JIT.h"
|
||||
@@ -47,6 +49,11 @@ static void* const s_memory = []() -> void*
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
LLVMLinkInMCJIT();
|
||||
|
||||
#ifdef MAP_32BIT
|
||||
auto ptr = ::mmap(nullptr, s_memory_size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_32BIT, -1, 0);
|
||||
if (ptr != MAP_FAILED)
|
||||
return ptr;
|
||||
#else
|
||||
for (u64 addr = 0x10000000; addr <= 0x80000000 - s_memory_size; addr += 0x1000000)
|
||||
{
|
||||
if (auto ptr = utils::memory_reserve(s_memory_size, (void*)addr))
|
||||
@@ -54,6 +61,7 @@ static void* const s_memory = []() -> void*
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return utils::memory_reserve(s_memory_size);
|
||||
}();
|
||||
@@ -258,11 +266,11 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
|
||||
}
|
||||
|
||||
virtual void deregisterEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
virtual void deregisterEHFrames() override
|
||||
{
|
||||
LOG_ERROR(GENERAL, "deregisterEHFrames() called"); // Not expected
|
||||
|
||||
return RTDyldMemoryManager::deregisterEHFrames(addr, load_addr, size);
|
||||
return RTDyldMemoryManager::deregisterEHFrames();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+219
-48
@@ -4,15 +4,19 @@
|
||||
#include "sema.h"
|
||||
|
||||
#include "Utilities/sysinfo.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "rpcs3_version.h"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <chrono>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
@@ -49,23 +53,32 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
||||
|
||||
namespace logs
|
||||
{
|
||||
constexpr std::size_t s_log_size = 128 * 1024 * 1024;
|
||||
constexpr std::size_t s_log_size_s = s_log_size / 2;
|
||||
constexpr std::size_t s_log_size_t = s_log_size / 4 + s_log_size_s;
|
||||
constexpr std::size_t s_log_size_e = s_log_size / 8 + s_log_size_t;
|
||||
constexpr std::size_t s_log_size_f = s_log_size / 16 + s_log_size_e;
|
||||
// Memory-mapped buffer size
|
||||
constexpr u64 s_log_size = 32 * 1024 * 1024;
|
||||
|
||||
class file_writer
|
||||
{
|
||||
fs::file m_file;
|
||||
std::string m_name;
|
||||
std::thread m_writer;
|
||||
fs::file m_fout;
|
||||
fs::file m_fout2;
|
||||
u64 m_max_size;
|
||||
|
||||
#ifdef _WIN32
|
||||
::HANDLE m_fmap;
|
||||
#endif
|
||||
atomic_t<std::size_t> m_pos{0};
|
||||
std::size_t m_size{0};
|
||||
uchar* m_fptr{};
|
||||
z_stream m_zs{};
|
||||
semaphore<> m_m;
|
||||
|
||||
alignas(128) atomic_t<u64> m_buf{0}; // MSB (40 bit): push begin, LSB (24 bis): push size
|
||||
alignas(128) atomic_t<u64> m_out{0}; // Amount of bytes written to file
|
||||
|
||||
uchar m_zout[65536];
|
||||
|
||||
// Write buffered logs immediately
|
||||
bool flush(u64 bufv);
|
||||
|
||||
public:
|
||||
file_writer(const std::string& name);
|
||||
@@ -119,7 +132,7 @@ namespace logs
|
||||
static file_listener* get_logger()
|
||||
{
|
||||
// Use magic static
|
||||
static file_listener logger("RPCS3.log");
|
||||
static file_listener logger("RPCS3");
|
||||
return &logger;
|
||||
}
|
||||
|
||||
@@ -291,13 +304,45 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u
|
||||
logs::file_writer::file_writer(const std::string& name)
|
||||
: m_name(name)
|
||||
{
|
||||
const std::string log_name = fs::get_config_dir() + name + ".log";
|
||||
const std::string buf_name = fs::get_config_dir() + name + ".buf";
|
||||
|
||||
try
|
||||
{
|
||||
if (!m_file.open(fs::get_config_dir() + name, fs::read + fs::write + fs::create + fs::trunc + fs::unshare))
|
||||
if (!m_file.open(buf_name, fs::read + fs::write + fs::create + fs::trunc + fs::unshare))
|
||||
{
|
||||
fmt::throw_exception("Can't create file %s (error %s)", name, fs::g_tls_error);
|
||||
if (fs::g_tls_error == fs::error::acces)
|
||||
{
|
||||
if (fs::exists(buf_name))
|
||||
{
|
||||
fmt::throw_exception("Another instance of %s is running. Close it or kill its process, if necessary.", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Cannot create %s.log (access denied)."
|
||||
#ifdef _WIN32
|
||||
"\nNote that %s cannot be installed in Program Files or similar directory with limited permissions."
|
||||
#else
|
||||
"\nPlease, check %s permissions in '~/.config/'."
|
||||
#endif
|
||||
, name, name);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::throw_exception("Cannot create %s.log (error %s)", name, fs::g_tls_error);
|
||||
}
|
||||
|
||||
// Check free space
|
||||
fs::device_stat stats{};
|
||||
if (!fs::statfs(fs::get_config_dir(), stats) || stats.avail_free < s_log_size * 8)
|
||||
{
|
||||
fmt::throw_exception("Not enough free space (%f KB)", stats.avail_free / 1000000.);
|
||||
}
|
||||
|
||||
// Limit log size to ~25% of free space
|
||||
m_max_size = stats.avail_free / 4;
|
||||
|
||||
// Initialize memory mapped file
|
||||
#ifdef _WIN32
|
||||
m_fmap = CreateFileMappingW(m_file.get_handle(), 0, PAGE_READWRITE, s_log_size >> 32, s_log_size & 0xffffffff, 0);
|
||||
m_fptr = m_fmap ? (uchar*)MapViewOfFile(m_fmap, FILE_MAP_WRITE, 0, 0, 0) : nullptr;
|
||||
@@ -307,44 +352,94 @@ logs::file_writer::file_writer(const std::string& name)
|
||||
#endif
|
||||
|
||||
verify(name.c_str()), m_fptr;
|
||||
std::memset(m_fptr, '\n', s_log_size);
|
||||
|
||||
// Rotate backups (TODO)
|
||||
fs::remove_file(fs::get_config_dir() + name + "1.gz");
|
||||
fs::remove_file(fs::get_config_dir() + name + "1.log.gz");
|
||||
fs::create_dir(fs::get_config_dir() + "old_logs");
|
||||
fs::rename(fs::get_config_dir() + m_name + ".gz", fs::get_config_dir() + "old_logs/" + m_name + ".gz", true);
|
||||
fs::rename(fs::get_config_dir() + m_name + ".log.gz", fs::get_config_dir() + "old_logs/" + m_name + ".log.gz", true);
|
||||
|
||||
// Actual log file (allowed to fail)
|
||||
m_fout.open(log_name, fs::rewrite);
|
||||
|
||||
// Compressed log
|
||||
if (!m_fout2.open(log_name + ".gz", fs::rewrite) || deflateInit2(&m_zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||
{
|
||||
m_fout2.close();
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::thread([text = std::string{e.what()}]{ report_fatal_error(text); }).detach();
|
||||
return;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
catch_all_exceptions();
|
||||
std::thread([]{ report_fatal_error("Unknown error" HERE); }).detach();
|
||||
return;
|
||||
}
|
||||
|
||||
m_writer = std::thread([this]()
|
||||
{
|
||||
thread_ctrl::set_native_priority(-1);
|
||||
|
||||
while (true)
|
||||
{
|
||||
const u64 bufv = m_buf;
|
||||
|
||||
if (bufv & 0xffffff)
|
||||
{
|
||||
// Wait if threads are writing logs
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!flush(bufv))
|
||||
{
|
||||
if (m_out == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logs::file_writer::~file_writer()
|
||||
{
|
||||
if (m_size == 0)
|
||||
if (!m_fptr)
|
||||
{
|
||||
m_size = std::min<std::size_t>(+m_pos, s_log_size);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compress
|
||||
z_stream zs{};
|
||||
if (deflateInit2(&zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) == Z_OK)
|
||||
// Stop writer thread
|
||||
while (m_out << 24 < m_buf)
|
||||
{
|
||||
auto buf = std::make_unique<uchar[]>(s_log_size);
|
||||
zs.avail_in = ::narrow<u32>(m_size);
|
||||
zs.avail_out = s_log_size;
|
||||
zs.next_in = m_fptr;
|
||||
zs.next_out = buf.get();
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
if (deflate(&zs, Z_FINISH) != Z_STREAM_ERROR)
|
||||
{
|
||||
fs::file(fs::get_config_dir() + m_name + ".gz", fs::rewrite).write(buf.get(), zs.total_out);
|
||||
}
|
||||
m_out = -1;
|
||||
m_writer.join();
|
||||
|
||||
if (deflateEnd(&zs) != Z_OK)
|
||||
if (m_fout2)
|
||||
{
|
||||
m_zs.avail_in = 0;
|
||||
m_zs.next_in = nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
m_zs.avail_out = sizeof(m_zout);
|
||||
m_zs.next_out = m_zout;
|
||||
|
||||
if (deflate(&m_zs, Z_FINISH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (m_zs.avail_out == 0);
|
||||
|
||||
deflateEnd(&m_zs);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -353,33 +448,109 @@ logs::file_writer::~file_writer()
|
||||
#else
|
||||
::munmap(m_fptr, s_log_size);
|
||||
#endif
|
||||
m_file.trunc(m_size);
|
||||
}
|
||||
|
||||
bool logs::file_writer::flush(u64 bufv)
|
||||
{
|
||||
semaphore_lock lock(m_m);
|
||||
|
||||
const u64 st = +m_out;
|
||||
const u64 end = std::min<u64>((st + s_log_size) & ~(s_log_size - 1), bufv >> 24);
|
||||
|
||||
if (end > st)
|
||||
{
|
||||
// Avoid writing too big fragments
|
||||
const u64 size = std::min<u64>(end - st, sizeof(m_zout) / 2);
|
||||
|
||||
// Write uncompressed
|
||||
if (m_fout && st < m_max_size && m_fout.write(m_fptr + st % s_log_size, size) != size)
|
||||
{
|
||||
m_fout.close();
|
||||
}
|
||||
|
||||
// Write compressed
|
||||
if (m_fout2 && st < m_max_size)
|
||||
{
|
||||
m_zs.avail_in = size;
|
||||
m_zs.next_in = m_fptr + st % s_log_size;
|
||||
|
||||
do
|
||||
{
|
||||
m_zs.avail_out = sizeof(m_zout);
|
||||
m_zs.next_out = m_zout;
|
||||
|
||||
if (deflate(&m_zs, Z_NO_FLUSH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
|
||||
{
|
||||
deflateEnd(&m_zs);
|
||||
m_fout2.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (m_zs.avail_out == 0);
|
||||
}
|
||||
|
||||
m_out += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void logs::file_writer::log(logs::level sev, const char* text, std::size_t size)
|
||||
{
|
||||
// Adaptive log limit
|
||||
const auto lim =
|
||||
sev >= logs::level::success ? s_log_size_s :
|
||||
sev == logs::level::todo ? s_log_size_t :
|
||||
sev == logs::level::error ? s_log_size_e : s_log_size_f;
|
||||
|
||||
if (m_pos >= lim)
|
||||
if (!m_fptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Acquire memory
|
||||
const auto pos = m_pos.fetch_add(size);
|
||||
// TODO: write bigger fragment directly in blocking manner
|
||||
while (size && size <= 0xffffff)
|
||||
{
|
||||
u64 bufv;
|
||||
|
||||
// Write if possible
|
||||
if (pos + size <= s_log_size)
|
||||
{
|
||||
std::memcpy(m_fptr + pos, text, size);
|
||||
}
|
||||
else if (pos <= s_log_size)
|
||||
{
|
||||
m_size = pos;
|
||||
const auto pos = m_buf.atomic_op([&](u64& v) -> uchar*
|
||||
{
|
||||
const u64 v1 = v >> 24;
|
||||
const u64 v2 = v & 0xffffff;
|
||||
|
||||
if (UNLIKELY(v2 + size > 0xffffff || v1 + v2 + size >= m_out + s_log_size))
|
||||
{
|
||||
bufv = v;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
v += size;
|
||||
return m_fptr + (v1 + v2) % s_log_size;
|
||||
});
|
||||
|
||||
if (UNLIKELY(!pos))
|
||||
{
|
||||
if ((bufv & 0xffffff) + size > 0xffffff || bufv & 0xffffff)
|
||||
{
|
||||
// Concurrency limit reached
|
||||
std::this_thread::yield();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue is full, need to write out
|
||||
flush(bufv);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pos + size > m_fptr + s_log_size)
|
||||
{
|
||||
const auto frag = m_fptr + s_log_size - pos;
|
||||
std::memcpy(pos, text, frag);
|
||||
std::memcpy(m_fptr, text + frag, size - frag);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(pos, text, size);
|
||||
}
|
||||
|
||||
m_buf += (u64{size} << 24) - size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+37
-43
@@ -16,6 +16,12 @@
|
||||
#ifdef __APPLE__
|
||||
#define _XOPEN_SOURCE
|
||||
#define __USE_GNU
|
||||
#include <mach/thread_act.h>
|
||||
#include <mach/thread_policy.h>
|
||||
#endif
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <pthread_np.h>
|
||||
#define cpu_set_t cpuset_t
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
@@ -31,32 +37,6 @@ thread_local u64 g_tls_fault_all = 0;
|
||||
thread_local u64 g_tls_fault_rsx = 0;
|
||||
thread_local u64 g_tls_fault_spu = 0;
|
||||
|
||||
static void report_fatal_error(const std::string& msg)
|
||||
{
|
||||
static semaphore<> g_report_only_once;
|
||||
|
||||
g_report_only_once.wait();
|
||||
|
||||
std::string _msg = msg + "\n"
|
||||
"HOW TO REPORT ERRORS: https://github.com/RPCS3/rpcs3/wiki/How-to-ask-for-Support\n"
|
||||
"Please, don't send incorrect reports. Thanks for understanding.\n";
|
||||
|
||||
#ifdef _WIN32
|
||||
_msg += "\nPress Yes or Enter to visit the URL above immediately.";
|
||||
const std::size_t buf_size = _msg.size() + 1;
|
||||
const int size = static_cast<int>(buf_size);
|
||||
std::unique_ptr<wchar_t[]> buffer(new wchar_t[buf_size]);
|
||||
MultiByteToWideChar(CP_UTF8, 0, _msg.c_str(), size, buffer.get(), size);
|
||||
|
||||
if (MessageBoxW(0, buffer.get(), L"Fatal error", MB_ICONERROR | MB_YESNO) == IDYES)
|
||||
{
|
||||
ShellExecuteW(0, L"open", L"https://github.com/RPCS3/rpcs3/wiki/How-to-ask-for-Support", 0, 0, SW_SHOWNORMAL);
|
||||
}
|
||||
#else
|
||||
std::printf("Fatal error: \n%s", _msg.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
[[noreturn]] void catch_all_exceptions()
|
||||
{
|
||||
try
|
||||
@@ -71,8 +51,6 @@ static void report_fatal_error(const std::string& msg)
|
||||
{
|
||||
report_fatal_error("Unhandled exception (unknown)");
|
||||
}
|
||||
|
||||
std::abort();
|
||||
}
|
||||
|
||||
enum x64_reg_t : u32
|
||||
@@ -1491,13 +1469,11 @@ const bool s_exception_handler_set = []() -> bool
|
||||
if (!AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)exception_handler))
|
||||
{
|
||||
report_fatal_error("AddVectoredExceptionHandler() failed.");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)exception_filter))
|
||||
{
|
||||
report_fatal_error("SetUnhandledExceptionFilter() failed.");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1544,7 +1520,6 @@ static void signal_handler(int sig, siginfo_t* info, void* uct)
|
||||
|
||||
// TODO (debugger interaction)
|
||||
report_fatal_error(fmt::format("Segfault %s location %p at %p.", cause, info->si_addr, RIP(context)));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
const bool s_exception_handler_set = []() -> bool
|
||||
@@ -1768,7 +1743,7 @@ bool thread_ctrl::_wait_for(u64 usec)
|
||||
}
|
||||
}
|
||||
}
|
||||
while (_this->m_cond.wait(_lock, std::exchange(usec, usec == -1 ? -1 : 0)));
|
||||
while (_this->m_cond.wait(_lock, std::exchange(usec, usec > cond_variable::max_timeout ? -1 : 0)));
|
||||
|
||||
// Timeout
|
||||
return false;
|
||||
@@ -1884,20 +1859,30 @@ void thread_ctrl::set_native_priority(int priority)
|
||||
HANDLE _this_thread = GetCurrentThread();
|
||||
INT native_priority = THREAD_PRIORITY_NORMAL;
|
||||
|
||||
switch (priority)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
if (priority > 0)
|
||||
native_priority = THREAD_PRIORITY_ABOVE_NORMAL;
|
||||
break;
|
||||
case -1:
|
||||
if (priority < 0)
|
||||
native_priority = THREAD_PRIORITY_BELOW_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
SetThreadPriority(_this_thread, native_priority);
|
||||
if (!SetThreadPriority(_this_thread, native_priority))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "SetThreadPriority() failed: 0x%x", GetLastError());
|
||||
}
|
||||
#else
|
||||
int policy;
|
||||
struct sched_param param;
|
||||
|
||||
pthread_getschedparam(pthread_self(), &policy, ¶m);
|
||||
|
||||
if (priority > 0)
|
||||
param.sched_priority = sched_get_priority_max(policy);
|
||||
if (priority < 0)
|
||||
param.sched_priority = sched_get_priority_min(policy);
|
||||
|
||||
if (int err = pthread_setschedparam(pthread_self(), policy, ¶m))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "pthraed_setschedparam() failed: %d", err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1906,6 +1891,15 @@ void thread_ctrl::set_ideal_processor_core(int core)
|
||||
#ifdef _WIN32
|
||||
HANDLE _this_thread = GetCurrentThread();
|
||||
SetThreadIdealProcessor(_this_thread, core);
|
||||
#elif __APPLE__
|
||||
thread_affinity_policy_data_t policy = { static_cast<integer_t>(core) };
|
||||
thread_port_t mach_thread = pthread_mach_thread_np(pthread_self());
|
||||
thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY, (thread_policy_t)&policy, 1);
|
||||
#elif defined(__linux__) || defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
cpu_set_t cs;
|
||||
CPU_ZERO(&cs);
|
||||
CPU_SET(core, &cs);
|
||||
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cs);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "sema.h"
|
||||
#include "cond.h"
|
||||
|
||||
// Report error and call std::abort(), defined in main.cpp
|
||||
[[noreturn]] void report_fatal_error(const std::string&);
|
||||
|
||||
// Will report exception and call std::abort() if put in catch(...)
|
||||
[[noreturn]] void catch_all_exceptions();
|
||||
|
||||
|
||||
+4
-3
@@ -10,12 +10,13 @@
|
||||
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
||||
{
|
||||
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
|
||||
const bool is_inf = _timeout > max_timeout;
|
||||
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = _timeout * -10;
|
||||
|
||||
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout))
|
||||
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, is_inf ? nullptr : &timeout))
|
||||
{
|
||||
verify(HERE), rc == WAIT_TIMEOUT;
|
||||
|
||||
@@ -37,12 +38,12 @@ bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
||||
|
||||
for (u32 value = _old + 1;; value = m_value)
|
||||
{
|
||||
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, _timeout == -1 ? nullptr : &timeout, nullptr, 0) == 0
|
||||
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, is_inf ? nullptr : &timeout, nullptr, 0) == 0
|
||||
? 0
|
||||
: errno;
|
||||
|
||||
// Normal or timeout wakeup
|
||||
if (!err || (_timeout != -1 && err == ETIMEDOUT))
|
||||
if (!err || (!is_inf && err == ETIMEDOUT))
|
||||
{
|
||||
// Cleanup (remove waiter)
|
||||
verify(HERE), m_value--;
|
||||
|
||||
@@ -47,4 +47,6 @@ public:
|
||||
imp_wake(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr u64 max_timeout = u64{UINT32_MAX} / 1000 * 1000000;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ rem // Official git repository and contact information can be found at
|
||||
rem // https://github.com/RPCS3/rpcs3 and http://rpcs3.net/.
|
||||
|
||||
setlocal ENABLEDELAYEDEXPANSION
|
||||
setlocal ENABLEEXTENSIONS
|
||||
|
||||
set GIT_VERSION_FILE=%~p0..\rpcs3\git-version.h
|
||||
if not defined GIT (
|
||||
@@ -54,9 +55,35 @@ if errorlevel 1 (
|
||||
goto done
|
||||
)
|
||||
|
||||
rem // Get commit count from HEAD by default
|
||||
for /F %%I IN ('call %GIT% rev-list HEAD --count') do set GIT_VERSION=%%I
|
||||
|
||||
rem // If we're in AppVeyor and we're building for master: Get commit count from unshallowed upstream
|
||||
if defined APPVEYOR (
|
||||
if not defined APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH (
|
||||
call %GIT% remote add upstream https://github.com/RPCS3/RPCS3.git
|
||||
call %GIT% fetch --unshallow upstream
|
||||
for /F %%I IN ('call %GIT% rev-list --count upstream/master') do set GIT_VERSION=%%I
|
||||
)
|
||||
)
|
||||
|
||||
if defined APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH (
|
||||
if "%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"=="master" (
|
||||
for /f "tokens=1* delims=/" %%a in ("%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%") do set user=%%a
|
||||
set "GIT_BRANCH=!user!/%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"
|
||||
) else (
|
||||
set GIT_BRANCH=%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%
|
||||
)
|
||||
|
||||
) else (
|
||||
for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I
|
||||
)
|
||||
|
||||
rem // Get last commit (shortened) and concat after commit count in GIT_VERSION
|
||||
for /F %%I IN ('call %GIT% rev-parse --short HEAD') do set GIT_VERSION=%GIT_VERSION%-%%I
|
||||
for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I
|
||||
|
||||
rem // Echo obtained GIT_VERSION for debug purposes if needed
|
||||
echo %GIT_VERSION%
|
||||
|
||||
rem // Don't modify the file if it already has the current version.
|
||||
if exist "%GIT_VERSION_FILE%" (
|
||||
@@ -69,7 +96,7 @@ if exist "%GIT_VERSION_FILE%" (
|
||||
echo // This is a generated file. > "%GIT_VERSION_FILE%"
|
||||
echo. >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_VERSION "%GIT_VERSION%" >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_BRANCH "%GIT_BRANCH%" >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_BRANCH ^"%GIT_BRANCH%^" >> "%GIT_VERSION_FILE%"
|
||||
echo. >> "%GIT_VERSION_FILE%"
|
||||
echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%"
|
||||
|
||||
+20
-1
@@ -7,6 +7,25 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
bool utils::has_ssse3()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x200;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_avx()
|
||||
{
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
bool utils::has_rtm()
|
||||
{
|
||||
// Check RTM and MPX extensions in order to filter out TSX on Haswell CPUs
|
||||
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4800) == 0x4800;
|
||||
return g_value;
|
||||
}
|
||||
|
||||
std::string utils::get_system_info()
|
||||
{
|
||||
std::string result;
|
||||
@@ -49,7 +68,7 @@ std::string utils::get_system_info()
|
||||
|
||||
if (has_avx())
|
||||
{
|
||||
result += " | AVX";
|
||||
result += " | AVX+";
|
||||
}
|
||||
|
||||
if (has_rtm())
|
||||
|
||||
+3
-13
@@ -16,21 +16,11 @@ namespace utils
|
||||
return {0u+regs[0], 0u+regs[1], 0u+regs[2], 0u+regs[3]};
|
||||
}
|
||||
|
||||
inline bool has_ssse3()
|
||||
{
|
||||
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x200;
|
||||
}
|
||||
bool has_ssse3();
|
||||
|
||||
inline bool has_avx()
|
||||
{
|
||||
return get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000;
|
||||
}
|
||||
bool has_avx();
|
||||
|
||||
inline bool has_rtm()
|
||||
{
|
||||
// Check RTM and MPX extensions in order to filter out TSX on Haswell CPUs
|
||||
return get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4800) == 0x4800;
|
||||
}
|
||||
bool has_rtm();
|
||||
|
||||
inline bool transaction_enter()
|
||||
{
|
||||
|
||||
+21
-1
@@ -48,7 +48,7 @@
|
||||
#define CONCATENATE_DETAIL(x, y) x ## y
|
||||
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
|
||||
|
||||
#define STRINGIZE_DETAIL(x) #x
|
||||
#define STRINGIZE_DETAIL(x) #x ""
|
||||
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
|
||||
|
||||
#define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
|
||||
@@ -534,6 +534,26 @@ inline u64 cntlz64(u64 arg, bool nonzero = false)
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 cnttz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward(&res, arg) || nonzero ? res : 32;
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctzll(arg) : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cnttz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward64(&res, arg) || nonzero ? res : 64;
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Helper function, used by ""_u16, ""_u32, ""_u64
|
||||
constexpr u8 to_u8(char c)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
|
||||
Submodule Vulkan/Vulkan-LoaderAndValidationLayers updated: 115665a3b5...85926a33d4
@@ -19,12 +19,12 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -39,20 +39,20 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release /m</NMakeBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /m</NMakeCleanCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /m
|
||||
</NMakeReBuildCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /m</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /m</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /m</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
|
||||
+1
-1
Submodule Vulkan/glslang updated: 0e07119ae2...cf571f7310
@@ -19,12 +19,12 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -39,24 +39,24 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release /m</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /m</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /m</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /m
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /m
|
||||
</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /m
|
||||
</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
|
||||
+72
-34
@@ -1,44 +1,82 @@
|
||||
version: '0.0.3-{build}'
|
||||
#---------------------------------#
|
||||
# general configuration #
|
||||
#---------------------------------#
|
||||
|
||||
os: Visual Studio 2015
|
||||
platform: x64
|
||||
clone_folder: C:\rpcs3
|
||||
clone_depth: 3
|
||||
test: off
|
||||
# version format
|
||||
version: '0.0.4-{build}'
|
||||
|
||||
configuration:
|
||||
# Release
|
||||
- ReleaseLLVM
|
||||
#---------------------------------#
|
||||
# environment configuration #
|
||||
#---------------------------------#
|
||||
|
||||
before_build:
|
||||
- ps: $env:Date="$(git show -s --date=short --format='%ad')"
|
||||
- git submodule update --init 3rdparty/ffmpeg 3rdparty/pugixml asmjit 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp 3rdparty/cereal 3rdparty/zlib 3rdparty/hidapi 3rdparty/Optional
|
||||
- 7z x zlib.7z -aos -oC:\rpcs3\ > null
|
||||
- 7z x vulkan.7z -aos -oC:\rpcs3\Vulkan > null
|
||||
- set BRANCH=%APPVEYOR_REPO_NAME%/%APPVEYOR_REPO_BRANCH%/#%APPVEYOR_PULL_REQUEST_NUMBER%
|
||||
- if "%APPVEYOR_PULL_REQUEST_NUMBER%"=="" (set BRANCH=%APPVEYOR_REPO_NAME%/%APPVEYOR_REPO_BRANCH%)
|
||||
- set CXXFLAGS=-DBRANCH=%BRANCH%
|
||||
- if %configuration%==Release (cmake -G "Visual Studio 14 Win64" -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
|
||||
else (7z x llvmlibs.7z -aos -oC:\rpcs3 > null && cmake -G "Visual Studio 14 Win64" -DLLVM_DIR=C:/rpcs3/llvm_build/lib/cmake/llvm -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
|
||||
# Build worker image (VM template)
|
||||
image: Visual Studio 2017
|
||||
|
||||
build_script:
|
||||
- cmake --build . --config Release -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
# clone directory
|
||||
clone_folder: c:\projects\rpcs3
|
||||
|
||||
install:
|
||||
- if not exist llvmlibs.7z appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRY2k3Q2Yya05lcm8" -FileName llvmlibs.7z
|
||||
- if not exist vulkan.7z appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRaHYxV3VOS2d0a0U" -FileName vulkan.7z
|
||||
- if not exist zlib.7z appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B-HVE3xvheVFX05lRFdnZlh5aUU" -FileName zlib.7z
|
||||
- set QTDIR=C:\Qt\5.8\msvc2015_64
|
||||
- set OPENALDIR=C:\rpcs3\3rdparty\OpenAL
|
||||
- set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%QTDIR%;%PATH%
|
||||
- set COMMIT_SHA=%APPVEYOR_REPO_COMMIT:~0,8%
|
||||
# set clone depth
|
||||
clone_depth: 3 # clone entire repository history if not defined
|
||||
|
||||
artifacts:
|
||||
- path: bin
|
||||
name: 'rpcs3-v0.0.3-$(Date)-$(COMMIT_SHA)_win64'
|
||||
type: zip
|
||||
# environment variables
|
||||
environment:
|
||||
QTDIR: C:\Qt\5.9\msvc2017_64
|
||||
|
||||
# build cache to preserve files/folders between builds
|
||||
cache:
|
||||
- llvmlibs.7z -> appveyor.yml
|
||||
- vulkan.7z -> appveyor.yml
|
||||
- zlib.7z -> appveyor.yml
|
||||
|
||||
# scripts that run after cloning repository
|
||||
install:
|
||||
- git submodule update --init 3rdparty/cereal 3rdparty/ffmpeg 3rdparty/GSL 3rdparty/hidapi 3rdparty/libpng 3rdparty/Optional 3rdparty/pugixml 3rdparty/zlib asmjit Utilities/yaml-cpp Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
|
||||
#---------------------------------#
|
||||
# build configuration #
|
||||
#---------------------------------#
|
||||
|
||||
# build platform, i.e. x86, x64, Any CPU. This setting is optional.
|
||||
platform: x64
|
||||
|
||||
# build Configuration, i.e. Debug, Release, etc.
|
||||
configuration: Release - LLVM
|
||||
|
||||
build:
|
||||
parallel: true # enable MSBuild parallel builds
|
||||
project: rpcs3.sln # path to Visual Studio solution or project
|
||||
# MSBuild verbosity level
|
||||
verbosity: normal
|
||||
|
||||
# scripts to run before build
|
||||
before_build:
|
||||
- set BRANCH=%APPVEYOR_REPO_NAME%/%APPVEYOR_REPO_BRANCH%/#%APPVEYOR_PULL_REQUEST_NUMBER%
|
||||
- if "%APPVEYOR_PULL_REQUEST_NUMBER%"=="" (set BRANCH=%APPVEYOR_REPO_NAME%/%APPVEYOR_REPO_BRANCH%)
|
||||
- set PATH=%PATH%;%QTDIR%
|
||||
- if not exist llvmlibs.7z appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRY2k3Q2Yya05lcm8" -FileName llvmlibs.7z
|
||||
- 7z x llvmlibs.7z -aos -o%APPVEYOR_BUILD_FOLDER% > null
|
||||
- if not exist vulkan.7z appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRaHYxV3VOS2d0a0U" -FileName vulkan.7z
|
||||
- 7z x vulkan.7z -aos -o"%APPVEYOR_BUILD_FOLDER%\lib\%CONFIGURATION%-%PLATFORM%" > null
|
||||
|
||||
# scripts to run *after* solution is built and *before* automatic packaging occurs (web apps, NuGet packages, Azure Cloud Services)
|
||||
before_package:
|
||||
- rm %APPVEYOR_BUILD_FOLDER%\bin\rpcs3.exp
|
||||
- rm %APPVEYOR_BUILD_FOLDER%\bin\rpcs3.lib
|
||||
- rm %APPVEYOR_BUILD_FOLDER%\bin\rpcs3.pdb
|
||||
- set COMMIT_DATE=%APPVEYOR_REPO_COMMIT_TIMESTAMP:~0,10%
|
||||
- set COMMIT_SHA=%APPVEYOR_REPO_COMMIT:~0,8%
|
||||
|
||||
#---------------------------------#
|
||||
# tests configuration #
|
||||
#---------------------------------#
|
||||
|
||||
# to disable automatic tests
|
||||
test: off
|
||||
|
||||
#---------------------------------#
|
||||
# artifacts configuration #
|
||||
#---------------------------------#
|
||||
|
||||
# pushing entire folder as a zip archive
|
||||
artifacts:
|
||||
- path: bin
|
||||
name: 'rpcs3-v0.0.4-$(COMMIT_DATE)-$(COMMIT_SHA)_win64'
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
|
||||
@@ -40,7 +40,7 @@ QWidget {
|
||||
|
||||
/* Mouse tooltips */
|
||||
QToolTip {
|
||||
border: 1px solid #323232;
|
||||
border: 0.05em solid #323232;
|
||||
background-color: #323232;
|
||||
color: #ecf0f1;
|
||||
}
|
||||
@@ -49,29 +49,29 @@ QToolTip {
|
||||
QHeaderView::section {
|
||||
background-color: #323232;
|
||||
color: #ecf0f1;
|
||||
padding-left: 4px;
|
||||
border: 1px solid #323232;
|
||||
padding-left: 0.2em;
|
||||
border: 0.05em solid #323232;
|
||||
}
|
||||
|
||||
/* Settings Dialog: Tabs */
|
||||
QTabBar::tab {
|
||||
color: #ecf0f1;
|
||||
border: 1px solid #444;
|
||||
border: 0.05em solid #444;
|
||||
border-bottom-style: none;
|
||||
background-color: #333333;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
margin-right: -2px;
|
||||
padding-left: 0.9em;
|
||||
padding-right: 0.9em;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
margin-right: -0.2em;
|
||||
}
|
||||
QTabBar::tab:last {
|
||||
margin-right: 0px;
|
||||
margin-right: 0em;
|
||||
}
|
||||
QTabBar::tab:!selected {
|
||||
color: #b1b1b1;
|
||||
border-bottom-style: solid;
|
||||
margin-top: 3px;
|
||||
margin-top: 0.15em;
|
||||
background-color: #292929;
|
||||
}
|
||||
QTabBar::tab:hover {
|
||||
@@ -81,11 +81,11 @@ QTabBar::tab:hover {
|
||||
|
||||
/* Checkboxes */
|
||||
QCheckBox::indicator {
|
||||
border-radius: 2px;
|
||||
border: 1px solid #ecf0f1;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-top: 1px;
|
||||
border-radius: 0.1em;
|
||||
border: 0.05em solid #ecf0f1;
|
||||
margin-top: 0.05em;
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
}
|
||||
QCheckBox::indicator:checked {
|
||||
background-color: #2ecc71; /* Green */
|
||||
@@ -99,11 +99,11 @@ QCheckBox::indicator::disabled {
|
||||
|
||||
/* Radio Buttons */
|
||||
QRadioButton::indicator {
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ecf0f1;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-top: 1px;
|
||||
border-radius: 0.4em;
|
||||
border: 0.05em solid;
|
||||
border-color: white;
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
}
|
||||
QRadioButton::indicator:checked {
|
||||
background-color: #2ecc71; /* Green */
|
||||
@@ -119,10 +119,10 @@ QRadioButton::indicator::disabled {
|
||||
QComboBox {
|
||||
background-color: #404040;
|
||||
color: #fff;
|
||||
border: 1px solid #1e1e1e;
|
||||
border-radius: 3px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 4px;
|
||||
border: 0.05em solid #1e1e1e;
|
||||
border-radius: 0.15em;
|
||||
padding-bottom: 0.2em;
|
||||
padding-left: 0.4em;
|
||||
}
|
||||
QComboBox::disabled {
|
||||
background-color: #828790;
|
||||
@@ -131,15 +131,15 @@ QComboBox::disabled {
|
||||
|
||||
/* Group Boxes (Settings Dialog) */
|
||||
QGroupBox {
|
||||
margin-top: 17px;
|
||||
font-size: 13px;
|
||||
border: 1px solid #bdc3c7;
|
||||
border-radius: 4px;
|
||||
margin-top: 1em;
|
||||
font-size: 8pt;
|
||||
border: 0.05em solid #bdc3c7;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top center;
|
||||
padding: 5px 10px 5px 10px;
|
||||
subcontrol-position: top;
|
||||
padding: 0.3em 0.5em 0.3em 0.5em;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@@ -154,19 +154,19 @@ QPushButton::disabled {
|
||||
|
||||
/* Log and Debugger borders */
|
||||
QTextEdit {
|
||||
border: 1px solid #828790;
|
||||
border: 0.05em solid #828790;
|
||||
}
|
||||
|
||||
/* For dock buttons to be visible */
|
||||
QDockWidget::close-button, QDockWidget::float-button {
|
||||
background-color: #bdc3c7;
|
||||
border: 1px solid #000;
|
||||
border-radius: 4px;
|
||||
border: 0.05em solid #000;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
/* Disable ugly borders */
|
||||
QTabWidget::pane {
|
||||
border: 0px solid #828790;
|
||||
border: 0em solid #828790;
|
||||
}
|
||||
|
||||
/* Top menu bar */
|
||||
@@ -174,14 +174,14 @@ QMenuBar::item:selected {
|
||||
background: #444444;
|
||||
}
|
||||
QMenu::item {
|
||||
padding-left: 20px;
|
||||
padding-right: 15px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: 1.5em;
|
||||
padding-right: 0.75em;
|
||||
padding-top: 0.25em;
|
||||
padding-bottom: 0.25em;
|
||||
}
|
||||
QMenu::item:selected {
|
||||
background: #444444;
|
||||
border: 1px solid #bdc3c7;
|
||||
border: 0.05em solid #bdc3c7;
|
||||
}
|
||||
QMenu::item:disabled {
|
||||
background-color: #444444;
|
||||
@@ -223,7 +223,7 @@ QLabel#color_button {
|
||||
|
||||
/* Seachbar on main toolbar */
|
||||
QLineEdit#mw_searchbar {
|
||||
margin-left:14px;
|
||||
margin-left: 0.7em;
|
||||
color: #ecf0f1;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
Submodule llvm updated: 4423e35117...f30c918816
@@ -18,12 +18,12 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
@@ -39,30 +39,30 @@
|
||||
<NMakePreprocessorDefinitions>
|
||||
</NMakePreprocessorDefinitions>
|
||||
<NMakeBuildCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /m
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /m
|
||||
</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /m
|
||||
</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakePreprocessorDefinitions />
|
||||
<NMakeBuildCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release /m
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /m
|
||||
</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /m
|
||||
</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -32,7 +32,7 @@ Controller.prototype.ComponentSelectionPageCallback = function() {
|
||||
var widget = gui.currentPageWidget();
|
||||
|
||||
widget.deselectAll();
|
||||
widget.selectComponent("qt.591.gcc_64");
|
||||
widget.selectComponent("qt.592.gcc_64");
|
||||
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<UseOfMfc>false</UseOfMfc>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2006
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asmjit", "asmjitsrc\asmjit.vcxproj", "{AC40FF01-426E-4838-A317-66354CEFAE88}"
|
||||
EndProject
|
||||
@@ -243,4 +243,7 @@ Global
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122} = {DDF904CA-2771-441A-8629-5DF2EB922A79}
|
||||
{A107C21C-418A-4697-BB10-20C3AA60E2E4} = {FA1E6C16-CA63-45F8-8D52-E21DF396BE36}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {06CC7920-E085-4B81-9582-8DE8AAD42510}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake_modules")
|
||||
set(RES_FILES "")
|
||||
@@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD 14)
|
||||
include(CheckCCompilerFlag)
|
||||
|
||||
# Qt section
|
||||
find_package(Qt5 5.7 COMPONENTS Widgets)
|
||||
find_package(Qt5 5.9 COMPONENTS Widgets)
|
||||
if (WIN32)
|
||||
find_package(Qt5WinExtras REQUIRED)
|
||||
set(RPCS3_QT_LIBS Qt5::Widgets Qt5::WinExtras)
|
||||
@@ -24,13 +24,13 @@ endif()
|
||||
|
||||
# Let's make sure we have Qt before we continue
|
||||
if (NOT Qt5Widgets_FOUND)
|
||||
if (Qt5Widgets_VERSION VERSION_LESS 5.7.0)
|
||||
message("Minimum supported Qt5 version is 5.7! You have version ${Qt5Widgets_VERSION} installed, please upgrade!")
|
||||
if (Qt5Widgets_VERSION VERSION_LESS 5.9.0)
|
||||
message("Minimum supported Qt5 version is 5.9! You have version ${Qt5Widgets_VERSION} installed, please upgrade!")
|
||||
if ("${CMAKE_SYSTEM}" MATCHES "Linux")
|
||||
message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt.
|
||||
If you're on Ubuntu or Linux Mint, there are PPAs you can use to install an up-to-date qt5 version.
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt591-xenial
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt591-trusty
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt592-xenial
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt592-trusty
|
||||
just make sure to run
|
||||
source /opt/qt59/bin/qt59-env.sh
|
||||
before re-running cmake")
|
||||
@@ -44,7 +44,7 @@ before re-running cmake")
|
||||
|
||||
message("CMake was unable to find Qt5!")
|
||||
if (WIN32)
|
||||
message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\5.8\\msvc2017_64\\)")
|
||||
message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\5.9\\msvc2017_64\\)")
|
||||
elseif("${CMAKE_SYSTEM}" MATCHES "Linux")
|
||||
message(FATAL_ERROR "Make sure to install your distro's qt5 package!")
|
||||
else()
|
||||
@@ -342,7 +342,6 @@ if (NOT WIN32 AND VULKAN_FOUND)
|
||||
"${RPCS3_SRC_DIR}/../Vulkan/glslang/glslang/MachineIndependent/*.cpp"
|
||||
"${RPCS3_SRC_DIR}/../Vulkan/glslang/glslang/OSDependent/Unix/*.cpp"
|
||||
"${RPCS3_SRC_DIR}/../Vulkan/glslang/OGLCompilersDLL/*.cpp"
|
||||
"${RPCS3_SRC_DIR}/../Vulkan/glslang/hlsl/*.cpp"
|
||||
"${RPCS3_SRC_DIR}/../Vulkan/glslang/SPIRV/*.cpp"
|
||||
)
|
||||
|
||||
|
||||
+65
-25
@@ -4,27 +4,19 @@
|
||||
#include "sha1.h"
|
||||
#include "key_vault.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/VFS.h"
|
||||
#include "unpkg.h"
|
||||
|
||||
bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>& sync, const std::string& pkg_filepath)
|
||||
bool pkg_install(const std::string& path, atomic_t<double>& sync)
|
||||
{
|
||||
const std::size_t BUF_SIZE = 8192 * 1024; // 8 MB
|
||||
|
||||
std::vector<fs::file> filelist;
|
||||
u32 cur_file=0;
|
||||
|
||||
fs::file original_file(pkg_filepath);
|
||||
original_file.seek(pkg_f.pos());
|
||||
|
||||
filelist.push_back(std::move(original_file));
|
||||
|
||||
// Save current file offset (probably zero)
|
||||
const u64 start_offset = pkg_f.pos();
|
||||
u64 cur_offset = start_offset;
|
||||
u64 cur_file_offset = start_offset;
|
||||
|
||||
// Get basic PKG information
|
||||
PKGHeader header;
|
||||
filelist.emplace_back(fs::file{path});
|
||||
u32 cur_file = 0;
|
||||
u64 cur_offset = 0;
|
||||
u64 cur_file_offset = 0;
|
||||
|
||||
auto archive_seek = [&](const s64 new_offset, const fs::seek_mode damode = fs::seek_set)
|
||||
{
|
||||
@@ -73,6 +65,8 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
return num_read;
|
||||
};
|
||||
|
||||
// Get basic PKG information
|
||||
PKGHeader header;
|
||||
|
||||
if (archive_read(&header, sizeof(header)) != sizeof(header))
|
||||
{
|
||||
@@ -108,18 +102,18 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
}
|
||||
}
|
||||
|
||||
if (header.pkg_size > pkg_f.size())
|
||||
if (header.pkg_size > filelist[0].size())
|
||||
{
|
||||
//Check if multi-files pkg
|
||||
if (pkg_filepath.length() < 7 || pkg_filepath.substr(pkg_filepath.length() - 7).compare("_00.pkg") != 0)
|
||||
// Check if multi-files pkg
|
||||
if (path.size() < 7 || path.compare(path.size() - 7, 7, "_00.pkg", 7) != 0)
|
||||
{
|
||||
LOG_ERROR(LOADER, "PKG file size mismatch (pkg_size=0x%llx)", header.pkg_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name_wo_number = pkg_filepath.substr(0, pkg_filepath.length() - 7);
|
||||
u64 cursize = pkg_f.size();
|
||||
while (cursize != header.pkg_size)
|
||||
std::string name_wo_number = path.substr(0, path.size() - 7);
|
||||
u64 cursize = filelist[0].size();
|
||||
while (cursize < header.pkg_size)
|
||||
{
|
||||
std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, filelist.size());
|
||||
|
||||
@@ -131,7 +125,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
}
|
||||
|
||||
cursize += archive_file.size();
|
||||
filelist.push_back(std::move(archive_file));
|
||||
filelist.emplace_back(std::move(archive_file));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +137,12 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
|
||||
be_t<u32> drm_type{0};
|
||||
be_t<u32> content_type{0};
|
||||
std::string install_id;
|
||||
|
||||
// Read title ID and use it as an installation directory
|
||||
install_id.resize(9);
|
||||
archive_seek(55);
|
||||
archive_read(&install_id.front(), install_id.size());
|
||||
|
||||
archive_seek(header.pkg_info_off);
|
||||
|
||||
@@ -179,18 +179,50 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
|
||||
break;
|
||||
}
|
||||
case 0xA:
|
||||
{
|
||||
if (packet.size > 8)
|
||||
{
|
||||
// Read an actual installation directory (DLC)
|
||||
install_id.resize(packet.size);
|
||||
archive_read(&install_id.front(), packet.size);
|
||||
install_id = install_id.c_str() + 8;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
archive_seek(packet.size, fs::seek_cur);
|
||||
}
|
||||
|
||||
// If false, an existing directory is being overwritten: cannot cancel the operation
|
||||
bool was_null = true;
|
||||
|
||||
// Get full path and create the directory
|
||||
const std::string dir = Emu.GetHddDir() + "game/" + install_id + '/';
|
||||
|
||||
if (!fs::create_dir(dir))
|
||||
{
|
||||
if (fs::g_tls_error == fs::error::exist)
|
||||
{
|
||||
was_null = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(LOADER, "PKG: Could not create the installation directory %s", dir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate buffer with BUF_SIZE size or more if required
|
||||
const std::unique_ptr<u128[]> buf(new u128[std::max<u64>(BUF_SIZE, sizeof(PKGEntry) * header.file_count) / sizeof(u128)]);
|
||||
|
||||
// Define decryption subfunction (`psp` arg selects the key for specific block)
|
||||
auto decrypt = [&](u64 offset, u64 size, const uchar* key) -> u64
|
||||
{
|
||||
archive_seek(start_offset + header.data_offset + offset);
|
||||
archive_seek(header.data_offset + offset);
|
||||
|
||||
// Read the data and set available size
|
||||
const u64 read = archive_read(buf.get(), size);
|
||||
@@ -334,8 +366,16 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
|
||||
if (sync.fetch_add((block_size + 0.0) / header.data_size) < 0.)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Package installation cancelled: %s", dir);
|
||||
return false;
|
||||
if (was_null)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Package installation cancelled: %s", dir);
|
||||
out.close();
|
||||
fs::remove_all(dir, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cannot cancel the installation
|
||||
sync += 1.;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,4 +57,4 @@ struct PKGEntry
|
||||
be_t<u32> pad; // Padding (zeros)
|
||||
};
|
||||
|
||||
bool pkg_install(const class fs::file& pkg_f, const std::string& dir, atomic_t<double>&, const std::string& pkg_filepath);
|
||||
bool pkg_install(const std::string& path, atomic_t<double>&);
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
|
||||
@@ -50,6 +50,8 @@ OpenALThread::OpenALThread()
|
||||
{
|
||||
m_format = g_cfg.audio.convert_to_u16 ? AL_FORMAT_71CHN16 : AL_FORMAT_71CHN32;
|
||||
}
|
||||
|
||||
m_buffers = std::make_unique<ALuint[]>(g_cfg.audio.frames);
|
||||
}
|
||||
|
||||
OpenALThread::~OpenALThread()
|
||||
@@ -83,7 +85,7 @@ void OpenALThread::Close()
|
||||
if (alIsSource(m_source))
|
||||
alDeleteSources(1, &m_source);
|
||||
|
||||
alDeleteBuffers(g_al_buffers_count, m_buffers);
|
||||
alDeleteBuffers(g_cfg.audio.frames, m_buffers.get());
|
||||
checkForAlError("alDeleteBuffers");
|
||||
}
|
||||
|
||||
@@ -98,7 +100,7 @@ void OpenALThread::Open(const void* src, int size)
|
||||
alGenSources(1, &m_source);
|
||||
checkForAlError("alGenSources");
|
||||
|
||||
alGenBuffers(g_al_buffers_count, m_buffers);
|
||||
alGenBuffers(g_cfg.audio.frames, m_buffers.get());
|
||||
checkForAlError("alGenBuffers");
|
||||
|
||||
alSourcei(m_source, AL_LOOPING, AL_FALSE);
|
||||
@@ -106,13 +108,13 @@ void OpenALThread::Open(const void* src, int size)
|
||||
|
||||
m_buffer_size = size;
|
||||
|
||||
for (uint i = 0; i<g_al_buffers_count; ++i)
|
||||
for (int i = 0; i < g_cfg.audio.frames; ++i)
|
||||
{
|
||||
alBufferData(m_buffers[i], m_format, src, m_buffer_size, 48000);
|
||||
checkForAlError("alBufferData");
|
||||
}
|
||||
|
||||
alSourceQueueBuffers(m_source, g_al_buffers_count, m_buffers);
|
||||
alSourceQueueBuffers(m_source, g_cfg.audio.frames, m_buffers.get());
|
||||
checkForAlError("alSourceQueueBuffers");
|
||||
Play();
|
||||
}
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
|
||||
#include "Emu/Audio/AudioThread.h"
|
||||
#include "3rdparty/OpenAL/include/alext.h"
|
||||
#include <memory>
|
||||
|
||||
class OpenALThread : public AudioThread
|
||||
{
|
||||
private:
|
||||
static const uint g_al_buffers_count = 24;
|
||||
|
||||
ALint m_format;
|
||||
ALuint m_source;
|
||||
ALuint m_buffers[g_al_buffers_count];
|
||||
std::unique_ptr<ALuint[]> m_buffers;
|
||||
ALsizei m_buffer_size;
|
||||
|
||||
public:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "Utilities/GSL.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "ALSAThread.h"
|
||||
@@ -9,10 +8,12 @@
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
static thread_local snd_pcm_t* s_tls_handle{nullptr};
|
||||
static thread_local snd_pcm_hw_params_t* s_tls_hw_params{nullptr};
|
||||
static thread_local snd_pcm_sw_params_t* s_tls_sw_params{nullptr};
|
||||
|
||||
static void error(int err, const char* reason)
|
||||
{
|
||||
fprintf(stderr, "ALSA: %s failed: %s\n", reason, snd_strerror(err));
|
||||
LOG_ERROR(GENERAL, "ALSA: %s failed: %s\n", reason, snd_strerror(err));
|
||||
}
|
||||
|
||||
static bool check(int err, const char* reason)
|
||||
@@ -28,55 +29,86 @@ static bool check(int err, const char* reason)
|
||||
|
||||
ALSAThread::ALSAThread()
|
||||
{
|
||||
snd_pcm_hw_params_t* hw_params{nullptr};
|
||||
|
||||
auto at_ret = gsl::finally([&]()
|
||||
{
|
||||
if (hw_params)
|
||||
{
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
}
|
||||
});
|
||||
|
||||
if (!check(snd_pcm_open(&s_tls_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK), "snd_pcm_open"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_malloc(&hw_params), "snd_pcm_hw_params_malloc"))
|
||||
if (!check(snd_pcm_hw_params_malloc(&s_tls_hw_params), "snd_pcm_hw_params_malloc"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_any(s_tls_handle, hw_params), "snd_pcm_hw_params_any"))
|
||||
if (!check(snd_pcm_hw_params_any(s_tls_handle, s_tls_hw_params), "snd_pcm_hw_params_any"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_access(s_tls_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "snd_pcm_hw_params_set_access"))
|
||||
if (!check(snd_pcm_hw_params_set_access(s_tls_handle, s_tls_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "snd_pcm_hw_params_set_access"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_format(s_tls_handle, hw_params, g_cfg.audio.convert_to_u16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_FLOAT_LE), "snd_pcm_hw_params_set_format"))
|
||||
if (!check(snd_pcm_hw_params_set_format(s_tls_handle, s_tls_hw_params, g_cfg.audio.convert_to_u16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_FLOAT_LE), "snd_pcm_hw_params_set_format"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_rate(s_tls_handle, hw_params, 48000, 0), "snd_pcm_hw_params_set_rate_near"))
|
||||
uint rate = 48000;
|
||||
if (!check(snd_pcm_hw_params_set_rate_near(s_tls_handle, s_tls_hw_params, &rate, nullptr), "snd_pcm_hw_params_set_rate_near"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_channels(s_tls_handle, hw_params, g_cfg.audio.downmix_to_2ch ? 2 : 8), "snd_pcm_hw_params_set_channels"))
|
||||
if (!check(snd_pcm_hw_params_set_channels(s_tls_handle, s_tls_hw_params, g_cfg.audio.downmix_to_2ch ? 2 : 8), "snd_pcm_hw_params_set_channels"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_buffer_size(s_tls_handle, hw_params, 8 * 256), "snd_pcm_hw_params_set_buffer_size"))
|
||||
//uint period = 5333;
|
||||
//if (!check(snd_pcm_hw_params_set_period_time_near(s_tls_handle, s_tls_hw_params, &period, nullptr), "snd_pcm_hw_params_set_period_time_near"))
|
||||
// return;
|
||||
|
||||
//if (!check(snd_pcm_hw_params_set_periods(s_tls_handle, s_tls_hw_params, 4, 0), "snd_pcm_hw_params_set_periods"))
|
||||
// return;
|
||||
|
||||
snd_pcm_uframes_t bufsize_frames = g_cfg.audio.frames * 256;
|
||||
snd_pcm_uframes_t period_frames = 256;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_buffer_size_near(s_tls_handle, s_tls_hw_params, &bufsize_frames), "snd_pcm_hw_params_set_buffer_size_near"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_set_period_size(s_tls_handle, hw_params, 256, 0), "snd_pcm_hw_params_set_period_size"))
|
||||
if (!check(snd_pcm_hw_params_set_period_size_near(s_tls_handle, s_tls_hw_params, &period_frames, 0), "snd_pcm_hw_params_set_period_size"))
|
||||
return;
|
||||
|
||||
//if (!check(snd_pcm_hw_params_set_periods(s_tls_handle, hw_params, 2, 0), "snd_pcm_hw_params_set_periods"))
|
||||
// return;
|
||||
if (!check(snd_pcm_hw_params(s_tls_handle, s_tls_hw_params), "snd_pcm_hw_params"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params(s_tls_handle, hw_params), "snd_pcm_hw_params"))
|
||||
if (!check(snd_pcm_hw_params_get_buffer_size(s_tls_hw_params, &bufsize_frames), "snd_pcm_hw_params_get_buffer_size"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_hw_params_get_period_size(s_tls_hw_params, &period_frames, nullptr), "snd_pcm_hw_params_get_period_size"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_sw_params_malloc(&s_tls_sw_params), "snd_pcm_sw_params_malloc"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_sw_params_current(s_tls_handle, s_tls_sw_params), "snd_pcm_sw_params_current"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_sw_params_set_start_threshold(s_tls_handle, s_tls_sw_params, period_frames), "snd_pcm_sw_params_set_start_threshold"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_sw_params_set_stop_threshold(s_tls_handle, s_tls_sw_params, bufsize_frames), "snd_pcm_sw_params_set_stop_threshold"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_sw_params(s_tls_handle, s_tls_sw_params), "snd_pcm_sw_params"))
|
||||
return;
|
||||
|
||||
if (!check(snd_pcm_prepare(s_tls_handle), "snd_pcm_prepare"))
|
||||
return;
|
||||
|
||||
LOG_NOTICE(GENERAL, "ALSA: bufsize_frames=%u, period_frames=%u", bufsize_frames, period_frames);
|
||||
}
|
||||
|
||||
ALSAThread::~ALSAThread()
|
||||
{
|
||||
if (s_tls_sw_params)
|
||||
{
|
||||
snd_pcm_sw_params_free(s_tls_sw_params);
|
||||
}
|
||||
|
||||
if (s_tls_hw_params)
|
||||
{
|
||||
snd_pcm_hw_params_free(s_tls_hw_params);
|
||||
}
|
||||
|
||||
if (s_tls_handle)
|
||||
{
|
||||
snd_pcm_close(s_tls_handle);
|
||||
@@ -105,29 +137,30 @@ void ALSAThread::AddData(const void* src, int size)
|
||||
size /= g_cfg.audio.convert_to_u16 ? 2 : 4;
|
||||
size /= g_cfg.audio.downmix_to_2ch ? 2 : 8;
|
||||
|
||||
int res;
|
||||
int res = snd_pcm_writei(s_tls_handle, src, size);
|
||||
|
||||
while (true)
|
||||
if (res == -EAGAIN)
|
||||
{
|
||||
res = snd_pcm_writei(s_tls_handle, src, size);
|
||||
LOG_WARNING(GENERAL, "ALSA: EAGAIN");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res == -EAGAIN)
|
||||
if (res < 0)
|
||||
{
|
||||
res = snd_pcm_recover(s_tls_handle, res, 0);
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (res == -EPIPE)
|
||||
{
|
||||
snd_pcm_prepare(s_tls_handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
LOG_WARNING(GENERAL, "ALSA: failed to recover (%d)", res);
|
||||
return;
|
||||
}
|
||||
|
||||
res = snd_pcm_writei(s_tls_handle, src, size);
|
||||
}
|
||||
|
||||
if (res != size)
|
||||
{
|
||||
error(res, "snd_pcm_writei");
|
||||
LOG_WARNING(GENERAL, "ALSA: error (%d)", res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ extern "C"
|
||||
{
|
||||
#include "libavcodec/avcodec.h"
|
||||
#include "libavformat/avformat.h"
|
||||
#include "libswresample/swresample.h"
|
||||
}
|
||||
|
||||
#include "cellPamf.h"
|
||||
|
||||
@@ -57,6 +57,8 @@ void audio_config::on_init(const std::shared_ptr<void>& _this)
|
||||
|
||||
void audio_config::on_task()
|
||||
{
|
||||
thread_ctrl::set_native_priority(1);
|
||||
|
||||
AudioDumper m_dump(g_cfg.audio.dump_to_file ? 2 : 0); // Init AudioDumper for 2 channels if enabled
|
||||
|
||||
float buf2ch[2 * BUFFER_SIZE]{}; // intermediate buffer for 2 channels
|
||||
@@ -337,8 +339,8 @@ void audio_config::on_task()
|
||||
case 8: m_dump.WriteData(&buf8ch, sizeof(buf8ch)); break; // write file data (8 ch)
|
||||
}
|
||||
|
||||
cellAudio.trace("Audio perf: start=%d (access=%d, AddData=%d, events=%d, dump=%d)",
|
||||
time_pos, stamp1 - stamp0, stamp2 - stamp1, stamp3 - stamp2, get_system_time() - stamp3);
|
||||
cellAudio.trace("Audio perf: (access=%d, AddData=%d, events=%d, dump=%d)",
|
||||
stamp1 - stamp0, stamp2 - stamp1, stamp3 - stamp2, get_system_time() - stamp3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -554,7 +554,7 @@ s32 cellFontExtend(u32 a1, u32 a2, u32 a3)
|
||||
//Something happens
|
||||
}
|
||||
//Something happens?
|
||||
return CELL_OK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s32 cellFontRenderCharGlyphImageVertical()
|
||||
|
||||
@@ -94,7 +94,7 @@ struct content_permission final
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!temp.empty())
|
||||
if (temp.size() > 1)
|
||||
{
|
||||
fs::remove_all(temp);
|
||||
}
|
||||
@@ -255,7 +255,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
|
||||
{
|
||||
*type = CELL_GAME_GAMETYPE_DISC;
|
||||
*attributes = 0; // TODO
|
||||
if (dirName) strcpy_trunc(*dirName, ""); // ???
|
||||
// TODO: dirName might be a read only string when BootCheck is called on a disc game. (e.g. Ben 10 Ultimate Alien: Cosmic Destruction)
|
||||
|
||||
if (!fxm::make<content_permission>("", std::move(sfo)))
|
||||
{
|
||||
@@ -659,6 +659,11 @@ error_code cellGameGetParamString(s32 id, vm::ptr<char> buf, u32 bufsize)
|
||||
{
|
||||
cellGame.warning("cellGameGetParamString(id=%d, buf=*0x%x, bufsize=%d)", id, buf, bufsize);
|
||||
|
||||
if (!buf || bufsize == 0)
|
||||
{
|
||||
return CELL_GAME_ERROR_PARAM;
|
||||
}
|
||||
|
||||
const auto prm = fxm::get<content_permission>();
|
||||
|
||||
if (!prm)
|
||||
@@ -685,6 +690,11 @@ error_code cellGameSetParamString(s32 id, vm::cptr<char> buf)
|
||||
{
|
||||
cellGame.warning("cellGameSetParamString(id=%d, buf=*0x%x)", id, buf);
|
||||
|
||||
if (!buf)
|
||||
{
|
||||
return CELL_GAME_ERROR_PARAM;
|
||||
}
|
||||
|
||||
const auto prm = fxm::get<content_permission>();
|
||||
|
||||
if (!prm)
|
||||
|
||||
@@ -74,7 +74,7 @@ s32 cellMouseGetInfo(vm::ptr<CellMouseInfo> info)
|
||||
for (u32 i=0; i<CELL_MAX_MICE; i++) info->vendor_id[i] = current_info.vendor_id[i];
|
||||
for (u32 i=0; i<CELL_MAX_MICE; i++) info->product_id[i] = current_info.product_id[i];
|
||||
for (u32 i=0; i<CELL_MAX_MICE; i++) info->status[i] = current_info.status[i];
|
||||
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ s32 cellMouseInfoTabletMode(u32 port_no, vm::ptr<CellMouseInfoTablet> info)
|
||||
|
||||
info->is_supported = 0; // Unimplemented: (0=Tablet mode is not supported)
|
||||
info->mode = 1; // Unimplemented: (1=Mouse mode)
|
||||
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ s32 cellMouseGetData(u32 port_no, vm::ptr<CellMouseData> data)
|
||||
{
|
||||
return CELL_MOUSE_ERROR_NO_DEVICE;
|
||||
}
|
||||
|
||||
|
||||
MouseData& current_data = handler->GetData(port_no);
|
||||
data->update = current_data.update;
|
||||
data->buttons = current_data.buttons;
|
||||
@@ -134,22 +134,27 @@ s32 cellMouseGetData(u32 port_no, vm::ptr<CellMouseData> data)
|
||||
|
||||
s32 cellMouseGetDataList(u32 port_no, vm::ptr<CellMouseDataList> data)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(sys_io);
|
||||
|
||||
return CELL_OK;
|
||||
sys_io.todo("cellMouseGetDataList(port_no=%d, data=0x%x", port_no, data);
|
||||
if (g_cfg.io.mouse == mouse_handler::null)
|
||||
return CELL_MOUSE_ERROR_NO_DEVICE;
|
||||
else
|
||||
{
|
||||
data->list_num = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
s32 cellMouseSetTabletMode(u32 port_no, u32 mode)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(sys_io);
|
||||
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMouseGetTabletDataList(u32 port_no, u32 data_addr)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(sys_io);
|
||||
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialog
|
||||
return CELL_MSGDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
const MsgDialogType _type = { type };
|
||||
const MsgDialogType _type = {type ^ CELL_MSGDIALOG_TYPE_BG_INVISIBLE};
|
||||
|
||||
switch (_type.button_type.unshifted())
|
||||
{
|
||||
|
||||
@@ -37,7 +37,8 @@ enum : u32
|
||||
SAVEDATA_OP_FIXED_SAVE = 6,
|
||||
SAVEDATA_OP_FIXED_LOAD = 7,
|
||||
|
||||
SAVEDATA_OP_FIXED_DELETE = 14,
|
||||
SAVEDATA_OP_LIST_DELETE = 13,
|
||||
SAVEDATA_OP_FIXED_DELETE = 14,
|
||||
};
|
||||
|
||||
namespace
|
||||
@@ -52,6 +53,7 @@ namespace
|
||||
CellSaveDataStatSet statSet;
|
||||
CellSaveDataFileGet fileGet;
|
||||
CellSaveDataFileSet fileSet;
|
||||
CellSaveDataDoneGet doneGet;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -81,11 +83,12 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
vm::ptr<CellSaveDataStatSet> statSet = g_savedata_context.ptr(&savedata_context::statSet);
|
||||
vm::ptr<CellSaveDataFileGet> fileGet = g_savedata_context.ptr(&savedata_context::fileGet);
|
||||
vm::ptr<CellSaveDataFileSet> fileSet = g_savedata_context.ptr(&savedata_context::fileSet);
|
||||
vm::ptr<CellSaveDataDoneGet> doneGet = g_savedata_context.ptr(&savedata_context::doneGet);
|
||||
|
||||
//TODO: get current user ID
|
||||
// userId(0) = CELL_SYSUTIL_USERID_CURRENT
|
||||
// path of the specified user (00000001 by default)
|
||||
const std::string& base_dir = vfs::get(fmt::format("/dev_hdd0/home/%08u/savedata/", userId ? userId : 1u));
|
||||
const std::string base_dir = vfs::get(fmt::format("/dev_hdd0/home/%08u/savedata/", userId ? userId : 1u));
|
||||
|
||||
result->userdata = userdata; // probably should be assigned only once (allows the callback to change it)
|
||||
|
||||
@@ -98,9 +101,9 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
listGet->dirNum = 0;
|
||||
listGet->dirListNum = 0;
|
||||
listGet->dirList.set(setBuf->buf.addr());
|
||||
memset(listGet->reserved, 0, sizeof(listGet->reserved));
|
||||
std::memset(listGet->reserved, 0, sizeof(listGet->reserved));
|
||||
|
||||
const auto prefix_list = fmt::split(setList->dirNamePrefix.get_ptr(), { "|" });
|
||||
const auto prefix_list = fmt::split(setList->dirNamePrefix.get_ptr(), {"|"});
|
||||
|
||||
// get the saves matching the supplied prefix
|
||||
for (auto&& entry : fs::dir(base_dir))
|
||||
@@ -204,10 +207,11 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
auto& dir = *dir_list++;
|
||||
strcpy_trunc(dir.dirName, entry.dirName);
|
||||
strcpy_trunc(dir.listParam, entry.listParam);
|
||||
memset(dir.reserved, 0, sizeof(dir.reserved));
|
||||
std::memset(dir.reserved, 0, sizeof(dir.reserved));
|
||||
}
|
||||
|
||||
s32 selected = -1;
|
||||
s32 focused = -1;
|
||||
|
||||
if (funcList)
|
||||
{
|
||||
@@ -217,14 +221,14 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
if (result->result < 0)
|
||||
{
|
||||
//TODO: display dialog
|
||||
cellSaveData.warning("savedata_op(): funcList returned < 0.");
|
||||
cellSaveData.warning("savedata_op(): funcList returned result=%d.", result->result);
|
||||
return CELL_SAVEDATA_ERROR_CBRESULT;
|
||||
}
|
||||
|
||||
// if the callback has returned ok, lets return OK.
|
||||
// typically used at game launch when no list is actually required.
|
||||
// CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM is only valid for funcFile and funcDone
|
||||
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST)
|
||||
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST || result->result == CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -243,19 +247,6 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
return true;
|
||||
}), save_entries.end());
|
||||
|
||||
// Add any new data (not currently supported by the UI)
|
||||
//if (listSet->newData)
|
||||
//{
|
||||
// SaveDataEntry *_saveDataEntry = new SaveDataEntry();
|
||||
// _saveDataEntry->dirName = listSet->newData->dirName.get_ptr();
|
||||
|
||||
// save_entry.dirName = listSet->newData->dirName.get_ptr();
|
||||
// save_entries.emplace_back(*_saveDataEntry);
|
||||
//}
|
||||
|
||||
// Focus save data
|
||||
s32 focused = -1;
|
||||
|
||||
switch (const u32 pos_type = listSet->focusPosition)
|
||||
{
|
||||
case CELL_SAVEDATA_FOCUSPOS_DIRNAME:
|
||||
@@ -323,14 +314,52 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
return CELL_SAVEDATA_ERROR_PARAM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display Save Data List but do so asynchronously in the GUI thread.
|
||||
bool hasNewData = (bool)listSet->newData; // newData
|
||||
auto delete_save = [&](const std::string& del_path)
|
||||
{
|
||||
strcpy_trunc(doneGet->dirName, save_entries[selected].dirName);
|
||||
doneGet->hddFreeSizeKB = 40 * 1024 * 1024; // 40 GB
|
||||
doneGet->sizeKB = 0;
|
||||
doneGet->excResult = CELL_OK;
|
||||
std::memset(doneGet->reserved, 0, sizeof(doneGet->reserved));
|
||||
|
||||
const fs::dir _dir{del_path};
|
||||
|
||||
for (auto&& file : _dir)
|
||||
{
|
||||
if (!file.is_directory)
|
||||
{
|
||||
doneGet->sizeKB += ::align(file.size, 4096);
|
||||
|
||||
if (!fs::remove_file(del_path + file.name))
|
||||
{
|
||||
doneGet->excResult = CELL_SAVEDATA_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_dir)
|
||||
{
|
||||
doneGet->excResult = CELL_SAVEDATA_ERROR_NODATA;
|
||||
}
|
||||
|
||||
if (!doneGet->excResult && !fs::remove_dir(del_path))
|
||||
{
|
||||
doneGet->excResult = CELL_SAVEDATA_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
funcDone(ppu, result, doneGet);
|
||||
};
|
||||
|
||||
while (funcList)
|
||||
{
|
||||
// Display Save Data List asynchronously in the GUI thread.
|
||||
atomic_t<bool> dlg_result(false);
|
||||
|
||||
Emu.CallAfter([&]()
|
||||
{
|
||||
selected = Emu.GetCallbacks().get_save_dialog()->ShowSaveDataList(save_entries, focused, hasNewData, listSet);
|
||||
selected = Emu.GetCallbacks().get_save_dialog()->ShowSaveDataList(save_entries, focused, operation, listSet);
|
||||
dlg_result = true;
|
||||
});
|
||||
|
||||
@@ -351,6 +380,29 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
return CELL_CANCEL;
|
||||
}
|
||||
|
||||
if (operation == SAVEDATA_OP_LIST_DELETE)
|
||||
{
|
||||
delete_save(base_dir + save_entries[selected].dirName + '/');
|
||||
|
||||
if (result->result < 0)
|
||||
{
|
||||
cellSaveData.warning("savedata_op(): funcDone returned result=%d.", result->result);
|
||||
return CELL_SAVEDATA_ERROR_CBRESULT;
|
||||
}
|
||||
|
||||
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST || result->result == CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// CELL_SAVEDATA_CBRESULT_OK_NEXT expected
|
||||
save_entries.erase(save_entries.cbegin() + selected);
|
||||
focused = save_entries.empty() ? -1 : selected;
|
||||
selected = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (funcFixed)
|
||||
@@ -359,7 +411,7 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
funcFixed(ppu, result, listGet, fixedSet);
|
||||
|
||||
// skip all following steps if OK_LAST
|
||||
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST)
|
||||
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST || result->result == CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -370,8 +422,8 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
// depends on fixedSet->option
|
||||
// 0 = none
|
||||
// 1 = skip confirmation dialog
|
||||
|
||||
cellSaveData.warning("savedata_op(): funcFixed returned < 0.");
|
||||
|
||||
cellSaveData.warning("savedata_op(): funcFixed returned result=%d.", result->result);
|
||||
return CELL_SAVEDATA_ERROR_CBRESULT;
|
||||
}
|
||||
|
||||
@@ -394,6 +446,18 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
save_entry.dirName = fixedSet->dirName.get_ptr();
|
||||
}
|
||||
|
||||
if (operation == SAVEDATA_OP_FIXED_DELETE)
|
||||
{
|
||||
delete_save(base_dir + save_entries[selected].dirName + '/');
|
||||
|
||||
if (result->result < 0)
|
||||
{
|
||||
cellSaveData.warning("savedata_op(): funcDone_ returned result=%d.", result->result);
|
||||
return CELL_SAVEDATA_ERROR_CBRESULT;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (selected >= 0)
|
||||
@@ -504,15 +568,15 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
|
||||
if (result->result < 0)
|
||||
{
|
||||
cellSaveData.warning("savedata_op(): funcStat returned 0x%x", result->result);
|
||||
cellSaveData.warning("savedata_op(): funcStat returned result=%d.", result->result);
|
||||
return CELL_SAVEDATA_ERROR_CBRESULT;
|
||||
}
|
||||
|
||||
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST)
|
||||
if (result->result == CELL_SAVEDATA_CBRESULT_OK_LAST || result->result == CELL_SAVEDATA_CBRESULT_OK_LAST_NOCONFIRM)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
if (statSet->setParam)
|
||||
{
|
||||
// Update PARAM.SFO
|
||||
@@ -544,7 +608,7 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
case CELL_SAVEDATA_RECREATE_NO:
|
||||
{
|
||||
//CELL_SAVEDATA_RECREATE_NO = overwrite and let the user know, not data is corrupt.
|
||||
//cellSaveData.error("Savedata %s considered broken", save_entry.dirName);
|
||||
//cellSaveData.error("Savedata %s considered broken", save_entry.dirName);
|
||||
//TODO: if this is a save, and it's not auto, then show a dialog
|
||||
// fallthrough
|
||||
}
|
||||
@@ -591,7 +655,7 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
|
||||
// Create save directory if necessary
|
||||
if (psf.size() && save_entry.isNew && !fs::create_dir(dir_path))
|
||||
{
|
||||
{
|
||||
cellSaveData.warning("savedata_op(): failed to create %s", dir_path);
|
||||
return CELL_SAVEDATA_ERROR_ACCESS_ERROR;
|
||||
}
|
||||
@@ -607,7 +671,7 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
|
||||
if (result->result < 0)
|
||||
{
|
||||
cellSaveData.warning("savedata_op(): funcFile returned < 0.");
|
||||
cellSaveData.warning("savedata_op(): funcFile returned result=%d.", result->result);
|
||||
return CELL_SAVEDATA_ERROR_CBRESULT;
|
||||
}
|
||||
|
||||
@@ -720,9 +784,8 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 static NEVER_INLINE save_op_get_list_item(vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB, u32 userId)
|
||||
static NEVER_INLINE s32 savedata_get_list_item(vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB, u32 userId)
|
||||
{
|
||||
|
||||
//TODO: accurately get the current user
|
||||
if (userId == 0)
|
||||
{
|
||||
@@ -806,7 +869,6 @@ s32 cellSaveDataListLoad(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf
|
||||
version, setList, setBuf, funcList, funcStat, funcFile, container);
|
||||
|
||||
return savedata_op(ppu, SAVEDATA_OP_LIST_LOAD, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
|
||||
|
||||
}
|
||||
|
||||
s32 cellSaveDataFixedSave2(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
|
||||
@@ -834,7 +896,6 @@ s32 cellSaveDataFixedSave(ppu_thread& ppu, u32 version, PSetList setList, PSetBu
|
||||
version, setList, setBuf, funcFixed, funcStat, funcFile, container);
|
||||
|
||||
return savedata_op(ppu, SAVEDATA_OP_FIXED_SAVE, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
|
||||
|
||||
}
|
||||
|
||||
s32 cellSaveDataFixedLoad(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
|
||||
@@ -914,10 +975,10 @@ s32 cellSaveDataDelete(u32 container)
|
||||
|
||||
s32 cellSaveDataFixedDelete(ppu_thread& ppu, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
|
||||
{
|
||||
cellSaveData.todo("cellSaveDataFixedDelete(setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)",
|
||||
cellSaveData.warning("cellSaveDataFixedDelete(setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)",
|
||||
setList, setBuf, funcFixed, funcDone, container, userdata);
|
||||
|
||||
return CELL_OK;
|
||||
return savedata_op(ppu, SAVEDATA_OP_FIXED_DELETE, 0, vm::null, 1, setList, setBuf, vm::null, funcFixed, vm::null, vm::null, container, 2, userdata, 0, funcDone);
|
||||
}
|
||||
|
||||
s32 cellSaveDataUserListSave(ppu_thread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
|
||||
@@ -986,26 +1047,24 @@ s32 cellSaveDataUserListAutoLoad(ppu_thread& ppu, u32 version, u32 userId, u32 e
|
||||
|
||||
s32 cellSaveDataUserFixedDelete(ppu_thread& ppu, u32 userId, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
|
||||
{
|
||||
cellSaveData.todo("cellSaveDataUserFixedDelete(userId=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)",
|
||||
cellSaveData.error("cellSaveDataUserFixedDelete(userId=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)",
|
||||
userId, setList, setBuf, funcFixed, funcDone, container, userdata);
|
||||
|
||||
return CELL_OK;
|
||||
return savedata_op(ppu, SAVEDATA_OP_FIXED_DELETE, 0, vm::null, 1, setList, setBuf, vm::null, funcFixed, vm::null, vm::null, container, 6, userdata, userId, funcDone);
|
||||
}
|
||||
|
||||
void cellSaveDataEnableOverlay(s32 enable)
|
||||
{
|
||||
cellSaveData.error("cellSaveDataEnableOverlay(enable=%d)", enable);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Functions (Extensions)
|
||||
s32 cellSaveDataListDelete(ppu_thread& ppu, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellSaveData);
|
||||
cellSaveData.warning("cellSaveDataListDelete(setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)", setList, setBuf, funcList, funcDone, container, userdata);
|
||||
|
||||
return CELL_OK;
|
||||
return savedata_op(ppu, SAVEDATA_OP_LIST_DELETE, 0, vm::null, 0, setList, setBuf, funcList, vm::null, vm::null, vm::null, container, 0x40, userdata, 0, funcDone);
|
||||
}
|
||||
|
||||
s32 cellSaveDataListImport(ppu_thread& ppu, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
|
||||
@@ -1039,15 +1098,15 @@ s32 cellSaveDataFixedExport(ppu_thread& ppu, vm::cptr<char> dirName, u32 maxSize
|
||||
s32 cellSaveDataGetListItem(vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat> dir, vm::ptr<CellSaveDataSystemFileParam> sysFileParam, vm::ptr<u32> bind, vm::ptr<u32> sizeKB)
|
||||
{
|
||||
cellSaveData.warning("cellSavaDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x)", dirName, dir, sysFileParam, bind, sizeKB);
|
||||
|
||||
return save_op_get_list_item(dirName, dir, sysFileParam, bind, sizeKB, 0);
|
||||
|
||||
return savedata_get_list_item(dirName, dir, sysFileParam, bind, sizeKB, 0);
|
||||
}
|
||||
|
||||
s32 cellSaveDataUserListDelete(ppu_thread& ppu, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellSaveData);
|
||||
cellSaveData.error("cellSaveDataUserListDelete(userId=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)", userId, setList, setBuf, funcList, funcDone, container, userdata);
|
||||
|
||||
return CELL_OK;
|
||||
return savedata_op(ppu, SAVEDATA_OP_LIST_DELETE, 0, vm::null, 0, setList, setBuf, funcList, vm::null, vm::null, vm::null, container, 0x40, userdata, userId, funcDone);
|
||||
}
|
||||
|
||||
s32 cellSaveDataUserListImport(ppu_thread& ppu, u32 userId, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
|
||||
@@ -1082,7 +1141,7 @@ s32 cellSaveDataUserGetListItem(u32 userId, vm::cptr<char> dirName, vm::ptr<Cell
|
||||
{
|
||||
cellSaveData.warning("cellSavaDataGetListItem(dirName=%s, dir=*0x%x, sysFileParam=*0x%x, bind=*0x%x, sizeKB=*0x%x, userID=*0x%x)", dirName, dir, sysFileParam, bind, sizeKB, userId);
|
||||
|
||||
return save_op_get_list_item(dirName, dir, sysFileParam, bind, sizeKB, userId);
|
||||
return savedata_get_list_item(dirName, dir, sysFileParam, bind, sizeKB, userId);
|
||||
}
|
||||
|
||||
void cellSysutil_SaveData_init()
|
||||
|
||||
@@ -291,5 +291,5 @@ class SaveDialogBase
|
||||
public:
|
||||
virtual ~SaveDialogBase();
|
||||
|
||||
virtual s32 ShowSaveDataList(std::vector<SaveDataEntry>& save_entries, s32 focused, bool isSaving, vm::ptr<CellSaveDataListSet> listSet) = 0;
|
||||
virtual s32 ShowSaveDataList(std::vector<SaveDataEntry>& save_entries, s32 focused, u32 op, vm::ptr<CellSaveDataListSet> listSet) = 0;
|
||||
};
|
||||
|
||||
@@ -3,14 +3,22 @@
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "cellSearch.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
logs::channel cellSearch("cellSearch");
|
||||
|
||||
s32 cellSearchInitialize(CellSearchMode mode, u32 container, vm::ptr<CellSearchSystemCallback> func, vm::ptr<u32> userData)
|
||||
s32 cellSearchInitialize(ppu_thread& ppu, CellSearchMode mode, u32 container, vm::ptr<CellSearchSystemCallback> func, vm::ptr<u32> userData)
|
||||
{
|
||||
cellSearch.warning("cellSearchInitialize()");
|
||||
|
||||
// TODO: Store the arguments somewhere so we can use them later.
|
||||
|
||||
//inform callback that search is alive
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
func(ppu, CELL_SEARCH_EVENT_INITIALIZE_RESULT, CELL_OK, vm::null, userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ struct vdec_thread : ppu_thread
|
||||
{
|
||||
AVCodec* codec{};
|
||||
AVCodecContext* ctx{};
|
||||
SwsContext* sws{};
|
||||
|
||||
const s32 type;
|
||||
const u32 profile;
|
||||
@@ -147,6 +148,7 @@ struct vdec_thread : ppu_thread
|
||||
{
|
||||
avcodec_close(ctx);
|
||||
avcodec_free_context(&ctx);
|
||||
sws_freeContext(sws);
|
||||
}
|
||||
|
||||
virtual std::string dump() const override
|
||||
@@ -361,7 +363,7 @@ struct vdec_thread : ppu_thread
|
||||
{
|
||||
au_count--;
|
||||
}
|
||||
|
||||
|
||||
while (std::lock_guard<std::mutex>{mutex}, max_frames && out.size() > max_frames)
|
||||
{
|
||||
thread_ctrl::wait();
|
||||
@@ -483,7 +485,7 @@ s32 cellVdecClose(ppu_thread& ppu, u32 handle)
|
||||
vdec->cmd_push({vdec_cmd::close, 0});
|
||||
vdec->max_frames = 0;
|
||||
}
|
||||
|
||||
|
||||
vdec->notify();
|
||||
vdec->join();
|
||||
idm::remove<ppu_thread>(handle);
|
||||
@@ -586,7 +588,7 @@ s32 cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u
|
||||
}
|
||||
|
||||
vdec->notify();
|
||||
|
||||
|
||||
if (outBuff)
|
||||
{
|
||||
const int w = frame->width;
|
||||
@@ -632,7 +634,7 @@ s32 cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SwsContext, void(*)(SwsContext*)> sws(sws_getContext(w, h, in_f, w, h, out_f, SWS_POINT, NULL, NULL, NULL), sws_freeContext);
|
||||
vdec->sws = sws_getCachedContext(vdec->sws, w, h, in_f, w, h, out_f, SWS_POINT, NULL, NULL, NULL);
|
||||
|
||||
u8* in_data[4] = { frame->data[0], frame->data[1], frame->data[2], alpha_plane.get() };
|
||||
int in_line[4] = { frame->linesize[0], frame->linesize[1], frame->linesize[2], w * 1 };
|
||||
@@ -648,7 +650,7 @@ s32 cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u
|
||||
out_line[2] = w / 2;
|
||||
}
|
||||
|
||||
sws_scale(sws.get(), in_data, in_line, 0, h, out_data, out_line);
|
||||
sws_scale(vdec->sws, in_data, in_line, 0, h, out_data, out_line);
|
||||
|
||||
//const u32 buf_size = align(av_image_get_buffer_size(vdec->ctx->pix_fmt, vdec->ctx->width, vdec->ctx->height, 1), 128);
|
||||
|
||||
@@ -771,7 +773,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
|
||||
avc->transfer_characteristics = CELL_VDEC_AVC_TC_ITU_R_BT_709_5;
|
||||
avc->matrix_coefficients = CELL_VDEC_AVC_MXC_ITU_R_BT_709_5; // important
|
||||
avc->timing_info_present_flag = true;
|
||||
|
||||
|
||||
switch (frc)
|
||||
{
|
||||
case CELL_VDEC_FRC_24000DIV1001: avc->frameRateCode = CELL_VDEC_AVC_FRC_24000DIV1001; break;
|
||||
@@ -838,7 +840,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
|
||||
mp2->horizontal_size = frame->width;
|
||||
mp2->vertical_size = frame->height;
|
||||
mp2->aspect_ratio_information = CELL_VDEC_MPEG2_ARI_SAR_1_1; // ???
|
||||
|
||||
|
||||
switch (frc)
|
||||
{
|
||||
case CELL_VDEC_FRC_24000DIV1001: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_24000DIV1001; break;
|
||||
|
||||
@@ -55,7 +55,7 @@ s32 cellVpostClose(u32 handle)
|
||||
return CELL_VPOST_ERROR_C_ARG_HDL_INVALID;
|
||||
}
|
||||
|
||||
idm::remove<VpostInstance>(handle);
|
||||
idm::remove<VpostInstance>(handle);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ s32 cellVpostExec(u32 handle, vm::cptr<u8> inPicBuff, vm::cptr<CellVpostCtrlPara
|
||||
|
||||
//u64 stamp1 = get_system_time();
|
||||
|
||||
std::unique_ptr<SwsContext, void(*)(SwsContext*)> sws(sws_getContext(w, h, AV_PIX_FMT_YUVA420P, ow, oh, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL), sws_freeContext);
|
||||
vpost->sws = sws_getCachedContext(vpost->sws, w, h, AV_PIX_FMT_YUVA420P, ow, oh, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL);
|
||||
|
||||
//u64 stamp2 = get_system_time();
|
||||
|
||||
@@ -129,7 +129,7 @@ s32 cellVpostExec(u32 handle, vm::cptr<u8> inPicBuff, vm::cptr<CellVpostCtrlPara
|
||||
u8* out_data[4] = { outPicBuff.get_ptr(), NULL, NULL, NULL };
|
||||
int out_line[4] = { static_cast<int>(ow*4), 0, 0, 0 };
|
||||
|
||||
sws_scale(sws.get(), in_data, in_line, 0, h, out_data, out_line);
|
||||
sws_scale(vpost->sws, in_data, in_line, 0, h, out_data, out_line);
|
||||
|
||||
//ConLog.Write("cellVpostExec() perf (access=%d, getContext=%d, scale=%d, finalize=%d)",
|
||||
//stamp1 - stamp0, stamp2 - stamp1, stamp3 - stamp2, get_system_time() - stamp3);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "libswscale/swscale.h"
|
||||
}
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
// Error Codes
|
||||
@@ -327,8 +332,15 @@ public:
|
||||
|
||||
const bool to_rgba;
|
||||
|
||||
SwsContext* sws{};
|
||||
|
||||
VpostInstance(bool rgba)
|
||||
: to_rgba(rgba)
|
||||
{
|
||||
}
|
||||
|
||||
~VpostInstance()
|
||||
{
|
||||
sws_freeContext(sws);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -147,7 +147,8 @@ s32 sceNpSnsFbCheckThrottle()
|
||||
|
||||
s32 sceNpSnsFbCheckConfig()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
sceNpSns.todo("sceNpSnsFbCheckConfig()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sceNpSnsFbLoadThrottle()
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
logs::channel sceNpTrophy("sceNpTrophy");
|
||||
|
||||
TrophyNotificationBase::~TrophyNotificationBase()
|
||||
{
|
||||
}
|
||||
|
||||
struct trophy_context_t
|
||||
{
|
||||
static const u32 id_base = 1;
|
||||
@@ -264,7 +268,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
|
||||
const size_t kTargetBufferLength = 31;
|
||||
char target[kTargetBufferLength + 1];
|
||||
target[kTargetBufferLength] = 0;
|
||||
strcpy_trunc(target, fmt::format("TROP_%02d.SFM", /*rpcs3::config.system.language.value()*/0));
|
||||
strcpy_trunc(target, fmt::format("TROP_%02d.SFM", static_cast<s32>(g_cfg.sys.language)));
|
||||
|
||||
if (trp.ContainsEntry(target))
|
||||
{
|
||||
@@ -286,7 +290,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
|
||||
for (s32 i = 0; i <= 18; i++)
|
||||
{
|
||||
strcpy_trunc(target, fmt::format("TROP_%02d.SFM", i));
|
||||
if (i != /*rpcs3::config.system.language.value()*/0)
|
||||
if (i != g_cfg.sys.language)
|
||||
{
|
||||
trp.RemoveEntry(target);
|
||||
}
|
||||
@@ -490,6 +494,40 @@ error_code sceNpTrophyUnlockTrophy(u32 context, u32 handle, s32 trophyId, vm::pt
|
||||
*platinumId = SCE_NP_TROPHY_INVALID_TROPHY_ID; // TODO
|
||||
}
|
||||
|
||||
if (g_cfg.misc.show_trophy_popups)
|
||||
{
|
||||
// Figure out how many zeros are needed for padding. (either 0, 1, or 2)
|
||||
std::string padding = "";
|
||||
if (trophyId < 10)
|
||||
{
|
||||
padding = "00";
|
||||
}
|
||||
else if (trophyId < 100)
|
||||
{
|
||||
padding = "0";
|
||||
}
|
||||
|
||||
// Get icon for the notification.
|
||||
std::string trophyIconPath = "/dev_hdd0/home/00000001/trophy/" + ctxt->trp_name + "/TROP" + padding + std::to_string(trophyId) + ".PNG";
|
||||
fs::file trophyIconFile = fs::file(vfs::get(trophyIconPath));
|
||||
u32 iconSize = trophyIconFile.size();
|
||||
std::vector<uchar> trophyIconData;
|
||||
trophyIconFile.read(trophyIconData, iconSize);
|
||||
|
||||
vm::ptr<SceNpTrophyDetails> details = vm::make_var(SceNpTrophyDetails());
|
||||
vm::ptr<SceNpTrophyData> _ = vm::make_var(SceNpTrophyData());
|
||||
|
||||
s32 ret = sceNpTrophyGetTrophyInfo(context, handle, trophyId, details, _);
|
||||
if (ret != CELL_OK)
|
||||
{
|
||||
sceNpTrophy.error("Failed to get info for trophy dialog. Error code %x", ret);
|
||||
*details = SceNpTrophyDetails();
|
||||
}
|
||||
Emu.CallAfter([det = *details, trophyIconData]() {
|
||||
Emu.GetCallbacks().get_trophy_notification_dialog()->ShowTrophyNotification(det, trophyIconData);
|
||||
});
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -137,3 +137,14 @@ enum
|
||||
};
|
||||
|
||||
using SceNpTrophyStatusCallback = s32(u32 context, u32 status, s32 completed, s32 total, vm::ptr<void> arg);
|
||||
|
||||
// Forward declare this function since trophyunlock needs it
|
||||
error_code sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, vm::ptr<SceNpTrophyDetails> details, vm::ptr<SceNpTrophyData> data);
|
||||
|
||||
class TrophyNotificationBase
|
||||
{
|
||||
public:
|
||||
virtual ~TrophyNotificationBase();
|
||||
|
||||
virtual s32 ShowTrophyNotification(const SceNpTrophyDetails& trophy, const std::vector<uchar>& trophyIconBfr) = 0;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "Utilities/bit_set.h"
|
||||
#include "Utilities/BEType.h"
|
||||
#include "PPUOpcodes.h"
|
||||
|
||||
// PPU Function Attributes
|
||||
enum class ppu_attr : u32
|
||||
@@ -941,3 +942,738 @@ struct ppu_iname
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
// PPU Analyser Context
|
||||
struct ppu_acontext
|
||||
{
|
||||
// General-purpose register range
|
||||
struct spec_gpr
|
||||
{
|
||||
// Integral range: normalized undef = (0;UINT64_MAX), unnormalized undefs are possible (when max = min - 1)
|
||||
// Bit range: constant 0 = (0;0), constant 1 = (1;1), normalized undef = (0;1), unnormalized undef = (1;0)
|
||||
|
||||
u64 imin = 0; // Integral range begin
|
||||
u64 imax = -1; // Integral range end
|
||||
u64 bmin = 0; // Bit range begin
|
||||
u64 bmax = -1; // Bit range end
|
||||
|
||||
void set_undef()
|
||||
{
|
||||
imin = 0;
|
||||
imax = -1;
|
||||
bmin = 0;
|
||||
bmax = -1;
|
||||
}
|
||||
|
||||
// (Number of possible values - 1), 0 = const
|
||||
u64 div() const
|
||||
{
|
||||
return imax - imin;
|
||||
}
|
||||
|
||||
// Return zero bits for zeros, ones for ones or undefs
|
||||
u64 mask() const
|
||||
{
|
||||
return bmin | bmax;
|
||||
}
|
||||
|
||||
// Return one bits for ones, zeros for zeros or undefs
|
||||
u64 ones() const
|
||||
{
|
||||
return bmin & bmax;
|
||||
}
|
||||
|
||||
// Return one bits for undefs
|
||||
u64 undefs() const
|
||||
{
|
||||
return bmin ^ bmax;
|
||||
}
|
||||
|
||||
// Return number of trailing zero bits
|
||||
u64 tz() const
|
||||
{
|
||||
return ::cnttz64(mask());
|
||||
}
|
||||
|
||||
// Range NOT
|
||||
spec_gpr operator ~() const
|
||||
{
|
||||
spec_gpr r;
|
||||
r.imin = ~imax;
|
||||
r.imax = ~imin;
|
||||
r.bmin = ~bmax;
|
||||
r.bmax = ~bmin;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Range ADD
|
||||
spec_gpr operator +(const spec_gpr& rhs) const
|
||||
{
|
||||
spec_gpr r{};
|
||||
|
||||
const u64 adiv = div();
|
||||
const u64 bdiv = rhs.div();
|
||||
|
||||
// Check overflow, generate normalized range
|
||||
if (adiv != -1 && bdiv != -1 && adiv <= adiv + bdiv)
|
||||
{
|
||||
r = range(imin + rhs.imin, imax + rhs.imax);
|
||||
}
|
||||
|
||||
// Carry for bitrange computation
|
||||
u64 cmin = 0;
|
||||
u64 cmax = 0;
|
||||
|
||||
const u64 amask = mask();
|
||||
const u64 bmask = rhs.mask();
|
||||
const u64 aones = ones();
|
||||
const u64 bones = rhs.ones();
|
||||
|
||||
for (u32 i = 0; i < 64; i++)
|
||||
{
|
||||
cmin += ((amask >> i) & 1) + ((bmask >> i) & 1);
|
||||
cmax += ((aones >> i) & 1) + ((bones >> i) & 1);
|
||||
|
||||
// Discover some constant bits
|
||||
if (cmin == cmax)
|
||||
{
|
||||
r.bmin |= (cmin & 1) << i;
|
||||
r.bmax &= ~((~cmin & 1) << i);
|
||||
}
|
||||
|
||||
cmin >>= 1;
|
||||
cmax >>= 1;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// Range AND
|
||||
spec_gpr operator &(const spec_gpr& rhs) const
|
||||
{
|
||||
// Ignore inverted ranges (TODO)
|
||||
if (imin > imax || rhs.imin > rhs.imax)
|
||||
{
|
||||
return approx(ones() & rhs.ones(), mask() & rhs.mask());
|
||||
}
|
||||
|
||||
// Const (TODO: remove when unnecessary)
|
||||
if (imin == imax && rhs.imin == rhs.imax)
|
||||
{
|
||||
return fixed(imin & rhs.imin);
|
||||
}
|
||||
|
||||
// Swap (TODO: remove when unnecessary)
|
||||
if (imin == imax || rhs.undefs() > undefs())
|
||||
{
|
||||
return rhs & *this;
|
||||
}
|
||||
|
||||
// Copy and attempt to partially preserve integral range
|
||||
spec_gpr r = *this;
|
||||
|
||||
for (u32 i = 63; ~i; i--)
|
||||
{
|
||||
const u64 m = 1ull << i;
|
||||
|
||||
if (!(rhs.mask() & m))
|
||||
{
|
||||
if (r.undefs() & m)
|
||||
{
|
||||
// undef -> 0
|
||||
r.imin &= ~(m - 1);
|
||||
r.imax |= (m - 1);
|
||||
r.imin &= ~m;
|
||||
r.imax &= ~m;
|
||||
}
|
||||
else if (r.ones() & m)
|
||||
{
|
||||
// 1 -> 0
|
||||
if ((r.imin ^ r.imax) > (m - 1))
|
||||
{
|
||||
r.imin &= ~(m - 1);
|
||||
r.imax |= (m - 1);
|
||||
}
|
||||
|
||||
r.imin &= ~m;
|
||||
r.imax &= ~m;
|
||||
}
|
||||
}
|
||||
else if (rhs.undefs() & m)
|
||||
{
|
||||
// -> undef
|
||||
r.imin &= ~(m - 1);
|
||||
r.imax |= (m - 1);
|
||||
r.imin &= ~m;
|
||||
r.imax |= m;
|
||||
}
|
||||
}
|
||||
|
||||
r.bmin = ones() & rhs.ones();
|
||||
r.bmax = mask() & rhs.mask();
|
||||
return r;
|
||||
}
|
||||
|
||||
// Range OR
|
||||
spec_gpr operator |(const spec_gpr& rhs) const
|
||||
{
|
||||
// Ignore inverted ranges (TODO)
|
||||
if (imin > imax || rhs.imin > rhs.imax)
|
||||
{
|
||||
return approx(ones() | rhs.ones(), mask() | rhs.mask());
|
||||
}
|
||||
|
||||
// Const (TODO: remove when unnecessary)
|
||||
if (imin == imax && rhs.imin == rhs.imax)
|
||||
{
|
||||
return fixed(imin | rhs.imin);
|
||||
}
|
||||
|
||||
// Swap (TODO: remove when unnecessary)
|
||||
if (imin == imax || rhs.undefs() > undefs())
|
||||
{
|
||||
return rhs | *this;
|
||||
}
|
||||
|
||||
// Copy and attempt to partially preserve integral range
|
||||
spec_gpr r = *this;
|
||||
|
||||
for (u32 i = 63; ~i; i--)
|
||||
{
|
||||
const u64 m = 1ull << i;
|
||||
|
||||
if (rhs.ones() & m)
|
||||
{
|
||||
if (r.undefs() & m)
|
||||
{
|
||||
// undef -> 1
|
||||
r.imin &= ~(m - 1);
|
||||
r.imax |= (m - 1);
|
||||
r.imin |= m;
|
||||
r.imax |= m;
|
||||
}
|
||||
else if (!(r.mask() & m))
|
||||
{
|
||||
// 0 -> 1
|
||||
if ((r.imin ^ r.imax) > (m - 1))
|
||||
{
|
||||
r.imin &= ~(m - 1);
|
||||
r.imax |= (m - 1);
|
||||
}
|
||||
|
||||
r.imin |= m;
|
||||
r.imax |= m;
|
||||
}
|
||||
}
|
||||
else if (rhs.undefs() & m)
|
||||
{
|
||||
// -> undef
|
||||
r.imin &= ~(m - 1);
|
||||
r.imax |= (m - 1);
|
||||
r.imin &= ~m;
|
||||
r.imax |= m;
|
||||
}
|
||||
}
|
||||
|
||||
r.bmin = ones() | rhs.ones();
|
||||
r.bmax = mask() | rhs.mask();
|
||||
return r;
|
||||
}
|
||||
|
||||
// Range XOR
|
||||
spec_gpr operator ^(const spec_gpr& rhs)
|
||||
{
|
||||
return (~*this & rhs) | (*this & ~rhs);
|
||||
}
|
||||
|
||||
// Check whether the value is in range
|
||||
bool test(u64 value) const
|
||||
{
|
||||
if (imin <= imax)
|
||||
{
|
||||
if (value < imin || value > imax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value < imin && value > imax)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((value & mask()) != value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((value | ones()) != value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Constant value
|
||||
static spec_gpr fixed(u64 value)
|
||||
{
|
||||
spec_gpr r;
|
||||
r.imin = value;
|
||||
r.imax = value;
|
||||
r.bmin = value;
|
||||
r.bmax = value;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Range (tz = number of constant trailing zeros)
|
||||
static spec_gpr range(u64 min, u64 max, u64 tz = 0)
|
||||
{
|
||||
const u64 mask = tz < 64 ? ~0ull << tz : 0ull;
|
||||
|
||||
spec_gpr r;
|
||||
r.bmin = 0;
|
||||
r.bmax = mask;
|
||||
|
||||
// Normalize min/max for tz (TODO)
|
||||
if (min < max)
|
||||
{
|
||||
// Inverted constant MSB mask
|
||||
const u64 mix = ~0ull >> ::cntlz64(min ^ max, true);
|
||||
r.bmin |= min & ~mix;
|
||||
r.bmax &= max | mix;
|
||||
|
||||
r.imin = (min + ~mask) & mask;
|
||||
r.imax = max & mask;
|
||||
verify("Impossible range" HERE), r.imin <= r.imax;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.imin = min & mask;
|
||||
r.imax = (max + ~mask) & mask;
|
||||
verify("Impossible range" HERE), r.imin >= r.imax;
|
||||
}
|
||||
|
||||
// Fix const values
|
||||
if (r.imin == r.imax)
|
||||
{
|
||||
r.bmin = r.imin;
|
||||
r.bmax = r.imax;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// Make from bitrange (normalize, approximate range values)
|
||||
static spec_gpr approx(u64 bmin, u64 bmax)
|
||||
{
|
||||
spec_gpr r;
|
||||
r.imin = bmin & ~(bmin ^ bmax);
|
||||
r.imax = bmax | (bmin ^ bmax);
|
||||
r.bmin = bmin & ~(bmin ^ bmax);
|
||||
r.bmax = bmax | (bmin ^ bmax);
|
||||
return r;
|
||||
}
|
||||
} gpr[32]{};
|
||||
|
||||
// Vector registers (draft)
|
||||
struct spec_vec
|
||||
{
|
||||
u8 imin8[16]{};
|
||||
u8 imax8[16]{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255};
|
||||
u16 imin16[8]{};
|
||||
u16 imax16[8]{0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff};
|
||||
u32 imin32[4]{};
|
||||
u32 imax32[4]{0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu};
|
||||
u64 bmin64[2]{};
|
||||
u64 bmax64[2]{0xffffffffffffffffull, 0xffffffffffffffffull};
|
||||
};
|
||||
|
||||
// Info
|
||||
u32 cia;
|
||||
|
||||
// Analyser step
|
||||
void UNK(ppu_opcode_t);
|
||||
|
||||
void MFVSCR(ppu_opcode_t);
|
||||
void MTVSCR(ppu_opcode_t);
|
||||
void VADDCUW(ppu_opcode_t);
|
||||
void VADDFP(ppu_opcode_t);
|
||||
void VADDSBS(ppu_opcode_t);
|
||||
void VADDSHS(ppu_opcode_t);
|
||||
void VADDSWS(ppu_opcode_t);
|
||||
void VADDUBM(ppu_opcode_t);
|
||||
void VADDUBS(ppu_opcode_t);
|
||||
void VADDUHM(ppu_opcode_t);
|
||||
void VADDUHS(ppu_opcode_t);
|
||||
void VADDUWM(ppu_opcode_t);
|
||||
void VADDUWS(ppu_opcode_t);
|
||||
void VAND(ppu_opcode_t);
|
||||
void VANDC(ppu_opcode_t);
|
||||
void VAVGSB(ppu_opcode_t);
|
||||
void VAVGSH(ppu_opcode_t);
|
||||
void VAVGSW(ppu_opcode_t);
|
||||
void VAVGUB(ppu_opcode_t);
|
||||
void VAVGUH(ppu_opcode_t);
|
||||
void VAVGUW(ppu_opcode_t);
|
||||
void VCFSX(ppu_opcode_t);
|
||||
void VCFUX(ppu_opcode_t);
|
||||
void VCMPBFP(ppu_opcode_t);
|
||||
void VCMPEQFP(ppu_opcode_t);
|
||||
void VCMPEQUB(ppu_opcode_t);
|
||||
void VCMPEQUH(ppu_opcode_t);
|
||||
void VCMPEQUW(ppu_opcode_t);
|
||||
void VCMPGEFP(ppu_opcode_t);
|
||||
void VCMPGTFP(ppu_opcode_t);
|
||||
void VCMPGTSB(ppu_opcode_t);
|
||||
void VCMPGTSH(ppu_opcode_t);
|
||||
void VCMPGTSW(ppu_opcode_t);
|
||||
void VCMPGTUB(ppu_opcode_t);
|
||||
void VCMPGTUH(ppu_opcode_t);
|
||||
void VCMPGTUW(ppu_opcode_t);
|
||||
void VCTSXS(ppu_opcode_t);
|
||||
void VCTUXS(ppu_opcode_t);
|
||||
void VEXPTEFP(ppu_opcode_t);
|
||||
void VLOGEFP(ppu_opcode_t);
|
||||
void VMADDFP(ppu_opcode_t);
|
||||
void VMAXFP(ppu_opcode_t);
|
||||
void VMAXSB(ppu_opcode_t);
|
||||
void VMAXSH(ppu_opcode_t);
|
||||
void VMAXSW(ppu_opcode_t);
|
||||
void VMAXUB(ppu_opcode_t);
|
||||
void VMAXUH(ppu_opcode_t);
|
||||
void VMAXUW(ppu_opcode_t);
|
||||
void VMHADDSHS(ppu_opcode_t);
|
||||
void VMHRADDSHS(ppu_opcode_t);
|
||||
void VMINFP(ppu_opcode_t);
|
||||
void VMINSB(ppu_opcode_t);
|
||||
void VMINSH(ppu_opcode_t);
|
||||
void VMINSW(ppu_opcode_t);
|
||||
void VMINUB(ppu_opcode_t);
|
||||
void VMINUH(ppu_opcode_t);
|
||||
void VMINUW(ppu_opcode_t);
|
||||
void VMLADDUHM(ppu_opcode_t);
|
||||
void VMRGHB(ppu_opcode_t);
|
||||
void VMRGHH(ppu_opcode_t);
|
||||
void VMRGHW(ppu_opcode_t);
|
||||
void VMRGLB(ppu_opcode_t);
|
||||
void VMRGLH(ppu_opcode_t);
|
||||
void VMRGLW(ppu_opcode_t);
|
||||
void VMSUMMBM(ppu_opcode_t);
|
||||
void VMSUMSHM(ppu_opcode_t);
|
||||
void VMSUMSHS(ppu_opcode_t);
|
||||
void VMSUMUBM(ppu_opcode_t);
|
||||
void VMSUMUHM(ppu_opcode_t);
|
||||
void VMSUMUHS(ppu_opcode_t);
|
||||
void VMULESB(ppu_opcode_t);
|
||||
void VMULESH(ppu_opcode_t);
|
||||
void VMULEUB(ppu_opcode_t);
|
||||
void VMULEUH(ppu_opcode_t);
|
||||
void VMULOSB(ppu_opcode_t);
|
||||
void VMULOSH(ppu_opcode_t);
|
||||
void VMULOUB(ppu_opcode_t);
|
||||
void VMULOUH(ppu_opcode_t);
|
||||
void VNMSUBFP(ppu_opcode_t);
|
||||
void VNOR(ppu_opcode_t);
|
||||
void VOR(ppu_opcode_t);
|
||||
void VPERM(ppu_opcode_t);
|
||||
void VPKPX(ppu_opcode_t);
|
||||
void VPKSHSS(ppu_opcode_t);
|
||||
void VPKSHUS(ppu_opcode_t);
|
||||
void VPKSWSS(ppu_opcode_t);
|
||||
void VPKSWUS(ppu_opcode_t);
|
||||
void VPKUHUM(ppu_opcode_t);
|
||||
void VPKUHUS(ppu_opcode_t);
|
||||
void VPKUWUM(ppu_opcode_t);
|
||||
void VPKUWUS(ppu_opcode_t);
|
||||
void VREFP(ppu_opcode_t);
|
||||
void VRFIM(ppu_opcode_t);
|
||||
void VRFIN(ppu_opcode_t);
|
||||
void VRFIP(ppu_opcode_t);
|
||||
void VRFIZ(ppu_opcode_t);
|
||||
void VRLB(ppu_opcode_t);
|
||||
void VRLH(ppu_opcode_t);
|
||||
void VRLW(ppu_opcode_t);
|
||||
void VRSQRTEFP(ppu_opcode_t);
|
||||
void VSEL(ppu_opcode_t);
|
||||
void VSL(ppu_opcode_t);
|
||||
void VSLB(ppu_opcode_t);
|
||||
void VSLDOI(ppu_opcode_t);
|
||||
void VSLH(ppu_opcode_t);
|
||||
void VSLO(ppu_opcode_t);
|
||||
void VSLW(ppu_opcode_t);
|
||||
void VSPLTB(ppu_opcode_t);
|
||||
void VSPLTH(ppu_opcode_t);
|
||||
void VSPLTISB(ppu_opcode_t);
|
||||
void VSPLTISH(ppu_opcode_t);
|
||||
void VSPLTISW(ppu_opcode_t);
|
||||
void VSPLTW(ppu_opcode_t);
|
||||
void VSR(ppu_opcode_t);
|
||||
void VSRAB(ppu_opcode_t);
|
||||
void VSRAH(ppu_opcode_t);
|
||||
void VSRAW(ppu_opcode_t);
|
||||
void VSRB(ppu_opcode_t);
|
||||
void VSRH(ppu_opcode_t);
|
||||
void VSRO(ppu_opcode_t);
|
||||
void VSRW(ppu_opcode_t);
|
||||
void VSUBCUW(ppu_opcode_t);
|
||||
void VSUBFP(ppu_opcode_t);
|
||||
void VSUBSBS(ppu_opcode_t);
|
||||
void VSUBSHS(ppu_opcode_t);
|
||||
void VSUBSWS(ppu_opcode_t);
|
||||
void VSUBUBM(ppu_opcode_t);
|
||||
void VSUBUBS(ppu_opcode_t);
|
||||
void VSUBUHM(ppu_opcode_t);
|
||||
void VSUBUHS(ppu_opcode_t);
|
||||
void VSUBUWM(ppu_opcode_t);
|
||||
void VSUBUWS(ppu_opcode_t);
|
||||
void VSUMSWS(ppu_opcode_t);
|
||||
void VSUM2SWS(ppu_opcode_t);
|
||||
void VSUM4SBS(ppu_opcode_t);
|
||||
void VSUM4SHS(ppu_opcode_t);
|
||||
void VSUM4UBS(ppu_opcode_t);
|
||||
void VUPKHPX(ppu_opcode_t);
|
||||
void VUPKHSB(ppu_opcode_t);
|
||||
void VUPKHSH(ppu_opcode_t);
|
||||
void VUPKLPX(ppu_opcode_t);
|
||||
void VUPKLSB(ppu_opcode_t);
|
||||
void VUPKLSH(ppu_opcode_t);
|
||||
void VXOR(ppu_opcode_t);
|
||||
void TDI(ppu_opcode_t);
|
||||
void TWI(ppu_opcode_t);
|
||||
void MULLI(ppu_opcode_t);
|
||||
void SUBFIC(ppu_opcode_t);
|
||||
void CMPLI(ppu_opcode_t);
|
||||
void CMPI(ppu_opcode_t);
|
||||
void ADDIC(ppu_opcode_t);
|
||||
void ADDI(ppu_opcode_t);
|
||||
void ADDIS(ppu_opcode_t);
|
||||
void BC(ppu_opcode_t);
|
||||
void SC(ppu_opcode_t);
|
||||
void B(ppu_opcode_t);
|
||||
void MCRF(ppu_opcode_t);
|
||||
void BCLR(ppu_opcode_t);
|
||||
void CRNOR(ppu_opcode_t);
|
||||
void CRANDC(ppu_opcode_t);
|
||||
void ISYNC(ppu_opcode_t);
|
||||
void CRXOR(ppu_opcode_t);
|
||||
void CRNAND(ppu_opcode_t);
|
||||
void CRAND(ppu_opcode_t);
|
||||
void CREQV(ppu_opcode_t);
|
||||
void CRORC(ppu_opcode_t);
|
||||
void CROR(ppu_opcode_t);
|
||||
void BCCTR(ppu_opcode_t);
|
||||
void RLWIMI(ppu_opcode_t);
|
||||
void RLWINM(ppu_opcode_t);
|
||||
void RLWNM(ppu_opcode_t);
|
||||
void ORI(ppu_opcode_t);
|
||||
void ORIS(ppu_opcode_t);
|
||||
void XORI(ppu_opcode_t);
|
||||
void XORIS(ppu_opcode_t);
|
||||
void ANDI(ppu_opcode_t);
|
||||
void ANDIS(ppu_opcode_t);
|
||||
void RLDICL(ppu_opcode_t);
|
||||
void RLDICR(ppu_opcode_t);
|
||||
void RLDIC(ppu_opcode_t);
|
||||
void RLDIMI(ppu_opcode_t);
|
||||
void RLDCL(ppu_opcode_t);
|
||||
void RLDCR(ppu_opcode_t);
|
||||
void CMP(ppu_opcode_t);
|
||||
void TW(ppu_opcode_t);
|
||||
void LVSL(ppu_opcode_t);
|
||||
void LVEBX(ppu_opcode_t);
|
||||
void SUBFC(ppu_opcode_t);
|
||||
void ADDC(ppu_opcode_t);
|
||||
void MULHDU(ppu_opcode_t);
|
||||
void MULHWU(ppu_opcode_t);
|
||||
void MFOCRF(ppu_opcode_t);
|
||||
void LWARX(ppu_opcode_t);
|
||||
void LDX(ppu_opcode_t);
|
||||
void LWZX(ppu_opcode_t);
|
||||
void SLW(ppu_opcode_t);
|
||||
void CNTLZW(ppu_opcode_t);
|
||||
void SLD(ppu_opcode_t);
|
||||
void AND(ppu_opcode_t);
|
||||
void CMPL(ppu_opcode_t);
|
||||
void LVSR(ppu_opcode_t);
|
||||
void LVEHX(ppu_opcode_t);
|
||||
void SUBF(ppu_opcode_t);
|
||||
void LDUX(ppu_opcode_t);
|
||||
void DCBST(ppu_opcode_t);
|
||||
void LWZUX(ppu_opcode_t);
|
||||
void CNTLZD(ppu_opcode_t);
|
||||
void ANDC(ppu_opcode_t);
|
||||
void TD(ppu_opcode_t);
|
||||
void LVEWX(ppu_opcode_t);
|
||||
void MULHD(ppu_opcode_t);
|
||||
void MULHW(ppu_opcode_t);
|
||||
void LDARX(ppu_opcode_t);
|
||||
void DCBF(ppu_opcode_t);
|
||||
void LBZX(ppu_opcode_t);
|
||||
void LVX(ppu_opcode_t);
|
||||
void NEG(ppu_opcode_t);
|
||||
void LBZUX(ppu_opcode_t);
|
||||
void NOR(ppu_opcode_t);
|
||||
void STVEBX(ppu_opcode_t);
|
||||
void SUBFE(ppu_opcode_t);
|
||||
void ADDE(ppu_opcode_t);
|
||||
void MTOCRF(ppu_opcode_t);
|
||||
void STDX(ppu_opcode_t);
|
||||
void STWCX(ppu_opcode_t);
|
||||
void STWX(ppu_opcode_t);
|
||||
void STVEHX(ppu_opcode_t);
|
||||
void STDUX(ppu_opcode_t);
|
||||
void STWUX(ppu_opcode_t);
|
||||
void STVEWX(ppu_opcode_t);
|
||||
void SUBFZE(ppu_opcode_t);
|
||||
void ADDZE(ppu_opcode_t);
|
||||
void STDCX(ppu_opcode_t);
|
||||
void STBX(ppu_opcode_t);
|
||||
void STVX(ppu_opcode_t);
|
||||
void SUBFME(ppu_opcode_t);
|
||||
void MULLD(ppu_opcode_t);
|
||||
void ADDME(ppu_opcode_t);
|
||||
void MULLW(ppu_opcode_t);
|
||||
void DCBTST(ppu_opcode_t);
|
||||
void STBUX(ppu_opcode_t);
|
||||
void ADD(ppu_opcode_t);
|
||||
void DCBT(ppu_opcode_t);
|
||||
void LHZX(ppu_opcode_t);
|
||||
void EQV(ppu_opcode_t);
|
||||
void ECIWX(ppu_opcode_t);
|
||||
void LHZUX(ppu_opcode_t);
|
||||
void XOR(ppu_opcode_t);
|
||||
void MFSPR(ppu_opcode_t);
|
||||
void LWAX(ppu_opcode_t);
|
||||
void DST(ppu_opcode_t);
|
||||
void LHAX(ppu_opcode_t);
|
||||
void LVXL(ppu_opcode_t);
|
||||
void MFTB(ppu_opcode_t);
|
||||
void LWAUX(ppu_opcode_t);
|
||||
void DSTST(ppu_opcode_t);
|
||||
void LHAUX(ppu_opcode_t);
|
||||
void STHX(ppu_opcode_t);
|
||||
void ORC(ppu_opcode_t);
|
||||
void ECOWX(ppu_opcode_t);
|
||||
void STHUX(ppu_opcode_t);
|
||||
void OR(ppu_opcode_t);
|
||||
void DIVDU(ppu_opcode_t);
|
||||
void DIVWU(ppu_opcode_t);
|
||||
void MTSPR(ppu_opcode_t);
|
||||
void DCBI(ppu_opcode_t);
|
||||
void NAND(ppu_opcode_t);
|
||||
void STVXL(ppu_opcode_t);
|
||||
void DIVD(ppu_opcode_t);
|
||||
void DIVW(ppu_opcode_t);
|
||||
void LVLX(ppu_opcode_t);
|
||||
void LDBRX(ppu_opcode_t);
|
||||
void LSWX(ppu_opcode_t);
|
||||
void LWBRX(ppu_opcode_t);
|
||||
void LFSX(ppu_opcode_t);
|
||||
void SRW(ppu_opcode_t);
|
||||
void SRD(ppu_opcode_t);
|
||||
void LVRX(ppu_opcode_t);
|
||||
void LSWI(ppu_opcode_t);
|
||||
void LFSUX(ppu_opcode_t);
|
||||
void SYNC(ppu_opcode_t);
|
||||
void LFDX(ppu_opcode_t);
|
||||
void LFDUX(ppu_opcode_t);
|
||||
void STVLX(ppu_opcode_t);
|
||||
void STDBRX(ppu_opcode_t);
|
||||
void STSWX(ppu_opcode_t);
|
||||
void STWBRX(ppu_opcode_t);
|
||||
void STFSX(ppu_opcode_t);
|
||||
void STVRX(ppu_opcode_t);
|
||||
void STFSUX(ppu_opcode_t);
|
||||
void STSWI(ppu_opcode_t);
|
||||
void STFDX(ppu_opcode_t);
|
||||
void STFDUX(ppu_opcode_t);
|
||||
void LVLXL(ppu_opcode_t);
|
||||
void LHBRX(ppu_opcode_t);
|
||||
void SRAW(ppu_opcode_t);
|
||||
void SRAD(ppu_opcode_t);
|
||||
void LVRXL(ppu_opcode_t);
|
||||
void DSS(ppu_opcode_t);
|
||||
void SRAWI(ppu_opcode_t);
|
||||
void SRADI(ppu_opcode_t);
|
||||
void EIEIO(ppu_opcode_t);
|
||||
void STVLXL(ppu_opcode_t);
|
||||
void STHBRX(ppu_opcode_t);
|
||||
void EXTSH(ppu_opcode_t);
|
||||
void STVRXL(ppu_opcode_t);
|
||||
void EXTSB(ppu_opcode_t);
|
||||
void STFIWX(ppu_opcode_t);
|
||||
void EXTSW(ppu_opcode_t);
|
||||
void ICBI(ppu_opcode_t);
|
||||
void DCBZ(ppu_opcode_t);
|
||||
void LWZ(ppu_opcode_t);
|
||||
void LWZU(ppu_opcode_t);
|
||||
void LBZ(ppu_opcode_t);
|
||||
void LBZU(ppu_opcode_t);
|
||||
void STW(ppu_opcode_t);
|
||||
void STWU(ppu_opcode_t);
|
||||
void STB(ppu_opcode_t);
|
||||
void STBU(ppu_opcode_t);
|
||||
void LHZ(ppu_opcode_t);
|
||||
void LHZU(ppu_opcode_t);
|
||||
void LHA(ppu_opcode_t);
|
||||
void LHAU(ppu_opcode_t);
|
||||
void STH(ppu_opcode_t);
|
||||
void STHU(ppu_opcode_t);
|
||||
void LMW(ppu_opcode_t);
|
||||
void STMW(ppu_opcode_t);
|
||||
void LFS(ppu_opcode_t);
|
||||
void LFSU(ppu_opcode_t);
|
||||
void LFD(ppu_opcode_t);
|
||||
void LFDU(ppu_opcode_t);
|
||||
void STFS(ppu_opcode_t);
|
||||
void STFSU(ppu_opcode_t);
|
||||
void STFD(ppu_opcode_t);
|
||||
void STFDU(ppu_opcode_t);
|
||||
void LD(ppu_opcode_t);
|
||||
void LDU(ppu_opcode_t);
|
||||
void LWA(ppu_opcode_t);
|
||||
void STD(ppu_opcode_t);
|
||||
void STDU(ppu_opcode_t);
|
||||
void FDIVS(ppu_opcode_t);
|
||||
void FSUBS(ppu_opcode_t);
|
||||
void FADDS(ppu_opcode_t);
|
||||
void FSQRTS(ppu_opcode_t);
|
||||
void FRES(ppu_opcode_t);
|
||||
void FMULS(ppu_opcode_t);
|
||||
void FMADDS(ppu_opcode_t);
|
||||
void FMSUBS(ppu_opcode_t);
|
||||
void FNMSUBS(ppu_opcode_t);
|
||||
void FNMADDS(ppu_opcode_t);
|
||||
void MTFSB1(ppu_opcode_t);
|
||||
void MCRFS(ppu_opcode_t);
|
||||
void MTFSB0(ppu_opcode_t);
|
||||
void MTFSFI(ppu_opcode_t);
|
||||
void MFFS(ppu_opcode_t);
|
||||
void MTFSF(ppu_opcode_t);
|
||||
void FCMPU(ppu_opcode_t);
|
||||
void FRSP(ppu_opcode_t);
|
||||
void FCTIW(ppu_opcode_t);
|
||||
void FCTIWZ(ppu_opcode_t);
|
||||
void FDIV(ppu_opcode_t);
|
||||
void FSUB(ppu_opcode_t);
|
||||
void FADD(ppu_opcode_t);
|
||||
void FSQRT(ppu_opcode_t);
|
||||
void FSEL(ppu_opcode_t);
|
||||
void FMUL(ppu_opcode_t);
|
||||
void FRSQRTE(ppu_opcode_t);
|
||||
void FMSUB(ppu_opcode_t);
|
||||
void FMADD(ppu_opcode_t);
|
||||
void FNMSUB(ppu_opcode_t);
|
||||
void FNMADD(ppu_opcode_t);
|
||||
void FCMPO(ppu_opcode_t);
|
||||
void FNEG(ppu_opcode_t);
|
||||
void FMR(ppu_opcode_t);
|
||||
void FNABS(ppu_opcode_t);
|
||||
void FABS(ppu_opcode_t);
|
||||
void FCTID(ppu_opcode_t);
|
||||
void FCTIDZ(ppu_opcode_t);
|
||||
void FCFID(ppu_opcode_t);
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, const ppu_mod
|
||||
, m_module(module)
|
||||
, m_is_be(false)
|
||||
, m_info(info)
|
||||
, m_pure_attr(AttributeSet::get(m_context, AttributeSet::FunctionIndex, {Attribute::NoUnwind, Attribute::ReadNone}))
|
||||
, m_pure_attr(AttributeList::get(m_context,AttributeList::FunctionIndex, AttrBuilder().addAttribute(Attribute::NoUnwind).addAttribute(Attribute::ReadNone)))
|
||||
{
|
||||
// There is no weak linkage on JIT, so let's create variables with different names for each module part
|
||||
const u32 gsuffix = m_info.name.empty() ? info.funcs[0].addr : info.funcs[0].addr - m_info.segs[0].addr;
|
||||
@@ -133,7 +133,7 @@ Function* PPUTranslator::Translate(const ppu_function& info)
|
||||
const u64 base = m_reloc ? m_reloc->addr : 0;
|
||||
m_addr = info.addr - base;
|
||||
|
||||
m_thread = &*m_function->getArgumentList().begin();
|
||||
m_thread = &*m_function->arg_begin();
|
||||
m_base_loaded = m_ir->CreateLoad(m_base);
|
||||
|
||||
m_body = BasicBlock::Create(m_context, "__body", m_function);
|
||||
@@ -290,7 +290,7 @@ Value* PPUTranslator::RegInit(Value*& local)
|
||||
|
||||
Value* PPUTranslator::RegLoad(Value*& local)
|
||||
{
|
||||
const uint index = ::narrow<uint>(&local - m_locals);
|
||||
const auto index = ::narrow<uint>(&local - m_locals);
|
||||
|
||||
if (local)
|
||||
{
|
||||
@@ -315,7 +315,7 @@ void PPUTranslator::FlushRegisters()
|
||||
|
||||
for (auto& local : m_locals)
|
||||
{
|
||||
const uint index = static_cast<uint>(&local - m_locals);
|
||||
const auto index = ::narrow<uint>(&local - m_locals);
|
||||
|
||||
// Store value if necessary
|
||||
if (local && m_globals[index])
|
||||
|
||||
@@ -120,7 +120,7 @@ class PPUTranslator final //: public CPUTranslator
|
||||
std::map<u64, const ppu_reloc*> m_relocs;
|
||||
|
||||
// Attributes for function calls which are "pure" and may be optimized away if their results are unused
|
||||
const llvm::AttributeSet m_pure_attr;
|
||||
const llvm::AttributeList m_pure_attr;
|
||||
|
||||
// IR builder
|
||||
llvm::IRBuilder<>* m_ir;
|
||||
@@ -404,7 +404,7 @@ public:
|
||||
|
||||
// Call a function with attribute list
|
||||
template<typename... Args>
|
||||
llvm::CallInst* Call(llvm::Type* ret, llvm::AttributeSet attr, llvm::StringRef name, Args... args)
|
||||
llvm::CallInst* Call(llvm::Type* ret, llvm::AttributeList attr, llvm::StringRef name, Args... args)
|
||||
{
|
||||
// Call the function
|
||||
return m_ir->CreateCall(m_module->getOrInsertFunction(name, llvm::FunctionType::get(ret, {args->getType()...}, false), attr), {args...});
|
||||
@@ -414,7 +414,7 @@ public:
|
||||
template<typename... Args>
|
||||
llvm::CallInst* Call(llvm::Type* ret, llvm::StringRef name, Args... args)
|
||||
{
|
||||
return Call(ret, llvm::AttributeSet{}, name, args...);
|
||||
return Call(ret, llvm::AttributeList{}, name, args...);
|
||||
}
|
||||
|
||||
// Handle compilation errors
|
||||
|
||||
@@ -999,6 +999,7 @@ u32 SPUThread::get_ch_count(u32 ch)
|
||||
case SPU_RdSigNotify2: return ch_snr2.get_count();
|
||||
case MFC_RdAtomicStat: return ch_atomic_stat.get_count();
|
||||
case SPU_RdEventStat: return get_events() != 0;
|
||||
case MFC_Cmd: return std::max(16 - mfc_queue.size(), (u32)0);
|
||||
}
|
||||
|
||||
fmt::throw_exception("Unknown/illegal channel (ch=%d [%s])" HERE, ch, ch < 128 ? spu_ch_name[ch] : "???");
|
||||
@@ -1434,8 +1435,9 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
|
||||
case MFC_Cmd:
|
||||
{
|
||||
ch_mfc_cmd.cmd = MFC(value & 0xff);
|
||||
auto cmd = ch_mfc_cmd; // save and restore previous command arguments
|
||||
process_mfc_cmd();
|
||||
ch_mfc_cmd = {}; // clear non-persistent data
|
||||
ch_mfc_cmd = cmd;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1326,6 +1326,7 @@ error_code sys_fs_disk_free(vm::ps3::cptr<char> path, vm::ptr<u64> total_free, v
|
||||
error_code sys_fs_utime(vm::ps3::cptr<char> path, vm::ps3::cptr<CellFsUtimbuf> timep)
|
||||
{
|
||||
sys_fs.warning("sys_fs_utime(path=%s, timep=*0x%x)", path, timep);
|
||||
sys_fs.warning("** actime=%u, modtime=%u", timep->actime, timep->modtime);
|
||||
|
||||
const std::string local_path = vfs::get(path.get_ptr());
|
||||
|
||||
|
||||
@@ -1237,6 +1237,18 @@ s32 sys_net_bnet_setsockopt(ppu_thread& ppu, s32 s, s32 level, s32 optname, vm::
|
||||
native_linger.l_linger = ((const sys_net_linger*)optval.get_ptr())->l_linger;
|
||||
break;
|
||||
}
|
||||
case SYS_NET_SO_USECRYPTO:
|
||||
{
|
||||
//TODO
|
||||
sys_net.error("sys_net_bnet_setsockopt(s=%d, SOL_SOCKET): Stubbed option (0x%x) (SYS_NET_SO_USECRYPTO)", s, optname);
|
||||
return 0;
|
||||
}
|
||||
case SYS_NET_SO_USESIGNATURE:
|
||||
{
|
||||
//TODO
|
||||
sys_net.error("sys_net_bnet_setsockopt(s=%d, SOL_SOCKET): Stubbed option (0x%x) (SYS_NET_SO_USESIGNATURE)", s, optname);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
{
|
||||
sys_net.error("sys_net_bnet_setsockopt(s=%d, SOL_SOCKET): unknown option (0x%x)", s, optname);
|
||||
@@ -1333,24 +1345,38 @@ s32 sys_net_bnet_socket(ppu_thread& ppu, s32 family, s32 type, s32 protocol)
|
||||
{
|
||||
sys_net.warning("sys_net_bnet_socket(family=%d, type=%d, protocol=%d)", family, type, protocol);
|
||||
|
||||
if (family != SYS_NET_AF_INET)
|
||||
if (family != SYS_NET_AF_INET && family != SYS_NET_AF_UNSPEC)
|
||||
{
|
||||
sys_net.error("sys_net_bnet_socket(): unknown family (%d)", family);
|
||||
}
|
||||
|
||||
if (type != SYS_NET_SOCK_STREAM && type != SYS_NET_SOCK_DGRAM)
|
||||
if (type != SYS_NET_SOCK_STREAM && type != SYS_NET_SOCK_DGRAM && type != SYS_NET_SOCK_DGRAM_P2P)
|
||||
{
|
||||
sys_net.error("sys_net_bnet_socket(): unsupported type (%d)", type);
|
||||
return -SYS_NET_EPROTONOSUPPORT;
|
||||
}
|
||||
|
||||
const int native_domain = AF_INET;
|
||||
const int native_domain = family == SYS_NET_AF_INET ? AF_INET :
|
||||
family == SYS_NET_AF_UNSPEC ? AF_UNSPEC : AF_INET;
|
||||
|
||||
const int native_type =
|
||||
type == SYS_NET_SOCK_STREAM ? SOCK_STREAM :
|
||||
type == SYS_NET_SOCK_DGRAM ? SOCK_DGRAM : SOCK_RAW;
|
||||
const int native_proto =
|
||||
type == SYS_NET_SOCK_STREAM ? 0 :
|
||||
type == SYS_NET_SOCK_DGRAM ? 0 : 0;
|
||||
type == SYS_NET_SOCK_DGRAM ? SOCK_DGRAM :
|
||||
type == SYS_NET_SOCK_DGRAM_P2P ? SOCK_DGRAM : SOCK_RAW;
|
||||
|
||||
int native_proto =
|
||||
protocol == SYS_NET_IPPROTO_IP ? IPPROTO_IP :
|
||||
protocol == SYS_NET_IPPROTO_ICMP ? IPPROTO_ICMP :
|
||||
protocol == SYS_NET_IPPROTO_IGMP ? IPPROTO_IGMP :
|
||||
protocol == SYS_NET_IPPROTO_TCP ? IPPROTO_TCP :
|
||||
protocol == SYS_NET_IPPROTO_UDP ? IPPROTO_UDP :
|
||||
protocol == SYS_NET_IPPROTO_ICMPV6 ? IPPROTO_ICMPV6 : 0;
|
||||
|
||||
if (native_domain == AF_UNSPEC && type == SYS_NET_SOCK_DGRAM)
|
||||
{
|
||||
//Windows gets all errory if you try a unspec socket with protocol 0
|
||||
native_proto = IPPROTO_UDP;
|
||||
}
|
||||
|
||||
const auto native_socket = ::socket(native_domain, native_type, native_proto);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
#include "Crypto/unedat.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "sys_lwmutex.h"
|
||||
@@ -288,19 +289,23 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
|
||||
|
||||
vm::temporary_unlock(ppu);
|
||||
|
||||
Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data)]()
|
||||
Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data), disc = vfs::get("/dev_bdvd/"), klic = fxm::get_always<LoadedNpdrmKeys_t>()->devKlic]() mutable
|
||||
{
|
||||
sys_process.success("Process finished -> %s", argv[0]);
|
||||
Emu.SetForceBoot(true);
|
||||
Emu.Stop();
|
||||
Emu.argv = std::move(argv);
|
||||
Emu.envp = std::move(envp);
|
||||
Emu.data = std::move(data);
|
||||
Emu.BootGame(path, true);
|
||||
Emu.disc = std::move(disc);
|
||||
|
||||
if (Emu.IsReady() && !g_cfg.misc.autostart)
|
||||
if (klic != std::array<u8, 16>{})
|
||||
{
|
||||
Emu.Run();
|
||||
Emu.klic.assign(klic.begin(), klic.end());
|
||||
}
|
||||
|
||||
Emu.SetForceBoot(true);
|
||||
Emu.BootGame(path, true);
|
||||
});
|
||||
|
||||
thread_ctrl::eternalize();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Crypto/unedat.h"
|
||||
#include "sys_fs.h"
|
||||
#include "sys_prx.h"
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
@@ -76,13 +77,11 @@ static const std::unordered_map<std::string, int> s_prx_ignore
|
||||
{ "/dev_flash/sys/external/libsysutil_video_export.sprx", 0 },
|
||||
{ "/dev_flash/sys/external/libsysutil_video_player.sprx", 0 },
|
||||
{ "/dev_flash/sys/external/libsysutil_video_upload.sprx", 0 },
|
||||
{ "/dev_flash/sys/external/libusbd.sprx", 0 },
|
||||
{ "/dev_flash/sys/external/libusbpspcm.sprx", 0 },
|
||||
{ "/dev_flash/sys/external/libvdec.sprx", 0 },
|
||||
{ "/dev_flash/sys/external/libvoice.sprx", 0 },
|
||||
};
|
||||
|
||||
error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt)
|
||||
static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt, fs::file src = {})
|
||||
{
|
||||
std::string name = vpath.substr(vpath.find_last_of('/') + 1);
|
||||
std::string path = vfs::get(vpath);
|
||||
@@ -125,7 +124,12 @@ error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<sys_prx_
|
||||
return not_an_error(idm::last_id());
|
||||
}
|
||||
|
||||
const ppu_prx_object obj = decrypt_self(fs::file(path), fxm::get_always<LoadedNpdrmKeys_t>()->devKlic.data());
|
||||
if (!src)
|
||||
{
|
||||
src.open(path);
|
||||
}
|
||||
|
||||
const ppu_prx_object obj = decrypt_self(std::move(src), fxm::get_always<LoadedNpdrmKeys_t>()->devKlic.data());
|
||||
|
||||
if (obj != elf_error::ok)
|
||||
{
|
||||
@@ -154,14 +158,23 @@ error_code sys_prx_get_ppu_guid()
|
||||
|
||||
error_code _sys_prx_load_module_by_fd(s32 fd, u64 offset, u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt)
|
||||
{
|
||||
sys_prx.todo("_sys_prx_load_module_by_fd(fd=%d, offset=0x%x, flags=0x%x, pOpt=*0x%x)", fd, offset, flags, pOpt);
|
||||
return CELL_OK;
|
||||
sys_prx.warning("_sys_prx_load_module_by_fd(fd=%d, offset=0x%x, flags=0x%x, pOpt=*0x%x)", fd, offset, flags, pOpt);
|
||||
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
return prx_load_module(fmt::format("%s_x%x", file->name.data(), offset), flags, pOpt, lv2_file::make_view(file, offset));
|
||||
}
|
||||
|
||||
error_code _sys_prx_load_module_on_memcontainer_by_fd(s32 fd, u64 offset, u32 mem_ct, u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt)
|
||||
{
|
||||
sys_prx.todo("_sys_prx_load_module_on_memcontainer_by_fd(fd=%d, offset=0x%x, mem_ct=0x%x, flags=0x%x, pOpt=*0x%x)", fd, offset, mem_ct, flags, pOpt);
|
||||
return CELL_OK;
|
||||
sys_prx.warning("_sys_prx_load_module_on_memcontainer_by_fd(fd=%d, offset=0x%x, mem_ct=0x%x, flags=0x%x, pOpt=*0x%x)", fd, offset, mem_ct, flags, pOpt);
|
||||
|
||||
return _sys_prx_load_module_by_fd(fd, offset, flags, pOpt);
|
||||
}
|
||||
|
||||
error_code _sys_prx_load_module_list(s32 count, vm::cpptr<char, u32, u64> path_list, u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt, vm::ptr<u32> id_list)
|
||||
@@ -268,7 +281,7 @@ error_code _sys_prx_unload_module(u32 id, u64 flags, vm::ptr<sys_prx_unload_modu
|
||||
ppu_unload_prx(*prx);
|
||||
|
||||
//s32 result = prx->exit ? prx->exit() : CELL_OK;
|
||||
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -242,6 +242,7 @@ s32 sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64 a4, u6
|
||||
case 0x001: // FIFO
|
||||
render->ctrl->get = a3;
|
||||
render->ctrl->put = a4;
|
||||
render->internal_get = a3;
|
||||
break;
|
||||
|
||||
case 0x100: // Display mode set
|
||||
@@ -322,6 +323,12 @@ s32 sys_rsx_context_attribute(s32 context_id, u32 package_id, u64 a3, u64 a4, u6
|
||||
//a5 low bits = ret.format = base | ((base + ((size - 1) / 0x10000)) << 13) | (comp << 26) | (1 << 30);
|
||||
|
||||
auto& tile = render->tiles[a3];
|
||||
|
||||
// When tile is going to be unbinded, we can use it as a hint that the address will no longer be used as a surface and can be removed/invalidated
|
||||
// Todo: There may be more checks such as format/size/width can could be done
|
||||
if (tile.binded && a5 == 0)
|
||||
render->notify_tile_unbound(a3);
|
||||
|
||||
tile.location = ((a4 >> 32) & 0xF) - 1;
|
||||
tile.offset = ((((a4 >> 32) & 0xFFFFFFFF) >> 16) * 0x10000);
|
||||
tile.size = ((((a4 & 0x7FFFFFFF) >> 16) + 1) * 0x10000) - tile.offset;
|
||||
|
||||
@@ -296,7 +296,7 @@ error_code sys_spu_thread_group_create(vm::ptr<u32> id, u32 num, s32 prio, vm::p
|
||||
|
||||
// TODO: max num value should be affected by sys_spu_initialize() settings
|
||||
|
||||
if (!num || num > 6 || prio < 16 || prio > 255)
|
||||
if (attr->nsize > 0x80 || !num || num > 6 || ((prio < 16 || prio > 255) && attr->type != SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT))
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
@@ -306,7 +306,7 @@ error_code sys_spu_thread_group_create(vm::ptr<u32> id, u32 num, s32 prio, vm::p
|
||||
sys_spu.todo("Unsupported SPU Thread Group type (0x%x)", attr->type);
|
||||
}
|
||||
|
||||
*id = idm::make<lv2_spu_group>(std::string(attr->name.get_ptr(), attr->nsize - 1), num, prio, attr->type, attr->ct);
|
||||
*id = idm::make<lv2_spu_group>(std::string(attr->name.get_ptr(), std::max<u32>(attr->nsize, 1) - 1), num, prio, attr->type, attr->ct);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ const HCRYPTPROV s_crypto_provider = []() -> HCRYPTPROV
|
||||
{
|
||||
HCRYPTPROV result;
|
||||
|
||||
if (!CryptAcquireContextW(&result, nullptr, nullptr, PROV_RSA_FULL, 0))
|
||||
if (!CryptAcquireContextW(&result, nullptr, nullptr, PROV_RSA_FULL, 0) && !CryptAcquireContextW(&result, nullptr, nullptr, PROV_RSA_FULL, CRYPT_NEWKEYSET))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
+157
-21
@@ -4,14 +4,37 @@
|
||||
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "sys_usbd.h"
|
||||
|
||||
#include "sys_ppu_thread.h"
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
logs::channel sys_usbd("sys_usbd");
|
||||
|
||||
s32 sys_usbd_initialize()
|
||||
std::vector<usbDevice> devices = {
|
||||
// System devices
|
||||
usbDevice{ DeviceListUnknownDataType{0x1, 0x2, 0x2, 0x44}, 50, deviceDescriptor{0x12, 0x1, 0x0200, 0, 0, 0, 0x40, 0x054C, 0x0250, 0x0009, 3, 4, 5, 1}},
|
||||
usbDevice{ DeviceListUnknownDataType{0x1, 0x3, 0x2, 0xAD}, 326, deviceDescriptor{0x12, 0x1, 0x0200, 0xE0, 0x1, 0x1, 0x40, 0x054C, 0x0267, 0x0001, 1, 2, 0, 1}},
|
||||
// USB Drive
|
||||
usbDevice{ DeviceListUnknownDataType{0x0, 0x4, 0x2, 0x0}, 50, deviceDescriptor{0x12, 0x1, 0x0200, 0, 0, 0, 0x40, 0x1516, 0x1226, 0x0100, 1, 2, 3, 1}},
|
||||
// Skylanders Portal
|
||||
usbDevice{ DeviceListUnknownDataType{0x0, 0x5, 0x2, 0xF8}, 59, deviceDescriptor{0x12, 0x1, 0x0200, 0, 0, 0, 0x20, 0x1430, 0x0150, 0x0100, 1, 2, 0, 1}},
|
||||
};
|
||||
|
||||
bool has_register_extra_ldd;
|
||||
int receive_event_called_count;
|
||||
|
||||
/*
|
||||
* sys_usbd_initialize changes handle to a semi-unique identifier.
|
||||
* identifier generation is speculated to be:
|
||||
* f(n+1) = (f(n) + 2<<9) % 14847 + f_min (probably just f(0)-14847 but it could be different)
|
||||
* Thanks to @flash-fire for thinking up the identifier generation code.
|
||||
* TODO: try to get hardware to return not CELL_OK (perhaps set handle to NULL).
|
||||
*/
|
||||
s32 sys_usbd_initialize(vm::ptr<u32> handle)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_initialize()");
|
||||
sys_usbd.warning("sys_usbd_initialize(0x%x)", handle.get_ptr());
|
||||
*handle = 805322496;
|
||||
has_register_extra_ldd = false;
|
||||
receive_event_called_count = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -21,21 +44,118 @@ s32 sys_usbd_finalize()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_device_list()
|
||||
s32 sys_usbd_get_device_list(u32 handle, vm::ptr<DeviceListUnknownDataType> device_list, char unknown)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_get_device_list()");
|
||||
return CELL_OK;
|
||||
sys_usbd.warning("sys_usbd_get_device_list(handle=%d, device_list=0x%x, unknown=%c)", handle, device_list, unknown);
|
||||
// Unknown is just 0x7F
|
||||
for (int i = 0; i < devices.size(); i++)
|
||||
{
|
||||
device_list[i] = devices[i].basicDevice;
|
||||
}
|
||||
return devices.size();
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_descriptor_size()
|
||||
s32 sys_usbd_register_extra_ldd(u32 handle, vm::ptr<void> lddOps, u16 strLen, u16 vendorID, u16 productID, u16 unk1)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_get_descriptor_size()");
|
||||
return CELL_OK;
|
||||
sys_usbd.warning("sys_usbd_register_extra_ldd(handle=%u, lddOps=0x%x, unk1=%u, vendorID=%u, productID=%u, unk2=%u)", handle, lddOps, strLen, vendorID, productID, unk1);
|
||||
has_register_extra_ldd = true;
|
||||
|
||||
// No idea what 9 means.
|
||||
return 9;
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_descriptor()
|
||||
s32 sys_usbd_get_descriptor_size(u32 handle, u8 deviceNumber)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_get_descriptor()");
|
||||
sys_usbd.warning("sys_usbd_get_descriptor_size(handle=%u, deviceNumber=%u)", handle, deviceNumber);
|
||||
return devices[deviceNumber-2].descSize;
|
||||
}
|
||||
|
||||
s32 sys_usbd_get_descriptor(u32 handle, u8 deviceNumber, vm::ptr<deviceDescriptor> descriptor, s64 descSize)
|
||||
{
|
||||
sys_usbd.warning("sys_usbd_get_descriptor(handle=%u, deviceNumber=%u, descriptor=0x%x, descSize=%u)", handle, deviceNumber, descriptor, descSize);
|
||||
|
||||
// Just gonna have to hack it for now
|
||||
|
||||
u8 device = deviceNumber-2;
|
||||
unsigned char* desc = (unsigned char*)descriptor.get_ptr();
|
||||
if (device == 0)
|
||||
{
|
||||
unsigned char bytes[] = {0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x40, 0x4c, 0x5, 0x50, 0x2, 0x9, 0x0, 0x3, 0x4, 0x5, 0x1,
|
||||
0x9, 0x2, 0x20, 0x0, 0x1, 0x1, 0x0, 0x80, 0xfa,
|
||||
0x9, 0x4, 0x0, 0x0, 0x2, 0x8, 0x5, 0x50, 0x0,
|
||||
0x7, 0x5, 0x81, 0x2, 0x0, 0x2, 0x0,
|
||||
0x7, 0x5, 0x2, 0x2, 0x0, 0x2, 0x0};
|
||||
memcpy(desc, bytes, descSize);
|
||||
}
|
||||
else if (device == 1)
|
||||
{
|
||||
unsigned char bytes[] = {0x12, 0x1, 0x0, 0x2, 0xe0, 0x1, 0x1, 0x40, 0x4c, 0x5, 0x67, 0x2, 0x0, 0x1, 0x1, 0x2, 0x0, 0x1,
|
||||
0x9, 0x2, 0x34, 0x1, 0x4, 0x1, 0x0, 0xc0, 0x0,
|
||||
0x9, 0x4, 0x0, 0x0, 0x3, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x81, 0x3, 0x40, 0x0, 0x1,
|
||||
0x7, 0x5, 0x2, 0x2, 0x0, 0x2, 0x0,
|
||||
0x7, 0x5, 0x82, 0x2, 0x0, 0x2, 0x0,
|
||||
0x9, 0x4, 0x1, 0x0, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x0, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x0, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x1, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0xb, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0xb, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x2, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x13, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x13, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x3, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x1b, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x1b, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x4, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x23, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x23, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x5, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x33, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x33, 0x0, 0x4,
|
||||
0x9, 0x4, 0x1, 0x6, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x3, 0x1, 0x40, 0x0, 0x4,
|
||||
0x7, 0x5, 0x83, 0x1, 0x40, 0x0, 0x4,
|
||||
0x9, 0x4, 0x2, 0x0, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x4, 0x3, 0x40, 0x0, 0x1,
|
||||
0x7, 0x5, 0x85, 0x3, 0x40, 0x0, 0x1,
|
||||
0x9, 0x4, 0x2, 0x1, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x4, 0x3, 0x80, 0x0, 0x1,
|
||||
0x7, 0x5, 0x85, 0x3, 0x80, 0x0, 0x1,
|
||||
0x9, 0x4, 0x2, 0x2, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x4, 0x3, 0x0, 0x1, 0x1,
|
||||
0x7, 0x5, 0x85, 0x3, 0x0, 0x1, 0x1,
|
||||
0x9, 0x4, 0x2, 0x3, 0x2, 0xe0, 0x1, 0x1, 0x0,
|
||||
0x7, 0x5, 0x4, 0x3, 0x0, 0x4, 0x1,
|
||||
0x7, 0x5, 0x85, 0x3, 0x0, 0x4, 0x1,
|
||||
0x9, 0x4, 0x3, 0x0, 0x0, 0xfe, 0x1, 0x0, 0x0,
|
||||
0x7, 0x21, 0x7, 0x88, 0x13, 0xff, 0x3};
|
||||
|
||||
memcpy(desc, bytes, descSize);
|
||||
}
|
||||
else if (device == 2)
|
||||
{
|
||||
// USB Drive
|
||||
unsigned char bytes[] = {0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x40, 0x16, 0x15, 0x26, 0x12, 0x0, 0x1, 0x1, 0x2, 0x3, 0x1,
|
||||
0x9, 0x2, 0x20, 0x0, 0x1, 0x1, 0x0, 0x80, 0xfa,
|
||||
0x9, 0x4, 0x0, 0x0, 0x2, 0x8, 0x6, 0x50, 0x0,
|
||||
0x7, 0x5, 0x81, 0x2, 0x0, 0x2, 0x0,
|
||||
0x7, 0x5, 0x2, 0x2, 0x0, 0x2, 0x0};
|
||||
memcpy(desc, bytes, descSize);
|
||||
}
|
||||
else if (device == 3 || device == 4)
|
||||
{
|
||||
// Skylanders Portal
|
||||
unsigned char bytes[] = {0x12, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x20, 0x30, 0x14, 0x50, 0x1, 0x0, 0x1, 0x1, 0x2, 0x0, 0x1,
|
||||
0x9, 0x2, 0x29, 0x0, 0x1, 0x1, 0x0, 0x80, 0x96,
|
||||
0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0,
|
||||
0x9, 0x21, 0x11, 0x1, 0x0, 0x1, 0x22, 0x1d, 0x0,
|
||||
0x7, 0x5, 0x81, 0x3, 0x20, 0x0, 0x1,
|
||||
0x7, 0x5, 0x1, 0x3, 0x20, 0x0, 0x1};
|
||||
memcpy(desc, bytes, descSize);
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -51,9 +171,9 @@ s32 sys_usbd_unregister_ldd()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_open_pipe()
|
||||
s32 sys_usbd_open_pipe(u32 handle, vm::ptr<void> endDisc)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_open_pipe()");
|
||||
sys_usbd.todo("sys_usbd_open_pipe(handle=%d, endDisc=0x%x)", handle, endDisc);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -69,9 +189,31 @@ s32 sys_usbd_close_pipe()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_receive_event()
|
||||
s32 sys_usbd_receive_event(ppu_thread& ppu, u32 handle, vm::ptr<u64> arg1, vm::ptr<u64> arg2, vm::ptr<u64> arg3)
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_receive_event()");
|
||||
sys_usbd.todo("sys_usbd_receive_event(handle=%u, arg1=0x%x, arg2=0x%x, arg3=0x%x)", handle, arg1, arg2, arg3);
|
||||
|
||||
_sys_ppu_thread_exit(ppu, CELL_OK);
|
||||
|
||||
// TODO
|
||||
/*if (receive_event_called_count == 0)
|
||||
{
|
||||
*arg1 = 2;
|
||||
*arg2 = 5;
|
||||
*arg3 = 0;
|
||||
receive_event_called_count++;
|
||||
}
|
||||
else if (receive_event_called_count == 1)
|
||||
{
|
||||
*arg1 = 1;
|
||||
*arg2 = 6;
|
||||
*arg3 = 0;
|
||||
receive_event_called_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
_sys_ppu_thread_exit(ppu, CELL_OK);
|
||||
}*/
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -140,9 +282,3 @@ s32 sys_usbd_get_device_speed()
|
||||
sys_usbd.todo("sys_usbd_get_device_speed()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sys_usbd_register_extra_ldd()
|
||||
{
|
||||
sys_usbd.todo("sys_usbd_register_extra_ldd()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
// SysCalls
|
||||
|
||||
s32 sys_usbd_initialize();
|
||||
struct DeviceListUnknownDataType
|
||||
{
|
||||
u8 unk1;
|
||||
u8 deviceID;
|
||||
u8 unk3;
|
||||
u8 unk4;
|
||||
} ;
|
||||
|
||||
/* 0x01 device descriptor */
|
||||
struct deviceDescriptor
|
||||
{
|
||||
u8 bLength; /* descriptor size in bytes */
|
||||
u8 bDescriptorType; /* constant USB_DESCRIPTOR_TYPE_DEVICE */
|
||||
u16 bcdUSB; /* USB spec release compliance number */
|
||||
u8 bDeviceClass; /* class code */
|
||||
u8 bDeviceSubClass; /* subclass code */
|
||||
u8 bDeviceProtocol; /* protocol code */
|
||||
u8 bMaxPacketSize0; /* max packet size for endpoint 0 */
|
||||
u16 idVendor; /* USB-IF Vendor ID (VID) */
|
||||
u16 idProduct; /* Product ID (PID) */
|
||||
u16 bcdDevice; /* device release number */
|
||||
u8 iManufacturer; /* manufacturer string descriptor index */
|
||||
u8 iProduct; /* product string desccriptor index */
|
||||
u8 iSerialNumber; /* serial number string descriptor index */
|
||||
u8 bNumConfigurations; /* number of configurations */
|
||||
};
|
||||
|
||||
struct usbDevice
|
||||
{
|
||||
DeviceListUnknownDataType basicDevice;
|
||||
s32 descSize;
|
||||
deviceDescriptor descriptor;
|
||||
};
|
||||
|
||||
s32 sys_usbd_initialize(vm::ptr<u32> handle);
|
||||
s32 sys_usbd_finalize();
|
||||
s32 sys_usbd_get_device_list();
|
||||
s32 sys_usbd_get_descriptor_size();
|
||||
s32 sys_usbd_get_descriptor();
|
||||
s32 sys_usbd_get_device_list(u32 handle, vm::ptr<DeviceListUnknownDataType> device_list, char unknown);
|
||||
s32 sys_usbd_get_descriptor_size(u32 handle, u8 deviceNumber);
|
||||
s32 sys_usbd_get_descriptor(u32 handle, u8 deviceNumber, vm::ptr<deviceDescriptor> descriptor, s64 descSize);
|
||||
s32 sys_usbd_register_ldd();
|
||||
s32 sys_usbd_unregister_ldd();
|
||||
s32 sys_usbd_open_pipe();
|
||||
s32 sys_usbd_open_pipe(u32 handle, vm::ptr<void> endDisc);
|
||||
s32 sys_usbd_open_default_pipe();
|
||||
s32 sys_usbd_close_pipe();
|
||||
s32 sys_usbd_receive_event();
|
||||
s32 sys_usbd_receive_event(ppu_thread& ppu, u32 handle, vm::ptr<u64> arg1, vm::ptr<u64> arg2, vm::ptr<u64> arg3);
|
||||
s32 sys_usbd_detect_event();
|
||||
s32 sys_usbd_attach();
|
||||
s32 sys_usbd_transfer_data();
|
||||
@@ -24,4 +60,4 @@ s32 sys_usbd_send_event();
|
||||
s32 sys_usbd_allocate_memory();
|
||||
s32 sys_usbd_free_memory();
|
||||
s32 sys_usbd_get_device_speed();
|
||||
s32 sys_usbd_register_extra_ldd();
|
||||
s32 sys_usbd_register_extra_ldd(u32 handle, vm::ptr<void> lddOps, u16 strLen, u16 vendorID, u16 productID, u16 unk1);
|
||||
|
||||
+268
-1
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "stdafx.h"
|
||||
#include "../../Utilities/Config.h"
|
||||
#include "../../Utilities/types.h"
|
||||
|
||||
// TODO: HLE info (constants, structs, etc.) should not be available here
|
||||
@@ -159,6 +162,13 @@ struct Pad
|
||||
u32 m_device_capability;
|
||||
u32 m_device_type;
|
||||
|
||||
// Cable State: 0 - 1 plugged in ?
|
||||
u8 m_cable_state;
|
||||
|
||||
// DS4: 0 - 9 while unplugged, 0 - 10 while plugged in, 11 charge complete
|
||||
// XInput: 0 = Empty, 1 = Low, 2 = Medium, 3 = Full
|
||||
u8 m_battery_level;
|
||||
|
||||
std::vector<Button> m_buttons;
|
||||
std::vector<AnalogStick> m_sticks;
|
||||
std::vector<AnalogSensor> m_sensors;
|
||||
@@ -238,23 +248,280 @@ struct Pad
|
||||
}
|
||||
};
|
||||
|
||||
struct pad_config : cfg::node
|
||||
{
|
||||
std::string cfg_type = "";
|
||||
std::string cfg_name = "";
|
||||
|
||||
cfg::string ls_left { this, "Left Stick Left", "" };
|
||||
cfg::string ls_down { this, "Left Stick Down", "" };
|
||||
cfg::string ls_right{ this, "Left Stick Right", "" };
|
||||
cfg::string ls_up { this, "Left Stick Up", "" };
|
||||
cfg::string rs_left { this, "Right Stick Left", "" };
|
||||
cfg::string rs_down { this, "Right Stick Down", "" };
|
||||
cfg::string rs_right{ this, "Right Stick Right", "" };
|
||||
cfg::string rs_up { this, "Right Stick Up", "" };
|
||||
cfg::string start { this, "Start", "" };
|
||||
cfg::string select { this, "Select", "" };
|
||||
cfg::string ps { this, "PS Button", "" };
|
||||
cfg::string square { this, "Square", "" };
|
||||
cfg::string cross { this, "Cross", "" };
|
||||
cfg::string circle { this, "Circle", "" };
|
||||
cfg::string triangle{ this, "Triangle", "" };
|
||||
cfg::string left { this, "Left", "" };
|
||||
cfg::string down { this, "Down", "" };
|
||||
cfg::string right { this, "Right", "" };
|
||||
cfg::string up { this, "Up", "" };
|
||||
cfg::string r1 { this, "R1", "" };
|
||||
cfg::string r2 { this, "R2", "" };
|
||||
cfg::string r3 { this, "R3", "" };
|
||||
cfg::string l1 { this, "L1", "" };
|
||||
cfg::string l2 { this, "L2", "" };
|
||||
cfg::string l3 { this, "L3", "" };
|
||||
|
||||
cfg::_int<0, 1000000> lstickdeadzone{ this, "Left Stick Deadzone", 0 };
|
||||
cfg::_int<0, 1000000> rstickdeadzone{ this, "Right Stick Deadzone", 0 };
|
||||
cfg::_int<0, 1000000> ltriggerthreshold{ this, "Left Trigger Threshold", 0 };
|
||||
cfg::_int<0, 1000000> rtriggerthreshold{ this, "Right Trigger Threshold", 0 };
|
||||
cfg::_int<0, 1000000> padsquircling{ this, "Pad Squircling Factor", 0 };
|
||||
|
||||
cfg::_int<0, 255> colorR{ this, "Color Value R", 0 };
|
||||
cfg::_int<0, 255> colorG{ this, "Color Value G", 0 };
|
||||
cfg::_int<0, 255> colorB{ this, "Color Value B", 0 };
|
||||
|
||||
cfg::_bool enable_vibration_motor_large{ this, "Enable Large Vibration Motor", true };
|
||||
cfg::_bool enable_vibration_motor_small{ this, "Enable Small Vibration Motor", true };
|
||||
cfg::_bool switch_vibration_motors{ this, "Switch Vibration Motors", false };
|
||||
|
||||
bool load()
|
||||
{
|
||||
if (fs::file cfg_file{ cfg_name, fs::read })
|
||||
{
|
||||
return from_string(cfg_file.to_string());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
fs::file(cfg_name, fs::rewrite).write(to_string());
|
||||
}
|
||||
|
||||
bool exist()
|
||||
{
|
||||
return fs::is_file(cfg_name);
|
||||
}
|
||||
};
|
||||
|
||||
class PadHandlerBase
|
||||
{
|
||||
protected:
|
||||
static const u32 MAX_GAMEPADS = 7;
|
||||
|
||||
int m_trigger_threshold = 0;
|
||||
int m_thumb_threshold = 0;
|
||||
|
||||
bool b_has_deadzones = false;
|
||||
bool b_has_rumble = false;
|
||||
bool b_has_config = false;
|
||||
pad_config m_pad_config;
|
||||
|
||||
template <typename T>
|
||||
T lerp(T v0, T v1, T t) {
|
||||
return std::fma(t, v1, std::fma(-t, v0, v0));
|
||||
}
|
||||
|
||||
// Search an unordered map for a string value and return found keycode
|
||||
int FindKeyCode(std::unordered_map<u32, std::string> map, const std::string& name)
|
||||
{
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
if (it->second == name) return it->first;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
long FindKeyCode(std::unordered_map<u64, std::string> map, const std::string& name)
|
||||
{
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
if (it->second == name) return it->first;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
// Get normalized trigger value based on the range defined by a threshold
|
||||
u16 NormalizeTriggerInput(u16 value, int threshold)
|
||||
{
|
||||
if (value <= threshold || threshold >= trigger_max)
|
||||
{
|
||||
return static_cast<u16>(0);
|
||||
}
|
||||
else if (threshold <= trigger_min)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (u16)(float(trigger_max) * float(value - threshold) / float(trigger_max - threshold));
|
||||
}
|
||||
};
|
||||
|
||||
// Get new scaled value between 0 and 255 based on its minimum and maximum
|
||||
float ScaleStickInput(s32 raw_value, int minimum, int maximum)
|
||||
{
|
||||
// value based on max range converted to [0, 1]
|
||||
float val = float(Clamp(raw_value, minimum, maximum) - minimum) / float(abs(maximum) + abs(minimum));
|
||||
return 255.0f * val;
|
||||
};
|
||||
|
||||
// normalizes a directed input, meaning it will correspond to a single "button" and not an axis with two directions
|
||||
// the input values must lie in 0+
|
||||
u16 NormalizeDirectedInput(u16 raw_value, float threshold, float maximum)
|
||||
{
|
||||
if (threshold >= maximum || maximum <= 0)
|
||||
{
|
||||
return static_cast<u16>(0);
|
||||
}
|
||||
|
||||
float val = float(Clamp(raw_value, 0, maximum)) / maximum; // value based on max range converted to [0, 1]
|
||||
|
||||
if (threshold <= 0)
|
||||
{
|
||||
return static_cast<u16>(255.0f * val);
|
||||
}
|
||||
else
|
||||
{
|
||||
float thresh = threshold / maximum; // threshold converted to [0, 1]
|
||||
return static_cast<u16>(255.0f * std::min(1.0f, (val - thresh) / (1.0f - thresh)));
|
||||
}
|
||||
};
|
||||
|
||||
u16 NormalizeStickInput(s32 raw_value, int threshold, bool ignore_threshold = false)
|
||||
{
|
||||
if (ignore_threshold)
|
||||
{
|
||||
return static_cast<u16>(ScaleStickInput(raw_value, 0, thumb_max));
|
||||
}
|
||||
else
|
||||
{
|
||||
return NormalizeDirectedInput(raw_value, threshold, thumb_max);
|
||||
}
|
||||
}
|
||||
|
||||
// This function normalizes stick deadzone based on the DS3's deadzone, which is ~13%
|
||||
// X and Y is expected to be in (-255) to 255 range, deadzone should be in terms of thumb stick range
|
||||
// return is new x and y values in 0-255 range
|
||||
std::tuple<u16, u16> NormalizeStickDeadzone(s32 inX, s32 inY, u32 deadzone)
|
||||
{
|
||||
const float dzRange = deadzone / float((std::abs(thumb_max) + std::abs(thumb_min)));
|
||||
|
||||
float X = inX / 255.0f;
|
||||
float Y = inY / 255.0f;
|
||||
|
||||
if (dzRange > 0.f)
|
||||
{
|
||||
const float mag = std::min(sqrtf(X*X + Y*Y), 1.f);
|
||||
|
||||
if (mag <= 0)
|
||||
{
|
||||
return std::tuple<u16, u16>(ConvertAxis(X), ConvertAxis(Y));
|
||||
}
|
||||
|
||||
if (mag > dzRange) {
|
||||
float pos = lerp(0.13f, 1.f, (mag - dzRange) / (1 - dzRange));
|
||||
float scale = pos / mag;
|
||||
X = X * scale;
|
||||
Y = Y * scale;
|
||||
}
|
||||
else {
|
||||
float pos = lerp(0.f, 0.13f, mag / dzRange);
|
||||
float scale = pos / mag;
|
||||
X = X * scale;
|
||||
Y = Y * scale;
|
||||
}
|
||||
}
|
||||
return std::tuple<u16, u16>( ConvertAxis(X), ConvertAxis(Y) );
|
||||
};
|
||||
|
||||
// get clamped value between min and max
|
||||
s32 Clamp(f32 input, s32 min, s32 max)
|
||||
{
|
||||
if (input > max)
|
||||
return max;
|
||||
else if (input < min)
|
||||
return min;
|
||||
else return static_cast<s32>(input);
|
||||
};
|
||||
|
||||
// get clamped value between 0 and 255
|
||||
u16 Clamp0To255(f32 input)
|
||||
{
|
||||
return static_cast<u16>(Clamp(input, 0, 255));
|
||||
};
|
||||
|
||||
// get clamped value between 0 and 1023
|
||||
u16 Clamp0To1023(f32 input)
|
||||
{
|
||||
return static_cast<u16>(Clamp(input, 0, 1023));
|
||||
}
|
||||
|
||||
// input has to be [-1,1]. result will be [0,255]
|
||||
u16 ConvertAxis(float value)
|
||||
{
|
||||
return static_cast<u16>((value + 1.0)*(255.0 / 2.0));
|
||||
};
|
||||
|
||||
// The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle
|
||||
// using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange
|
||||
// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of 8000
|
||||
// This function assumes inX and inY is already in 0-255
|
||||
std::tuple<u16, u16> ConvertToSquirclePoint(u16 inX, u16 inY, float squircle_factor)
|
||||
{
|
||||
// convert inX and Y to a (-1, 1) vector;
|
||||
const f32 x = ((f32)inX - 127.5f) / 127.5f;
|
||||
const f32 y = ((f32)inY - 127.5f) / 127.5f;
|
||||
|
||||
// compute angle and len of given point to be used for squircle radius
|
||||
const f32 angle = std::atan2(y, x);
|
||||
const f32 r = std::sqrt(std::pow(x, 2.f) + std::pow(y, 2.f));
|
||||
|
||||
// now find len/point on the given squircle from our current angle and radius in polar coords
|
||||
// https://thatsmaths.com/2016/07/14/squircles/
|
||||
const f32 newLen = (1 + std::pow(std::sin(2 * angle), 2.f) / (squircle_factor / 1000.f)) * r;
|
||||
|
||||
// we now have len and angle, convert to cartisian
|
||||
const int newX = Clamp0To255(((newLen * std::cos(angle)) + 1) * 127.5f);
|
||||
const int newY = Clamp0To255(((newLen * std::sin(angle)) + 1) * 127.5f);
|
||||
return std::tuple<u16, u16>(newX, newY);
|
||||
}
|
||||
|
||||
public:
|
||||
s32 thumb_min = 0;
|
||||
s32 thumb_max = 255;
|
||||
s32 trigger_min = 0;
|
||||
s32 trigger_max = 255;
|
||||
s32 vibration_min = 0;
|
||||
s32 vibration_max = 255;
|
||||
|
||||
virtual bool Init() { return true; };
|
||||
virtual ~PadHandlerBase() = default;
|
||||
|
||||
//Does it have GUI Config?
|
||||
bool has_config() { return b_has_config; };
|
||||
bool has_rumble() { return b_has_rumble; };
|
||||
bool has_deadzones() { return b_has_deadzones; };
|
||||
pad_config* GetConfig() { return &m_pad_config; };
|
||||
//Sets window to config the controller(optional)
|
||||
virtual void ConfigController(std::string device) {};
|
||||
virtual void GetNextButtonPress(const std::string& padId, const std::function<void(u16, std::string, int[])>& callback) {};
|
||||
virtual void TestVibration(const std::string& padId, u32 largeMotor, u32 smallMotor) {};
|
||||
//Return list of devices for that handler
|
||||
virtual std::vector<std::string> ListDevices() = 0;
|
||||
//Callback called during pad_thread::ThreadFunc
|
||||
virtual void ThreadProc() = 0;
|
||||
//Binds a Pad to a device
|
||||
virtual bool bindPadToDevice(std::shared_ptr<Pad> pad, const std::string& device) = 0;
|
||||
|
||||
private:
|
||||
virtual void TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold = false) {};
|
||||
};
|
||||
|
||||
@@ -456,18 +456,22 @@ void write_vertex_array_data_to_buffer(gsl::span<gsl::byte> raw_dst_span, gsl::s
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
std::tuple<T, T> upload_untouched(gsl::span<to_be_t<const T>> src, gsl::span<T> dst, bool is_primitive_restart_enabled, T primitive_restart_index)
|
||||
std::tuple<T, T, u32> upload_untouched(gsl::span<to_be_t<const T>> src, gsl::span<T> dst, bool is_primitive_restart_enabled, T primitive_restart_index)
|
||||
{
|
||||
T min_index = -1;
|
||||
T max_index = 0;
|
||||
|
||||
verify(HERE), (dst.size_bytes() >= src.size_bytes());
|
||||
|
||||
size_t dst_idx = 0;
|
||||
u32 dst_idx = 0;
|
||||
for (T index : src)
|
||||
{
|
||||
if (is_primitive_restart_enabled && index == primitive_restart_index)
|
||||
{
|
||||
// List types do not need primitive restart. Just skip over this instead
|
||||
if (rsx::method_registers.current_draw_clause.is_disjoint_primitive)
|
||||
continue;
|
||||
|
||||
index = -1;
|
||||
}
|
||||
else
|
||||
@@ -478,125 +482,108 @@ std::tuple<T, T> upload_untouched(gsl::span<to_be_t<const T>> src, gsl::span<T>
|
||||
|
||||
dst[dst_idx++] = index;
|
||||
}
|
||||
return std::make_tuple(min_index, max_index);
|
||||
return std::make_tuple(min_index, max_index, dst_idx);
|
||||
}
|
||||
|
||||
// FIXME: expanded primitive type may not support primitive restart correctly
|
||||
template<typename T>
|
||||
std::tuple<T, T> expand_indexed_triangle_fan(gsl::span<to_be_t<const T>> src, gsl::span<T> dst, bool is_primitive_restart_enabled, T primitive_restart_index)
|
||||
std::tuple<T, T, u32> expand_indexed_triangle_fan(gsl::span<to_be_t<const T>> src, gsl::span<T> dst, bool is_primitive_restart_enabled, T primitive_restart_index)
|
||||
{
|
||||
T min_index = -1;
|
||||
const T invalid_index = (T)-1;
|
||||
|
||||
T min_index = invalid_index;
|
||||
T max_index = 0;
|
||||
|
||||
verify(HERE), (dst.size() >= 3 * (src.size() - 2));
|
||||
|
||||
const T index0 = src[0];
|
||||
if (!is_primitive_restart_enabled || index0 != -1) // Cut
|
||||
u32 dst_idx = 0;
|
||||
u32 src_idx = 0;
|
||||
|
||||
bool needs_anchor = true;
|
||||
T anchor = invalid_index;
|
||||
T last_index = invalid_index;
|
||||
|
||||
for (size_t src_idx = 0; src_idx < src.size(); ++src_idx)
|
||||
{
|
||||
min_index = std::min(min_index, index0);
|
||||
max_index = std::max(max_index, index0);
|
||||
if (needs_anchor)
|
||||
{
|
||||
if (is_primitive_restart_enabled && src[src_idx] == primitive_restart_index)
|
||||
continue;
|
||||
|
||||
anchor = src[src_idx];
|
||||
needs_anchor = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_primitive_restart_enabled && src[src_idx] == primitive_restart_index)
|
||||
{
|
||||
needs_anchor = true;
|
||||
last_index = invalid_index;
|
||||
continue;
|
||||
}
|
||||
|
||||
T index = src[src_idx];
|
||||
max_index = std::max(max_index, index);
|
||||
min_index = std::min(min_index, index);
|
||||
|
||||
if (last_index == invalid_index)
|
||||
{
|
||||
//Need at least one anchor and one outer index to create a triange
|
||||
last_index = index;
|
||||
continue;
|
||||
}
|
||||
|
||||
dst[dst_idx++] = anchor;
|
||||
dst[dst_idx++] = last_index;
|
||||
dst[dst_idx++] = index;
|
||||
|
||||
last_index = index;
|
||||
}
|
||||
|
||||
size_t dst_idx = 0;
|
||||
while (src.size() > 2)
|
||||
{
|
||||
gsl::span<to_be_t<const T>> tri_indexes = src.subspan(0, 2);
|
||||
T index1 = tri_indexes[0];
|
||||
if (is_primitive_restart_enabled && index1 == primitive_restart_index)
|
||||
{
|
||||
index1 = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_index = std::min(min_index, index1);
|
||||
max_index = std::max(max_index, index1);
|
||||
}
|
||||
T index2 = tri_indexes[1];
|
||||
if (is_primitive_restart_enabled && index2 == primitive_restart_index)
|
||||
{
|
||||
index2 = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_index = std::min(min_index, index2);
|
||||
max_index = std::max(max_index, index2);
|
||||
}
|
||||
|
||||
dst[dst_idx++] = index0;
|
||||
dst[dst_idx++] = index1;
|
||||
dst[dst_idx++] = index2;
|
||||
|
||||
src = src.subspan(2);
|
||||
}
|
||||
return std::make_tuple(min_index, max_index);
|
||||
return std::make_tuple(min_index, max_index, dst_idx);
|
||||
}
|
||||
|
||||
// FIXME: expanded primitive type may not support primitive restart correctly
|
||||
template<typename T>
|
||||
std::tuple<T, T> expand_indexed_quads(gsl::span<to_be_t<const T>> src, gsl::span<T> dst, bool is_primitive_restart_enabled, T primitive_restart_index)
|
||||
std::tuple<T, T, u32> expand_indexed_quads(gsl::span<to_be_t<const T>> src, gsl::span<T> dst, bool is_primitive_restart_enabled, T primitive_restart_index)
|
||||
{
|
||||
T min_index = -1;
|
||||
T max_index = 0;
|
||||
|
||||
verify(HERE), (4 * dst.size_bytes() >= 6 * src.size_bytes());
|
||||
|
||||
size_t dst_idx = 0;
|
||||
while (!src.empty())
|
||||
u32 dst_idx = 0;
|
||||
u8 set_size = 0;
|
||||
T tmp_indices[4];
|
||||
|
||||
for (int src_idx = 0; src_idx < src.size(); ++src_idx)
|
||||
{
|
||||
gsl::span<to_be_t<const T>> quad_indexes = src.subspan(0, 4);
|
||||
T index0 = quad_indexes[0];
|
||||
if (is_primitive_restart_enabled && index0 == primitive_restart_index)
|
||||
T index = src[src_idx];
|
||||
if (is_primitive_restart_enabled && index == primitive_restart_index)
|
||||
{
|
||||
index0 = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_index = std::min(min_index, index0);
|
||||
max_index = std::max(max_index, index0);
|
||||
}
|
||||
T index1 = quad_indexes[1];
|
||||
if (is_primitive_restart_enabled && index1 == primitive_restart_index)
|
||||
{
|
||||
index1 = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_index = std::min(min_index, index1);
|
||||
max_index = std::max(max_index, index1);
|
||||
}
|
||||
T index2 = quad_indexes[2];
|
||||
if (is_primitive_restart_enabled && index2 == primitive_restart_index)
|
||||
{
|
||||
index2 = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_index = std::min(min_index, index2);
|
||||
max_index = std::max(max_index, index2);
|
||||
}
|
||||
T index3 = quad_indexes[3];
|
||||
if (is_primitive_restart_enabled &&index3 == primitive_restart_index)
|
||||
{
|
||||
index3 = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_index = std::min(min_index, index3);
|
||||
max_index = std::max(max_index, index3);
|
||||
//empty temp buffer
|
||||
set_size = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// First triangle
|
||||
dst[dst_idx++] = index0;
|
||||
dst[dst_idx++] = index1;
|
||||
dst[dst_idx++] = index2;
|
||||
// Second triangle
|
||||
dst[dst_idx++] = index2;
|
||||
dst[dst_idx++] = index3;
|
||||
dst[dst_idx++] = index0;
|
||||
tmp_indices[set_size++] = index;
|
||||
max_index = std::max(max_index, index);
|
||||
min_index = std::min(min_index, index);
|
||||
|
||||
src = src.subspan(4);
|
||||
if (set_size == 4)
|
||||
{
|
||||
// First triangle
|
||||
dst[dst_idx++] = tmp_indices[0];
|
||||
dst[dst_idx++] = tmp_indices[1];
|
||||
dst[dst_idx++] = tmp_indices[2];
|
||||
// Second triangle
|
||||
dst[dst_idx++] = tmp_indices[2];
|
||||
dst[dst_idx++] = tmp_indices[3];
|
||||
dst[dst_idx++] = tmp_indices[0];
|
||||
|
||||
set_size = 0;
|
||||
}
|
||||
}
|
||||
return std::make_tuple(min_index, max_index);
|
||||
|
||||
return std::make_tuple(min_index, max_index, dst_idx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,7 +701,7 @@ namespace
|
||||
|
||||
// TODO: Unify indexed and non indexed primitive expansion ?
|
||||
template<typename T>
|
||||
std::tuple<T, T> write_index_array_data_to_buffer_impl(gsl::span<T> dst,
|
||||
std::tuple<T, T, u32> write_index_array_data_to_buffer_impl(gsl::span<T> dst,
|
||||
gsl::span<const be_t<T>> src,
|
||||
rsx::primitive_type draw_mode, bool restart_index_enabled, u32 restart_index, const std::vector<std::pair<u32, u32> > &first_count_arguments,
|
||||
std::function<bool(rsx::primitive_type)> expands)
|
||||
@@ -743,7 +730,7 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<u32, u32> write_index_array_data_to_buffer(gsl::span<gsl::byte> dst,
|
||||
std::tuple<u32, u32, u32> write_index_array_data_to_buffer(gsl::span<gsl::byte> dst,
|
||||
gsl::span<const gsl::byte> src,
|
||||
rsx::index_array_type type, rsx::primitive_type draw_mode, bool restart_index_enabled, u32 restart_index, const std::vector<std::pair<u32, u32> > &first_count_arguments,
|
||||
std::function<bool(rsx::primitive_type)> expands)
|
||||
|
||||
@@ -29,10 +29,10 @@ u32 get_index_type_size(rsx::index_array_type type);
|
||||
|
||||
/**
|
||||
* Write count indexes using (first, first + count) ranges.
|
||||
* Returns min/max index found during the process.
|
||||
* Returns min/max index found during the process and the number of valid indices written to the buffer.
|
||||
* The function expands index buffer for non native primitive type if expands(draw_mode) return true.
|
||||
*/
|
||||
std::tuple<u32, u32> write_index_array_data_to_buffer(gsl::span<gsl::byte> dst, gsl::span<const gsl::byte> src,
|
||||
std::tuple<u32, u32, u32> write_index_array_data_to_buffer(gsl::span<gsl::byte> dst, gsl::span<const gsl::byte> src,
|
||||
rsx::index_array_type, rsx::primitive_type draw_mode, bool restart_index_enabled, u32 restart_index, const std::vector<std::pair<u32, u32> > &first_count_arguments,
|
||||
std::function<bool(rsx::primitive_type)> expands);
|
||||
|
||||
|
||||
@@ -38,10 +38,17 @@ void FragmentProgramDecompiler::SetDst(std::string code, bool append_mask)
|
||||
|
||||
if (!dst.no_dest)
|
||||
{
|
||||
code = NoOverflow(code);
|
||||
if (dst.exp_tex)
|
||||
{
|
||||
//If dst.exp_tex really is _bx2 postfix, we need to unpack dynamic range
|
||||
AddCode("//exp tex flag is set");
|
||||
code = "((" + code + "- 0.5) * 2.)";
|
||||
}
|
||||
|
||||
if (dst.saturate)
|
||||
code = saturate(code);
|
||||
else
|
||||
code = ClampValue(code, dst.prec);
|
||||
}
|
||||
|
||||
code += (append_mask ? "$m" : "");
|
||||
@@ -188,20 +195,13 @@ std::string FragmentProgramDecompiler::NotZeroPositive(const std::string& code)
|
||||
return "max(abs(" + code + "), 1.E-10)";
|
||||
}
|
||||
|
||||
std::string FragmentProgramDecompiler::NoOverflow(const std::string& code)
|
||||
std::string FragmentProgramDecompiler::ClampValue(const std::string& code, u32 precision)
|
||||
{
|
||||
//FP16 is expected to overflow alot easier at 0+-65504
|
||||
//FP32 can still work upto 0+-3.4E38
|
||||
//See http://http.download.nvidia.com/developer/Papers/2005/FP_Specials/FP_Specials.pdf
|
||||
|
||||
if (dst.exp_tex)
|
||||
{
|
||||
//If dst.exp_tex really is _bx2 postfix, we need to unpack dynamic range
|
||||
AddCode("//exp tex flag is set");
|
||||
return "((" + code + "- 0.5) * 2.)";
|
||||
}
|
||||
|
||||
switch (dst.prec)
|
||||
switch (precision)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
@@ -209,6 +209,10 @@ std::string FragmentProgramDecompiler::NoOverflow(const std::string& code)
|
||||
return "clamp(" + code + ", -65504., 65504.)";
|
||||
case 2:
|
||||
return "clamp(" + code + ", -2., 2.)";
|
||||
case 3:
|
||||
return "clamp(" + code + ", -1., 1.)";
|
||||
case 4:
|
||||
return "clamp(" + code + ", 0., 1.)";
|
||||
}
|
||||
|
||||
return code;
|
||||
@@ -394,7 +398,9 @@ template<typename T> std::string FragmentProgramDecompiler::GetSRC(T src)
|
||||
|
||||
if (strncmp(swizzle.c_str(), f, 4) != 0) ret += "." + swizzle;
|
||||
|
||||
//Warning: Modifier order matters. e.g neg should be applied after precision clamping (tested with Naruto UNS)
|
||||
if (src.abs) ret = "abs(" + ret + ")";
|
||||
if (src1.input_prec_mod) ret = ClampValue(ret, src1.input_prec_mod);
|
||||
if (src.neg) ret = "-" + ret;
|
||||
|
||||
return ret;
|
||||
@@ -735,21 +741,12 @@ std::string FragmentProgramDecompiler::Decompile()
|
||||
if (SIP()) break;
|
||||
if (handle_tex_srb(opcode)) break;
|
||||
|
||||
if (forced_unit == FORCE_NONE)
|
||||
{
|
||||
if (handle_sct(opcode)) break;
|
||||
if (handle_scb(opcode)) break;
|
||||
}
|
||||
else if (forced_unit == FORCE_SCT)
|
||||
{
|
||||
forced_unit = FORCE_NONE;
|
||||
if (handle_sct(opcode)) break;
|
||||
}
|
||||
else if (forced_unit == FORCE_SCB)
|
||||
{
|
||||
forced_unit = FORCE_NONE;
|
||||
if (handle_scb(opcode)) break;
|
||||
}
|
||||
//FENCT/FENCB do not actually reject instructions if they dont match the forced unit
|
||||
//Tested with Dark Souls II where the repecting FENCX instruction will result in empty luminance averaging shaders
|
||||
//TODO: More reasearch is needed to determine what real HW does
|
||||
if (handle_sct(opcode)) break;
|
||||
if (handle_scb(opcode)) break;
|
||||
forced_unit = FORCE_NONE;
|
||||
|
||||
LOG_ERROR(RSX, "Unknown/illegal instruction: 0x%x (forced unit %d)", opcode, prev_force_unit);
|
||||
break;
|
||||
|
||||
@@ -55,8 +55,8 @@ class FragmentProgramDecompiler
|
||||
std::string NotZero(const std::string& code);
|
||||
std::string NotZeroPositive(const std::string& code);
|
||||
|
||||
//Prevents operations from overflowing the max range (tested with fp_dynamic3 autotest sample)
|
||||
std::string NoOverflow(const std::string& code);
|
||||
//Prevents operations from overflowing the desired range (tested with fp_dynamic3 autotest sample, DS2 for src1.input_prec_mod)
|
||||
std::string ClampValue(const std::string& code, u32 precision);
|
||||
|
||||
/**
|
||||
* Returns true if the dst set is not a vector (i.e only a single component)
|
||||
|
||||
@@ -278,6 +278,8 @@ namespace glsl
|
||||
OS << "vec4 read_location(int location)\n";
|
||||
OS << "{\n";
|
||||
OS << " attribute_desc desc = fetch_desc(location);\n";
|
||||
OS << " //if attribute is disabled return 1 (makes all operations with it nop except add/sub - TODO)\n";
|
||||
OS << " if (desc.attribute_size == 0) return vec4(1.);\n";
|
||||
OS << "\n";
|
||||
OS << " int vertex_id = " << vertex_id_name << " - int(vertex_base_index);\n";
|
||||
OS << " if (desc.frequency == 0)\n";
|
||||
@@ -334,11 +336,6 @@ namespace glsl
|
||||
OS << " return decodeLinearDepth(texture(tex, coord.xy).r);\n";
|
||||
OS << "}\n\n";
|
||||
|
||||
OS << "vec4 texture2DReconstruct(sampler2DRect tex, vec2 coord)\n";
|
||||
OS << "{\n";
|
||||
OS << " return decodeLinearDepth(texture(tex, coord.xy).r);\n";
|
||||
OS << "}\n\n";
|
||||
|
||||
OS << "vec4 get_wpos()\n";
|
||||
OS << "{\n";
|
||||
OS << " float abs_scale = abs(wpos_scale);\n";
|
||||
@@ -350,4 +347,75 @@ namespace glsl
|
||||
{
|
||||
program_common::insert_fog_declaration(OS, "vec4", "fog_c");
|
||||
}
|
||||
|
||||
static std::string getFunctionImpl(FUNCTION f)
|
||||
{
|
||||
switch (f)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
case FUNCTION::FUNCTION_DP2:
|
||||
return "vec4(dot($0.xy, $1.xy))";
|
||||
case FUNCTION::FUNCTION_DP2A:
|
||||
return "vec4(dot($0.xy, $1.xy) + $2.x)";
|
||||
case FUNCTION::FUNCTION_DP3:
|
||||
return "vec4(dot($0.xyz, $1.xyz))";
|
||||
case FUNCTION::FUNCTION_DP4:
|
||||
return "vec4(dot($0, $1))";
|
||||
case FUNCTION::FUNCTION_DPH:
|
||||
return "vec4(dot(vec4($0.xyz, 1.0), $1))";
|
||||
case FUNCTION::FUNCTION_SFL:
|
||||
return "vec4(0., 0., 0., 0.)";
|
||||
case FUNCTION::FUNCTION_STR:
|
||||
return "vec4(1., 1., 1., 1.)";
|
||||
case FUNCTION::FUNCTION_FRACT:
|
||||
return "fract($0)";
|
||||
case FUNCTION::FUNCTION_REFL:
|
||||
return "vec4($0 - 2.0 * (dot($0, $1)) * $1)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D:
|
||||
return "texture($t, $0.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D_PROJ:
|
||||
return "textureProj($t, $0.x, $1.x)"; // Note: $1.x is bias
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D_LOD:
|
||||
return "textureLod($t, $0.x, $1.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D_GRAD:
|
||||
return "textureGrad($t, $0.x, $1.x, $2.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D:
|
||||
return "texture($t, $0.xy * texture_parameters[$_i].xy)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_PROJ:
|
||||
return "textureProj($t, $0 , $1.x)"; // Note: $1.x is bias
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_LOD:
|
||||
return "textureLod($t, $0.xy * texture_parameters[$_i].xy, $1.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_GRAD:
|
||||
return "textureGrad($t, $0.xy * texture_parameters[$_i].xy , $1.xy, $2.xy)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SHADOW2D:
|
||||
return "texture($t, $0.xyz)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SHADOW2D_PROJ:
|
||||
return "textureProj($t, $0, $1.x)"; // Note: $1.x is bias
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE:
|
||||
return "texture($t, $0.xyz)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE_PROJ:
|
||||
return "texture($t, ($0.xyz / $0.w))";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE_LOD:
|
||||
return "textureLod($t, $0.xyz, $1.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE_GRAD:
|
||||
return "textureGrad($t, $0.xyz, $1.xyz, $2.xyz)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE3D:
|
||||
return "texture($t, $0.xyz)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE3D_PROJ:
|
||||
return "textureProj($t, $0.xyzw, $1.x)"; // Note: $1.x is bias
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE3D_LOD:
|
||||
return "textureLod($t, $0.xyz, $1.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE3D_GRAD:
|
||||
return "textureGrad($t, $0.xyz, $1.xyz, $2.xyz)";
|
||||
case FUNCTION::FUNCTION_DFDX:
|
||||
return "dFdx($0)";
|
||||
case FUNCTION::FUNCTION_DFDY:
|
||||
return "dFdy($0)";
|
||||
case FUNCTION::FUNCTION_VERTEX_TEXTURE_FETCH2D:
|
||||
return "textureLod($t, $0.xy, 0)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_DEPTH_RGBA:
|
||||
return "texture2DReconstruct($t, $0.xy * texture_parameters[$_i].xy)";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,8 @@ struct data_heap
|
||||
size_t m_min_guard_size; //If an allocation touches the guard region, reset the heap to avoid going over budget
|
||||
size_t m_current_allocated_size;
|
||||
size_t m_largest_allocated_pool;
|
||||
|
||||
char* m_name;
|
||||
public:
|
||||
data_heap() = default;
|
||||
~data_heap() = default;
|
||||
@@ -54,8 +56,10 @@ public:
|
||||
|
||||
size_t m_get_pos; // End of free space
|
||||
|
||||
void init(size_t heap_size, size_t min_guard_size=0x10000)
|
||||
void init(size_t heap_size, const char* buffer_name = "unnamed", size_t min_guard_size=0x10000)
|
||||
{
|
||||
m_name = const_cast<char*>(buffer_name);
|
||||
|
||||
m_size = heap_size;
|
||||
m_put_pos = 0;
|
||||
m_get_pos = heap_size - 1;
|
||||
@@ -71,8 +75,8 @@ public:
|
||||
{
|
||||
if (!can_alloc<Alignement>(size))
|
||||
{
|
||||
fmt::throw_exception("Working buffer not big enough, buffer_length=%d allocated=%d requested=%d guard=%d largest_pool=%d" HERE,
|
||||
m_size, m_current_allocated_size, size, m_min_guard_size, m_largest_allocated_pool);
|
||||
fmt::throw_exception("[%s] Working buffer not big enough, buffer_length=%d allocated=%d requested=%d guard=%d largest_pool=%d" HERE,
|
||||
m_name, m_size, m_current_allocated_size, size, m_min_guard_size, m_largest_allocated_pool);
|
||||
}
|
||||
|
||||
size_t alloc_size = align(size, Alignement);
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace rsx
|
||||
struct surface_subresource_storage
|
||||
{
|
||||
surface_type surface = nullptr;
|
||||
u32 base_address = 0;
|
||||
|
||||
u16 x = 0;
|
||||
u16 y = 0;
|
||||
@@ -29,8 +30,8 @@ namespace rsx
|
||||
|
||||
surface_subresource_storage() {}
|
||||
|
||||
surface_subresource_storage(surface_type src, u16 X, u16 Y, u16 W, u16 H, bool _Bound, bool _Depth, bool _Clipped = false)
|
||||
: surface(src), x(X), y(Y), w(W), h(H), is_bound(_Bound), is_depth_surface(_Depth), is_clipped(_Clipped)
|
||||
surface_subresource_storage(u32 addr, surface_type src, u16 X, u16 Y, u16 W, u16 H, bool _Bound, bool _Depth, bool _Clipped = false)
|
||||
: base_address(addr), surface(src), x(X), y(Y), w(W), h(H), is_bound(_Bound), is_depth_surface(_Depth), is_clipped(_Clipped)
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -46,11 +47,25 @@ namespace rsx
|
||||
template <typename image_storage_type>
|
||||
struct render_target_descriptor
|
||||
{
|
||||
GcmTileInfo *tile = nullptr;
|
||||
rsx::surface_antialiasing aa_mode = rsx::surface_antialiasing::center_1_sample;
|
||||
|
||||
u16 raster_offset_x = 0;
|
||||
u16 raster_offset_y = 0;
|
||||
u32 raster_address_offset = 0;
|
||||
|
||||
virtual image_storage_type get_surface() const = 0;
|
||||
virtual u16 get_surface_width() const = 0;
|
||||
virtual u16 get_surface_height() const = 0;
|
||||
virtual u16 get_rsx_pitch() const = 0;
|
||||
virtual u16 get_native_pitch() const = 0;
|
||||
|
||||
void set_raster_offset(u16 x, u16 y, u8 bpp)
|
||||
{
|
||||
raster_offset_x = x;
|
||||
raster_offset_y = y;
|
||||
raster_address_offset = (y * get_rsx_pitch()) + (x * bpp);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -114,6 +129,7 @@ namespace rsx
|
||||
std::tuple<u32, surface_type> m_bound_depth_stencil = {};
|
||||
|
||||
std::list<surface_storage_type> invalidated_resources;
|
||||
u64 cache_tag = 0ull;
|
||||
|
||||
surface_store() = default;
|
||||
~surface_store() = default;
|
||||
@@ -163,8 +179,11 @@ namespace rsx
|
||||
new_surface_storage = std::move(rtt);
|
||||
|
||||
if (old_surface)
|
||||
{
|
||||
//Exchange this surface with the invalidated one
|
||||
Traits::notify_surface_invalidated(old_surface_storage);
|
||||
rtt = std::move(old_surface_storage);
|
||||
}
|
||||
else
|
||||
//rtt is now empty - erase it
|
||||
invalidated_resources.erase(It);
|
||||
@@ -177,8 +196,11 @@ namespace rsx
|
||||
}
|
||||
|
||||
if (old_surface != nullptr && new_surface == nullptr)
|
||||
{
|
||||
//This was already determined to be invalid and is excluded from testing above
|
||||
Traits::notify_surface_invalidated(old_surface_storage);
|
||||
invalidated_resources.push_back(std::move(old_surface_storage));
|
||||
}
|
||||
|
||||
if (new_surface != nullptr)
|
||||
{
|
||||
@@ -227,8 +249,11 @@ namespace rsx
|
||||
new_surface_storage = std::move(ds);
|
||||
|
||||
if (old_surface)
|
||||
{
|
||||
//Exchange this surface with the invalidated one
|
||||
Traits::notify_surface_invalidated(old_surface_storage);
|
||||
ds = std::move(old_surface_storage);
|
||||
}
|
||||
else
|
||||
invalidated_resources.erase(It);
|
||||
|
||||
@@ -240,8 +265,11 @@ namespace rsx
|
||||
}
|
||||
|
||||
if (old_surface != nullptr && new_surface == nullptr)
|
||||
{
|
||||
//This was already determined to be invalid and is excluded from testing above
|
||||
Traits::notify_surface_invalidated(old_surface_storage);
|
||||
invalidated_resources.push_back(std::move(old_surface_storage));
|
||||
}
|
||||
|
||||
if (new_surface != nullptr)
|
||||
{
|
||||
@@ -272,6 +300,8 @@ namespace rsx
|
||||
// u32 clip_x = clip_horizontal_reg;
|
||||
// u32 clip_y = clip_vertical_reg;
|
||||
|
||||
cache_tag++;
|
||||
|
||||
// Make previous RTTs sampleable
|
||||
for (std::tuple<u32, surface_type> &rtt : m_bound_render_targets)
|
||||
{
|
||||
@@ -494,8 +524,11 @@ namespace rsx
|
||||
|
||||
if (surface == ref)
|
||||
{
|
||||
Traits::notify_surface_invalidated(It->second);
|
||||
invalidated_resources.push_back(std::move(It->second));
|
||||
m_render_targets_storage.erase(It);
|
||||
|
||||
cache_tag++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -509,21 +542,63 @@ namespace rsx
|
||||
|
||||
if (surface == ref)
|
||||
{
|
||||
Traits::notify_surface_invalidated(It->second);
|
||||
invalidated_resources.push_back(std::move(It->second));
|
||||
m_depth_stencil_storage.erase(It);
|
||||
|
||||
cache_tag++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates surface that exists at an address
|
||||
*/
|
||||
void invalidate_surface_address(u32 addr, bool depth)
|
||||
{
|
||||
if (address_is_bound(addr, depth))
|
||||
{
|
||||
LOG_ERROR(RSX, "Cannot invalidate a currently bound render target!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!depth)
|
||||
{
|
||||
auto It = m_render_targets_storage.find(addr);
|
||||
if (It != m_render_targets_storage.end())
|
||||
{
|
||||
Traits::notify_surface_invalidated(It->second);
|
||||
invalidated_resources.push_back(std::move(It->second));
|
||||
m_render_targets_storage.erase(It);
|
||||
|
||||
cache_tag++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto It = m_depth_stencil_storage.find(addr);
|
||||
if (It != m_depth_stencil_storage.end())
|
||||
{
|
||||
Traits::notify_surface_invalidated(It->second);
|
||||
invalidated_resources.push_back(std::move(It->second));
|
||||
m_depth_stencil_storage.erase(It);
|
||||
|
||||
cache_tag++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clipping and fitting lookup funcrions
|
||||
* surface_overlaps - returns true if surface overlaps a given surface address and returns the relative x and y position of the surface address within the surface
|
||||
* address_is_bound - returns true if the surface at a given address is actively bound
|
||||
* get_surface_subresource_if_available - returns a sectiion descriptor that allows to crop surfaces stored in memory
|
||||
*/
|
||||
bool surface_overlaps_address(surface_type surface, u32 surface_address, u32 texaddr, u16 *x, u16 *y, bool scale_to_fit, bool double_height)
|
||||
bool surface_overlaps_address(surface_type surface, u32 surface_address, u32 texaddr, u16 *x, u16 *y)
|
||||
{
|
||||
bool is_subslice = false;
|
||||
u16 x_offset = 0;
|
||||
@@ -532,52 +607,70 @@ namespace rsx
|
||||
if (surface_address > texaddr)
|
||||
return false;
|
||||
|
||||
u32 offset = texaddr - surface_address;
|
||||
if (texaddr >= surface_address)
|
||||
const u32 offset = texaddr - surface_address;
|
||||
if (offset == 0)
|
||||
{
|
||||
if (offset == 0)
|
||||
*x = 0;
|
||||
*y = 0;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
surface_format_info info;
|
||||
Traits::get_surface_info(surface, &info);
|
||||
|
||||
bool doubled_x = false;
|
||||
bool doubled_y = false;
|
||||
|
||||
switch (surface->aa_mode)
|
||||
{
|
||||
case rsx::surface_antialiasing::square_rotated_4_samples:
|
||||
case rsx::surface_antialiasing::square_centered_4_samples:
|
||||
doubled_y = true;
|
||||
//fall through
|
||||
case rsx::surface_antialiasing::diagonal_centered_2_samples:
|
||||
doubled_x = true;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 range = info.rsx_pitch * info.surface_height;
|
||||
if (doubled_y) range <<= 1;
|
||||
|
||||
if (offset < range)
|
||||
{
|
||||
y_offset = (offset / info.rsx_pitch);
|
||||
x_offset = (offset % info.rsx_pitch) / info.bpp;
|
||||
|
||||
if (doubled_x) x_offset /= 2;
|
||||
if (doubled_y) y_offset /= 2;
|
||||
|
||||
is_subslice = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
surface_format_info info;
|
||||
Traits::get_surface_info(surface, &info);
|
||||
|
||||
u32 range = info.rsx_pitch * info.surface_height;
|
||||
if (double_height) range *= 2;
|
||||
|
||||
if (offset < range)
|
||||
{
|
||||
const u32 y = (offset / info.rsx_pitch);
|
||||
u32 x = (offset % info.rsx_pitch) / info.bpp;
|
||||
|
||||
if (scale_to_fit)
|
||||
{
|
||||
const f32 x_scale = (f32)info.rsx_pitch / info.native_pitch;
|
||||
x = (u32)((f32)x / x_scale);
|
||||
}
|
||||
|
||||
x_offset = x;
|
||||
y_offset = y;
|
||||
|
||||
if (double_height) y_offset /= 2;
|
||||
is_subslice = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_subslice)
|
||||
{
|
||||
*x = x_offset;
|
||||
*y = y_offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_subslice)
|
||||
{
|
||||
*x = x_offset;
|
||||
*y = y_offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//Fast hit test
|
||||
inline bool surface_overlaps_address_fast(surface_type surface, u32 surface_address, u32 texaddr)
|
||||
{
|
||||
if (surface_address > texaddr)
|
||||
return false;
|
||||
|
||||
const u32 offset = texaddr - surface_address;
|
||||
const u32 range = surface->get_rsx_pitch() * surface->get_surface_height();
|
||||
|
||||
return (offset < range);
|
||||
}
|
||||
|
||||
bool address_is_bound(u32 address, bool is_depth) const
|
||||
{
|
||||
if (is_depth)
|
||||
@@ -604,30 +697,35 @@ namespace rsx
|
||||
return true;
|
||||
}
|
||||
|
||||
surface_subresource get_surface_subresource_if_applicable(u32 texaddr, u16 requested_width, u16 requested_height, u16 requested_pitch, bool scale_to_fit = false, bool crop = false, bool ignore_depth_formats = false, bool double_height = false)
|
||||
surface_subresource get_surface_subresource_if_applicable(u32 texaddr, u16 requested_width, u16 requested_height, u16 requested_pitch,
|
||||
bool crop = false, bool ignore_depth_formats = false, bool ignore_color_formats = false)
|
||||
{
|
||||
auto test_surface = [&](surface_type surface, u32 this_address, u16 &x_offset, u16 &y_offset, u16 &w, u16 &h, bool &clipped)
|
||||
{
|
||||
if (surface_overlaps_address(surface, this_address, texaddr, &x_offset, &y_offset, scale_to_fit, double_height))
|
||||
if (surface_overlaps_address(surface, this_address, texaddr, &x_offset, &y_offset))
|
||||
{
|
||||
surface_format_info info;
|
||||
Traits::get_surface_info(surface, &info);
|
||||
|
||||
if (info.rsx_pitch != requested_pitch)
|
||||
return false;
|
||||
|
||||
u16 real_width = requested_width;
|
||||
u16 real_height = requested_height;
|
||||
|
||||
if (scale_to_fit)
|
||||
switch (surface->aa_mode)
|
||||
{
|
||||
f32 pitch_scaling = (f32)requested_pitch / info.native_pitch;
|
||||
real_width = (u16)((f32)requested_width / pitch_scaling);
|
||||
case rsx::surface_antialiasing::diagonal_centered_2_samples:
|
||||
real_width /= 2;
|
||||
break;
|
||||
case rsx::surface_antialiasing::square_centered_4_samples:
|
||||
case rsx::surface_antialiasing::square_rotated_4_samples:
|
||||
real_width /= 2;
|
||||
real_height /= 2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (region_fits(info.surface_width, info.surface_height, x_offset, y_offset, real_width, requested_height))
|
||||
if (region_fits(info.surface_width, info.surface_height, x_offset, y_offset, real_width, real_height))
|
||||
{
|
||||
w = real_width;
|
||||
h = requested_height;
|
||||
h = real_height;
|
||||
clipped = false;
|
||||
|
||||
return true;
|
||||
@@ -640,17 +738,17 @@ namespace rsx
|
||||
u16 remaining_height = info.surface_height - y_offset;
|
||||
|
||||
w = std::min(real_width, remaining_width);
|
||||
h = std::min(requested_height, remaining_height);
|
||||
h = std::min(real_height, remaining_height);
|
||||
clipped = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info.surface_width >= requested_width && info.surface_height >= requested_height)
|
||||
if (info.surface_width >= real_width && info.surface_height >= real_height)
|
||||
{
|
||||
LOG_WARNING(RSX, "Overlapping surface exceeds bounds; returning full surface region");
|
||||
w = real_width;
|
||||
h = requested_height;
|
||||
h = real_height;
|
||||
clipped = true;
|
||||
|
||||
return true;
|
||||
@@ -668,26 +766,55 @@ namespace rsx
|
||||
u16 w;
|
||||
u16 h;
|
||||
|
||||
for (auto &tex_info : m_render_targets_storage)
|
||||
if (!ignore_color_formats)
|
||||
{
|
||||
u32 this_address = std::get<0>(tex_info);
|
||||
surface = std::get<1>(tex_info).get();
|
||||
for (auto &tex_info : m_render_targets_storage)
|
||||
{
|
||||
const u32 this_address = std::get<0>(tex_info);
|
||||
if (texaddr < this_address)
|
||||
continue;
|
||||
|
||||
if (test_surface(surface, this_address, x_offset, y_offset, w, h, clipped))
|
||||
return { surface, x_offset, y_offset, w, h, address_is_bound(this_address, false), false, clipped };
|
||||
surface = std::get<1>(tex_info).get();
|
||||
if (surface->get_rsx_pitch() != requested_pitch)
|
||||
continue;
|
||||
|
||||
if (requested_width == 0 || requested_height == 0)
|
||||
{
|
||||
if (!surface_overlaps_address_fast(surface, this_address, texaddr))
|
||||
continue;
|
||||
else
|
||||
return{ this_address, surface, 0, 0, 0, 0, false, false, false };
|
||||
}
|
||||
|
||||
if (test_surface(surface, this_address, x_offset, y_offset, w, h, clipped))
|
||||
return{ this_address, surface, x_offset, y_offset, w, h, address_is_bound(this_address, false), false, clipped };
|
||||
}
|
||||
}
|
||||
|
||||
if (ignore_depth_formats)
|
||||
return{};
|
||||
|
||||
//Check depth surfaces for overlap
|
||||
for (auto &tex_info : m_depth_stencil_storage)
|
||||
if (!ignore_depth_formats)
|
||||
{
|
||||
u32 this_address = std::get<0>(tex_info);
|
||||
surface = std::get<1>(tex_info).get();
|
||||
//Check depth surfaces for overlap
|
||||
for (auto &tex_info : m_depth_stencil_storage)
|
||||
{
|
||||
const u32 this_address = std::get<0>(tex_info);
|
||||
if (texaddr < this_address)
|
||||
continue;
|
||||
|
||||
if (test_surface(surface, this_address, x_offset, y_offset, w, h, clipped))
|
||||
return { surface, x_offset, y_offset, w, h, address_is_bound(this_address, true), true, clipped };
|
||||
surface = std::get<1>(tex_info).get();
|
||||
if (surface->get_rsx_pitch() != requested_pitch)
|
||||
continue;
|
||||
|
||||
if (requested_width == 0 || requested_height == 0)
|
||||
{
|
||||
if (!surface_overlaps_address_fast(surface, this_address, texaddr))
|
||||
continue;
|
||||
else
|
||||
return{ this_address, surface, 0, 0, 0, 0, false, true, false };
|
||||
}
|
||||
|
||||
if (test_surface(surface, this_address, x_offset, y_offset, w, h, clipped))
|
||||
return{ this_address, surface, x_offset, y_offset, w, h, address_is_bound(this_address, true), true, clipped };
|
||||
}
|
||||
}
|
||||
|
||||
return{};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -410,7 +410,7 @@ namespace
|
||||
gsl::span<gsl::byte> dst{
|
||||
reinterpret_cast<gsl::byte*>(mapped_buffer), ::narrow<u32>(buffer_size)};
|
||||
|
||||
std::tie(min_index, max_index) =
|
||||
std::tie(min_index, max_index, index_count) =
|
||||
write_index_array_data_to_buffer(dst, command.raw_index_buffer, indexed_type,
|
||||
rsx::method_registers.current_draw_clause.primitive,
|
||||
rsx::method_registers.restart_index_enabled(),
|
||||
|
||||
@@ -183,4 +183,5 @@ protected:
|
||||
virtual std::array<std::vector<gsl::byte>, 4> copy_render_targets_to_memory() override;
|
||||
virtual std::array<std::vector<gsl::byte>, 2> copy_depth_stencil_buffer_to_memory() override;
|
||||
virtual std::pair<std::string, std::string> get_programs() const override;
|
||||
virtual void notify_tile_unbound(u32 tile) override;
|
||||
};
|
||||
|
||||
@@ -54,7 +54,7 @@ void D3D12GSRender::load_program()
|
||||
};
|
||||
|
||||
get_current_vertex_program();
|
||||
get_current_fragment_program(rtt_lookup_func);
|
||||
get_current_fragment_program_legacy(rtt_lookup_func);
|
||||
|
||||
if (!current_fragment_program.valid)
|
||||
return;
|
||||
@@ -316,4 +316,10 @@ std::pair<std::string, std::string> D3D12GSRender::get_programs() const
|
||||
{
|
||||
return std::make_pair(m_pso_cache.get_transform_program(current_vertex_program).content, m_pso_cache.get_shader_program(current_fragment_program).content);
|
||||
}
|
||||
|
||||
void D3D12GSRender::notify_tile_unbound(u32 tile)
|
||||
{
|
||||
u32 addr = rsx::get_address(tiles[tile].offset, tiles[tile].location);
|
||||
m_rtts.invalidate_surface_address(addr, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -103,9 +103,6 @@ namespace
|
||||
|
||||
void D3D12GSRender::clear_surface(u32 arg)
|
||||
{
|
||||
// Ignore clear if surface target is set to CELL_GCM_SURFACE_TARGET_NONE
|
||||
if (rsx::method_registers.surface_color_target() == rsx::surface_target::none) return;
|
||||
|
||||
std::chrono::time_point<steady_clock> start_duration = steady_clock::now();
|
||||
|
||||
std::chrono::time_point<steady_clock> rtt_duration_start = steady_clock::now();
|
||||
|
||||
@@ -142,6 +142,9 @@ struct render_target_traits
|
||||
//TODO
|
||||
}
|
||||
|
||||
static
|
||||
void notify_surface_invalidated(const ComPtr<ID3D12Resource>&)
|
||||
{}
|
||||
|
||||
static
|
||||
bool rtt_has_format_width_height(const ComPtr<ID3D12Resource> &rtt, surface_color_format surface_color_format, size_t width, size_t height, bool=false)
|
||||
|
||||
@@ -4,77 +4,6 @@
|
||||
|
||||
namespace gl
|
||||
{
|
||||
std::string getFunctionImpl(FUNCTION f)
|
||||
{
|
||||
switch (f)
|
||||
{
|
||||
default:
|
||||
abort();
|
||||
case FUNCTION::FUNCTION_DP2:
|
||||
return "vec4(dot($0.xy, $1.xy))";
|
||||
case FUNCTION::FUNCTION_DP2A:
|
||||
return "vec4(dot($0.xy, $1.xy) + $2.x)";
|
||||
case FUNCTION::FUNCTION_DP3:
|
||||
return "vec4(dot($0.xyz, $1.xyz))";
|
||||
case FUNCTION::FUNCTION_DP4:
|
||||
return "vec4(dot($0, $1))";
|
||||
case FUNCTION::FUNCTION_DPH:
|
||||
return "vec4(dot(vec4($0.xyz, 1.0), $1))";
|
||||
case FUNCTION::FUNCTION_SFL:
|
||||
return "vec4(0., 0., 0., 0.)";
|
||||
case FUNCTION::FUNCTION_STR:
|
||||
return "vec4(1., 1., 1., 1.)";
|
||||
case FUNCTION::FUNCTION_FRACT:
|
||||
return "fract($0)";
|
||||
case FUNCTION::FUNCTION_REFL:
|
||||
return "vec4($0 - 2.0 * (dot($0, $1)) * $1)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D:
|
||||
return "texture($t, $0.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D_PROJ:
|
||||
return "textureProj($t, $0.x, $1.x)"; // Note: $1.x is bias
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D_LOD:
|
||||
return "textureLod($t, $0.x, $1.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE1D_GRAD:
|
||||
return "textureGrad($t, $0.x, $1.x, $2.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D:
|
||||
return "texture($t, $0.xy * $t_coord_scale)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_PROJ:
|
||||
return "textureProj($t, $0 , $1.x)"; // Note: $1.x is bias
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_LOD:
|
||||
return "textureLod($t, $0.xy * $t_coord_scale, $1.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_GRAD:
|
||||
return "textureGrad($t, $0.xy * $t_coord_scale , $1.xy, $2.xy)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SHADOW2D:
|
||||
return "texture($t, $0.xyz)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SHADOW2D_PROJ:
|
||||
return "textureProj($t, $0, $1.x)"; // Note: $1.x is bias
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE:
|
||||
return "texture($t, $0.xyz)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE_PROJ:
|
||||
return "texture($t, ($0.xyz / $0.w))";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE_LOD:
|
||||
return "textureLod($t, $0.xyz, $1.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE_GRAD:
|
||||
return "textureGrad($t, $0.xyz, $1.xyz, $2.xyz)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE3D:
|
||||
return "texture($t, $0.xyz)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE3D_PROJ:
|
||||
return "textureProj($t, $0.xyzw, $1.x)"; // Note: $1.x is bias
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE3D_LOD:
|
||||
return "textureLod($t, $0.xyz, $1.x)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE3D_GRAD:
|
||||
return "textureGrad($t, $0.xyz, $1.xyz, $2.xyz)";
|
||||
case FUNCTION::FUNCTION_DFDX:
|
||||
return "dFdx($0)";
|
||||
case FUNCTION::FUNCTION_DFDY:
|
||||
return "dFdy($0)";
|
||||
case FUNCTION::FUNCTION_VERTEX_TEXTURE_FETCH2D:
|
||||
return "textureLod($t, $0.xy, 0)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLE2D_DEPTH_RGBA:
|
||||
return "texture2DReconstruct($t, $0.xy * $t_coord_scale)";
|
||||
}
|
||||
}
|
||||
|
||||
int get_varying_register_location(const std::string &var_name)
|
||||
{
|
||||
static const std::pair<std::string, int> reg_table[] =
|
||||
|
||||
@@ -6,6 +6,5 @@
|
||||
|
||||
namespace gl
|
||||
{
|
||||
std::string getFunctionImpl(FUNCTION f);
|
||||
int get_varying_register_location(const std::string &var_name);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ std::string GLFragmentDecompilerThread::getFloatTypeName(size_t elementCount)
|
||||
|
||||
std::string GLFragmentDecompilerThread::getFunction(FUNCTION f)
|
||||
{
|
||||
return gl::getFunctionImpl(f);
|
||||
return glsl::getFunctionImpl(f);
|
||||
}
|
||||
|
||||
std::string GLFragmentDecompilerThread::saturate(const std::string & code)
|
||||
@@ -173,11 +173,6 @@ namespace
|
||||
case rsx::texture_dimension_extended::texture_dimension_3d:
|
||||
case rsx::texture_dimension_extended::texture_dimension_cubemap: vec_type = "vec3";
|
||||
}
|
||||
|
||||
if (prog.unnormalized_coords & (1 << index))
|
||||
OS << "\t" << vec_type << " tex" << index << "_coord_scale = texture_parameters[" << index << "].xy / textureSize(tex" << index << ", 0);\n";
|
||||
else
|
||||
OS << "\t" << vec_type << " tex" << index << "_coord_scale = " << vec_type << "(1.);\n";
|
||||
}
|
||||
|
||||
std::string insert_texture_fetch(const RSXFragmentProgram& prog, int index)
|
||||
|
||||
+386
-284
@@ -24,14 +24,14 @@ namespace
|
||||
|
||||
GLGSRender::GLGSRender() : GSRender()
|
||||
{
|
||||
m_shaders_cache.reset(new gl::shader_cache(m_prog_buffer, "opengl", "v1"));
|
||||
m_shaders_cache.reset(new gl::shader_cache(m_prog_buffer, "opengl", "v1.1"));
|
||||
|
||||
if (g_cfg.video.disable_vertex_cache)
|
||||
m_vertex_cache.reset(new gl::null_vertex_cache());
|
||||
else
|
||||
m_vertex_cache.reset(new gl::weak_vertex_cache());
|
||||
|
||||
supports_multidraw = true;
|
||||
supports_multidraw = !g_cfg.video.strict_rendering_mode;
|
||||
}
|
||||
|
||||
extern CellGcmContextData current_context;
|
||||
@@ -51,7 +51,7 @@ namespace
|
||||
case rsx::comparison_function::greater_or_equal: return GL_GEQUAL;
|
||||
case rsx::comparison_function::always: return GL_ALWAYS;
|
||||
}
|
||||
throw;
|
||||
fmt::throw_exception("Unsupported comparison op 0x%X" HERE, (u32)op);;
|
||||
}
|
||||
|
||||
GLenum stencil_op(rsx::stencil_op op)
|
||||
@@ -67,7 +67,7 @@ namespace
|
||||
case rsx::stencil_op::incr_wrap: return GL_INCR_WRAP;
|
||||
case rsx::stencil_op::decr_wrap: return GL_DECR_WRAP;
|
||||
}
|
||||
throw;
|
||||
fmt::throw_exception("Unsupported stencil op 0x%X" HERE, (u32)op);
|
||||
}
|
||||
|
||||
GLenum blend_equation(rsx::blend_equation op)
|
||||
@@ -75,16 +75,20 @@ namespace
|
||||
switch (op)
|
||||
{
|
||||
// Note : maybe add is signed on gl
|
||||
case rsx::blend_equation::add_signed:
|
||||
LOG_TRACE(RSX, "blend equation add_signed used. Emulating using FUNC_ADD");
|
||||
case rsx::blend_equation::add: return GL_FUNC_ADD;
|
||||
case rsx::blend_equation::min: return GL_MIN;
|
||||
case rsx::blend_equation::max: return GL_MAX;
|
||||
case rsx::blend_equation::substract: return GL_FUNC_SUBTRACT;
|
||||
case rsx::blend_equation::reverse_substract_signed:
|
||||
LOG_TRACE(RSX, "blend equation reverse_subtract_signed used. Emulating using FUNC_REVERSE_SUBTRACT");
|
||||
case rsx::blend_equation::reverse_substract: return GL_FUNC_REVERSE_SUBTRACT;
|
||||
case rsx::blend_equation::reverse_substract_signed: throw "unsupported";
|
||||
case rsx::blend_equation::add_signed: throw "unsupported";
|
||||
case rsx::blend_equation::reverse_add_signed: throw "unsupported";
|
||||
case rsx::blend_equation::reverse_add_signed:
|
||||
default:
|
||||
LOG_ERROR(RSX, "Blend equation 0x%X is unimplemented!", (u32)op);
|
||||
return GL_FUNC_ADD;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
GLenum blend_factor(rsx::blend_factor op)
|
||||
@@ -107,7 +111,7 @@ namespace
|
||||
case rsx::blend_factor::constant_alpha: return GL_CONSTANT_ALPHA;
|
||||
case rsx::blend_factor::one_minus_constant_alpha: return GL_ONE_MINUS_CONSTANT_ALPHA;
|
||||
}
|
||||
throw;
|
||||
fmt::throw_exception("Unsupported blend factor 0x%X" HERE, (u32)op);
|
||||
}
|
||||
|
||||
GLenum logic_op(rsx::logic_op op)
|
||||
@@ -131,7 +135,7 @@ namespace
|
||||
case rsx::logic_op::logic_nand: return GL_NAND;
|
||||
case rsx::logic_op::logic_set: return GL_SET;
|
||||
}
|
||||
throw;
|
||||
fmt::throw_exception("Unsupported logic op 0x%X" HERE, (u32)op);
|
||||
}
|
||||
|
||||
GLenum front_face(rsx::front_face op)
|
||||
@@ -143,7 +147,7 @@ namespace
|
||||
case rsx::front_face::cw: return (invert ? GL_CCW : GL_CW);
|
||||
case rsx::front_face::ccw: return (invert ? GL_CW : GL_CCW);
|
||||
}
|
||||
throw;
|
||||
fmt::throw_exception("Unsupported front face 0x%X" HERE, (u32)op);
|
||||
}
|
||||
|
||||
GLenum cull_face(rsx::cull_face op)
|
||||
@@ -156,7 +160,7 @@ namespace
|
||||
case rsx::cull_face::back: return (invert ? GL_FRONT : GL_BACK);
|
||||
case rsx::cull_face::front_and_back: return GL_FRONT_AND_BACK;
|
||||
}
|
||||
throw;
|
||||
fmt::throw_exception("Unsupported cull face 0x%X" HERE, (u32)op);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,141 +175,13 @@ void GLGSRender::begin()
|
||||
return;
|
||||
|
||||
init_buffers();
|
||||
|
||||
if (!framebuffer_status_valid)
|
||||
return;
|
||||
|
||||
std::chrono::time_point<steady_clock> then = steady_clock::now();
|
||||
|
||||
bool color_mask_b = rsx::method_registers.color_mask_b();
|
||||
bool color_mask_g = rsx::method_registers.color_mask_g();
|
||||
bool color_mask_r = rsx::method_registers.color_mask_r();
|
||||
bool color_mask_a = rsx::method_registers.color_mask_a();
|
||||
|
||||
gl_state.color_mask(color_mask_r, color_mask_g, color_mask_b, color_mask_a);
|
||||
gl_state.depth_mask(rsx::method_registers.depth_write_enabled());
|
||||
gl_state.stencil_mask(rsx::method_registers.stencil_mask());
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.depth_test_enabled(), GL_DEPTH_TEST))
|
||||
{
|
||||
gl_state.depth_func(comparison_op(rsx::method_registers.depth_func()));
|
||||
|
||||
float range_near = rsx::method_registers.clip_min();
|
||||
float range_far = rsx::method_registers.clip_max();
|
||||
|
||||
//Workaround to preserve depth precision but respect z direction
|
||||
//Ni no Kuni sets a very restricted z range (0.9x - 1.) and depth reads / tests are broken
|
||||
if (range_near <= range_far)
|
||||
gl_state.depth_range(0.f, 1.f);
|
||||
else
|
||||
gl_state.depth_range(1.f, 0.f);
|
||||
}
|
||||
|
||||
if (glDepthBoundsEXT && (gl_state.enable(rsx::method_registers.depth_bounds_test_enabled(), GL_DEPTH_BOUNDS_TEST_EXT)))
|
||||
{
|
||||
gl_state.depth_bounds(rsx::method_registers.depth_bounds_min(), rsx::method_registers.depth_bounds_max());
|
||||
}
|
||||
|
||||
gl_state.enable(rsx::method_registers.dither_enabled(), GL_DITHER);
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.blend_enabled(), GL_BLEND))
|
||||
{
|
||||
glBlendFuncSeparate(blend_factor(rsx::method_registers.blend_func_sfactor_rgb()),
|
||||
blend_factor(rsx::method_registers.blend_func_dfactor_rgb()),
|
||||
blend_factor(rsx::method_registers.blend_func_sfactor_a()),
|
||||
blend_factor(rsx::method_registers.blend_func_dfactor_a()));
|
||||
|
||||
auto blend_colors = rsx::get_constant_blend_colors();
|
||||
glBlendColor(blend_colors[0], blend_colors[1], blend_colors[2], blend_colors[3]);
|
||||
|
||||
glBlendEquationSeparate(blend_equation(rsx::method_registers.blend_equation_rgb()),
|
||||
blend_equation(rsx::method_registers.blend_equation_a()));
|
||||
}
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.stencil_test_enabled(), GL_STENCIL_TEST))
|
||||
{
|
||||
glStencilFunc(comparison_op(rsx::method_registers.stencil_func()),
|
||||
rsx::method_registers.stencil_func_ref(),
|
||||
rsx::method_registers.stencil_func_mask());
|
||||
|
||||
glStencilOp(stencil_op(rsx::method_registers.stencil_op_fail()), stencil_op(rsx::method_registers.stencil_op_zfail()),
|
||||
stencil_op(rsx::method_registers.stencil_op_zpass()));
|
||||
|
||||
if (rsx::method_registers.two_sided_stencil_test_enabled())
|
||||
{
|
||||
glStencilMaskSeparate(GL_BACK, rsx::method_registers.back_stencil_mask());
|
||||
|
||||
glStencilFuncSeparate(GL_BACK, comparison_op(rsx::method_registers.back_stencil_func()),
|
||||
rsx::method_registers.back_stencil_func_ref(), rsx::method_registers.back_stencil_func_mask());
|
||||
|
||||
glStencilOpSeparate(GL_BACK, stencil_op(rsx::method_registers.back_stencil_op_fail()),
|
||||
stencil_op(rsx::method_registers.back_stencil_op_zfail()), stencil_op(rsx::method_registers.back_stencil_op_zpass()));
|
||||
}
|
||||
}
|
||||
|
||||
gl_state.enablei(rsx::method_registers.blend_enabled_surface_1(), GL_BLEND, 1);
|
||||
gl_state.enablei(rsx::method_registers.blend_enabled_surface_2(), GL_BLEND, 2);
|
||||
gl_state.enablei(rsx::method_registers.blend_enabled_surface_3(), GL_BLEND, 3);
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.logic_op_enabled(), GL_COLOR_LOGIC_OP))
|
||||
{
|
||||
gl_state.logic_op(logic_op(rsx::method_registers.logic_operation()));
|
||||
}
|
||||
|
||||
gl_state.line_width(rsx::method_registers.line_width());
|
||||
gl_state.enable(rsx::method_registers.line_smooth_enabled(), GL_LINE_SMOOTH);
|
||||
|
||||
gl_state.enable(rsx::method_registers.poly_offset_point_enabled(), GL_POLYGON_OFFSET_POINT);
|
||||
gl_state.enable(rsx::method_registers.poly_offset_line_enabled(), GL_POLYGON_OFFSET_LINE);
|
||||
gl_state.enable(rsx::method_registers.poly_offset_fill_enabled(), GL_POLYGON_OFFSET_FILL);
|
||||
|
||||
//offset_bias is the constant factor, multiplied by the implementation factor R
|
||||
//offst_scale is the slope factor, multiplied by the triangle slope factor M
|
||||
gl_state.polygon_offset(rsx::method_registers.poly_offset_scale(), rsx::method_registers.poly_offset_bias());
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.cull_face_enabled(), GL_CULL_FACE))
|
||||
{
|
||||
gl_state.cull_face(cull_face(rsx::method_registers.cull_face_mode()));
|
||||
}
|
||||
|
||||
gl_state.front_face(front_face(rsx::method_registers.front_face_mode()));
|
||||
|
||||
//TODO
|
||||
//NV4097_SET_ANISO_SPREAD
|
||||
//NV4097_SET_SPECULAR_ENABLE
|
||||
//NV4097_SET_TWO_SIDE_LIGHT_EN
|
||||
//NV4097_SET_FLAT_SHADE_OP
|
||||
//NV4097_SET_EDGE_FLAG
|
||||
|
||||
|
||||
|
||||
//NV4097_SET_COLOR_KEY_COLOR
|
||||
//NV4097_SET_SHADER_CONTROL
|
||||
//NV4097_SET_ZMIN_MAX_CONTROL
|
||||
//NV4097_SET_ANTI_ALIASING_CONTROL
|
||||
//NV4097_SET_CLIP_ID_TEST_ENABLE
|
||||
|
||||
std::chrono::time_point<steady_clock> now = steady_clock::now();
|
||||
m_begin_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(now - then).count();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
GLenum get_gl_target_for_texture(const rsx::fragment_texture& tex)
|
||||
GLenum get_gl_target_for_texture(const rsx::texture_dimension_extended type)
|
||||
{
|
||||
switch (tex.get_extended_texture_dimension())
|
||||
{
|
||||
case rsx::texture_dimension_extended::texture_dimension_1d: return GL_TEXTURE_1D;
|
||||
case rsx::texture_dimension_extended::texture_dimension_2d: return GL_TEXTURE_2D;
|
||||
case rsx::texture_dimension_extended::texture_dimension_cubemap: return GL_TEXTURE_CUBE_MAP;
|
||||
case rsx::texture_dimension_extended::texture_dimension_3d: return GL_TEXTURE_3D;
|
||||
}
|
||||
fmt::throw_exception("Unknown texture target" HERE);
|
||||
}
|
||||
|
||||
GLenum get_gl_target_for_texture(const rsx::vertex_texture& tex)
|
||||
{
|
||||
switch (tex.get_extended_texture_dimension())
|
||||
switch (type)
|
||||
{
|
||||
case rsx::texture_dimension_extended::texture_dimension_1d: return GL_TEXTURE_1D;
|
||||
case rsx::texture_dimension_extended::texture_dimension_2d: return GL_TEXTURE_2D;
|
||||
@@ -318,12 +194,17 @@ namespace
|
||||
|
||||
void GLGSRender::end()
|
||||
{
|
||||
std::chrono::time_point<steady_clock> state_check_start = steady_clock::now();
|
||||
|
||||
if (skip_frame || !framebuffer_status_valid || (conditional_render_enabled && conditional_render_test_failed) || !check_program_state())
|
||||
{
|
||||
rsx::thread::end();
|
||||
return;
|
||||
}
|
||||
|
||||
std::chrono::time_point<steady_clock> state_check_end = steady_clock::now();
|
||||
m_begin_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(state_check_end - state_check_start).count();
|
||||
|
||||
if (manually_flush_ring_buffers)
|
||||
{
|
||||
//Use approximations to reseve space. This path is mostly for debug purposes anyway
|
||||
@@ -342,6 +223,73 @@ void GLGSRender::end()
|
||||
std::optional<std::tuple<GLenum, u32> > indexed_draw_info;
|
||||
std::tie(vertex_draw_count, actual_vertex_count, vertex_base, indexed_draw_info) = set_vertex_buffer();
|
||||
|
||||
//Load textures
|
||||
{
|
||||
std::chrono::time_point<steady_clock> textures_start = steady_clock::now();
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
void* unused = nullptr;
|
||||
bool update_framebuffer_sourced = false;
|
||||
|
||||
if (surface_store_tag != m_rtts.cache_tag)
|
||||
{
|
||||
update_framebuffer_sourced = true;
|
||||
surface_store_tag = m_rtts.cache_tag;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rsx::limits::fragment_textures_count; ++i)
|
||||
{
|
||||
if (!fs_sampler_state[i])
|
||||
fs_sampler_state[i] = std::make_unique<gl::texture_cache::sampled_image_descriptor>();
|
||||
|
||||
if (m_samplers_dirty || m_textures_dirty[i] ||
|
||||
(update_framebuffer_sourced && fs_sampler_state[i]->upload_context == rsx::texture_upload_context::framebuffer_storage))
|
||||
{
|
||||
auto sampler_state = static_cast<gl::texture_cache::sampled_image_descriptor*>(fs_sampler_state[i].get());
|
||||
|
||||
if (rsx::method_registers.fragment_textures[i].enabled())
|
||||
{
|
||||
*sampler_state = m_gl_texture_cache.upload_texture(unused, rsx::method_registers.fragment_textures[i], m_rtts);
|
||||
|
||||
if (m_textures_dirty[i])
|
||||
m_gl_sampler_states[i].apply(rsx::method_registers.fragment_textures[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
*sampler_state = {};
|
||||
}
|
||||
|
||||
m_textures_dirty[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < rsx::limits::vertex_textures_count; ++i)
|
||||
{
|
||||
if (!vs_sampler_state[i])
|
||||
vs_sampler_state[i] = std::make_unique<gl::texture_cache::sampled_image_descriptor>();
|
||||
|
||||
if (m_samplers_dirty || m_vertex_textures_dirty[i] ||
|
||||
(update_framebuffer_sourced && vs_sampler_state[i]->upload_context == rsx::texture_upload_context::framebuffer_storage))
|
||||
{
|
||||
auto sampler_state = static_cast<gl::texture_cache::sampled_image_descriptor*>(vs_sampler_state[i].get());
|
||||
|
||||
if (rsx::method_registers.vertex_textures[i].enabled())
|
||||
{
|
||||
*sampler_state = m_gl_texture_cache.upload_texture(unused, rsx::method_registers.vertex_textures[i], m_rtts);
|
||||
}
|
||||
else
|
||||
*sampler_state = {};
|
||||
|
||||
m_vertex_textures_dirty[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_samplers_dirty.store(false);
|
||||
|
||||
std::chrono::time_point<steady_clock> textures_end = steady_clock::now();
|
||||
m_textures_upload_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(textures_end - textures_start).count();
|
||||
}
|
||||
|
||||
std::chrono::time_point<steady_clock> program_start = steady_clock::now();
|
||||
//Load program here since it is dependent on vertex state
|
||||
|
||||
@@ -361,20 +309,102 @@ void GLGSRender::end()
|
||||
//glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
|
||||
}
|
||||
|
||||
//Bind textures and resolve external copy operations
|
||||
std::chrono::time_point<steady_clock> textures_start = steady_clock::now();
|
||||
int unused_location;
|
||||
|
||||
for (int i = 0; i < rsx::limits::fragment_textures_count; ++i)
|
||||
{
|
||||
if (m_program->uniforms.has_location("tex" + std::to_string(i), &unused_location))
|
||||
{
|
||||
auto sampler_state = static_cast<gl::texture_cache::sampled_image_descriptor*>(fs_sampler_state[i].get());
|
||||
|
||||
if (sampler_state->flag)
|
||||
continue;
|
||||
|
||||
sampler_state->flag = true;
|
||||
auto &tex = rsx::method_registers.fragment_textures[i];
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
|
||||
if (tex.enabled())
|
||||
{
|
||||
GLenum target = get_gl_target_for_texture(sampler_state->image_type);
|
||||
if (sampler_state->image_handle)
|
||||
{
|
||||
glBindTexture(target, sampler_state->image_handle);
|
||||
}
|
||||
else if (sampler_state->external_subresource_desc.external_handle)
|
||||
{
|
||||
void *unused = nullptr;
|
||||
glBindTexture(target, m_gl_texture_cache.create_temporary_subresource(unused, sampler_state->external_subresource_desc));
|
||||
sampler_state->flag = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindTexture(target, GL_NONE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, GL_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < rsx::limits::vertex_textures_count; ++i)
|
||||
{
|
||||
if (m_program->uniforms.has_location("vtex" + std::to_string(i), &unused_location))
|
||||
{
|
||||
auto sampler_state = static_cast<gl::texture_cache::sampled_image_descriptor*>(vs_sampler_state[i].get());
|
||||
|
||||
if (sampler_state->flag)
|
||||
continue;
|
||||
|
||||
sampler_state->flag = true;
|
||||
glActiveTexture(GL_TEXTURE0 + rsx::limits::fragment_textures_count + i);
|
||||
|
||||
if (sampler_state->image_handle)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, sampler_state->image_handle);
|
||||
}
|
||||
else if (sampler_state->external_subresource_desc.external_handle)
|
||||
{
|
||||
void *unused = nullptr;
|
||||
glBindTexture(GL_TEXTURE_2D, m_gl_texture_cache.create_temporary_subresource(unused, sampler_state->external_subresource_desc));
|
||||
sampler_state->flag = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, GL_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::time_point<steady_clock> textures_end = steady_clock::now();
|
||||
m_textures_upload_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(textures_end - textures_start).count();
|
||||
|
||||
update_draw_state();
|
||||
|
||||
//Check if depth buffer is bound and valid
|
||||
//If ds is not initialized clear it; it seems new depth textures should have depth cleared
|
||||
auto copy_rtt_contents = [](gl::render_target *surface)
|
||||
{
|
||||
//Copy data from old contents onto this one
|
||||
//1. Clip a rectangular region defning the data
|
||||
//2. Perform a GPU blit
|
||||
u16 parent_w = surface->old_contents->width();
|
||||
u16 parent_h = surface->old_contents->height();
|
||||
u16 copy_w, copy_h;
|
||||
if (surface->get_compatible_internal_format() == surface->old_contents->get_compatible_internal_format())
|
||||
{
|
||||
//Copy data from old contents onto this one
|
||||
//1. Clip a rectangular region defning the data
|
||||
//2. Perform a GPU blit
|
||||
u16 parent_w = surface->old_contents->width();
|
||||
u16 parent_h = surface->old_contents->height();
|
||||
u16 copy_w, copy_h;
|
||||
|
||||
std::tie(std::ignore, std::ignore, copy_w, copy_h) = rsx::clip_region<u16>(parent_w, parent_h, 0, 0, surface->width(), surface->height(), true);
|
||||
glCopyImageSubData(surface->old_contents->id(), GL_TEXTURE_2D, 0, 0, 0, 0, surface->id(), GL_TEXTURE_2D, 0, 0, 0, 0, copy_w, copy_h, 1);
|
||||
surface->set_cleared();
|
||||
}
|
||||
//TODO: download image contents and reupload them or do a memory cast to copy memory contents if not compatible
|
||||
|
||||
std::tie(std::ignore, std::ignore, copy_w, copy_h) = rsx::clip_region<u16>(parent_w, parent_h, 0, 0, surface->width(), surface->height(), true);
|
||||
glCopyImageSubData(surface->old_contents->id(), GL_TEXTURE_2D, 0, 0, 0, 0, surface->id(), GL_TEXTURE_2D, 0, 0, 0, 0, copy_w, copy_h, 1);
|
||||
surface->set_cleared();
|
||||
surface->old_contents = nullptr;
|
||||
};
|
||||
|
||||
@@ -452,43 +482,6 @@ void GLGSRender::end()
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
std::chrono::time_point<steady_clock> textures_start = steady_clock::now();
|
||||
|
||||
//Setup textures
|
||||
//Setting unused texture to 0 is not needed, but makes program validation happy if we choose to enforce it
|
||||
for (int i = 0; i < rsx::limits::fragment_textures_count; ++i)
|
||||
{
|
||||
int location;
|
||||
if (rsx::method_registers.fragment_textures[i].enabled() && m_program->uniforms.has_location("tex" + std::to_string(i), &location))
|
||||
{
|
||||
m_gl_texture_cache.upload_and_bind_texture(i, get_gl_target_for_texture(rsx::method_registers.fragment_textures[i]), rsx::method_registers.fragment_textures[i], m_rtts);
|
||||
|
||||
if (m_textures_dirty[i])
|
||||
{
|
||||
m_gl_sampler_states[i].apply(rsx::method_registers.fragment_textures[i]);
|
||||
m_textures_dirty[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Vertex textures
|
||||
for (int i = 0; i < rsx::limits::vertex_textures_count; ++i)
|
||||
{
|
||||
int texture_index = i + rsx::limits::fragment_textures_count;
|
||||
int location;
|
||||
|
||||
if (!rsx::method_registers.vertex_textures[i].enabled())
|
||||
continue;
|
||||
|
||||
if (m_program->uniforms.has_location("vtex" + std::to_string(i), &location))
|
||||
{
|
||||
m_gl_texture_cache.upload_and_bind_texture(texture_index, GL_TEXTURE_2D, rsx::method_registers.vertex_textures[i], m_rtts);
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::time_point<steady_clock> textures_end = steady_clock::now();
|
||||
m_textures_upload_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(textures_end - textures_start).count();
|
||||
|
||||
std::chrono::time_point<steady_clock> draw_start = steady_clock::now();
|
||||
|
||||
if (g_cfg.video.debug_output)
|
||||
@@ -497,14 +490,15 @@ void GLGSRender::end()
|
||||
}
|
||||
|
||||
const GLenum draw_mode = gl::draw_mode(rsx::method_registers.current_draw_clause.primitive);
|
||||
bool single_draw = rsx::method_registers.current_draw_clause.first_count_commands.size() <= 1 || rsx::method_registers.current_draw_clause.is_disjoint_primitive;
|
||||
bool single_draw = !supports_multidraw || (rsx::method_registers.current_draw_clause.first_count_commands.size() <= 1 || rsx::method_registers.current_draw_clause.is_disjoint_primitive);
|
||||
|
||||
if (indexed_draw_info)
|
||||
{
|
||||
const GLenum index_type = std::get<0>(indexed_draw_info.value());
|
||||
const u32 index_offset = std::get<1>(indexed_draw_info.value());
|
||||
const bool restarts_valid = gl::is_primitive_native(rsx::method_registers.current_draw_clause.primitive) && !rsx::method_registers.current_draw_clause.is_disjoint_primitive;
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.restart_index_enabled(), GL_PRIMITIVE_RESTART))
|
||||
if (gl_state.enable(restarts_valid && rsx::method_registers.restart_index_enabled(), GL_PRIMITIVE_RESTART))
|
||||
{
|
||||
glPrimitiveRestartIndex((index_type == GL_UNSIGNED_SHORT)? 0xffff: 0xffffffff);
|
||||
}
|
||||
@@ -545,21 +539,32 @@ void GLGSRender::end()
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<GLint> firsts;
|
||||
std::vector<GLsizei> counts;
|
||||
const auto draw_count = rsx::method_registers.current_draw_clause.first_count_commands.size();
|
||||
|
||||
firsts.reserve(draw_count);
|
||||
counts.reserve(draw_count);
|
||||
|
||||
u32 base_index = rsx::method_registers.current_draw_clause.first_count_commands.front().first;
|
||||
for (const auto &range : rsx::method_registers.current_draw_clause.first_count_commands)
|
||||
if (gl::get_driver_caps().vendor_AMD == false)
|
||||
{
|
||||
firsts.push_back(range.first - base_index);
|
||||
counts.push_back(range.second);
|
||||
}
|
||||
std::vector<GLint> firsts;
|
||||
std::vector<GLsizei> counts;
|
||||
const auto draw_count = rsx::method_registers.current_draw_clause.first_count_commands.size();
|
||||
|
||||
glMultiDrawArrays(draw_mode, firsts.data(), counts.data(), (GLsizei)draw_count);
|
||||
firsts.reserve(draw_count);
|
||||
counts.reserve(draw_count);
|
||||
|
||||
for (const auto &range : rsx::method_registers.current_draw_clause.first_count_commands)
|
||||
{
|
||||
firsts.push_back(range.first - base_index);
|
||||
counts.push_back(range.second);
|
||||
}
|
||||
|
||||
glMultiDrawArrays(draw_mode, firsts.data(), counts.data(), (GLsizei)draw_count);
|
||||
}
|
||||
else
|
||||
{
|
||||
//MultiDrawArrays is broken on some primitive types using AMD. One known type is GL_TRIANGLE_STRIP but there could be more
|
||||
for (const auto &range : rsx::method_registers.current_draw_clause.first_count_commands)
|
||||
{
|
||||
glDrawArrays(draw_mode, range.first - base_index, range.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,6 +619,9 @@ void GLGSRender::on_init_thread()
|
||||
{
|
||||
GSRender::on_init_thread();
|
||||
|
||||
m_frame->disable_wm_event_queue();
|
||||
m_frame->hide();
|
||||
|
||||
gl::init();
|
||||
|
||||
//Enable adaptive vsync if vsync is requested
|
||||
@@ -754,12 +762,13 @@ void GLGSRender::on_init_thread()
|
||||
m_thread_id = std::this_thread::get_id();
|
||||
|
||||
m_shaders_cache->load();
|
||||
|
||||
m_frame->enable_wm_event_queue();
|
||||
m_frame->show();
|
||||
}
|
||||
|
||||
void GLGSRender::on_exit()
|
||||
{
|
||||
glFinish();
|
||||
|
||||
m_prog_buffer.clear();
|
||||
|
||||
if (draw_fbo)
|
||||
@@ -827,13 +836,15 @@ void GLGSRender::on_exit()
|
||||
glDeleteQueries(1, &query.handle);
|
||||
}
|
||||
|
||||
return GSRender::on_exit();
|
||||
glFlush();
|
||||
glFinish();
|
||||
|
||||
GSRender::on_exit();
|
||||
}
|
||||
|
||||
void GLGSRender::clear_surface(u32 arg)
|
||||
{
|
||||
if (skip_frame || !framebuffer_status_valid) return;
|
||||
if (rsx::method_registers.surface_color_target() == rsx::surface_target::none) return;
|
||||
if ((arg & 0xf3) == 0) return;
|
||||
|
||||
GLbitfield mask = 0;
|
||||
@@ -925,49 +936,23 @@ bool GLGSRender::do_method(u32 cmd, u32 arg)
|
||||
|
||||
bool GLGSRender::check_program_state()
|
||||
{
|
||||
auto rtt_lookup_func = [this](u32 texaddr, rsx::fragment_texture &tex, bool is_depth) -> std::tuple<bool, u16>
|
||||
{
|
||||
gl::render_target *surface = nullptr;
|
||||
if (!is_depth)
|
||||
surface = m_rtts.get_texture_from_render_target_if_applicable(texaddr);
|
||||
else
|
||||
{
|
||||
surface = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr);
|
||||
|
||||
if (!surface && m_gl_texture_cache.is_depth_texture(texaddr, (u32)get_texture_size(tex)))
|
||||
return std::make_tuple(true, 0);
|
||||
}
|
||||
|
||||
if (!surface)
|
||||
{
|
||||
auto rsc = m_rtts.get_surface_subresource_if_applicable(texaddr, 0, 0, tex.pitch());
|
||||
if (!rsc.surface || rsc.is_depth_surface != is_depth)
|
||||
return std::make_tuple(false, 0);
|
||||
|
||||
surface = rsc.surface;
|
||||
}
|
||||
|
||||
return std::make_tuple(true, surface->get_native_pitch());
|
||||
};
|
||||
|
||||
get_current_fragment_program(rtt_lookup_func);
|
||||
|
||||
if (current_fragment_program.valid == false)
|
||||
return false;
|
||||
|
||||
get_current_vertex_program();
|
||||
return true;
|
||||
return (rsx::method_registers.shader_program_address() != 0);
|
||||
}
|
||||
|
||||
void GLGSRender::load_program(u32 vertex_base, u32 vertex_count)
|
||||
{
|
||||
get_current_fragment_program(fs_sampler_state);
|
||||
verify(HERE), current_fragment_program.valid;
|
||||
|
||||
get_current_vertex_program();
|
||||
|
||||
auto &fragment_program = current_fragment_program;
|
||||
auto &vertex_program = current_vertex_program;
|
||||
|
||||
vertex_program.skip_vertex_input_check = true; //not needed for us since decoding is done server side
|
||||
fragment_program.unnormalized_coords = 0; //unused
|
||||
void* pipeline_properties = nullptr;
|
||||
|
||||
auto old_program = m_program;
|
||||
m_program = &m_prog_buffer.getGraphicPipelineState(vertex_program, fragment_program, pipeline_properties);
|
||||
m_program->use();
|
||||
|
||||
@@ -1034,6 +1019,122 @@ void GLGSRender::load_program(u32 vertex_base, u32 vertex_count)
|
||||
m_transform_constants_dirty = false;
|
||||
}
|
||||
|
||||
void GLGSRender::update_draw_state()
|
||||
{
|
||||
std::chrono::time_point<steady_clock> then = steady_clock::now();
|
||||
|
||||
bool color_mask_b = rsx::method_registers.color_mask_b();
|
||||
bool color_mask_g = rsx::method_registers.color_mask_g();
|
||||
bool color_mask_r = rsx::method_registers.color_mask_r();
|
||||
bool color_mask_a = rsx::method_registers.color_mask_a();
|
||||
|
||||
gl_state.color_mask(color_mask_r, color_mask_g, color_mask_b, color_mask_a);
|
||||
gl_state.depth_mask(rsx::method_registers.depth_write_enabled());
|
||||
gl_state.stencil_mask(rsx::method_registers.stencil_mask());
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.depth_test_enabled(), GL_DEPTH_TEST))
|
||||
{
|
||||
gl_state.depth_func(comparison_op(rsx::method_registers.depth_func()));
|
||||
|
||||
float range_near = rsx::method_registers.clip_min();
|
||||
float range_far = rsx::method_registers.clip_max();
|
||||
|
||||
//Workaround to preserve depth precision but respect z direction
|
||||
//Ni no Kuni sets a very restricted z range (0.9x - 1.) and depth reads / tests are broken
|
||||
if (range_near <= range_far)
|
||||
gl_state.depth_range(0.f, 1.f);
|
||||
else
|
||||
gl_state.depth_range(1.f, 0.f);
|
||||
}
|
||||
|
||||
if (glDepthBoundsEXT && (gl_state.enable(rsx::method_registers.depth_bounds_test_enabled(), GL_DEPTH_BOUNDS_TEST_EXT)))
|
||||
{
|
||||
gl_state.depth_bounds(rsx::method_registers.depth_bounds_min(), rsx::method_registers.depth_bounds_max());
|
||||
}
|
||||
|
||||
gl_state.enable(rsx::method_registers.dither_enabled(), GL_DITHER);
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.blend_enabled(), GL_BLEND))
|
||||
{
|
||||
glBlendFuncSeparate(blend_factor(rsx::method_registers.blend_func_sfactor_rgb()),
|
||||
blend_factor(rsx::method_registers.blend_func_dfactor_rgb()),
|
||||
blend_factor(rsx::method_registers.blend_func_sfactor_a()),
|
||||
blend_factor(rsx::method_registers.blend_func_dfactor_a()));
|
||||
|
||||
auto blend_colors = rsx::get_constant_blend_colors();
|
||||
glBlendColor(blend_colors[0], blend_colors[1], blend_colors[2], blend_colors[3]);
|
||||
|
||||
glBlendEquationSeparate(blend_equation(rsx::method_registers.blend_equation_rgb()),
|
||||
blend_equation(rsx::method_registers.blend_equation_a()));
|
||||
}
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.stencil_test_enabled(), GL_STENCIL_TEST))
|
||||
{
|
||||
glStencilFunc(comparison_op(rsx::method_registers.stencil_func()),
|
||||
rsx::method_registers.stencil_func_ref(),
|
||||
rsx::method_registers.stencil_func_mask());
|
||||
|
||||
glStencilOp(stencil_op(rsx::method_registers.stencil_op_fail()), stencil_op(rsx::method_registers.stencil_op_zfail()),
|
||||
stencil_op(rsx::method_registers.stencil_op_zpass()));
|
||||
|
||||
if (rsx::method_registers.two_sided_stencil_test_enabled())
|
||||
{
|
||||
glStencilMaskSeparate(GL_BACK, rsx::method_registers.back_stencil_mask());
|
||||
|
||||
glStencilFuncSeparate(GL_BACK, comparison_op(rsx::method_registers.back_stencil_func()),
|
||||
rsx::method_registers.back_stencil_func_ref(), rsx::method_registers.back_stencil_func_mask());
|
||||
|
||||
glStencilOpSeparate(GL_BACK, stencil_op(rsx::method_registers.back_stencil_op_fail()),
|
||||
stencil_op(rsx::method_registers.back_stencil_op_zfail()), stencil_op(rsx::method_registers.back_stencil_op_zpass()));
|
||||
}
|
||||
}
|
||||
|
||||
gl_state.enablei(rsx::method_registers.blend_enabled_surface_1(), GL_BLEND, 1);
|
||||
gl_state.enablei(rsx::method_registers.blend_enabled_surface_2(), GL_BLEND, 2);
|
||||
gl_state.enablei(rsx::method_registers.blend_enabled_surface_3(), GL_BLEND, 3);
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.logic_op_enabled(), GL_COLOR_LOGIC_OP))
|
||||
{
|
||||
gl_state.logic_op(logic_op(rsx::method_registers.logic_operation()));
|
||||
}
|
||||
|
||||
gl_state.line_width(rsx::method_registers.line_width());
|
||||
gl_state.enable(rsx::method_registers.line_smooth_enabled(), GL_LINE_SMOOTH);
|
||||
|
||||
gl_state.enable(rsx::method_registers.poly_offset_point_enabled(), GL_POLYGON_OFFSET_POINT);
|
||||
gl_state.enable(rsx::method_registers.poly_offset_line_enabled(), GL_POLYGON_OFFSET_LINE);
|
||||
gl_state.enable(rsx::method_registers.poly_offset_fill_enabled(), GL_POLYGON_OFFSET_FILL);
|
||||
|
||||
//offset_bias is the constant factor, multiplied by the implementation factor R
|
||||
//offst_scale is the slope factor, multiplied by the triangle slope factor M
|
||||
gl_state.polygon_offset(rsx::method_registers.poly_offset_scale(), rsx::method_registers.poly_offset_bias());
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.cull_face_enabled(), GL_CULL_FACE))
|
||||
{
|
||||
gl_state.cull_face(cull_face(rsx::method_registers.cull_face_mode()));
|
||||
}
|
||||
|
||||
gl_state.front_face(front_face(rsx::method_registers.front_face_mode()));
|
||||
|
||||
//TODO
|
||||
//NV4097_SET_ANISO_SPREAD
|
||||
//NV4097_SET_SPECULAR_ENABLE
|
||||
//NV4097_SET_TWO_SIDE_LIGHT_EN
|
||||
//NV4097_SET_FLAT_SHADE_OP
|
||||
//NV4097_SET_EDGE_FLAG
|
||||
|
||||
|
||||
|
||||
//NV4097_SET_COLOR_KEY_COLOR
|
||||
//NV4097_SET_SHADER_CONTROL
|
||||
//NV4097_SET_ZMIN_MAX_CONTROL
|
||||
//NV4097_SET_ANTI_ALIASING_CONTROL
|
||||
//NV4097_SET_CLIP_ID_TEST_ENABLE
|
||||
|
||||
std::chrono::time_point<steady_clock> now = steady_clock::now();
|
||||
m_begin_time += (u32)std::chrono::duration_cast<std::chrono::microseconds>(now - then).count();
|
||||
}
|
||||
|
||||
void GLGSRender::flip(int buffer)
|
||||
{
|
||||
if (skip_frame)
|
||||
@@ -1156,7 +1257,9 @@ void GLGSRender::flip(int buffer)
|
||||
m_text_printer.print_text(0, 72, m_frame->client_width(), m_frame->client_height(), "draw call execution: " + std::to_string(m_draw_time) + "us");
|
||||
|
||||
auto num_dirty_textures = m_gl_texture_cache.get_unreleased_textures_count();
|
||||
auto texture_memory_size = m_gl_texture_cache.get_texture_memory_in_use() / (1024 * 1024);
|
||||
m_text_printer.print_text(0, 108, m_frame->client_width(), m_frame->client_height(), "Unreleased textures: " + std::to_string(num_dirty_textures));
|
||||
m_text_printer.print_text(0, 126, m_frame->client_width(), m_frame->client_height(), "Texture memory: " + std::to_string(texture_memory_size) + "M");
|
||||
}
|
||||
|
||||
m_frame->flip(m_context);
|
||||
@@ -1169,10 +1272,6 @@ void GLGSRender::flip(int buffer)
|
||||
tex->remove();
|
||||
|
||||
m_rtts.invalidated_resources.clear();
|
||||
|
||||
if (g_cfg.video.invalidate_surface_cache_every_frame)
|
||||
m_rtts.invalidate_surface_cache_data(nullptr);
|
||||
|
||||
m_vertex_cache->purge();
|
||||
|
||||
//If we are skipping the next frame, do not reset perf counters
|
||||
@@ -1195,38 +1294,45 @@ u64 GLGSRender::timestamp() const
|
||||
|
||||
bool GLGSRender::on_access_violation(u32 address, bool is_writing)
|
||||
{
|
||||
if (is_writing)
|
||||
return m_gl_texture_cache.invalidate_address(address);
|
||||
else
|
||||
bool can_flush = (std::this_thread::get_id() == m_thread_id);
|
||||
auto result = m_gl_texture_cache.invalidate_address(address, is_writing, can_flush);
|
||||
|
||||
if (!result.violation_handled)
|
||||
return false;
|
||||
|
||||
{
|
||||
if (std::this_thread::get_id() != m_thread_id)
|
||||
{
|
||||
bool flushable;
|
||||
gl::cached_texture_section* section_to_post;
|
||||
|
||||
std::tie(flushable, section_to_post) = m_gl_texture_cache.address_is_flushable(address);
|
||||
if (!flushable) return false;
|
||||
|
||||
work_item &task = post_flush_request(address, section_to_post);
|
||||
|
||||
vm::temporary_unlock();
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(task.guard_mutex);
|
||||
task.cv.wait(lock, [&task] { return task.processed; });
|
||||
}
|
||||
|
||||
task.received = true;
|
||||
return task.result;
|
||||
}
|
||||
|
||||
return m_gl_texture_cache.flush_address(address);
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
m_samplers_dirty.store(true);
|
||||
}
|
||||
|
||||
if (result.num_flushable > 0)
|
||||
{
|
||||
work_item &task = post_flush_request(address, result);
|
||||
|
||||
vm::temporary_unlock();
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(task.guard_mutex);
|
||||
task.cv.wait(lock, [&task] { return task.processed; });
|
||||
}
|
||||
|
||||
task.received = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLGSRender::on_notify_memory_unmapped(u32 address_base, u32 size)
|
||||
{
|
||||
if (m_gl_texture_cache.invalidate_range(address_base, size, false))
|
||||
//Discard all memory in that range without bothering with writeback (Force it for strict?)
|
||||
if (m_gl_texture_cache.invalidate_range(address_base, size, true, true, false).violation_handled)
|
||||
{
|
||||
m_gl_texture_cache.purge_dirty();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
m_samplers_dirty.store(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLGSRender::do_local_task()
|
||||
@@ -1242,20 +1348,7 @@ void GLGSRender::do_local_task()
|
||||
if (q.processed) continue;
|
||||
|
||||
std::unique_lock<std::mutex> lock(q.guard_mutex);
|
||||
|
||||
//Check if the suggested section is valid
|
||||
if (!q.section_to_flush->is_flushed())
|
||||
{
|
||||
m_gl_texture_cache.flush_address(q.address_to_flush);
|
||||
q.result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Another thread has unlocked this memory region already
|
||||
//Return success
|
||||
q.result = true;
|
||||
}
|
||||
|
||||
q.result = m_gl_texture_cache.flush_all(q.section_data);
|
||||
q.processed = true;
|
||||
|
||||
//Notify thread waiting on this
|
||||
@@ -1264,14 +1357,14 @@ void GLGSRender::do_local_task()
|
||||
}
|
||||
}
|
||||
|
||||
work_item& GLGSRender::post_flush_request(u32 address, gl::cached_texture_section *section)
|
||||
work_item& GLGSRender::post_flush_request(u32 address, gl::texture_cache::thrashed_set& flush_data)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queue_guard);
|
||||
|
||||
work_queue.emplace_back();
|
||||
work_item &result = work_queue.back();
|
||||
result.address_to_flush = address;
|
||||
result.section_to_flush = section;
|
||||
result.section_data = std::move(flush_data);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1286,9 +1379,18 @@ void GLGSRender::synchronize_buffers()
|
||||
|
||||
bool GLGSRender::scaled_image_from_memory(rsx::blit_src_info& src, rsx::blit_dst_info& dst, bool interpolate)
|
||||
{
|
||||
m_samplers_dirty.store(true);
|
||||
return m_gl_texture_cache.blit(src, dst, interpolate, m_rtts);
|
||||
}
|
||||
|
||||
void GLGSRender::notify_tile_unbound(u32 tile)
|
||||
{
|
||||
//TODO: Handle texture writeback
|
||||
//u32 addr = rsx::get_address(tiles[tile].offset, tiles[tile].location);
|
||||
//on_notify_memory_unmapped(addr, tiles[tile].size);
|
||||
//m_rtts.invalidate_surface_address(addr, false);
|
||||
}
|
||||
|
||||
void GLGSRender::check_zcull_status(bool framebuffer_swap, bool force_read)
|
||||
{
|
||||
if (g_cfg.video.disable_zcull_queries)
|
||||
|
||||
@@ -28,7 +28,7 @@ struct work_item
|
||||
std::mutex guard_mutex;
|
||||
|
||||
u32 address_to_flush = 0;
|
||||
gl::cached_texture_section *section_to_flush = nullptr;
|
||||
gl::texture_cache::thrashed_set section_data;
|
||||
|
||||
volatile bool processed = false;
|
||||
volatile bool result = false;
|
||||
@@ -310,33 +310,6 @@ struct driver_state
|
||||
}
|
||||
};
|
||||
|
||||
struct sw_ring_buffer
|
||||
{
|
||||
std::vector<u8> data;
|
||||
u32 ring_pos = 0;
|
||||
u32 ring_length = 0;
|
||||
|
||||
sw_ring_buffer(u32 size)
|
||||
{
|
||||
data.resize(size);
|
||||
ring_length = size;
|
||||
}
|
||||
|
||||
void* get(u32 dwords)
|
||||
{
|
||||
const u32 required = (dwords << 2);
|
||||
if ((ring_pos + required) > ring_length)
|
||||
{
|
||||
ring_pos = 0;
|
||||
return data.data();
|
||||
}
|
||||
|
||||
void *result = data.data() + ring_pos;
|
||||
ring_pos += required;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class GLGSRender : public GSRender
|
||||
{
|
||||
private:
|
||||
@@ -405,6 +378,12 @@ private:
|
||||
const u32 occlusion_query_count = 128;
|
||||
std::array<occlusion_query_info, 128> occlusion_query_data = {};
|
||||
|
||||
std::mutex m_sampler_mutex;
|
||||
u64 surface_store_tag = 0;
|
||||
std::atomic_bool m_samplers_dirty = {true};
|
||||
std::array<std::unique_ptr<rsx::sampled_image_descriptor_base>, rsx::limits::fragment_textures_count> fs_sampler_state = {};
|
||||
std::array<std::unique_ptr<rsx::sampled_image_descriptor_base>, rsx::limits::vertex_textures_count> vs_sampler_state = {};
|
||||
|
||||
public:
|
||||
GLGSRender();
|
||||
|
||||
@@ -422,13 +401,15 @@ private:
|
||||
bool check_program_state();
|
||||
void load_program(u32 vertex_base, u32 vertex_count);
|
||||
|
||||
void update_draw_state();
|
||||
|
||||
public:
|
||||
void read_buffers();
|
||||
void write_buffers();
|
||||
void set_viewport();
|
||||
|
||||
void synchronize_buffers();
|
||||
work_item& post_flush_request(u32 address, gl::cached_texture_section *section);
|
||||
work_item& post_flush_request(u32 address, gl::texture_cache::thrashed_set& flush_data);
|
||||
|
||||
bool scaled_image_from_memory(rsx::blit_src_info& src_info, rsx::blit_dst_info& dst_info, bool interpolate) override;
|
||||
|
||||
@@ -453,6 +434,7 @@ protected:
|
||||
|
||||
bool on_access_violation(u32 address, bool is_writing) override;
|
||||
void on_notify_memory_unmapped(u32 address_base, u32 size) override;
|
||||
void notify_tile_unbound(u32 tile) override;
|
||||
|
||||
virtual std::array<std::vector<gsl::byte>, 4> copy_render_targets_to_memory() override;
|
||||
virtual std::array<std::vector<gsl::byte>, 2> copy_depth_stencil_buffer_to_memory() override;
|
||||
|
||||
@@ -84,6 +84,7 @@ namespace gl
|
||||
bool NV_texture_barrier_supported = false;
|
||||
bool initialized = false;
|
||||
bool vendor_INTEL = false;
|
||||
bool vendor_AMD = false;
|
||||
|
||||
void initialize()
|
||||
{
|
||||
@@ -192,6 +193,12 @@ namespace gl
|
||||
if (!EXT_dsa_supported && glGetTextureImageEXT && glTextureBufferRangeEXT)
|
||||
EXT_dsa_supported = true;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else if (vendor_string.find("amd") != std::string::npos || vendor_string.find("ati") != std::string::npos)
|
||||
{
|
||||
vendor_AMD = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@@ -165,21 +165,36 @@ void GLGSRender::init_buffers(bool skip_reading)
|
||||
|
||||
const u16 clip_horizontal = rsx::method_registers.surface_clip_width();
|
||||
const u16 clip_vertical = rsx::method_registers.surface_clip_height();
|
||||
const u16 clip_x = rsx::method_registers.surface_clip_origin_x();
|
||||
const u16 clip_y = rsx::method_registers.surface_clip_origin_y();
|
||||
|
||||
framebuffer_status_valid = false;
|
||||
|
||||
if (clip_horizontal == 0 || clip_vertical == 0)
|
||||
{
|
||||
LOG_ERROR(RSX, "Invalid framebuffer setup, w=%d, h=%d", clip_horizontal, clip_vertical);
|
||||
framebuffer_status_valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto surface_addresses = get_color_surface_addresses();
|
||||
const auto depth_address = get_zeta_surface_address();
|
||||
|
||||
for (const auto &addr: surface_addresses)
|
||||
{
|
||||
if (addr)
|
||||
{
|
||||
framebuffer_status_valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!framebuffer_status_valid && !depth_address)
|
||||
return;
|
||||
|
||||
const auto pitchs = get_pitchs();
|
||||
const auto surface_format = rsx::method_registers.surface_color();
|
||||
const auto depth_format = rsx::method_registers.surface_depth_fmt();
|
||||
|
||||
const auto surface_addresses = get_color_surface_addresses();
|
||||
const auto depth_address = get_zeta_surface_address();
|
||||
|
||||
m_rtts.prepare_render_target(nullptr, surface_format, depth_format, clip_horizontal, clip_vertical,
|
||||
rsx::method_registers.surface_color_target(),
|
||||
surface_addresses, depth_address);
|
||||
@@ -189,6 +204,11 @@ void GLGSRender::init_buffers(bool skip_reading)
|
||||
bool old_format_found = false;
|
||||
gl::texture::format old_format;
|
||||
|
||||
const auto color_offsets = get_offsets();
|
||||
const auto color_locations = get_locations();
|
||||
const auto aa_mode = rsx::method_registers.surface_antialias();
|
||||
const auto bpp = get_format_block_size_in_bytes(surface_format);
|
||||
|
||||
for (int i = 0; i < rsx::limits::color_buffers_count; ++i)
|
||||
{
|
||||
if (surface_info[i].pitch && g_cfg.video.write_color_buffers)
|
||||
@@ -204,14 +224,15 @@ void GLGSRender::init_buffers(bool skip_reading)
|
||||
|
||||
if (std::get<0>(m_rtts.m_bound_render_targets[i]))
|
||||
{
|
||||
__glcheck draw_fbo.color[i] = *std::get<1>(m_rtts.m_bound_render_targets[i]);
|
||||
auto rtt = std::get<1>(m_rtts.m_bound_render_targets[i]);
|
||||
draw_fbo.color[i] = *rtt;
|
||||
|
||||
std::get<1>(m_rtts.m_bound_render_targets[i])->set_rsx_pitch(pitchs[i]);
|
||||
rtt->set_rsx_pitch(pitchs[i]);
|
||||
surface_info[i] = { surface_addresses[i], pitchs[i], false, surface_format, depth_format, clip_horizontal, clip_vertical };
|
||||
|
||||
//Verify pitch given is correct if pitch <= 64 (especially 64)
|
||||
if (pitchs[i] <= 64)
|
||||
{
|
||||
{
|
||||
const u16 native_pitch = std::get<1>(m_rtts.m_bound_render_targets[i])->get_native_pitch();
|
||||
if (native_pitch > pitchs[i])
|
||||
{
|
||||
@@ -223,6 +244,14 @@ void GLGSRender::init_buffers(bool skip_reading)
|
||||
surface_info[i].pitch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
rtt->tile = find_tile(color_offsets[i], color_locations[i]);
|
||||
rtt->aa_mode = aa_mode;
|
||||
rtt->set_raster_offset(clip_x, clip_y, bpp);
|
||||
m_gl_texture_cache.notify_surface_changed(surface_addresses[i]);
|
||||
|
||||
if (surface_info[i].pitch)
|
||||
m_gl_texture_cache.tag_framebuffer(surface_addresses[i] + rtt->raster_address_offset);
|
||||
}
|
||||
else
|
||||
surface_info[i] = {};
|
||||
@@ -230,10 +259,16 @@ void GLGSRender::init_buffers(bool skip_reading)
|
||||
|
||||
if (std::get<0>(m_rtts.m_bound_depth_stencil))
|
||||
{
|
||||
auto ds = std::get<1>(m_rtts.m_bound_depth_stencil);
|
||||
u8 texel_size = 2;
|
||||
|
||||
if (depth_format == rsx::surface_depth_format::z24s8)
|
||||
__glcheck draw_fbo.depth_stencil = *std::get<1>(m_rtts.m_bound_depth_stencil);
|
||||
{
|
||||
draw_fbo.depth_stencil = *ds;
|
||||
texel_size = 4;
|
||||
}
|
||||
else
|
||||
__glcheck draw_fbo.depth = *std::get<1>(m_rtts.m_bound_depth_stencil);
|
||||
draw_fbo.depth = *ds;
|
||||
|
||||
const u32 depth_surface_pitch = rsx::method_registers.surface_z_pitch();
|
||||
std::get<1>(m_rtts.m_bound_depth_stencil)->set_rsx_pitch(rsx::method_registers.surface_z_pitch());
|
||||
@@ -253,6 +288,13 @@ void GLGSRender::init_buffers(bool skip_reading)
|
||||
depth_surface_info.pitch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ds->aa_mode = aa_mode;
|
||||
ds->set_raster_offset(clip_x, clip_y, texel_size);
|
||||
m_gl_texture_cache.notify_surface_changed(depth_address);
|
||||
|
||||
if (depth_surface_info.pitch)
|
||||
m_gl_texture_cache.tag_framebuffer(depth_address + ds->raster_address_offset);
|
||||
}
|
||||
else
|
||||
depth_surface_info = {};
|
||||
@@ -390,7 +432,7 @@ void GLGSRender::read_buffers()
|
||||
}
|
||||
else
|
||||
{
|
||||
m_gl_texture_cache.invalidate_range(texaddr, range);
|
||||
m_gl_texture_cache.invalidate_range(texaddr, range, false, false, true);
|
||||
|
||||
std::unique_ptr<u8[]> buffer(new u8[pitch * height]);
|
||||
color_buffer.read(buffer.get(), width, height, pitch);
|
||||
|
||||
@@ -253,6 +253,10 @@ struct gl_render_target_traits
|
||||
static void invalidate_rtt_surface_contents(void *, gl::render_target *rtt, gl::render_target* /*old*/, bool forced) { if (forced) rtt->set_cleared(false); }
|
||||
static void invalidate_depth_surface_contents(void *, gl::render_target *ds, gl::render_target* /*old*/, bool) { ds->set_cleared(false); }
|
||||
|
||||
static
|
||||
void notify_surface_invalidated(const std::unique_ptr<gl::render_target>&)
|
||||
{}
|
||||
|
||||
static
|
||||
bool rtt_has_format_width_height(const std::unique_ptr<gl::render_target> &rtt, rsx::surface_color_format format, size_t width, size_t height, bool check_refs=false)
|
||||
{
|
||||
|
||||
@@ -473,6 +473,7 @@ namespace gl
|
||||
|
||||
glBindTexture(target, id);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glTexParameteri(target, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipmaps - 1);
|
||||
|
||||
|
||||
@@ -386,6 +386,8 @@ namespace gl
|
||||
|
||||
bool flush()
|
||||
{
|
||||
if (flushed) return true; //Already written, ignore
|
||||
|
||||
if (!copied)
|
||||
{
|
||||
LOG_WARNING(RSX, "Cache miss at address 0x%X. This is gonna hurt...", cpu_address_base);
|
||||
@@ -399,7 +401,6 @@ namespace gl
|
||||
}
|
||||
}
|
||||
|
||||
protect(utils::protection::rw);
|
||||
m_fence.wait_for_signal();
|
||||
flushed = true;
|
||||
|
||||
@@ -424,6 +425,16 @@ namespace gl
|
||||
rsx::scale_image_nearest(dst, const_cast<const void*>(data), width, height, rsx_pitch, real_pitch, pixel_size, samples);
|
||||
}
|
||||
|
||||
/* switch (gcm_format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT:
|
||||
rsx::shuffle_texel_data_wzyx<u16>(dst, rsx_pitch, width, height);
|
||||
break;
|
||||
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT:
|
||||
rsx::shuffle_texel_data_wzyx<u32>(dst, rsx_pitch, width, height);
|
||||
break;
|
||||
}*/
|
||||
|
||||
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
|
||||
@@ -443,22 +454,21 @@ namespace gl
|
||||
{
|
||||
//Read-only texture, destroy texture memory
|
||||
glDeleteTextures(1, &vram_texture);
|
||||
vram_texture = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Destroy pbo cache since vram texture is managed elsewhere
|
||||
glDeleteBuffers(1, &pbo_id);
|
||||
pbo_id = 0;
|
||||
pbo_size = 0;
|
||||
|
||||
if (scaled_texture)
|
||||
{
|
||||
glDeleteTextures(1, &scaled_texture);
|
||||
scaled_texture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vram_texture = 0;
|
||||
scaled_texture = 0;
|
||||
pbo_id = 0;
|
||||
pbo_size = 0;
|
||||
|
||||
if (!m_fence.is_empty())
|
||||
m_fence.destroy();
|
||||
}
|
||||
@@ -571,7 +581,7 @@ namespace gl
|
||||
m_temporary_surfaces.resize(0);
|
||||
}
|
||||
|
||||
u32 create_temporary_subresource(u32 src_id, GLenum sized_internal_fmt, u16 x, u16 y, u16 width, u16 height)
|
||||
u32 create_temporary_subresource_impl(u32 src_id, GLenum sized_internal_fmt, const GLenum dst_type, u16 x, u16 y, u16 width, u16 height)
|
||||
{
|
||||
u32 dst_id = 0;
|
||||
|
||||
@@ -589,19 +599,23 @@ namespace gl
|
||||
}
|
||||
|
||||
glGenTextures(1, &dst_id);
|
||||
glBindTexture(GL_TEXTURE_2D, dst_id);
|
||||
glBindTexture(dst_type, dst_id);
|
||||
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, sized_internal_fmt, width, height);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
if (dst_type == GL_TEXTURE_2D)
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, sized_internal_fmt, width, height);
|
||||
else if (dst_type == GL_TEXTURE_1D)
|
||||
glTexStorage1D(GL_TEXTURE_1D, 1, sized_internal_fmt, width);
|
||||
|
||||
glTexParameteri(dst_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(dst_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(dst_type, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(dst_type, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
|
||||
//Empty GL_ERROR
|
||||
glGetError();
|
||||
|
||||
glCopyImageSubData(src_id, GL_TEXTURE_2D, 0, x, y, 0,
|
||||
dst_id, GL_TEXTURE_2D, 0, 0, 0, 0, width, height, 1);
|
||||
dst_id, dst_type, 0, 0, 0, 0, width, height, 1);
|
||||
|
||||
m_temporary_surfaces.push_back(dst_id);
|
||||
|
||||
@@ -625,22 +639,59 @@ namespace gl
|
||||
u32 create_temporary_subresource_view(void*&, u32* src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h) override
|
||||
{
|
||||
const GLenum ifmt = gl::get_sized_internal_format(gcm_format);
|
||||
return create_temporary_subresource(*src, ifmt, x, y, w, h);
|
||||
return create_temporary_subresource_impl(*src, ifmt, GL_TEXTURE_2D, x, y, w, h);
|
||||
}
|
||||
|
||||
u32 create_temporary_subresource_view(void*&, gl::texture* src, u32 gcm_format, u16 x, u16 y, u16 w, u16 h) override
|
||||
{
|
||||
if (auto as_rtt = dynamic_cast<gl::render_target*>(src))
|
||||
{
|
||||
return create_temporary_subresource(src->id(), (GLenum)as_rtt->get_compatible_internal_format(), x, y, w, h);
|
||||
return create_temporary_subresource_impl(src->id(), (GLenum)as_rtt->get_compatible_internal_format(), GL_TEXTURE_2D, x, y, w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
const GLenum ifmt = gl::get_sized_internal_format(gcm_format);
|
||||
return create_temporary_subresource(src->id(), ifmt, x, y, w, h);
|
||||
return create_temporary_subresource_impl(src->id(), ifmt, GL_TEXTURE_2D, x, y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
u32 generate_cubemap_from_images(void*&, const u32 gcm_format, u16 size, std::array<u32, 6>& sources) override
|
||||
{
|
||||
const GLenum ifmt = gl::get_sized_internal_format(gcm_format);
|
||||
GLuint dst_id = 0;
|
||||
|
||||
glGenTextures(1, &dst_id);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, dst_id);
|
||||
glTexStorage2D(GL_TEXTURE_CUBE_MAP, 1, ifmt, size, size);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0);
|
||||
|
||||
//Empty GL_ERROR
|
||||
glGetError();
|
||||
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
if (sources[i] != 0)
|
||||
{
|
||||
glCopyImageSubData(sources[i], GL_TEXTURE_2D, 0, 0, 0, 0,
|
||||
dst_id, GL_TEXTURE_CUBE_MAP, 0, 0, 0, i, size, size, 1);
|
||||
}
|
||||
}
|
||||
|
||||
m_temporary_surfaces.push_back(dst_id);
|
||||
|
||||
if (GLenum err = glGetError())
|
||||
{
|
||||
LOG_WARNING(RSX, "Failed to copy image subresource with GL error 0x%X", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dst_id;
|
||||
}
|
||||
|
||||
cached_texture_section* create_new_texture(void*&, u32 rsx_address, u32 rsx_size, u16 width, u16 height, u16 depth, u16 mipmaps, const u32 gcm_format,
|
||||
const rsx::texture_upload_context context, const rsx::texture_dimension_extended type, const rsx::texture_create_flags flags,
|
||||
std::pair<std::array<u8, 4>, std::array<u8, 4>>& /*remap_vector*/) override
|
||||
@@ -670,10 +721,14 @@ namespace gl
|
||||
cached.set_depth_flag(depth_flag);
|
||||
cached.set_view_flags(flags);
|
||||
cached.set_context(context);
|
||||
cached.set_image_type(type);
|
||||
|
||||
//Its not necessary to lock blit dst textures as they are just reused as necessary
|
||||
if (context != rsx::texture_upload_context::blit_engine_dst || g_cfg.video.strict_rendering_mode)
|
||||
{
|
||||
cached.protect(utils::protection::ro);
|
||||
update_cache_tag();
|
||||
}
|
||||
|
||||
return &cached;
|
||||
}
|
||||
@@ -773,6 +828,7 @@ namespace gl
|
||||
}
|
||||
|
||||
clear_temporary_subresources();
|
||||
m_temporary_subresource_cache.clear();
|
||||
}
|
||||
|
||||
bool blit(rsx::blit_src_info& src, rsx::blit_dst_info& dst, bool linear_interpolate, gl_render_targets& m_rtts)
|
||||
@@ -780,15 +836,5 @@ namespace gl
|
||||
void* unused = nullptr;
|
||||
return upload_scaled_image(src, dst, linear_interpolate, unused, m_rtts, m_hw_blitter);
|
||||
}
|
||||
|
||||
template<typename RsxTextureType>
|
||||
void upload_and_bind_texture(int index, GLenum target, RsxTextureType &tex, gl_render_targets &m_rtts)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
void* unused = nullptr;
|
||||
|
||||
auto id = upload_texture(unused, tex, m_rtts);
|
||||
glBindTexture(target, id);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace
|
||||
u32 block_sz = vertex_draw_count * type_size;
|
||||
|
||||
gsl::span<gsl::byte> dst{ reinterpret_cast<gsl::byte*>(ptr), ::narrow<u32>(block_sz) };
|
||||
std::tie(min_index, max_index) = write_index_array_data_to_buffer(dst, raw_index_buffer,
|
||||
std::tie(min_index, max_index, vertex_draw_count) = write_index_array_data_to_buffer(dst, raw_index_buffer,
|
||||
type, draw_mode, rsx::method_registers.restart_index_enabled(), rsx::method_registers.restart_index(), first_count_commands,
|
||||
[](auto prim) { return !gl::is_primitive_native(prim); });
|
||||
|
||||
|
||||
@@ -18,10 +18,9 @@ std::string GLVertexDecompilerThread::getIntTypeName(size_t elementCount)
|
||||
return "ivec4";
|
||||
}
|
||||
|
||||
|
||||
std::string GLVertexDecompilerThread::getFunction(FUNCTION f)
|
||||
{
|
||||
return gl::getFunctionImpl(f);
|
||||
return glsl::getFunctionImpl(f);
|
||||
}
|
||||
|
||||
std::string GLVertexDecompilerThread::compareFunction(COMPARE f, const std::string &Op0, const std::string &Op1)
|
||||
|
||||
+12
-11
@@ -4,16 +4,6 @@
|
||||
|
||||
#include "GSRender.h"
|
||||
|
||||
draw_context_t GSFrameBase::new_context()
|
||||
{
|
||||
if (void* context = make_context())
|
||||
{
|
||||
return std::shared_ptr<void>(context, [this](void* ctxt) { delete_context(ctxt); });
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GSRender::GSRender()
|
||||
{
|
||||
m_frame = Emu.GetCallbacks().get_gs_frame().release();
|
||||
@@ -42,11 +32,22 @@ void GSRender::on_init_thread()
|
||||
{
|
||||
if (m_frame)
|
||||
{
|
||||
m_context = m_frame->new_context();
|
||||
m_context = m_frame->make_context();
|
||||
m_frame->set_current(m_context);
|
||||
}
|
||||
}
|
||||
|
||||
void GSRender::on_exit()
|
||||
{
|
||||
if (m_frame)
|
||||
{
|
||||
m_frame->delete_context(m_context);
|
||||
m_context = nullptr;
|
||||
}
|
||||
|
||||
rsx::thread::on_exit();
|
||||
}
|
||||
|
||||
void GSRender::flip(int buffer)
|
||||
{
|
||||
if (m_frame)
|
||||
|
||||
@@ -24,12 +24,15 @@ enum wm_event
|
||||
geometry_change_notice, //about to start resizing and/or moving the window
|
||||
geometry_change_in_progress, //window being resized and/or moved
|
||||
window_resized, //window was resized
|
||||
window_moved //window moved without resize
|
||||
window_minimized, //window was minimized
|
||||
window_restored, //window was restored from a minimized state
|
||||
window_moved, //window moved without resize
|
||||
window_visibility_changed
|
||||
};
|
||||
|
||||
using RSXDebuggerPrograms = std::vector<RSXDebuggerProgram>;
|
||||
|
||||
using draw_context_t = std::shared_ptr<void>;
|
||||
using draw_context_t = void*;
|
||||
|
||||
class GSFrameBase
|
||||
{
|
||||
@@ -42,8 +45,8 @@ public:
|
||||
virtual void hide() = 0;
|
||||
virtual void show() = 0;
|
||||
|
||||
draw_context_t new_context();
|
||||
|
||||
virtual void delete_context(draw_context_t ctx) = 0;
|
||||
virtual draw_context_t make_context() = 0;
|
||||
virtual void set_current(draw_context_t ctx) = 0;
|
||||
virtual void flip(draw_context_t ctx, bool skip_frame=false) = 0;
|
||||
virtual int client_width() = 0;
|
||||
@@ -52,12 +55,11 @@ public:
|
||||
virtual void* handle() const = 0;
|
||||
|
||||
protected:
|
||||
virtual void delete_context(void* ctx) = 0;
|
||||
virtual void* make_context() = 0;
|
||||
|
||||
//window manager event management
|
||||
wm_event m_raised_event;
|
||||
std::atomic_bool wm_event_raised = {};
|
||||
std::atomic_bool wm_event_queue_enabled = {};
|
||||
|
||||
public:
|
||||
//synchronize native window access
|
||||
@@ -83,6 +85,16 @@ public:
|
||||
|
||||
return get_default_wm_event();
|
||||
}
|
||||
|
||||
void disable_wm_event_queue()
|
||||
{
|
||||
wm_event_queue_enabled.store(false);
|
||||
}
|
||||
|
||||
void enable_wm_event_queue()
|
||||
{
|
||||
wm_event_queue_enabled.store(true);
|
||||
}
|
||||
};
|
||||
|
||||
class GSRender : public rsx::thread
|
||||
@@ -97,6 +109,7 @@ public:
|
||||
|
||||
void on_init_rsx() override;
|
||||
void on_init_thread() override;
|
||||
void on_exit() override;
|
||||
|
||||
void flip(int buffer) override;
|
||||
};
|
||||
|
||||
@@ -147,7 +147,7 @@ union SRC1
|
||||
u32 swizzle_w : 2;
|
||||
u32 neg : 1;
|
||||
u32 abs : 1;
|
||||
u32 input_mod_src0 : 3;
|
||||
u32 input_prec_mod : 3; // Looks to be a precision clamping modifier affecting all inputs (tested with Dark Souls II)
|
||||
u32 : 6;
|
||||
u32 scale : 3;
|
||||
u32 opcode_is_branch : 1;
|
||||
@@ -243,13 +243,10 @@ struct RSXFragmentProgram
|
||||
|
||||
void set_texture_dimension(const std::array<rsx::texture_dimension_extended, 16> &dimensions)
|
||||
{
|
||||
size_t id = 0;
|
||||
for (const rsx::texture_dimension_extended &dim : dimensions)
|
||||
texture_dimensions = 0;
|
||||
for (u32 i = 0, offset = 0; i < 16; ++i, offset += 2)
|
||||
{
|
||||
texture_dimensions &= ~(0x3 << (id * 2));
|
||||
u8 d = (u8)dim;
|
||||
texture_dimensions |= ((d & 0x3) << (id * 2));
|
||||
id++;
|
||||
texture_dimensions |= (u32)dimensions[i] << offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+126
-28
@@ -7,6 +7,7 @@
|
||||
#include "Emu/Cell/PPUCallback.h"
|
||||
|
||||
#include "Common/BufferUtils.h"
|
||||
#include "Common/texture_cache.h"
|
||||
#include "rsx_methods.h"
|
||||
#include "rsx_utils.h"
|
||||
|
||||
@@ -243,6 +244,7 @@ namespace rsx
|
||||
};
|
||||
m_rtts_dirty = true;
|
||||
memset(m_textures_dirty, -1, sizeof(m_textures_dirty));
|
||||
memset(m_vertex_textures_dirty, -1, sizeof(m_vertex_textures_dirty));
|
||||
m_transform_constants_dirty = true;
|
||||
}
|
||||
|
||||
@@ -392,6 +394,7 @@ namespace rsx
|
||||
|
||||
// Raise priority above other threads
|
||||
thread_ctrl::set_native_priority(1);
|
||||
thread_ctrl::set_ideal_processor_core(0);
|
||||
|
||||
// Round to nearest to deal with forward/reverse scaling
|
||||
fesetround(FE_TONEAREST);
|
||||
@@ -477,10 +480,10 @@ namespace rsx
|
||||
//Execute backend-local tasks first
|
||||
do_local_task();
|
||||
|
||||
const u32 get = ctrl->get;
|
||||
ctrl->get.store(internal_get.load());
|
||||
const u32 put = ctrl->put;
|
||||
|
||||
if (put == get || !Emu.IsRunning())
|
||||
if (put == internal_get || !Emu.IsRunning())
|
||||
{
|
||||
if (has_deferred_call)
|
||||
flush_command_queue();
|
||||
@@ -491,16 +494,16 @@ namespace rsx
|
||||
|
||||
//Validate put and get registers
|
||||
//TODO: Who should handle graphics exceptions??
|
||||
const u32 get_address = RSXIOMem.RealAddr(get);
|
||||
const u32 get_address = RSXIOMem.RealAddr(internal_get);
|
||||
|
||||
if (!get_address)
|
||||
{
|
||||
LOG_ERROR(RSX, "Invalid FIFO queue get/put registers found, get=0x%X, put=0x%X", get, put);
|
||||
LOG_ERROR(RSX, "Invalid FIFO queue get/put registers found, get=0x%X, put=0x%X", internal_get.load(), put);
|
||||
|
||||
if (mem_faults_count >= 3)
|
||||
{
|
||||
LOG_ERROR(RSX, "Application has failed to recover, discarding FIFO queue");
|
||||
ctrl->get = put;
|
||||
internal_get = put;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -512,29 +515,29 @@ namespace rsx
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 cmd = ReadIO32(get);
|
||||
const u32 cmd = ReadIO32(internal_get);
|
||||
const u32 count = (cmd >> 18) & 0x7ff;
|
||||
|
||||
if ((cmd & RSX_METHOD_OLD_JUMP_CMD_MASK) == RSX_METHOD_OLD_JUMP_CMD)
|
||||
{
|
||||
u32 offs = cmd & 0x1ffffffc;
|
||||
//LOG_WARNING(RSX, "rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", offs, m_ioAddress + get, cmd, get, put);
|
||||
ctrl->get = offs;
|
||||
internal_get = offs;
|
||||
continue;
|
||||
}
|
||||
if ((cmd & RSX_METHOD_NEW_JUMP_CMD_MASK) == RSX_METHOD_NEW_JUMP_CMD)
|
||||
{
|
||||
u32 offs = cmd & 0xfffffffc;
|
||||
//LOG_WARNING(RSX, "rsx jump(0x%x) #addr=0x%x, cmd=0x%x, get=0x%x, put=0x%x", offs, m_ioAddress + get, cmd, get, put);
|
||||
ctrl->get = offs;
|
||||
internal_get = offs;
|
||||
continue;
|
||||
}
|
||||
if ((cmd & RSX_METHOD_CALL_CMD_MASK) == RSX_METHOD_CALL_CMD)
|
||||
{
|
||||
m_call_stack.push(get + 4);
|
||||
m_call_stack.push(internal_get + 4);
|
||||
u32 offs = cmd & ~3;
|
||||
//LOG_WARNING(RSX, "rsx call(0x%x) #0x%x - 0x%x", offs, cmd, get);
|
||||
ctrl->get = offs;
|
||||
internal_get = offs;
|
||||
continue;
|
||||
}
|
||||
if (cmd == RSX_METHOD_RETURN_CMD)
|
||||
@@ -542,26 +545,26 @@ namespace rsx
|
||||
u32 get = m_call_stack.top();
|
||||
m_call_stack.pop();
|
||||
//LOG_WARNING(RSX, "rsx return(0x%x)", get);
|
||||
ctrl->get = get;
|
||||
internal_get = get;
|
||||
continue;
|
||||
}
|
||||
if (cmd == 0) //nop
|
||||
{
|
||||
ctrl->get = get + 4;
|
||||
internal_get += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Validate the args ptr if the command attempts to read from it
|
||||
const u32 args_address = RSXIOMem.RealAddr(get + 4);
|
||||
const u32 args_address = RSXIOMem.RealAddr(internal_get + 4);
|
||||
|
||||
if (!args_address && count)
|
||||
{
|
||||
LOG_ERROR(RSX, "Invalid FIFO queue args ptr found, get=0x%X, cmd=0x%X, count=%d", get, cmd, count);
|
||||
LOG_ERROR(RSX, "Invalid FIFO queue args ptr found, get=0x%X, cmd=0x%X, count=%d", internal_get.load(), cmd, count);
|
||||
|
||||
if (mem_faults_count >= 3)
|
||||
{
|
||||
LOG_ERROR(RSX, "Application has failed to recover, discarding FIFO queue");
|
||||
ctrl->get = put;
|
||||
internal_get = put;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -595,6 +598,7 @@ namespace rsx
|
||||
|
||||
bool execute_method_call = true;
|
||||
|
||||
//TODO: Flatten draw calls when multidraw is not supported to simplify checking in the end() methods
|
||||
if (supports_multidraw)
|
||||
{
|
||||
//TODO: Make this cleaner
|
||||
@@ -713,11 +717,11 @@ namespace rsx
|
||||
{
|
||||
//This is almost guaranteed to be heap corruption at this point
|
||||
//Ignore the rest of the chain
|
||||
ctrl->get = put;
|
||||
internal_get = put;
|
||||
continue;
|
||||
}
|
||||
|
||||
ctrl->get = get + (count + 1) * 4;
|
||||
internal_get += (count + 1) * 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1320,7 +1324,99 @@ namespace rsx
|
||||
return result;
|
||||
}
|
||||
|
||||
void thread::get_current_fragment_program(std::function<std::tuple<bool, u16>(u32, fragment_texture&, bool)> get_surface_info)
|
||||
void thread::get_current_fragment_program(const std::array<std::unique_ptr<rsx::sampled_image_descriptor_base>, rsx::limits::fragment_textures_count>& sampler_descriptors)
|
||||
{
|
||||
auto &result = current_fragment_program = {};
|
||||
|
||||
const u32 shader_program = rsx::method_registers.shader_program_address();
|
||||
if (shader_program == 0)
|
||||
return;
|
||||
|
||||
const u32 program_location = (shader_program & 0x3) - 1;
|
||||
const u32 program_offset = (shader_program & ~0x3);
|
||||
|
||||
result.offset = program_offset;
|
||||
result.addr = vm::base(rsx::get_address(program_offset, program_location));
|
||||
result.valid = true;
|
||||
result.ctrl = rsx::method_registers.shader_control();
|
||||
result.unnormalized_coords = 0;
|
||||
result.front_back_color_enabled = !rsx::method_registers.two_side_light_en();
|
||||
result.back_color_diffuse_output = !!(rsx::method_registers.vertex_attrib_output_mask() & CELL_GCM_ATTRIB_OUTPUT_MASK_BACKDIFFUSE);
|
||||
result.back_color_specular_output = !!(rsx::method_registers.vertex_attrib_output_mask() & CELL_GCM_ATTRIB_OUTPUT_MASK_BACKSPECULAR);
|
||||
result.front_color_diffuse_output = !!(rsx::method_registers.vertex_attrib_output_mask() & CELL_GCM_ATTRIB_OUTPUT_MASK_FRONTDIFFUSE);
|
||||
result.front_color_specular_output = !!(rsx::method_registers.vertex_attrib_output_mask() & CELL_GCM_ATTRIB_OUTPUT_MASK_FRONTSPECULAR);
|
||||
result.redirected_textures = 0;
|
||||
result.shadow_textures = 0;
|
||||
|
||||
std::array<texture_dimension_extended, 16> texture_dimensions;
|
||||
const auto resolution_scale = rsx::get_resolution_scale();
|
||||
|
||||
for (u32 i = 0; i < rsx::limits::fragment_textures_count; ++i)
|
||||
{
|
||||
auto &tex = rsx::method_registers.fragment_textures[i];
|
||||
result.texture_scale[i][0] = sampler_descriptors[i]->scale_x;
|
||||
result.texture_scale[i][1] = sampler_descriptors[i]->scale_y;
|
||||
result.textures_alpha_kill[i] = 0;
|
||||
result.textures_zfunc[i] = 0;
|
||||
|
||||
if (!tex.enabled())
|
||||
{
|
||||
texture_dimensions[i] = texture_dimension_extended::texture_dimension_2d;
|
||||
}
|
||||
else
|
||||
{
|
||||
texture_dimensions[i] = sampler_descriptors[i]->image_type;
|
||||
|
||||
if (tex.alpha_kill_enabled())
|
||||
{
|
||||
//alphakill can be ignored unless a valid comparison function is set
|
||||
const rsx::comparison_function func = (rsx::comparison_function)tex.zfunc();
|
||||
if (func < rsx::comparison_function::always && func > rsx::comparison_function::never)
|
||||
{
|
||||
result.textures_alpha_kill[i] = 1;
|
||||
result.textures_zfunc[i] = (u8)func;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 texaddr = rsx::get_address(tex.offset(), tex.location());
|
||||
const u32 raw_format = tex.format();
|
||||
|
||||
if (raw_format & CELL_GCM_TEXTURE_UN)
|
||||
result.unnormalized_coords |= (1 << i);
|
||||
|
||||
if (sampler_descriptors[i]->is_depth_texture)
|
||||
{
|
||||
const u32 format = raw_format & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN);
|
||||
switch (format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_A8R8G8B8:
|
||||
case CELL_GCM_TEXTURE_D8R8G8B8:
|
||||
case CELL_GCM_TEXTURE_A4R4G4B4:
|
||||
case CELL_GCM_TEXTURE_R5G6B5:
|
||||
result.redirected_textures |= (1 << i);
|
||||
break;
|
||||
case CELL_GCM_TEXTURE_DEPTH16:
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8:
|
||||
case CELL_GCM_TEXTURE_DEPTH16_FLOAT:
|
||||
{
|
||||
const auto compare_mode = (rsx::comparison_function)tex.zfunc();
|
||||
if (result.textures_alpha_kill[i] == 0 &&
|
||||
compare_mode < rsx::comparison_function::always &&
|
||||
compare_mode > rsx::comparison_function::never)
|
||||
result.shadow_textures |= (1 << i);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(RSX, "Depth texture bound to pipeline with unexpected format 0x%X", format);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.set_texture_dimension(texture_dimensions);
|
||||
}
|
||||
|
||||
void thread::get_current_fragment_program_legacy(std::function<std::tuple<bool, u16>(u32, fragment_texture&, bool)> get_surface_info)
|
||||
{
|
||||
auto &result = current_fragment_program = {};
|
||||
|
||||
@@ -1411,8 +1507,8 @@ namespace rsx
|
||||
case CELL_GCM_TEXTURE_D8R8G8B8:
|
||||
case CELL_GCM_TEXTURE_A4R4G4B4:
|
||||
case CELL_GCM_TEXTURE_R5G6B5:
|
||||
result.redirected_textures |= (1 << i);
|
||||
break;
|
||||
result.redirected_textures |= (1 << i);
|
||||
break;
|
||||
case CELL_GCM_TEXTURE_DEPTH16:
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8:
|
||||
case CELL_GCM_TEXTURE_DEPTH16_FLOAT:
|
||||
@@ -1425,7 +1521,7 @@ namespace rsx
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(RSX, "Depth texture bound to pipeline with unexpected format 0x%X", format);
|
||||
LOG_ERROR(RSX, "Depth texture bound to pipeline with unexpected format 0x%X", format);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1458,7 +1554,7 @@ namespace rsx
|
||||
{
|
||||
for (GcmTileInfo &tile : tiles)
|
||||
{
|
||||
if (!tile.binded || tile.location != location)
|
||||
if (!tile.binded || (tile.location & 1) != (location & 1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1570,6 +1666,7 @@ namespace rsx
|
||||
u32 volatile_offset = 0;
|
||||
u32 persistent_offset = 0;
|
||||
|
||||
//NOTE: Order is important! Transient ayout is always push_buffers followed by register data
|
||||
if (rsx::method_registers.current_draw_clause.is_immediate_draw)
|
||||
{
|
||||
for (const auto &info : layout.volatile_blocks)
|
||||
@@ -1764,12 +1861,7 @@ namespace rsx
|
||||
return;
|
||||
}
|
||||
|
||||
for (const u8 index : layout.referenced_registers)
|
||||
{
|
||||
memcpy(transient, rsx::method_registers.register_vertex_info[index].data.data(), 16);
|
||||
transient += 16;
|
||||
}
|
||||
|
||||
//NOTE: Order is important! Transient ayout is always push_buffers followed by register data
|
||||
if (draw_call.is_immediate_draw)
|
||||
{
|
||||
//NOTE: It is possible for immediate draw to only contain index data, so vertex data can be in persistent memory
|
||||
@@ -1779,6 +1871,12 @@ namespace rsx
|
||||
transient += info.second;
|
||||
}
|
||||
}
|
||||
|
||||
for (const u8 index : layout.referenced_registers)
|
||||
{
|
||||
memcpy(transient, rsx::method_registers.register_vertex_info[index].data.data(), 16);
|
||||
transient += 16;
|
||||
}
|
||||
}
|
||||
|
||||
if (persistent != nullptr)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user