Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a33e5e909e | |||
| 5cd8a00c8c | |||
| 488c4d198f | |||
| 081d7c5de7 | |||
| 09c77ff46c | |||
| 2102b5d4aa | |||
| 57f0ddd13c | |||
| 5f047034ae | |||
| ffe6c9ba5a | |||
| 910fc54ee2 | |||
| f00d9a7c7f | |||
| 2dce55d036 | |||
| 315798b1f4 | |||
| dece1e01f4 | |||
| a19ffba8e8 | |||
| e230867492 | |||
| 0c8e4c0887 | |||
| 84b8a08d26 | |||
| 2855869530 | |||
| d41b49d8b4 | |||
| 4804efc17d | |||
| 053ab585f4 | |||
| 20d4c09a1c | |||
| 68b3229756 | |||
| 87741141f1 | |||
| 705820c430 | |||
| 6b23e733d0 | |||
| 07cbf3da48 | |||
| af1b13550b | |||
| 8ccaabb502 | |||
| 01349b8cee | |||
| 4487cc8e7a | |||
| cb9e6e75db | |||
| e487480ca9 | |||
| bc9594ed57 | |||
| 817dfe8651 | |||
| ba54f7ae39 | |||
| f328cdbaef | |||
| f2e5b6c350 | |||
| ac82ecf387 | |||
| 4b1c052030 | |||
| c0a0cb98c5 | |||
| 4fd69a09c7 | |||
| eea2c20420 | |||
| f4d2fccdfe | |||
| c8a6bc6e24 | |||
| 7f542a5c99 | |||
| c1f32aa570 | |||
| 7bbadfd08b | |||
| 3a9428a88e | |||
| 7233640cf0 | |||
| effdb65ae4 | |||
| 91d0511154 | |||
| d4e88aa11c | |||
| ec0cd9f006 | |||
| 8db7ef4d5a | |||
| 504e3112dd | |||
| f96e9b6ed7 | |||
| f056b2f4ab | |||
| 445b7c0758 | |||
| 53f8b03acc | |||
| 900329a1de | |||
| 7c6c33eef8 | |||
| dacc9af38f | |||
| 8d7620d95f | |||
| 3043c73745 | |||
| f786c078c2 | |||
| 31cee7e4c8 | |||
| 6e8ccbcf17 | |||
| 72e54e8b60 | |||
| e0f53ace19 | |||
| 2444385763 | |||
| 6141bc5598 | |||
| 9138d537c8 | |||
| 05354698f0 | |||
| 14e15b2feb | |||
| 324d21d849 | |||
| aa30e865c2 | |||
| 0772b9c879 | |||
| 296052b1ff | |||
| be834a67d1 | |||
| b63461f599 | |||
| 1fb7cc4002 | |||
| a766478e15 | |||
| b61a69c877 | |||
| 7753ac60b0 | |||
| aabf3c6695 | |||
| d78b0467d8 |
@@ -60,6 +60,7 @@ rpcs3/git-version.h
|
||||
# Visual Studio Files
|
||||
.vs/*
|
||||
.vscode/*
|
||||
*.ipch
|
||||
*.vspx
|
||||
*.psess
|
||||
*.VC.*
|
||||
|
||||
+7
-3
@@ -7,14 +7,14 @@
|
||||
ignore = dirty
|
||||
[submodule "llvm"]
|
||||
path = llvm
|
||||
url = https://github.com/RPCS3/llvm
|
||||
url = https://github.com/llvm-mirror/llvm
|
||||
branch = release_60
|
||||
[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
|
||||
@@ -33,7 +33,7 @@
|
||||
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
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/Optional"]
|
||||
@@ -42,3 +42,7 @@
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/zeux/pugixml
|
||||
[submodule "polly"]
|
||||
path = polly
|
||||
url = https://github.com/llvm-mirror/polly
|
||||
branch = release_60
|
||||
|
||||
+20
-28
@@ -25,17 +25,15 @@ git:
|
||||
depth: false # Unshallow clone to obtain proper GIT_VERSION
|
||||
submodules: false
|
||||
|
||||
env:
|
||||
- QTVER=5.10.1
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
|
||||
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
|
||||
fi;
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
wget http://download.qt.io/official_releases/online_installers/qt-unified-linux-x64-online.run ;
|
||||
chmod a+x ./qt-unified-linux-x64-online.run ;
|
||||
export QT_QPA_PLATFORM=minimal ;
|
||||
travis_wait 60 ./qt-unified-linux-x64-online.run --script qt-installer-noninteractive.qs --no-force-installations --verbose ;
|
||||
fi;
|
||||
|
||||
install:
|
||||
# Install updated libglew-dev since the version provided by trusty is outdated
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
wget https://mirrors.kernel.org/ubuntu/pool/universe/g/glew/libglew-dev_2.0.0-5_amd64.deb;
|
||||
@@ -50,9 +48,8 @@ before_install:
|
||||
|
||||
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.10.1/gcc_64/lib/cmake
|
||||
- mkdir build ; cd build
|
||||
- export CMAKE_PREFIX_PATH=~/Qt/${QTVER}/gcc_64/lib/cmake
|
||||
- export CXXFLAGS="$CXXFLAGS -DTRAVIS=true";
|
||||
- if [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH";
|
||||
@@ -60,44 +57,39 @@ before_script:
|
||||
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH/#$TRAVIS_PULL_REQUEST";
|
||||
fi;
|
||||
- cmake .. -DCMAKE_INSTALL_PREFIX=/usr -G Ninja;
|
||||
|
||||
script:
|
||||
- ninja
|
||||
|
||||
after_script:
|
||||
- cd build
|
||||
- # AppImage generation
|
||||
- if [ -n "$UPLOAD_URL" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$CC" = "clang" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export LD_LIBRARY_PATH=~/Qt/5.10.1/gcc_64/lib;
|
||||
source /opt/qt510/bin/qt510-env.sh ;
|
||||
echo $LD_LIBRARY_PATH ;
|
||||
export LD_LIBRARY_PATH=/opt/qt510/lib ;
|
||||
DESTDIR=appdir ninja 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.10.1/gcc_64/bin/:${PATH} ;
|
||||
export PATH=~/Qt/$QTVER/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.10.1/gcc_64/lib/libQt5Svg.so.5 ./appdir/usr/lib/ ;
|
||||
cp ~/Qt/5.10.1/gcc_64/plugins/xcbglintegrations/* ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||
cp ~/Qt/5.10.1/gcc_64/plugins/imageformats/* ./appdir/usr/plugins/imageformats/ ;
|
||||
cp ~/Qt/5.10.1/gcc_64/plugins/platforms/* ./appdir/usr/plugins/platforms/ ;
|
||||
rm ./appdir/usr/lib/libfreetype.so.6 ;
|
||||
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 ;
|
||||
COMM_TAG="$(git describe --tags $(git rev-list --tags --max-count=1))"
|
||||
COMM_COUNT="$(git rev-list --count HEAD)"
|
||||
COMM_TAG="$(git describe --tags $(git rev-list --tags --max-count=1))" ;
|
||||
COMM_COUNT="$(git rev-list --count HEAD)" ;
|
||||
curl "${UPLOAD_URL}${TRAVIS_COMMIT:0:8}&t=${COMM_TAG}&a=${COMM_COUNT}" --upload-file ./RPCS3*.AppImage;
|
||||
fi;
|
||||
|
||||
script:
|
||||
# Add a command to show all the variables. May be useful for debugging Travis.
|
||||
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
||||
# And to ensure the versions of toolchain
|
||||
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-4.0
|
||||
- sourceline: 'ppa:jonathonf/binutils' # We need to update binutils to a newer version to link against the ffmpeg libs on.
|
||||
- sourceline: 'ppa:beineri/opt-qt-5.10.1-trusty' # <<WARNING>>: This needs to be updated manually whenever the QT Version changes. Add QT PPA since the installer is quite bad.
|
||||
packages:
|
||||
- binutils
|
||||
- cmake
|
||||
@@ -119,8 +111,8 @@ addons:
|
||||
- libstdc++-5-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
# We need to install qt 5.10.1 manually because the version trusty provides is too old.
|
||||
#- qtbase5-dev
|
||||
# From the beineri PPA
|
||||
- qt510-meta-minimal # <<WARNING>>: This needs to be updated manually whenever the QT Version changes.
|
||||
- libudev-dev
|
||||
- libevdev-dev
|
||||
- libpulse-dev
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/hidapi updated: ca39ce8939...4c2750112e
Vendored
+1
-1
Submodule 3rdparty/libpng updated: 1dcba4d6eb...bbbcaf1122
@@ -20,11 +20,11 @@ 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.10+](https://www.qt.io/download-open-source/) (required; add QTDIR environment variable if you do not want to use the Visual Studio Qt Plugin: e.g. `<QtInstallFolder>\5.10.1\msvc2015_64\`)
|
||||
* [Qt 5.10+](https://www.qt.io/download-open-source/) (required; add QTDIR environment variable if you do not want to use the Visual Studio Qt Plugin: e.g. `<QtInstallFolder>\5.10.1\msvc2017_64\`)
|
||||
* [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2015) (optional; see above)
|
||||
|
||||
### Linux
|
||||
@@ -48,14 +48,14 @@ 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 e.g `<QtInstallFolder>\5.10.1\msvc2015_64\` </br>
|
||||
1) Add `QTDIR` environment variable and set it to e.g `<QtInstallFolder>\5.10.1\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.10.1\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.10.1\msvc2017_64`. </br>
|
||||
2) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
### Building the projects
|
||||
|
||||
@@ -166,9 +166,10 @@ char GDBDebugServer::read_char()
|
||||
|
||||
u8 GDBDebugServer::read_hexbyte()
|
||||
{
|
||||
char buf[2];
|
||||
read(buf, 2);
|
||||
return static_cast<u8>(strtol(buf, nullptr, 16));
|
||||
std::string s = "";
|
||||
s += read_char();
|
||||
s += read_char();
|
||||
return hex_to_u8(s);
|
||||
}
|
||||
|
||||
void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
@@ -176,7 +177,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
char c = read_char();
|
||||
//interrupt
|
||||
if (UNLIKELY(c == 0x03)) {
|
||||
out_cmd.cmd = "\0x03";
|
||||
out_cmd.cmd = '\x03';
|
||||
out_cmd.data = "";
|
||||
out_cmd.checksum = 0;
|
||||
return;
|
||||
@@ -223,7 +224,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
}
|
||||
out_cmd.checksum = read_hexbyte();
|
||||
if (out_cmd.checksum != checksum) {
|
||||
throw new wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||
throw wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +428,25 @@ bool GDBDebugServer::send_reason()
|
||||
return send_cmd_ack("S05");
|
||||
}
|
||||
|
||||
void GDBDebugServer::wait_with_interrupts() {
|
||||
char c;
|
||||
while (!paused) {
|
||||
int result = recv(client_socket, &c, 1, 0);
|
||||
|
||||
if (result == SOCKET_ERROR) {
|
||||
if (check_errno_again()) {
|
||||
thread_ctrl::wait_for(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
gdbDebugServer.error("Error during socket read");
|
||||
fmt::throw_exception("Error during socket read" HERE);
|
||||
} else if (c == 0x03) {
|
||||
paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_extended_mode(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack("OK");
|
||||
@@ -453,8 +473,8 @@ bool GDBDebugServer::cmd_thread_info(gdb_cmd & cmd)
|
||||
result += u64_to_padded_hex(static_cast<u64>(cpu.id));
|
||||
};
|
||||
idm::select<ppu_thread>(on_select);
|
||||
idm::select<RawSPUThread>(on_select);
|
||||
idm::select<SPUThread>(on_select);
|
||||
//idm::select<RawSPUThread>(on_select);
|
||||
//idm::select<SPUThread>(on_select);
|
||||
|
||||
//todo: this may exceed max command length
|
||||
result = "m" + result + "l";
|
||||
@@ -464,12 +484,14 @@ bool GDBDebugServer::cmd_thread_info(gdb_cmd & cmd)
|
||||
|
||||
bool GDBDebugServer::cmd_current_thread(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack(selected_thread.expired() ? "" : u64_to_padded_hex(selected_thread.lock()->id));
|
||||
return send_cmd_ack(selected_thread.expired() ? "" : ("QC" + u64_to_padded_hex(selected_thread.lock()->id)));
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||
{
|
||||
select_thread(general_ops_thread_id);
|
||||
if (!select_thread(general_ops_thread_id)) {
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
@@ -487,7 +509,9 @@ bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||
|
||||
bool GDBDebugServer::cmd_write_register(gdb_cmd & cmd)
|
||||
{
|
||||
select_thread(general_ops_thread_id);
|
||||
if (!select_thread(general_ops_thread_id)) {
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
@@ -516,7 +540,7 @@ bool GDBDebugServer::cmd_read_memory(gdb_cmd & cmd)
|
||||
std::string result;
|
||||
result.reserve(len * 2);
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
if (vm::check_addr(addr, 1, vm::page_info_t::page_readable)) {
|
||||
result += to_hexbyte(vm::read8(addr + i));
|
||||
} else {
|
||||
break;
|
||||
@@ -542,7 +566,7 @@ bool GDBDebugServer::cmd_write_memory(gdb_cmd & cmd)
|
||||
u32 len = hex_to_u32(cmd.data.substr(s + 1, s2 - s - 1));
|
||||
const char* data_ptr = (cmd.data.c_str()) + s2 + 1;
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
if (vm::check_addr(addr + i, 1, vm::page_info_t::page_writable)) {
|
||||
u8 val;
|
||||
int res = sscanf_s(data_ptr, "%02hhX", &val);
|
||||
if (!res) {
|
||||
@@ -633,30 +657,31 @@ bool GDBDebugServer::cmd_vcont(gdb_cmd & cmd)
|
||||
{
|
||||
//todo: handle multiple actions and thread ids
|
||||
this->from_breakpoint = false;
|
||||
if (cmd.data[1] == 'c') {
|
||||
if (cmd.data[1] == 'c' || cmd.data[1] == 's') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
paused = false;
|
||||
if (cmd.data[1] == 's') {
|
||||
ppu->state += cpu_flag::dbg_step;
|
||||
}
|
||||
thread_ctrl::wait();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
return send_reason();
|
||||
} else if (cmd.data[1] == 's') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state += cpu_flag::dbg_step;
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
//special case if app didn't start yet (only loaded)
|
||||
if (!Emu.IsPaused() && !Emu.IsRunning()) {
|
||||
Emu.Run();
|
||||
}
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
} else {
|
||||
ppu->notify();
|
||||
}
|
||||
thread_ctrl::wait();
|
||||
wait_with_interrupts();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
select_thread(pausedBy);
|
||||
// we have to remove dbg_pause from thread that paused execution, otherwise
|
||||
// it will be paused forever (Emu.Resume only removes dbg_global_pause)
|
||||
ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
return send_reason();
|
||||
}
|
||||
return send_cmd_ack("");
|
||||
@@ -728,7 +753,9 @@ void GDBDebugServer::on_task()
|
||||
return;
|
||||
}
|
||||
//stop immediately
|
||||
Emu.Pause();
|
||||
if (Emu.IsRunning()) {
|
||||
Emu.Pause();
|
||||
}
|
||||
|
||||
try {
|
||||
char hostbuf[32];
|
||||
@@ -804,6 +831,15 @@ void GDBDebugServer::on_stop()
|
||||
named_thread::on_stop();
|
||||
}
|
||||
|
||||
void GDBDebugServer::pause_from(cpu_thread* t) {
|
||||
if (paused) {
|
||||
return;
|
||||
}
|
||||
paused = true;
|
||||
pausedBy = t->id;
|
||||
notify();
|
||||
}
|
||||
|
||||
u32 g_gdb_debugger_id = 0;
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
@@ -50,7 +50,7 @@ class GDBDebugServer : public named_thread {
|
||||
|
||||
//initialize server socket and start listening
|
||||
void start_server();
|
||||
//read at most cnt bytes to buf, returns nubmer of bytes actually read
|
||||
//read at most cnt bytes to buf, returns number of bytes actually read
|
||||
int read(void* buf, int cnt);
|
||||
//reads one character
|
||||
char read_char();
|
||||
@@ -90,6 +90,8 @@ class GDBDebugServer : public named_thread {
|
||||
//send reason of stop, returns false if sending response failed
|
||||
bool send_reason();
|
||||
|
||||
void wait_with_interrupts();
|
||||
|
||||
//commands
|
||||
bool cmd_extended_mode(gdb_cmd& cmd);
|
||||
bool cmd_reason(gdb_cmd& cmd);
|
||||
@@ -115,17 +117,24 @@ protected:
|
||||
void on_exit() override final;
|
||||
|
||||
public:
|
||||
static const u32 id_base = 1;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 0x100000;
|
||||
|
||||
bool from_breakpoint = true;
|
||||
bool stop = false;
|
||||
bool paused = false;
|
||||
u64 pausedBy;
|
||||
|
||||
virtual std::string get_name() const;
|
||||
virtual void on_stop() override final;
|
||||
void pause_from(cpu_thread* t);
|
||||
};
|
||||
|
||||
extern u32 g_gdb_debugger_id;
|
||||
|
||||
template <>
|
||||
struct id_manager::on_stop<GDBDebugServer> {
|
||||
static inline void func(GDBDebugServer* ptr)
|
||||
{
|
||||
if (ptr) ptr->on_stop();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -293,7 +293,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
|
||||
}
|
||||
|
||||
void deregisterEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
virtual void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -376,11 +376,11 @@ public:
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||
}
|
||||
|
||||
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
|
||||
static std::unique_ptr<llvm::WritableMemoryBuffer> load(const std::string& path)
|
||||
{
|
||||
if (fs::file cached{path, fs::read})
|
||||
{
|
||||
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
|
||||
return buf;
|
||||
}
|
||||
|
||||
+20
-7
@@ -21,7 +21,7 @@
|
||||
#include <mach/thread_act.h>
|
||||
#include <mach/thread_policy.h>
|
||||
#endif
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <pthread_np.h>
|
||||
#define cpu_set_t cpuset_t
|
||||
#endif
|
||||
@@ -1295,6 +1295,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
||||
}
|
||||
}
|
||||
|
||||
vm::temporary_unlock(*cpu);
|
||||
LOG_FATAL(MEMORY, "Access violation %s location 0x%x", is_writing ? "writing" : "reading", addr);
|
||||
cpu->state += cpu_flag::dbg_pause;
|
||||
cpu->check_state();
|
||||
@@ -1638,6 +1639,16 @@ void thread_ctrl::initialize()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
pthread_setname_np(m_name.substr(0, 15).c_str());
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
pthread_set_name_np(pthread_self(), m_name.c_str());
|
||||
#elif defined(__NetBSD__)
|
||||
pthread_setname_np(pthread_self(), "%s", (void*)m_name.c_str());
|
||||
#elif !defined(_WIN32)
|
||||
pthread_setname_np(pthread_self(), m_name.substr(0, 15).c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void thread_ctrl::finalize(std::exception_ptr eptr) noexcept
|
||||
@@ -1890,32 +1901,34 @@ u16 thread_ctrl::get_affinity_mask(thread_class group)
|
||||
}
|
||||
case native_core_arrangement::amd_ccx:
|
||||
{
|
||||
u16 spu_mask, ppu_mask, rsx_mask;
|
||||
u16 spu_mask, ppu_mask, rsx_mask, general;
|
||||
if (thread_count >= 16)
|
||||
{
|
||||
// Threadripper, R7
|
||||
// Assign threads 8-16
|
||||
// Assign threads 1-16
|
||||
// It appears some windows code is bound to lower core addresses, binding 8-16 is alot faster than 0-7
|
||||
ppu_mask = spu_mask = 0b1111111100000000;
|
||||
rsx_mask = all_cores_mask;
|
||||
rsx_mask = 0b11110000;
|
||||
general = 0b00001111;
|
||||
}
|
||||
else if (thread_count == 12)
|
||||
{
|
||||
// 1600/2600 (x)
|
||||
ppu_mask = spu_mask = 0b111111000000;
|
||||
rsx_mask = all_cores_mask;
|
||||
rsx_mask = 0b111000;
|
||||
general = 0b000111;
|
||||
}
|
||||
else
|
||||
{
|
||||
// R5 & R3 don't seem to improve performance no matter how these are shuffled
|
||||
ppu_mask = spu_mask = rsx_mask = 0b11111111 & all_cores_mask;
|
||||
ppu_mask = spu_mask = rsx_mask = general = 0b11111111;
|
||||
}
|
||||
|
||||
switch (group)
|
||||
{
|
||||
default:
|
||||
case thread_class::general:
|
||||
return all_cores_mask;
|
||||
return general = all_cores_mask;
|
||||
case thread_class::rsx:
|
||||
return rsx_mask;
|
||||
case thread_class::ppu:
|
||||
|
||||
+4
-4
@@ -64,7 +64,7 @@ void shared_mutex::imp_lock_shared(s64 _old)
|
||||
|
||||
if (value0 >= c_min)
|
||||
{
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
// Acquire writer lock
|
||||
@@ -79,7 +79,7 @@ void shared_mutex::imp_lock_shared(s64 _old)
|
||||
}
|
||||
|
||||
value1 += c_one - c_min;
|
||||
|
||||
|
||||
if (value1 > 0)
|
||||
{
|
||||
return;
|
||||
@@ -109,7 +109,7 @@ void shared_mutex::imp_lock_shared(s64 _old)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
imp_unlock_shared(value2);
|
||||
}
|
||||
#endif
|
||||
@@ -300,7 +300,7 @@ safe_writer_lock::safe_writer_lock(shared_mutex& mutex)
|
||||
|
||||
if (m_is_owned)
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
m_mutex.lock();
|
||||
g_tls_locks.emplace_back(&m_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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: 28d93dfc0f...354ad3ba8b
@@ -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">
|
||||
@@ -40,23 +40,23 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>start /W /B pre-build.bat
|
||||
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
|
||||
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>start /W /B pre-build.bat
|
||||
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
|
||||
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>start /W /B pre-build.bat
|
||||
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
|
||||
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>start /W /B pre-build.bat
|
||||
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
|
||||
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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
+14
-15
@@ -1,10 +1,10 @@
|
||||
|
||||
version: '{build}'
|
||||
|
||||
image: Visual Studio 2015
|
||||
image: Visual Studio 2017
|
||||
|
||||
environment:
|
||||
QTDIR: C:\Qt\5.10.0\msvc2015_64
|
||||
QTDIR: C:\Qt\5.10\msvc2017_64
|
||||
LLVMLIBS: https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRY2k3Q2Yya05lcm8
|
||||
VULKAN: https://drive.google.com/uc?export=download&id=1A2eOMmCO714i0U7J0qI4aEMKnuWl8l_R
|
||||
COMPATDB: https://rpcs3.net/compatibility?api=v1&export
|
||||
@@ -16,19 +16,18 @@ cache:
|
||||
|
||||
install:
|
||||
- ps: | # set env vars for versioning
|
||||
$commTag = $(git describe --tags $(git rev-list --tags --max-count=1))
|
||||
$commCount = $(git rev-list --count HEAD)
|
||||
$commHash = $env:APPVEYOR_REPO_COMMIT.Substring(0,8)
|
||||
$env:COMM_TAG = $(git describe --tags $(git rev-list --tags --max-count=1))
|
||||
$env:COMM_COUNT = $(git rev-list --count HEAD)
|
||||
$env:COMM_HASH = $env:APPVEYOR_REPO_COMMIT.Substring(0,8)
|
||||
|
||||
if ($env:APPVEYOR_PULL_REQUEST_NUMBER) {
|
||||
$env:BUILD = "{0}-{1}" -f $commTag, $commHash
|
||||
$env:BUILD = "rpcs3-{0}-{1}_win64.7z" -f $env:COMM_TAG, $env:COMM_HASH
|
||||
$env:AVVER = "{0}-{1}" -f $env:COMM_TAG.TrimStart("v"), $env:COMM_HASH
|
||||
}
|
||||
else {
|
||||
$env:BUILD = "{0}-{1}-{2}" -f $commTag, $commCount, $commHash
|
||||
$env:BUILD = "rpcs3-{0}-{1}-{2}_win64.7z" -f $env:COMM_TAG, $env:COMM_COUNT, $env:COMM_HASH
|
||||
$env:AVVER = "{0}-{1}" -f $env:COMM_TAG.TrimStart("v"), $env:COMM_COUNT
|
||||
}
|
||||
|
||||
$env:AVVER = "{0}-{1}" -f $commTag.TrimStart("v"), $env:APPVEYOR_BUILD_NUMBER
|
||||
$env:RPCS3 = "rpcs3-{0}_win64.7z" -f $env:BUILD
|
||||
|
||||
- ps: | # used for experimental build warnings for pr builds
|
||||
$env:BRANCH = "{0}/{1}/#{2}" -f $env:APPVEYOR_REPO_NAME, `
|
||||
@@ -82,20 +81,20 @@ after_build:
|
||||
copy-item compat_database.dat .\bin\GuiConfigs\compat_database.dat
|
||||
|
||||
- ps: | # package artifacts
|
||||
7z a -m0=LZMA2 -mx9 $env:RPCS3 .\bin\*
|
||||
7z a -m0=LZMA2 -mx9 $env:BUILD .\bin\*
|
||||
7z a -m0=LZMA2 -mx9 openssl_win64.7z C:\OpenSSL-Win64\bin\libeay32.dll `
|
||||
C:\OpenSSL-Win64\bin\ssleay32.dll
|
||||
|
||||
- ps: | # generate sha256 hashes
|
||||
(get-filehash $env:RPCS3 -algorithm SHA256).Hash > ("{0}.sha256" -f $env:RPCS3)
|
||||
(get-filehash openssl_win64.7z -algorithm SHA256).Hash > "openssl_win64.7z.sha256"
|
||||
(get-filehash $env:BUILD -algorithm SHA256).Hash | out-file ("{0}.sha256" -f $env:BUILD) -encoding ascii
|
||||
(get-filehash openssl_win64.7z -algorithm SHA256).Hash | out-file "openssl_win64.7z.sha256" -encoding ascii
|
||||
|
||||
test: off
|
||||
|
||||
artifacts:
|
||||
- path: $(RPCS3)
|
||||
- path: $(BUILD)
|
||||
name: rpcs3
|
||||
- path: $(RPCS3).sha256
|
||||
- path: $(BUILD).sha256
|
||||
name: rpcs3 sha256 hash
|
||||
- path: openssl_win64.7z
|
||||
name: openssl
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -244,9 +244,6 @@ QToolButton::hover {
|
||||
QLabel#gamelist_icon_background_color {
|
||||
color: transparent;
|
||||
}
|
||||
QLabel#gamelist_toolbar_icon_color {
|
||||
color: #828790;
|
||||
}
|
||||
|
||||
/* Set Windows Taskbar Thumbnail colors */
|
||||
QLabel#thumbnail_icon_color {
|
||||
|
||||
@@ -243,9 +243,6 @@ QToolButton::hover {
|
||||
QLabel#gamelist_icon_background_color {
|
||||
color: transparent;
|
||||
}
|
||||
QLabel#gamelist_toolbar_icon_color {
|
||||
color: #828790;
|
||||
}
|
||||
|
||||
/* Set Windows Taskbar Thumbnail colors */
|
||||
QLabel#thumbnail_icon_color {
|
||||
|
||||
@@ -241,9 +241,6 @@ QToolButton::hover {
|
||||
QLabel#gamelist_icon_background_color {
|
||||
color: transparent;
|
||||
}
|
||||
QLabel#gamelist_toolbar_icon_color {
|
||||
color: #828790;
|
||||
}
|
||||
|
||||
/* Set Windows Taskbar Thumbnail colors */
|
||||
QLabel#thumbnail_icon_color {
|
||||
|
||||
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
YoRHa Theme for RPCS3
|
||||
by Ani @ https://github.com/AniLeo
|
||||
r1 (2018.02.27)
|
||||
*/
|
||||
|
||||
/*
|
||||
Color Scheme
|
||||
|
||||
- Pod Programs
|
||||
8c806a
|
||||
bd9d86
|
||||
c1b398
|
||||
eadfb1
|
||||
ebe4d2
|
||||
|
||||
- Light
|
||||
b3ac98
|
||||
aea993
|
||||
|
||||
- Dark
|
||||
4d4940
|
||||
*/
|
||||
|
||||
|
||||
/* Every widget */
|
||||
QWidget {
|
||||
font-family: SCE-PS3 Rodin LATIN, Arial;
|
||||
font-size: 9.00pt;
|
||||
|
||||
color: #292929;
|
||||
background: transparent;
|
||||
alternate-background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
/* Debugger: Sets font-family to default (any invalid value could be provided) */
|
||||
#debugger QListWidget, #debugger QTextEdit {
|
||||
font-family: none;
|
||||
}
|
||||
|
||||
|
||||
/* Log+TTY: Use flat dark color background with default font for better readability */
|
||||
#log_frame, #tty_frame {
|
||||
background: rgba(52, 49, 40, 0.9);
|
||||
font-size: 8.50pt;
|
||||
font-family: none;
|
||||
}
|
||||
|
||||
|
||||
/* Debugger: Restore original font size */
|
||||
#debugger QListWidget, #debugger QTextEdit {
|
||||
font-size: 9.50pt;
|
||||
}
|
||||
|
||||
|
||||
/* LLE: Style QListWidget checkboxes (QListWidget) */
|
||||
#lleList::indicator {
|
||||
border: 0.05em solid #4d4940;
|
||||
}
|
||||
#lleList::indicator::unchecked {
|
||||
background-color: #b3ac98;
|
||||
}
|
||||
#lleList::indicator::checked {
|
||||
background-color: #4d4940;
|
||||
}
|
||||
#lleList::indicator::disabled {
|
||||
background-color: #828790;
|
||||
}
|
||||
#lleList::item::selected {
|
||||
color: #4d4940;
|
||||
}
|
||||
|
||||
|
||||
/* Mouse Tooltips */
|
||||
QToolTip {
|
||||
background-color: #b3ac98;
|
||||
color: #4d4940;
|
||||
border: 0.10em solid #4d4940;
|
||||
}
|
||||
|
||||
|
||||
/* CG Disasm: background-image doesn't work, use static color */
|
||||
QWidget#cg_disasm {
|
||||
background: #b3ac98;
|
||||
}
|
||||
|
||||
|
||||
/* Main Window, Dialogs and Trophy Manager which isn't a dialog */
|
||||
QDialog, QWidget#trophy_manager, QMainWindow#main_window {
|
||||
border-image: url("GuiConfigs/YoRHa-background.jpg");
|
||||
}
|
||||
|
||||
|
||||
/* Table headers */
|
||||
QHeaderView::section {
|
||||
text-transform: uppercase;
|
||||
background: #4d4940;
|
||||
color: #b3ac98;
|
||||
padding-left: 0.15em;
|
||||
padding-top: 0.15em;
|
||||
padding-bottom: 0.10em;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
/* All other Tabs */
|
||||
QTabBar {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
QTabBar::tab {
|
||||
background: transparent;
|
||||
padding-left: 0.50em;
|
||||
padding-right: 0.50em;
|
||||
padding-top: 0.25em;
|
||||
padding-bottom: 0.25em;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
QTabBar::tab::selected {
|
||||
background: #4d4940;
|
||||
color: #aea993;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
|
||||
/* Settings Dialog: Tabs */
|
||||
QTabBar#tab_bar_settings {
|
||||
border-bottom: 0.05em solid #4d4940;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
QTabBar::tab#tab_bar_settings {
|
||||
background: transparent;
|
||||
width: 5.20em;
|
||||
padding-left: 0.50em;
|
||||
padding-right: 0.50em;
|
||||
padding-top: 0.65em;
|
||||
padding-bottom: 0.65em;
|
||||
margin-right: 0.25em;
|
||||
font-size: 10.5pt;
|
||||
font-weight: 550;
|
||||
}
|
||||
QTabBar::tab:last#tab_bar_settings {
|
||||
margin-right: 0em;
|
||||
}
|
||||
QTabBar::tab:!selected:hover#tab_bar_settings {
|
||||
background: transparent;
|
||||
color: #4d4940;
|
||||
}
|
||||
QTabBar::tab::selected#tab_bar_settings {
|
||||
background: #4d4940;
|
||||
color: #aea993;
|
||||
border-bottom-style: solid;
|
||||
margin-top: 0.15em;
|
||||
}
|
||||
|
||||
|
||||
/* Checkboxes */
|
||||
QCheckBox::indicator {
|
||||
border-radius: 0.1em;
|
||||
border: 0.05em solid #4d4940;
|
||||
margin-top: 0.05em;
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
}
|
||||
QCheckBox::indicator:checked {
|
||||
background-color: #4d4940; /* Dark */
|
||||
}
|
||||
QCheckBox::indicator:unchecked {
|
||||
background-color: #b3ac98; /* Light */
|
||||
}
|
||||
QCheckBox::indicator::disabled {
|
||||
background-color: #828790; /* Gray */
|
||||
}
|
||||
|
||||
|
||||
/* Radio Buttons */
|
||||
QRadioButton::indicator {
|
||||
border-radius: 0.4em;
|
||||
border: 0.05em solid #4d4940;
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
}
|
||||
QRadioButton::indicator:checked {
|
||||
background-color: #4d4940; /* Dark */
|
||||
}
|
||||
QRadioButton::indicator:unchecked {
|
||||
background-color: #b3ac98; /* Light */
|
||||
}
|
||||
QRadioButton::indicator::disabled {
|
||||
background-color: #828790; /* Gray */
|
||||
}
|
||||
|
||||
|
||||
/* Combo Boxes */
|
||||
QComboBox {
|
||||
background: transparent;
|
||||
color: #4d4940;
|
||||
border: 0.05em solid #1e1e1e;
|
||||
border-radius: 0.15em;
|
||||
padding-bottom: 0.2em;
|
||||
padding-left: 0.4em;
|
||||
}
|
||||
QComboBox QAbstractItemView {
|
||||
background: #b3ac98;
|
||||
}
|
||||
QComboBox::disabled {
|
||||
background: #828790;
|
||||
color: #4d4940;
|
||||
}
|
||||
|
||||
|
||||
/* Group Boxes (Settings Dialog) */
|
||||
QGroupBox {
|
||||
margin-top: 1em;
|
||||
border: 0.05em solid #4d4940;
|
||||
text-transform: uppercase;
|
||||
font-size: 9.25pt;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top;
|
||||
padding: 0.3em 0.5em 0.3em 0.5em;
|
||||
color: #4d4940;
|
||||
}
|
||||
|
||||
|
||||
/* Buttons */
|
||||
QPushButton {
|
||||
background: #b3ac98;
|
||||
}
|
||||
QPushButton::disabled {
|
||||
background: #828790;
|
||||
}
|
||||
|
||||
|
||||
/* QSpinBox (Settings -> Emulator -> width/height) */
|
||||
QSpinBox {
|
||||
/* background: transparent; - This hits a weird bug in Qt where background flickers */
|
||||
background-color: #b3ac98;
|
||||
border: 0.05em solid #4d4940;
|
||||
border-radius: 0.10em;
|
||||
}
|
||||
|
||||
|
||||
/* Styles Sliders */
|
||||
QSlider::groove:horizontal {
|
||||
border: 0.10em solid #4d4940;
|
||||
border-radius: 0.10em;
|
||||
}
|
||||
QSlider::handle:horizontal {
|
||||
background: #4d4940;
|
||||
width: 0.50em;
|
||||
}
|
||||
|
||||
|
||||
/* Log and Debugger borders */
|
||||
QTextEdit {
|
||||
border: 0.05em solid #4d4940;
|
||||
}
|
||||
|
||||
|
||||
/* For dock buttons to be visible */
|
||||
QDockWidget {
|
||||
background: transparent;
|
||||
text-transform: uppercase;
|
||||
color: #b3ac98;
|
||||
font-weight: 500;
|
||||
}
|
||||
[floating="true"] {
|
||||
border-image: url("GuiConfigs/YoRHa-background.jpg");
|
||||
}
|
||||
QDockWidget::title {
|
||||
background: #4d4940;
|
||||
padding-top: 0.2em;
|
||||
}
|
||||
QDockWidget::close-button, QDockWidget::float-button {
|
||||
background-color: #b3ac98;
|
||||
}
|
||||
|
||||
|
||||
/* Disable ugly borders */
|
||||
QTabWidget::pane {
|
||||
border: 0em solid #4d4940;
|
||||
}
|
||||
|
||||
|
||||
/* Top menu bar */
|
||||
QMenuBar {
|
||||
height:1.50em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
QMenuBar::item {
|
||||
margin-right: 0.20em;
|
||||
margin-left: 0.20em;
|
||||
padding-left: 1.20em;
|
||||
padding-right: 1.20em;
|
||||
}
|
||||
QMenuBar::item:selected {
|
||||
background: #4d4940;
|
||||
color: #aea993
|
||||
}
|
||||
|
||||
|
||||
QMenu {
|
||||
background: #b3ac98;
|
||||
color: #4d4940;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
QMenu::item {
|
||||
padding-left: 1.5em;
|
||||
padding-right: 0.75em;
|
||||
padding-top: 0.25em;
|
||||
padding-bottom: 0.25em;
|
||||
|
||||
}
|
||||
QMenu::item:selected {
|
||||
background: #4d4940;
|
||||
color: #aea993;
|
||||
border: 0.05em solid #4d4940;
|
||||
}
|
||||
QMenu::item:disabled {
|
||||
background-color: #828790;
|
||||
color: #4d4940;
|
||||
}
|
||||
|
||||
|
||||
/* Pad Settings: Controller Image */
|
||||
QLabel#l_controller {
|
||||
color: #4d4940;
|
||||
}
|
||||
|
||||
|
||||
/* Game Grid Font */
|
||||
QLabel#gamegrid_font {
|
||||
font-weight: 600;
|
||||
color: #4d4940;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
||||
/* Debug UI Settings buttons */
|
||||
QLabel#color_button {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
/* Searchbar on main Toolbar */
|
||||
QLineEdit#mw_searchbar {
|
||||
margin-left: 0.7em;
|
||||
color: #4d4940;
|
||||
font-size: 10.25pt;
|
||||
}
|
||||
|
||||
|
||||
/* Uniform colors in Toolbar */
|
||||
QToolButton {
|
||||
background: transparent;
|
||||
color: transparent;
|
||||
}
|
||||
QToolButton::hover {
|
||||
background-color: #b3ac98;
|
||||
}
|
||||
|
||||
|
||||
/* Set Theme UI colors */
|
||||
QLabel#gamelist_icon_background_color {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
|
||||
/* Set Windows Taskbar Thumbnail colors */
|
||||
QLabel#thumbnail_icon_color {
|
||||
color: #4d4940;
|
||||
}
|
||||
|
||||
|
||||
QLabel#log_level_always {
|
||||
color: #00ffff; /* Cyan */
|
||||
}
|
||||
QLabel#log_level_fatal {
|
||||
color: #ff00ff; /* Fuchsia */
|
||||
}
|
||||
QLabel#log_level_error {
|
||||
color: #ff0000; /* Red */
|
||||
}
|
||||
QLabel#log_level_todo {
|
||||
color: #ff6000; /* Orange */
|
||||
}
|
||||
QLabel#log_level_success {
|
||||
color: #00ff00; /* Green */
|
||||
}
|
||||
QLabel#log_level_warning {
|
||||
color: #ffff00; /* Yellow */
|
||||
}
|
||||
QLabel#log_level_notice {
|
||||
color: #ffffff; /* White */
|
||||
}
|
||||
QLabel#log_level_trace {
|
||||
color: #808080; /* Gray */
|
||||
}
|
||||
QLabel#log_stack {
|
||||
color: #b3ac98; /* Light */
|
||||
}
|
||||
|
||||
/* Set TTY colors */
|
||||
#tty_frame {
|
||||
color: #b3ac98; /* Light */
|
||||
}
|
||||
|
||||
|
||||
/* Memory Viewer */
|
||||
QLabel#memory_viewer_address_panel {
|
||||
color: #0000ff; /* Font Color: Blue */
|
||||
}
|
||||
QLabel#memory_viewer_hex_panel {
|
||||
color: #4d4940; /* Font Color: Grey */
|
||||
}
|
||||
QLabel#memory_viewer_ascii_panel {
|
||||
color: #4d4940; /* Font Color: Grey */
|
||||
}
|
||||
|
||||
|
||||
/* Debugger colors */
|
||||
QLabel#debugger_frame_breakpoint {
|
||||
color: #000000; /* Font Color: Black */
|
||||
background-color: #ffff00; /* Yellow */
|
||||
}
|
||||
QLabel#debugger_frame_pc {
|
||||
color: #000000; /* Font Color: Black */
|
||||
background-color: #00ff00; /* Green */
|
||||
}
|
||||
|
||||
|
||||
/* Trophy Notification Popup */
|
||||
QWidget#trophy_notification_frame {
|
||||
background-color: #b3ac98;
|
||||
color: #4d4940;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 564 KiB |
+1
-1
Submodule llvm updated: 4423e35117...fde9f2060a
@@ -1,7 +1,7 @@
|
||||
REM You need cmake and python to update the project files
|
||||
REM this script relies on CWD being the path that this script is in
|
||||
|
||||
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 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_EXTERNAL_POLLY_SOURCE_DIR=../polly -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INCLUDE_UTILS=ON -DWITH_POLLY=ON -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||
|
||||
RD /S /Q cmake
|
||||
RD /S /Q CMakeFiles
|
||||
|
||||
@@ -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_EXTERNAL_POLLY_SOURCE_DIR=../polly -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INCLUDE_UTILS=ON -DWITH_POLLY=ON -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_EXTERNAL_POLLY_SOURCE_DIR=../polly -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INCLUDE_UTILS=ON -DWITH_POLLY=ON -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_EXTERNAL_POLLY_SOURCE_DIR=../polly -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INCLUDE_UTILS=ON -DWITH_POLLY=ON -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_EXTERNAL_POLLY_SOURCE_DIR=../polly -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INCLUDE_UTILS=ON -DWITH_POLLY=ON -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_EXTERNAL_POLLY_SOURCE_DIR=../polly -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INCLUDE_UTILS=ON -DWITH_POLLY=ON -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_EXTERNAL_POLLY_SOURCE_DIR=../polly -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INCLUDE_UTILS=ON -DWITH_POLLY=ON -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>
|
||||
|
||||
@@ -62,6 +62,9 @@ vcxproj_files = ["lib\Analysis\LLVMAnalysis.vcxproj",
|
||||
"lib\Transforms\Utils\LLVMTransformUtils.vcxproj",
|
||||
"lib\Transforms\Vectorize\LLVMVectorize.vcxproj",
|
||||
"include\llvm\IR\intrinsics_gen.vcxproj",
|
||||
"tools\polly\lib\Polly.vcxproj",
|
||||
"tools\polly\lib\External\PollyISL.vcxproj",
|
||||
"tools\polly\lib\External\PollyPPCG.vcxproj",
|
||||
"utils\TableGen\llvm-tblgen.vcxproj",
|
||||
]
|
||||
|
||||
|
||||
Submodule
+1
Submodule polly added at 5664a18f6d
@@ -33,8 +33,7 @@ Controller.prototype.ComponentSelectionPageCallback = function() {
|
||||
var widget = gui.currentPageWidget();
|
||||
|
||||
widget.deselectAll();
|
||||
widget.selectComponent("qt.qt5.5101.gcc_64");
|
||||
|
||||
widget.selectComponent("qt.qt5." + installer.environmentVariable("QTVER") + ".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.27130.2027
|
||||
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
|
||||
|
||||
+34
-6
@@ -6,17 +6,17 @@ set(CMAKE_CXX_STANDARD 14)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
# Qt section
|
||||
find_package(Qt5 5.10 COMPONENTS Widgets Network)
|
||||
find_package(Qt5 5.10 COMPONENTS Widgets Network Qml)
|
||||
if(WIN32)
|
||||
find_package(Qt5 5.10 COMPONENTS WinExtras REQUIRED)
|
||||
set(RPCS3_QT_LIBS Qt5::Widgets Qt5::WinExtras Qt5::Network)
|
||||
set(RPCS3_QT_LIBS Qt5::Widgets Qt5::WinExtras Qt5::Network Qt5::Qml)
|
||||
else()
|
||||
find_package(Qt5 5.10 COMPONENTS DBus Gui)
|
||||
if(Qt5DBus_FOUND)
|
||||
set(RPCS3_QT_LIBS Qt5::Widgets Qt5::DBus Qt5::Network)
|
||||
set(RPCS3_QT_LIBS Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Qml)
|
||||
add_definitions(-DHAVE_QTDBUS)
|
||||
else()
|
||||
set(RPCS3_QT_LIBS Qt5::Widgets Qt5::Network)
|
||||
set(RPCS3_QT_LIBS Qt5::Widgets Qt5::Network Qt5::Qml)
|
||||
endif()
|
||||
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
@@ -96,7 +96,7 @@ if(NOT MSVC)
|
||||
if(NOT APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--exclude-libs,ALL")
|
||||
endif()
|
||||
|
||||
|
||||
if(WIN32)
|
||||
set(CMAKE_RC_COMPILER_INIT windres)
|
||||
enable_language(RC)
|
||||
@@ -173,7 +173,7 @@ set(CMAKE_MODULE_PATH "${RPCS3_SRC_DIR}/cmake_modules")
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
if(NOT WITHOUT_LLVM)
|
||||
find_package(LLVM 4.0 CONFIG)
|
||||
find_package(LLVM 6.0 CONFIG)
|
||||
if(NOT LLVM_FOUND)
|
||||
message("System LLVM was not found, LLVM will be built from the submodule.")
|
||||
|
||||
@@ -198,6 +198,28 @@ if(NOT WITHOUT_LLVM)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Now let's look for polly.
|
||||
find_package(Polly)
|
||||
if(Polly_FOUND)
|
||||
add_definitions(-DPOLLY_AVAILABLE)
|
||||
endif()
|
||||
# Polly LLVM Compiler checks
|
||||
CHECK_C_COMPILER_FLAG("-mllvm -polly" HAS_POLLY)
|
||||
CHECK_C_COMPILER_FLAG("-mllvm -polly-target=hybrid" HAS_POLLY_HYBRID)
|
||||
CHECK_C_COMPILER_FLAG("-mllvm -polly-target=cpu" HAS_POLLY_CPU)
|
||||
|
||||
if (HAS_POLLY)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -polly -mllvm -polly-parallel -lgomp -mllvm -polly-vectorizer=stripmine")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mllvm -polly -mllvm -polly-parallel -lgomp -mllvm -polly-vectorizer=stripmine")
|
||||
if(HAS_POLLY_HYBRID)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -polly-target=hybrid")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -polly-target=cpu")
|
||||
elseif(HAS_POLLY_CPU)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -polly-target=cpu")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -mllvm -polly-target=cpu")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
if(APPLE)
|
||||
@@ -274,6 +296,7 @@ endif()
|
||||
include_directories(
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${LLVM_INCLUDE_DIRS}
|
||||
${Polly_INCLUDE_DIRS}
|
||||
"${RPCS3_SRC_DIR}/../3rdparty/pugixml/src"
|
||||
"${RPCS3_SRC_DIR}"
|
||||
"${RPCS3_SRC_DIR}/Loader"
|
||||
@@ -319,6 +342,7 @@ endif()
|
||||
link_directories(
|
||||
"${RPCS3_SRC_DIR}/../3rdparty/minidx12/"
|
||||
"${RPCS3_SRC_DIR}/../Vulkan"
|
||||
"${Polly_LIBRARY_DIRS}"
|
||||
)
|
||||
|
||||
|
||||
@@ -393,6 +417,10 @@ if(NOT MSVC)
|
||||
target_link_libraries(rpcs3 GLEW::GLEW)
|
||||
endif()
|
||||
|
||||
if(Polly_FOUND)
|
||||
target_link_libraries(rpcs3 Polly PollyISL PollyPPCG)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_link_libraries(rpcs3 ws2_32.lib Winmm.lib Psapi.lib gdi32.lib VKstatic.1 glslang OSDependent OGLCompiler SPIRV HLSL setupapi.lib hidapi-hid Shlwapi.lib)
|
||||
if(NOT MSVC)
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -101,7 +101,7 @@ bool cpu_thread::check_state()
|
||||
{
|
||||
#ifdef WITH_GDB_DEBUGGER
|
||||
if (test(state, cpu_flag::dbg_pause)) {
|
||||
fxm::get<GDBDebugServer>()->notify();
|
||||
fxm::get<GDBDebugServer>()->pause_from(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ struct llvm_value_t
|
||||
static_assert(std::is_same<T, void>::value, "llvm_value_t<> error: unknown type");
|
||||
|
||||
using type = void;
|
||||
using base = llvm_value_t;
|
||||
static constexpr uint esize = 0;
|
||||
static constexpr bool is_int = false;
|
||||
static constexpr bool is_sint = false;
|
||||
@@ -669,6 +670,117 @@ inline llvm_not_t<typename T1::type, T1> operator ~(T1 a1)
|
||||
return {a1};
|
||||
}
|
||||
|
||||
template <typename T, typename A1, typename A2, llvm::CmpInst::Predicate UPred>
|
||||
struct llvm_icmp_t
|
||||
{
|
||||
using type = std::conditional_t<llvm_value_t<T>::is_vector, bool[llvm_value_t<T>::is_vector], bool>;
|
||||
|
||||
A1 a1;
|
||||
A2 a2;
|
||||
|
||||
static_assert(llvm_value_t<T>::is_int, "llvm_eq_t<>: invalid type");
|
||||
|
||||
// Convert unsigned comparison predicate to signed if necessary
|
||||
static constexpr llvm::CmpInst::Predicate pred = llvm_value_t<T>::is_uint ? UPred :
|
||||
UPred == llvm::ICmpInst::ICMP_UGT ? llvm::ICmpInst::ICMP_SGT :
|
||||
UPred == llvm::ICmpInst::ICMP_UGE ? llvm::ICmpInst::ICMP_SGE :
|
||||
UPred == llvm::ICmpInst::ICMP_ULT ? llvm::ICmpInst::ICMP_SLT :
|
||||
UPred == llvm::ICmpInst::ICMP_ULE ? llvm::ICmpInst::ICMP_SLE : UPred;
|
||||
|
||||
static inline llvm::Value* icmp(llvm::IRBuilder<>* ir, llvm::Value* lhs, llvm::Value* rhs)
|
||||
{
|
||||
return ir->CreateICmp(pred, lhs, rhs);
|
||||
}
|
||||
|
||||
static inline llvm::Value* icmp(llvm::IRBuilder<>* ir, llvm::Value* lhs, u64 value)
|
||||
{
|
||||
return ir->CreateICmp(pred, lhs, llvm::ConstantInt::get(llvm_value_t<T>::get_type(ir->getContext()), value, llvm_value_t<T>::is_sint));
|
||||
}
|
||||
|
||||
llvm::Value* eval(llvm::IRBuilder<>* ir) const
|
||||
{
|
||||
const auto v1 = a1.eval(ir);
|
||||
const auto v2 = a2.eval(ir);
|
||||
|
||||
if (llvm_value_t<T>::is_int)
|
||||
{
|
||||
return icmp(ir, v1, v2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_EQ> operator ==(T1 a1, T2 a2)
|
||||
{
|
||||
return {a1, a2};
|
||||
}
|
||||
|
||||
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_EQ> operator ==(T1 a1, u64 a2)
|
||||
{
|
||||
return {a1, llvm_int_t{a2}};
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_NE> operator !=(T1 a1, T2 a2)
|
||||
{
|
||||
return {a1, a2};
|
||||
}
|
||||
|
||||
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_NE> operator !=(T1 a1, u64 a2)
|
||||
{
|
||||
return {a1, llvm_int_t{a2}};
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_UGT> operator >(T1 a1, T2 a2)
|
||||
{
|
||||
return {a1, a2};
|
||||
}
|
||||
|
||||
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_UGT> operator >(T1 a1, u64 a2)
|
||||
{
|
||||
return {a1, llvm_int_t{a2}};
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_UGE> operator >=(T1 a1, T2 a2)
|
||||
{
|
||||
return {a1, a2};
|
||||
}
|
||||
|
||||
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_UGE> operator >=(T1 a1, u64 a2)
|
||||
{
|
||||
return {a1, llvm_int_t{a2}};
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_ULT> operator <(T1 a1, T2 a2)
|
||||
{
|
||||
return {a1, a2};
|
||||
}
|
||||
|
||||
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_ULT> operator <(T1 a1, u64 a2)
|
||||
{
|
||||
return {a1, llvm_int_t{a2}};
|
||||
}
|
||||
|
||||
template <typename T1, typename T2, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<std::is_same<typename T1::type, typename T2::type>::value>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, T2, llvm::ICmpInst::ICMP_ULE> operator <=(T1 a1, T2 a2)
|
||||
{
|
||||
return {a1, a2};
|
||||
}
|
||||
|
||||
template <typename T1, typename = decltype(std::declval<T1>().eval(0)), typename = std::enable_if_t<llvm_value_t<typename T1::type>::is_int>>
|
||||
inline llvm_icmp_t<typename T1::type, T1, llvm_int_t, llvm::ICmpInst::ICMP_ULE> operator <=(T1 a1, u64 a2)
|
||||
{
|
||||
return {a1, llvm_int_t{a2}};
|
||||
}
|
||||
|
||||
class cpu_translator
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -191,13 +191,17 @@ s32 cellAudioOutGetDeviceInfo(u32 audioOut, u32 deviceIndex, vm::ptr<CellAudioOu
|
||||
if (deviceIndex) return CELL_AUDIO_OUT_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
info->portType = CELL_AUDIO_OUT_PORT_HDMI;
|
||||
info->availableModeCount = 1;
|
||||
info->availableModeCount = 2;
|
||||
info->state = CELL_AUDIO_OUT_DEVICE_STATE_AVAILABLE;
|
||||
info->latency = 1000;
|
||||
info->availableModes[0].type = CELL_AUDIO_OUT_CODING_TYPE_LPCM;
|
||||
info->availableModes[0].channel = CELL_AUDIO_OUT_CHNUM_8;
|
||||
info->availableModes[0].fs = CELL_AUDIO_OUT_FS_48KHZ;
|
||||
info->availableModes[0].layout = CELL_AUDIO_OUT_SPEAKER_LAYOUT_8CH_LREClrxy;
|
||||
info->availableModes[1].type = CELL_AUDIO_OUT_CODING_TYPE_LPCM;
|
||||
info->availableModes[1].channel = CELL_AUDIO_OUT_CHNUM_2;
|
||||
info->availableModes[1].fs = CELL_AUDIO_OUT_FS_48KHZ;
|
||||
info->availableModes[1].layout = CELL_AUDIO_OUT_SPEAKER_LAYOUT_2CH;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "cellCamera.h"
|
||||
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/lv2/sys_event.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
logs::channel cellCamera("cellCamera");
|
||||
|
||||
// **************
|
||||
// * Prototypes *
|
||||
// **************
|
||||
|
||||
s32 cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2);
|
||||
|
||||
// ************************
|
||||
// * HLE helper functions *
|
||||
// ************************
|
||||
|
||||
template <>
|
||||
void fmt_class_string<camera_handler>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -97,71 +111,126 @@ static const char* get_camera_attr_name(s32 value)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Custom struct to keep track of cameras
|
||||
struct camera_t
|
||||
static bool check_dev_num(u32 dev_num)
|
||||
{
|
||||
struct attr_t
|
||||
{
|
||||
u32 v1, v2;
|
||||
};
|
||||
return dev_num == 0;
|
||||
}
|
||||
|
||||
attr_t attr[500]{};
|
||||
};
|
||||
/**
|
||||
* \brief Sets read mode attribute (used for deciding how image data is passed to games)
|
||||
* Also sends it to the camera thread
|
||||
* NOTE: thread-safe (uses camera_thread::mutex)
|
||||
* \param dev_num Device number (always 0)
|
||||
* \param read_mode Either CELL_CAMERA_READ_FUNCCALL or CELL_CAMERA_READ_DIRECT
|
||||
* \return CELL error code or CELL_OK
|
||||
*/
|
||||
u32 set_and_send_read_mode(s32 dev_num, const s32 read_mode)
|
||||
{
|
||||
if (read_mode == CELL_CAMERA_READ_FUNCCALL ||
|
||||
read_mode == CELL_CAMERA_READ_DIRECT)
|
||||
{
|
||||
if (const auto status = cellCameraSetAttribute(dev_num, CELL_CAMERA_READMODE, read_mode, 0))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cellCamera.error("Unknown read mode set: %d", read_mode);
|
||||
}
|
||||
|
||||
// Send read mode to camera thread
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
g_camera->read_mode.exchange(read_mode);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
std::pair<u32, u32> get_video_resolution(const CellCameraInfoEx& info)
|
||||
{
|
||||
std::pair<u32, u32> res;
|
||||
switch (info.resolution)
|
||||
{
|
||||
case CELL_CAMERA_VGA: return{ 640, 480 };
|
||||
case CELL_CAMERA_QVGA: return { 320, 240 };
|
||||
case CELL_CAMERA_WGA: return{ 640, 360 };
|
||||
case CELL_CAMERA_SPECIFIED_WIDTH_HEIGHT: return{ info.width, info.height };
|
||||
case CELL_CAMERA_RESOLUTION_UNKNOWN:
|
||||
default: return{ 0, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
u32 get_video_buffer_size(const CellCameraInfoEx& info)
|
||||
{
|
||||
u32 width, height;
|
||||
std::tie(width, height) = get_video_resolution(info);
|
||||
|
||||
const auto bpp = 4;
|
||||
return width * height * bpp;
|
||||
}
|
||||
|
||||
// ************************
|
||||
// * cellCamera functions *
|
||||
// ************************
|
||||
|
||||
s32 cellCameraInit()
|
||||
{
|
||||
cellCamera.warning("cellCameraInit()");
|
||||
cellCamera.todo("cellCameraInit()");
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto camera = fxm::make<camera_t>();
|
||||
// Start camera thread
|
||||
const auto g_camera = fxm::make<camera_thread>();
|
||||
|
||||
if (!camera)
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_ALREADY_INIT;
|
||||
}
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
switch (g_cfg.io.camera_type)
|
||||
{
|
||||
case fake_camera_type::eyetoy:
|
||||
{
|
||||
camera->attr[CELL_CAMERA_SATURATION] = { 164 };
|
||||
camera->attr[CELL_CAMERA_BRIGHTNESS] = { 96 };
|
||||
camera->attr[CELL_CAMERA_AEC] = { 1 };
|
||||
camera->attr[CELL_CAMERA_AGC] = { 1 };
|
||||
camera->attr[CELL_CAMERA_AWB] = { 1 };
|
||||
camera->attr[CELL_CAMERA_ABC] = { 0 };
|
||||
camera->attr[CELL_CAMERA_LED] = { 1 };
|
||||
camera->attr[CELL_CAMERA_QS] = { 0 };
|
||||
camera->attr[CELL_CAMERA_NONZEROCOEFFS] = { 32, 32 };
|
||||
camera->attr[CELL_CAMERA_YUVFLAG] = { 0 };
|
||||
camera->attr[CELL_CAMERA_BACKLIGHTCOMP] = { 0 };
|
||||
camera->attr[CELL_CAMERA_MIRRORFLAG] = { 1 };
|
||||
camera->attr[CELL_CAMERA_422FLAG] = { 1 };
|
||||
camera->attr[CELL_CAMERA_USBLOAD] = { 4 };
|
||||
g_camera->attr[CELL_CAMERA_SATURATION] = { 164 };
|
||||
g_camera->attr[CELL_CAMERA_BRIGHTNESS] = { 96 };
|
||||
g_camera->attr[CELL_CAMERA_AEC] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_AGC] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_AWB] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_ABC] = { 0 };
|
||||
g_camera->attr[CELL_CAMERA_LED] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_QS] = { 0 };
|
||||
g_camera->attr[CELL_CAMERA_NONZEROCOEFFS] = { 32, 32 };
|
||||
g_camera->attr[CELL_CAMERA_YUVFLAG] = { 0 };
|
||||
g_camera->attr[CELL_CAMERA_BACKLIGHTCOMP] = { 0 };
|
||||
g_camera->attr[CELL_CAMERA_MIRRORFLAG] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_422FLAG] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_USBLOAD] = { 4 };
|
||||
break;
|
||||
}
|
||||
|
||||
case fake_camera_type::eyetoy2:
|
||||
{
|
||||
camera->attr[CELL_CAMERA_SATURATION] = { 64 };
|
||||
camera->attr[CELL_CAMERA_BRIGHTNESS] = { 8 };
|
||||
camera->attr[CELL_CAMERA_AEC] = { 1 };
|
||||
camera->attr[CELL_CAMERA_AGC] = { 1 };
|
||||
camera->attr[CELL_CAMERA_AWB] = { 1 };
|
||||
camera->attr[CELL_CAMERA_LED] = { 1 };
|
||||
camera->attr[CELL_CAMERA_BACKLIGHTCOMP] = { 0 };
|
||||
camera->attr[CELL_CAMERA_MIRRORFLAG] = { 1 };
|
||||
camera->attr[CELL_CAMERA_GAMMA] = { 1 };
|
||||
camera->attr[CELL_CAMERA_AGCLIMIT] = { 4 };
|
||||
camera->attr[CELL_CAMERA_DENOISE] = { 0 };
|
||||
camera->attr[CELL_CAMERA_FRAMERATEADJUST] = { 0 };
|
||||
camera->attr[CELL_CAMERA_PIXELOUTLIERFILTER] = { 1 };
|
||||
camera->attr[CELL_CAMERA_AGCLOW] = { 48 };
|
||||
camera->attr[CELL_CAMERA_AGCHIGH] = { 64 };
|
||||
g_camera->attr[CELL_CAMERA_SATURATION] = { 64 };
|
||||
g_camera->attr[CELL_CAMERA_BRIGHTNESS] = { 8 };
|
||||
g_camera->attr[CELL_CAMERA_AEC] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_AGC] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_AWB] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_LED] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_BACKLIGHTCOMP] = { 0 };
|
||||
g_camera->attr[CELL_CAMERA_MIRRORFLAG] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_GAMMA] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_AGCLIMIT] = { 4 };
|
||||
g_camera->attr[CELL_CAMERA_DENOISE] = { 0 };
|
||||
g_camera->attr[CELL_CAMERA_FRAMERATEADJUST] = { 0 };
|
||||
g_camera->attr[CELL_CAMERA_PIXELOUTLIERFILTER] = { 1 };
|
||||
g_camera->attr[CELL_CAMERA_AGCLOW] = { 48 };
|
||||
g_camera->attr[CELL_CAMERA_AGCHIGH] = { 64 };
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -175,9 +244,9 @@ s32 cellCameraInit()
|
||||
|
||||
s32 cellCameraEnd()
|
||||
{
|
||||
cellCamera.warning("cellCameraEnd()");
|
||||
cellCamera.todo("cellCameraEnd()");
|
||||
|
||||
if (!fxm::remove<camera_t>())
|
||||
if (!fxm::remove<camera_thread>())
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
@@ -185,7 +254,7 @@ s32 cellCameraEnd()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellCameraOpen()
|
||||
s32 cellCameraOpen() // seems unused
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
return CELL_OK;
|
||||
@@ -193,13 +262,63 @@ s32 cellCameraOpen()
|
||||
|
||||
s32 cellCameraOpenEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraOpenEx(dev_num=%d, type=*0x%x)", dev_num, info);
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
s32 read_mode = info->read_mode;
|
||||
|
||||
u32 status = set_and_send_read_mode(dev_num, read_mode);
|
||||
if (status != CELL_OK)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
status = cellCameraSetAttribute(dev_num, CELL_CAMERA_GAMEPID, status, 0); // yup, that's what libGem does
|
||||
if (status != CELL_OK)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
const auto vbuf_size = get_video_buffer_size(*info);
|
||||
|
||||
if (info->read_mode == CELL_CAMERA_READ_FUNCCALL && !info->buffer)
|
||||
{
|
||||
info->buffer = vm::cast(vm::alloc(vbuf_size, vm::memory_location_t::main));
|
||||
info->bytesize = vbuf_size;
|
||||
}
|
||||
|
||||
std::tie(info->width, info->height) = get_video_resolution(*info);
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
g_camera->is_open = true;
|
||||
g_camera->info = *info;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellCameraClose(s32 dev_num)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraClose(dev_num=%d)", dev_num);
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
vm::dealloc(g_camera->info.buffer.addr(), vm::memory_location_t::main);
|
||||
g_camera->is_open = false;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -211,15 +330,20 @@ s32 cellCameraGetDeviceGUID(s32 dev_num, vm::ptr<u32> guid)
|
||||
|
||||
s32 cellCameraGetType(s32 dev_num, vm::ptr<s32> type)
|
||||
{
|
||||
cellCamera.warning("cellCameraGetType(dev_num=%d, type=*0x%x)", dev_num, type);
|
||||
cellCamera.todo("cellCameraGetType(dev_num=%d, type=*0x%x)", dev_num, type);
|
||||
|
||||
const auto camera = fxm::get<camera_t>();
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!camera)
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (!check_dev_num(dev_num) || !type )
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
switch (g_cfg.io.camera_type)
|
||||
{
|
||||
case fake_camera_type::unknown: *type = CELL_CAMERA_TYPE_UNKNOWN; break;
|
||||
@@ -234,42 +358,80 @@ s32 cellCameraGetType(s32 dev_num, vm::ptr<s32> type)
|
||||
s32 cellCameraIsAvailable(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraIsAvailable(dev_num=%d)", dev_num);
|
||||
return CELL_OK;
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!g_camera || !check_dev_num(dev_num))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 cellCameraIsAttached(s32 dev_num)
|
||||
{
|
||||
cellCamera.warning("cellCameraIsAttached(dev_num=%d)", dev_num);
|
||||
cellCamera.todo("cellCameraIsAttached(dev_num=%d)", dev_num);
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::fake)
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0; // It's not CELL_OK lol
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
bool is_attached = g_camera->is_attached;
|
||||
|
||||
// "attach" camera here
|
||||
if (!is_attached)
|
||||
{
|
||||
g_camera->send_attach_state(true);
|
||||
is_attached = g_camera->is_attached;
|
||||
}
|
||||
|
||||
return is_attached;
|
||||
}
|
||||
|
||||
s32 cellCameraIsOpen(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraIsOpen(dev_num=%d)", dev_num);
|
||||
return CELL_OK;
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
bool is_open = g_camera->is_open;
|
||||
return is_open;
|
||||
}
|
||||
|
||||
s32 cellCameraIsStarted(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraIsStarted(dev_num=%d)", dev_num);
|
||||
return CELL_OK;
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
bool is_streaming = g_camera->is_streaming;
|
||||
return is_streaming;
|
||||
}
|
||||
|
||||
s32 cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr<u32> arg1, vm::ptr<u32> arg2)
|
||||
{
|
||||
cellCamera.warning("cellCameraGetAttribute(dev_num=%d, attrib=%d, arg1=*0x%x, arg2=*0x%x)", dev_num, attrib, arg1, arg2);
|
||||
|
||||
const auto attr_name = get_camera_attr_name(attrib);
|
||||
cellCamera.todo("cellCameraGetAttribute: get attrib %s to: 0x%x - 0x%x)", attr_name ? attr_name : "(invalid)", arg1, arg2);
|
||||
|
||||
const auto camera = fxm::get<camera_t>();
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!camera)
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
@@ -279,21 +441,30 @@ s32 cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr<u32> arg1, vm::ptr<u
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
*arg1 = camera->attr[attrib].v1;
|
||||
*arg2 = camera->attr[attrib].v2;
|
||||
cellCamera.todo("cellCameraGetAttribute: get attrib %s arg1: %d arg2: %d", attr_name, arg1, arg2);
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
if (arg1)
|
||||
{
|
||||
*arg1 = g_camera->attr[attrib].v1;
|
||||
}
|
||||
if (arg2)
|
||||
{
|
||||
*arg2 = g_camera->attr[attrib].v2;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2)
|
||||
{
|
||||
cellCamera.warning("cellCameraSetAttribute(dev_num=%d, attrib=%d, arg1=%d, arg2=%d)", dev_num, attrib, arg1, arg2);
|
||||
|
||||
const auto attr_name = get_camera_attr_name(attrib);
|
||||
cellCamera.todo("cellCameraSetAttribute: set attrib %s to: %d - %d)", attr_name ? attr_name : "(invalid)", arg1, arg2);
|
||||
|
||||
const auto camera = fxm::get<camera_t>();
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!camera)
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
@@ -303,15 +474,45 @@ s32 cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2)
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
camera->attr[attrib] = { arg1, arg2 };
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
g_camera->attr[attrib] = { arg1, arg2 };
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellCameraGetBufferSize(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
return CELL_OK;
|
||||
cellCamera.todo("cellCameraGetBufferSize(dev_num=%d, info=*0x%x)", dev_num, info);
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
// TODO: a bunch of arg checks. here's one
|
||||
if (!cellCameraIsAttached(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
const auto read_mode = info->read_mode;
|
||||
|
||||
u32 status = set_and_send_read_mode(dev_num, read_mode);
|
||||
if (status != CELL_OK)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
info->bytesize = get_video_buffer_size(g_camera->info);
|
||||
info->buffer = g_camera->info.buffer;
|
||||
|
||||
g_camera->info = *info;
|
||||
|
||||
return info->bytesize;
|
||||
}
|
||||
|
||||
s32 cellCameraGetBufferInfo()
|
||||
@@ -322,7 +523,24 @@ s32 cellCameraGetBufferInfo()
|
||||
|
||||
s32 cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraReadEx(dev_num=%d, read=0x%x)", dev_num, info);
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (!info)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
*info = g_camera->info;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -353,12 +571,21 @@ s32 cellCameraSetExtensionUnit(s32 dev_num, u16 value, u16 length, vm::ptr<u8> d
|
||||
s32 cellCameraReset(s32 dev_num)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellCameraStart(s32 dev_num)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraStart(dev_num=%d", dev_num);
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
g_camera->timer.Start();
|
||||
g_camera->is_streaming = true;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -370,19 +597,46 @@ s32 cellCameraRead(s32 dev_num, vm::ptr<u32> frame_num, vm::ptr<u32> bytes_read)
|
||||
|
||||
s32 cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraReadEx(dev_num=%d, read=0x%x)", dev_num, read);
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
read->timestamp = g_camera->timer.GetElapsedTimeInMicroSec();
|
||||
read->frame = g_camera->frame_num;
|
||||
read->bytesread = g_camera->is_streaming ?
|
||||
get_video_buffer_size(g_camera->info) : 0;
|
||||
|
||||
auto shared_data = fxm::get_always<gem_camera_shared>();
|
||||
|
||||
shared_data->frame_timestamp.exchange(read->timestamp);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellCameraReadComplete(s32 dev_num, u32 bufnum, u32 arg2)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
return CELL_OK;
|
||||
cellCamera.todo("cellCameraReadComplete(dev_num=%d, bufnum=%d, arg2=%d)", dev_num, bufnum, arg2);
|
||||
return cellCameraSetAttribute(dev_num, CELL_CAMERA_READFINISH, bufnum, arg2);
|
||||
}
|
||||
|
||||
s32 cellCameraStop(s32 dev_num)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraStop(dev_num=%d", dev_num);
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
g_camera->is_streaming = false;
|
||||
g_camera->timer.Stop();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -400,13 +654,43 @@ s32 cellCameraRemoveNotifyEventQueue(u64 key)
|
||||
|
||||
s32 cellCameraSetNotifyEventQueue2(u64 key, u64 source, u64 flag)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraSetNotifyEventQueue2(key=0x%x, source=%d, flag=%d)", key, source, flag);
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
{
|
||||
semaphore_lock lock_data_map(g_camera->mutex_notify_data_map);
|
||||
|
||||
g_camera->notify_data_map[key] = { source, flag };
|
||||
}
|
||||
{
|
||||
semaphore_lock lock(g_camera->mutex);
|
||||
|
||||
// send ATTACH event if necessary - HACKY
|
||||
g_camera->send_attach_state(true);
|
||||
}
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellCameraRemoveNotifyEventQueue2(u64 key)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraRemoveNotifyEventQueue2(key=0x%x", key);
|
||||
|
||||
const auto g_camera = fxm::get<camera_thread>();
|
||||
|
||||
if (!g_camera)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
semaphore_lock lock(g_camera->mutex_notify_data_map);
|
||||
|
||||
g_camera->notify_data_map.erase(key);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -447,3 +731,114 @@ DECLARE(ppu_module_manager::cellCamera)("cellCamera", []()
|
||||
REG_FUNC(cellCamera, cellCameraSetNotifyEventQueue2);
|
||||
REG_FUNC(cellCamera, cellCameraRemoveNotifyEventQueue2);
|
||||
});
|
||||
|
||||
void camera_thread::on_task()
|
||||
{
|
||||
while (fxm::check<camera_thread>() && !Emu.IsStopped())
|
||||
{
|
||||
std::chrono::steady_clock::time_point frame_start = std::chrono::steady_clock::now();
|
||||
|
||||
if (Emu.IsPaused())
|
||||
{
|
||||
std::this_thread::sleep_for(1ms); // hack
|
||||
continue;
|
||||
}
|
||||
|
||||
semaphore_lock lock(mutex_notify_data_map);
|
||||
|
||||
for (auto const& notify_data_entry : notify_data_map)
|
||||
{
|
||||
const auto& key = notify_data_entry.first;
|
||||
const auto& evt_data = notify_data_entry.second;
|
||||
|
||||
// handle FRAME_UPDATE
|
||||
if (is_streaming &&
|
||||
evt_data.flag & CELL_CAMERA_EFLAG_FRAME_UPDATE &&
|
||||
info.framerate != 0)
|
||||
{
|
||||
if (auto queue = lv2_event_queue::find(key))
|
||||
{
|
||||
u64 data2{ 0 };
|
||||
u64 data3{ 0 };
|
||||
|
||||
switch (read_mode.load())
|
||||
{
|
||||
case CELL_CAMERA_READ_FUNCCALL:
|
||||
{
|
||||
data2 = 0; // device id (always 0)
|
||||
data3 = 0; // unused
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_READ_DIRECT:
|
||||
{
|
||||
const u64 image_data_size = static_cast<u64>(info.bytesize);
|
||||
const u64 buffer_number = 0;
|
||||
const u64 camera_id = 0;
|
||||
|
||||
data2 = image_data_size << 32 | buffer_number << 16 | camera_id;
|
||||
data3 = timer.GetElapsedTimeInMicroSec(); // timestamp
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
cellCamera.error("Unknown read mode set: %d. This should never happen.", read_mode.load());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto send_status = queue->send(evt_data.source, CELL_CAMERA_FRAME_UPDATE, data2, data3);
|
||||
if (LIKELY(send_status))
|
||||
{
|
||||
++frame_num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const std::chrono::microseconds frame_target_time{ static_cast<u32>(1000000.0 / info.framerate) };
|
||||
|
||||
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
|
||||
|
||||
std::chrono::microseconds frame_processing_time = std::chrono::duration_cast<std::chrono::microseconds>(now - frame_start);
|
||||
|
||||
if (frame_processing_time < frame_target_time)
|
||||
{
|
||||
std::chrono::microseconds frame_idle_time = frame_target_time - frame_processing_time;
|
||||
std::this_thread::sleep_for(frame_idle_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void camera_thread::on_init(const std::shared_ptr<void>& _this)
|
||||
{
|
||||
named_thread::on_init(_this);
|
||||
}
|
||||
|
||||
void camera_thread::send_attach_state(bool attached)
|
||||
{
|
||||
semaphore_lock lock(mutex_notify_data_map);
|
||||
|
||||
if (!notify_data_map.empty())
|
||||
{
|
||||
for (auto const& notify_data_entry : notify_data_map)
|
||||
{
|
||||
const auto& key = notify_data_entry.first;
|
||||
const auto& evt_data = notify_data_entry.second;
|
||||
|
||||
if (auto queue = lv2_event_queue::find(key))
|
||||
{
|
||||
const auto send_result = queue->send(evt_data.source, attached ? CELL_CAMERA_ATTACH : CELL_CAMERA_DETACH, 0, 0);
|
||||
if (LIKELY(send_result))
|
||||
{
|
||||
is_attached = attached;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're not expected to send any events for attaching/detaching
|
||||
is_attached = attached;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/Timer.h"
|
||||
#include "Emu/Cell/lv2/sys_memory.h"
|
||||
#include "Utilities/sema.h"
|
||||
#include "Utilities/Thread.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
|
||||
// Error Codes
|
||||
@@ -20,7 +26,7 @@ enum
|
||||
CELL_CAMERA_ERROR_FATAL = 0x8014080f,
|
||||
};
|
||||
|
||||
// Event types
|
||||
// Event masks
|
||||
enum
|
||||
{
|
||||
CELL_CAMERA_EFLAG_FRAME_UPDATE = 0x00000001,
|
||||
@@ -31,6 +37,26 @@ enum
|
||||
CELL_CAMERA_EFLAG_RESET = 0x00000020,
|
||||
};
|
||||
|
||||
// Event types
|
||||
enum
|
||||
{
|
||||
CELL_CAMERA_DETACH = 0,
|
||||
CELL_CAMERA_ATTACH = 1,
|
||||
CELL_CAMERA_FRAME_UPDATE = 2,
|
||||
CELL_CAMERA_OPEN = 3,
|
||||
CELL_CAMERA_CLOSE = 4,
|
||||
CELL_CAMERA_START = 5,
|
||||
CELL_CAMERA_STOP = 6,
|
||||
CELL_CAMERA_RESET = 7
|
||||
};
|
||||
|
||||
// Read mode
|
||||
enum
|
||||
{
|
||||
CELL_CAMERA_READ_FUNCCALL = 0,
|
||||
CELL_CAMERA_READ_DIRECT = 1,
|
||||
};
|
||||
|
||||
// Colormatching
|
||||
enum
|
||||
{
|
||||
@@ -286,14 +312,14 @@ enum CellCameraAttribute : s32
|
||||
|
||||
struct CellCameraInfoEx
|
||||
{
|
||||
be_t<s32> format; // CellCameraFormat
|
||||
be_t<s32> format; // CellCameraFormat
|
||||
be_t<s32> resolution; // CellCameraResolution
|
||||
be_t<s32> framerate;
|
||||
|
||||
vm::bptr<u8> buffer;
|
||||
be_t<s32> bytesize;
|
||||
be_t<s32> width;
|
||||
be_t<s32> height;
|
||||
be_t<s32> width; // only used if resolution == CELL_CAMERA_SPECIFIED_WIDTH_HEIGHT
|
||||
be_t<s32> height; // likewise
|
||||
be_t<s32> dev_num;
|
||||
be_t<s32> guid;
|
||||
|
||||
@@ -311,3 +337,52 @@ struct CellCameraReadEx
|
||||
be_t<s64> timestamp;
|
||||
vm::bptr<u8> pbuf;
|
||||
};
|
||||
|
||||
class camera_thread final : public named_thread
|
||||
{
|
||||
private:
|
||||
struct notify_event_data
|
||||
{
|
||||
u64 source;
|
||||
u64 flag;
|
||||
};
|
||||
|
||||
void on_task() override;
|
||||
|
||||
std::string get_name() const override { return "Camera Thread"; }
|
||||
|
||||
public:
|
||||
void on_init(const std::shared_ptr<void>&) override;
|
||||
void send_attach_state(bool attached);
|
||||
|
||||
std::map<u64, notify_event_data> notify_data_map;
|
||||
|
||||
semaphore<> mutex;
|
||||
semaphore<> mutex_notify_data_map;
|
||||
Timer timer;
|
||||
|
||||
atomic_t<u8> read_mode;
|
||||
atomic_t<bool> is_streaming;
|
||||
atomic_t<bool> is_attached;
|
||||
atomic_t<bool> is_open;
|
||||
|
||||
CellCameraInfoEx info;
|
||||
|
||||
struct attr_t
|
||||
{
|
||||
u32 v1, v2;
|
||||
};
|
||||
attr_t attr[500]{};
|
||||
|
||||
lv2_memory_container container;
|
||||
atomic_t<u32> frame_num;
|
||||
|
||||
camera_thread() : read_mode(CELL_CAMERA_READ_FUNCCALL) {}
|
||||
~camera_thread() = default;
|
||||
};
|
||||
|
||||
/// Shared data between cellGem and cellCamera
|
||||
struct gem_camera_shared
|
||||
{
|
||||
atomic_t<s64> frame_timestamp; // latest read timestamp from cellCamera (cellCameraRead(Ex))
|
||||
};
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
|
||||
|
||||
logs::channel cellCrossController("cellCrossController");
|
||||
|
||||
enum
|
||||
{
|
||||
CELL_CROSS_CONTROLLER_STATUS_INITIALIZED = 1,
|
||||
CELL_CROSS_CONTROLLER_STATUS_FINALIZED = 2
|
||||
};
|
||||
|
||||
struct CellCrossControllerParam
|
||||
{
|
||||
vm::bcptr<char> pPackageFileName;
|
||||
@@ -26,6 +33,15 @@ using CellCrossControllerCallback = void(s32 status, s32 errorCode, vm::ptr<void
|
||||
s32 cellCrossControllerInitialize(vm::cptr<CellCrossControllerParam> pParam, vm::cptr<CellCrossControllerPackageInfo> pPkgInfo, vm::ptr<CellCrossControllerCallback> cb, vm::ptr<void> userdata) // LittleBigPlanet 2 and 3
|
||||
{
|
||||
cellCrossController.todo("cellCrossControllerInitialize(pParam=*0x%x, pPkgInfo=*0x%x, cb=*0x%x, userdata=*0x%x)", pParam, pPkgInfo, cb, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
cb(ppu, CELL_CROSS_CONTROLLER_STATUS_INITIALIZED, CELL_OK, vm::null, userdata);
|
||||
cb(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, CELL_OK, vm::null, userdata);
|
||||
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
@@ -554,7 +554,7 @@ s32 cellFontExtend(u32 a1, u32 a2, u32 a3)
|
||||
//Something happens
|
||||
}
|
||||
//Something happens?
|
||||
return CELL_OK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s32 cellFontRenderCharGlyphImageVertical()
|
||||
|
||||
@@ -63,6 +63,22 @@ void fmt_class_string<CellGameDataError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<CellDiscGameError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
STR_CASE(CELL_DISCGAME_ERROR_INTERNAL);
|
||||
STR_CASE(CELL_DISCGAME_ERROR_NOT_DISCBOOT);
|
||||
STR_CASE(CELL_DISCGAME_ERROR_PARAM);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
// If dir is empty:
|
||||
// contentInfo = "/dev_bdvd/PS3_GAME"
|
||||
// usrdir = "/dev_bdvd/PS3_GAME/USRDIR"
|
||||
@@ -83,6 +99,8 @@ struct content_permission final
|
||||
// Temporary directory path
|
||||
std::string temp;
|
||||
|
||||
bool can_create = false;
|
||||
|
||||
template <typename Dir, typename Sfo>
|
||||
content_permission(Dir&& dir, Sfo&& sfo)
|
||||
: dir(std::forward<Dir>(dir))
|
||||
@@ -182,7 +200,7 @@ s32 cellHddGameGetSizeKB(vm::ptr<u32> size)
|
||||
{
|
||||
cellGame.warning("cellHddGameGetSizeKB(size=*0x%x)", size);
|
||||
|
||||
const std::string local_dir = vfs::get("/dev_hdd0/game/" + Emu.GetTitleID());
|
||||
const std::string local_dir = vfs::get(Emu.GetDir());
|
||||
|
||||
if (!fs::is_dir(local_dir))
|
||||
{
|
||||
@@ -210,7 +228,7 @@ s32 cellGameDataGetSizeKB(vm::ptr<u32> size)
|
||||
{
|
||||
cellGame.warning("cellGameDataGetSizeKB(size=*0x%x)", size);
|
||||
|
||||
const std::string local_dir = vfs::get("/dev_hdd0/game/" + Emu.GetTitleID());
|
||||
const std::string local_dir = vfs::get(Emu.GetDir());
|
||||
|
||||
if (!fs::is_dir(local_dir))
|
||||
{
|
||||
@@ -270,7 +288,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
|
||||
*attributes = CELL_GAME_ATTRIBUTE_PATCH; // TODO
|
||||
if (dirName) strcpy_trunc(*dirName, Emu.GetTitleID()); // ???
|
||||
|
||||
if (!fxm::make<content_permission>("", psf::load_object(fs::file(vfs::get("/dev_hdd0/game/" + Emu.GetTitleID() + "/PARAM.SFO")))))
|
||||
if (!fxm::make<content_permission>("", psf::load_object(fs::file(vfs::get(Emu.GetDir() + "PARAM.SFO")))))
|
||||
{
|
||||
return CELL_GAME_ERROR_BUSY;
|
||||
}
|
||||
@@ -281,7 +299,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
|
||||
*attributes = 0; // TODO
|
||||
if (dirName) strcpy_trunc(*dirName, Emu.GetTitleID());
|
||||
|
||||
if (!fxm::make<content_permission>(Emu.GetTitleID(), psf::load_object(fs::file(vfs::get("/dev_hdd0/game/" + Emu.GetTitleID() + "/PARAM.SFO")))))
|
||||
if (!fxm::make<content_permission>(Emu.GetTitleID(), psf::load_object(fs::file(vfs::get(Emu.GetDir() + "PARAM.SFO")))))
|
||||
{
|
||||
return CELL_GAME_ERROR_BUSY;
|
||||
}
|
||||
@@ -309,7 +327,7 @@ error_code cellGamePatchCheck(vm::ptr<CellGameContentSize> size, vm::ptr<void> r
|
||||
return CELL_GAME_ERROR_NOTPATCH;
|
||||
}
|
||||
|
||||
if (!fxm::make<content_permission>(Emu.GetTitleID(), psf::load_object(fs::file(vfs::get("/dev_hdd0/game/" + Emu.GetTitleID() + "/PARAM.SFO")))))
|
||||
if (!fxm::make<content_permission>(Emu.GetTitleID(), psf::load_object(fs::file(vfs::get(Emu.GetDir() + "PARAM.SFO")))))
|
||||
{
|
||||
return CELL_GAME_ERROR_BUSY;
|
||||
}
|
||||
@@ -344,6 +362,11 @@ error_code cellGameDataCheck(u32 type, vm::cptr<char> dirName, vm::ptr<CellGameC
|
||||
return CELL_GAME_ERROR_BUSY;
|
||||
}
|
||||
|
||||
if (type == CELL_GAME_GAMETYPE_GAMEDATA)
|
||||
{
|
||||
prm->can_create = true;
|
||||
}
|
||||
|
||||
const std::string dir = prm->dir.empty() ? "/dev_bdvd/PS3_GAME"s : "/dev_hdd0/game/" + prm->dir;
|
||||
|
||||
if (!fs::is_dir(vfs::get(dir)))
|
||||
@@ -353,7 +376,6 @@ error_code cellGameDataCheck(u32 type, vm::cptr<char> dirName, vm::ptr<CellGameC
|
||||
}
|
||||
|
||||
prm->sfo = psf::load_object(fs::file(vfs::get(dir + "/PARAM.SFO")));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -380,18 +402,17 @@ error_code cellGameContentPermit(vm::ptr<char[CELL_GAME_PATH_MAX]> contentInfoPa
|
||||
// Make temporary directory persistent
|
||||
const auto vdir = vfs::get(dir);
|
||||
|
||||
if (fs::exists(vdir))
|
||||
{
|
||||
fmt::throw_exception("cellGameContentPermit(): epic fail: directory '%s' already exists", dir);
|
||||
}
|
||||
|
||||
if (fs::rename(prm->temp, vdir, false))
|
||||
{
|
||||
cellGame.success("cellGameContentPermit(): directory '%s' has been created", dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("cellGameContentPermit(): failed to initialize directory '%s'", dir);
|
||||
cellGame.error("cellGameContentPermit(): failed to initialize directory '%s' (%s)", dir, fs::g_tls_error);
|
||||
strcpy_trunc(*contentInfoPath, dir);
|
||||
strcpy_trunc(*usrdirPath, dir + "/USRDIR");
|
||||
verify(HERE), fxm::remove<content_permission>();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// Create PARAM.SFO
|
||||
@@ -444,7 +465,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
|
||||
cbGet->sizeKB = CELL_GAMEDATA_SIZEKB_NOTCALC;
|
||||
cbGet->sysSizeKB = 0;
|
||||
|
||||
psf::registry sfo = psf::load_object(fs::file(vfs::get(Emu.GetCat() == "DG" ? "/dev_bdvd/PS3_GAME/PARAM.SFO"s : "/dev_hdd0/game/" + Emu.GetTitleID() + "/PARAM.SFO")));
|
||||
psf::registry sfo = psf::load_object(fs::file(vfs::get(dir + "/PARAM.SFO")));
|
||||
|
||||
cbGet->getParam.attribute = CELL_GAMEDATA_ATTR_NORMAL;
|
||||
cbGet->getParam.parentalLevel = psf::get_integer(sfo, "PARENTAL_LEVEL", 0);
|
||||
@@ -549,6 +570,11 @@ error_code cellGameCreateGameData(vm::ptr<CellGameSetInitParams> init, vm::ptr<c
|
||||
return CELL_GAME_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!prm->can_create)
|
||||
{
|
||||
return CELL_GAME_ERROR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
std::string tmp_contentInfo = "/dev_hdd1/game/" + prm->dir;
|
||||
std::string tmp_usrdir = "/dev_hdd1/game/" + prm->dir + "/USRDIR";
|
||||
|
||||
@@ -831,16 +857,18 @@ s32 cellDiscGameGetBootDiscInfo(vm::ptr<CellDiscGameSystemFileParam> getParam)
|
||||
{
|
||||
cellGame.warning("cellDiscGameGetBootDiscInfo(getParam=*0x%x)", getParam);
|
||||
|
||||
if (!getParam)
|
||||
return CELL_DISCGAME_ERROR_PARAM;
|
||||
|
||||
// This is also called by non-disc games, see NPUB90029
|
||||
const std::string dir = "/dev_bdvd/PS3_GAME"s;
|
||||
|
||||
if (!fs::is_dir(vfs::get(dir)))
|
||||
{
|
||||
// Not a disc game. TODO: Fetch PARAM.SFO from proper game dir
|
||||
cellGame.warning("cellDiscGameGetBootDiscInfo(): directory '%s' not found", dir);
|
||||
getParam->parentalLevel = 0;
|
||||
strcpy_trunc(getParam->titleId, "0");
|
||||
return CELL_OK;
|
||||
|
||||
return CELL_DISCGAME_ERROR_NOT_DISCBOOT;
|
||||
}
|
||||
|
||||
const auto& psf = psf::load_object(fs::file(vfs::get(dir + "/PARAM.SFO")));
|
||||
|
||||
@@ -43,6 +43,13 @@ enum CellGameDataError : u32
|
||||
CELL_GAMEDATA_ERROR_FAILURE = 0x8002b607,
|
||||
};
|
||||
|
||||
enum CellDiscGameError : u32
|
||||
{
|
||||
CELL_DISCGAME_ERROR_INTERNAL = 0x8002bd01,
|
||||
CELL_DISCGAME_ERROR_NOT_DISCBOOT = 0x8002bd02,
|
||||
CELL_DISCGAME_ERROR_PARAM = 0x8002bd03,
|
||||
};
|
||||
|
||||
// Definitions
|
||||
enum
|
||||
{
|
||||
|
||||
@@ -1,25 +1,326 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "cellGem.h"
|
||||
|
||||
#include "cellCamera.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "pad_thread.h"
|
||||
#include "Utilities/Timer.h"
|
||||
|
||||
logs::channel cellGem("cellGem");
|
||||
|
||||
// **********************
|
||||
// * HLE helper structs *
|
||||
// **********************
|
||||
|
||||
struct gem_t
|
||||
{
|
||||
struct gem_color
|
||||
{
|
||||
float r, g, b;
|
||||
|
||||
gem_color() : r(0.0f), g(0.0f), b(0.0f) {}
|
||||
gem_color(float r_, float g_, float b_)
|
||||
{
|
||||
r = clamp(r_);
|
||||
g = clamp(g_);
|
||||
b = clamp(b_);
|
||||
}
|
||||
|
||||
float clamp(float f) const
|
||||
{
|
||||
return std::max(0.0f, std::min(f, 1.0f));
|
||||
}
|
||||
};
|
||||
|
||||
struct gem_controller
|
||||
{
|
||||
u32 status; // connection status (CELL_GEM_STATUS_DISCONNECTED or CELL_GEM_STATUS_READY)
|
||||
u32 port; // assigned port
|
||||
bool enabled_magnetometer; // whether the magnetometer is enabled (probably used for additional rotational precision)
|
||||
bool calibrated_magnetometer; // whether the magnetometer is calibrated
|
||||
bool enabled_filtering; // whether filtering is enabled
|
||||
u8 rumble; // rumble intensity
|
||||
gem_color sphere_rgb; // RGB color of the sphere LED
|
||||
|
||||
gem_controller() :
|
||||
status(CELL_GEM_STATUS_DISCONNECTED),
|
||||
enabled_filtering(false), rumble(0), sphere_rgb() {}
|
||||
};
|
||||
|
||||
CellGemAttribute attribute;
|
||||
CellGemVideoConvertAttribute vc_attribute;
|
||||
u64 status_flags;
|
||||
bool enable_pitch_correction;
|
||||
u32 inertial_counter;
|
||||
|
||||
std::array<gem_controller, CELL_GEM_MAX_NUM> controllers;
|
||||
u32 connected_controllers;
|
||||
|
||||
Timer timer;
|
||||
|
||||
// helper functions
|
||||
bool is_controller_ready(u32 gem_num) const
|
||||
{
|
||||
return controllers[gem_num].status == CELL_GEM_STATUS_READY;
|
||||
}
|
||||
|
||||
void reset_controller(u32 gem_num)
|
||||
{
|
||||
switch (g_cfg.io.move)
|
||||
{
|
||||
default:
|
||||
case move_handler::null:
|
||||
{
|
||||
connected_controllers = 0;
|
||||
|
||||
controllers[gem_num].status = CELL_GEM_STATUS_DISCONNECTED;
|
||||
controllers[gem_num].port = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case move_handler::fake:
|
||||
{
|
||||
// fake one connected controller
|
||||
connected_controllers = 1;
|
||||
|
||||
if (gem_num < connected_controllers)
|
||||
{
|
||||
controllers[gem_num].status = CELL_GEM_STATUS_READY;
|
||||
controllers[gem_num].port = 7u - gem_num;
|
||||
}
|
||||
else
|
||||
{
|
||||
controllers[gem_num].status = CELL_GEM_STATUS_DISCONNECTED;
|
||||
controllers[gem_num].port = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
s32 cellGemCalibrate()
|
||||
// ************************
|
||||
// * HLE helper functions *
|
||||
// ************************
|
||||
|
||||
template <>
|
||||
void fmt_class_string<move_handler>::format(std::string& out, u64 arg)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
format_enum(out, arg, [](auto value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case move_handler::null: return "Null";
|
||||
case move_handler::fake: return "Fake";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Verifies that a Move controller id is valid
|
||||
* \param gem_num Move controler ID to verify
|
||||
* \return True if the ID is valid, false otherwise
|
||||
*/
|
||||
static bool check_gem_num(const u32 gem_num)
|
||||
{
|
||||
return gem_num >= 0 && gem_num < CELL_GEM_MAX_NUM;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Maps Move controller data (digital buttons, and analog Trigger data) to DS3 pad input.
|
||||
* Unavoidably buttons conflict with DS3 mappings, which is problematic for some games.
|
||||
* \param port_no DS3 port number to use
|
||||
* \param digital_buttons Bitmask filled with CELL_GEM_CTRL_* values
|
||||
* \param analog_t Analog value of Move's Trigger. Currently mapped to R2.
|
||||
* \return true on success, false if port_no controller is invalid
|
||||
*/
|
||||
static bool map_to_ds3_input(const u32 port_no, be_t<u16>& digital_buttons, be_t<u16>& analog_t)
|
||||
{
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const PadInfo& rinfo = handler->GetInfo();
|
||||
|
||||
if (port_no >= rinfo.max_connect || port_no >= rinfo.now_connect)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& pads = handler->GetPads();
|
||||
auto pad = pads[port_no];
|
||||
|
||||
for (Button& button : pad->m_buttons)
|
||||
{
|
||||
//here we check btns, and set pad accordingly,
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2)
|
||||
{
|
||||
if (button.m_pressed) pad->m_digital_2 |= button.m_outKeyCode;
|
||||
else pad->m_digital_2 &= ~button.m_outKeyCode;
|
||||
|
||||
switch (button.m_outKeyCode)
|
||||
{
|
||||
case CELL_PAD_CTRL_SQUARE:
|
||||
pad->m_press_square = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CROSS:
|
||||
pad->m_press_cross = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_CIRCLE:
|
||||
pad->m_press_circle = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_TRIANGLE:
|
||||
pad->m_press_triangle = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R1:
|
||||
pad->m_press_R1 = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L1:
|
||||
pad->m_press_L1 = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_R2:
|
||||
pad->m_press_R2 = button.m_value;
|
||||
break;
|
||||
case CELL_PAD_CTRL_L2:
|
||||
pad->m_press_L2 = button.m_value;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (button.m_flush)
|
||||
{
|
||||
button.m_pressed = false;
|
||||
button.m_flush = false;
|
||||
button.m_value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&digital_buttons, 0, sizeof(digital_buttons));
|
||||
|
||||
// map the Move key to R1 and the Trigger to R2
|
||||
|
||||
if (pad->m_press_R1)
|
||||
digital_buttons |= CELL_GEM_CTRL_MOVE;
|
||||
if (pad->m_press_R2)
|
||||
digital_buttons |= CELL_GEM_CTRL_T;
|
||||
|
||||
if (pad->m_press_cross)
|
||||
digital_buttons |= CELL_GEM_CTRL_CROSS;
|
||||
if (pad->m_press_circle)
|
||||
digital_buttons |= CELL_GEM_CTRL_CIRCLE;
|
||||
if (pad->m_press_square)
|
||||
digital_buttons |= CELL_GEM_CTRL_SQUARE;
|
||||
if (pad->m_press_triangle)
|
||||
digital_buttons |= CELL_GEM_CTRL_TRIANGLE;
|
||||
if (pad->m_digital_1)
|
||||
digital_buttons |= CELL_GEM_CTRL_SELECT;
|
||||
if (pad->m_digital_2)
|
||||
digital_buttons |= CELL_GEM_CTRL_START;
|
||||
|
||||
analog_t = pad->m_press_R2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Maps external Move controller data to DS3 input
|
||||
* Implementation detail: CellGemExtPortData's digital/analog fields map the same way as
|
||||
* libPad, so no translation is needed.
|
||||
* \param port_no DS3 port number to use
|
||||
* \param ext External data to modify
|
||||
* \return true on success, false if port_no controller is invalid
|
||||
*/
|
||||
static bool map_ext_to_ds3_input(const u32 port_no, CellGemExtPortData& ext)
|
||||
{
|
||||
const auto handler = fxm::get<pad_thread>();
|
||||
|
||||
if (!handler)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& pads = handler->GetPads();
|
||||
|
||||
const PadInfo& rinfo = handler->GetInfo();
|
||||
|
||||
if (port_no >= rinfo.max_connect)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//We have a choice here of NO_DEVICE or READ_FAILED...lets try no device for now
|
||||
if (port_no >= rinfo.now_connect)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pad = pads[port_no];
|
||||
|
||||
ext.status = 0; // CELL_GEM_EXT_CONNECTED | CELL_GEM_EXT_EXT0 | CELL_GEM_EXT_EXT1
|
||||
ext.analog_left_x = pad->m_analog_left_x;
|
||||
ext.analog_left_y = pad->m_analog_left_y;
|
||||
ext.analog_right_x = pad->m_analog_right_x;
|
||||
ext.analog_right_y = pad->m_analog_right_y;
|
||||
ext.digital1 = pad->m_digital_1;
|
||||
ext.digital2 = pad->m_digital_2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// *********************
|
||||
// * cellGem functions *
|
||||
// *********************
|
||||
|
||||
s32 cellGemCalibrate(u32 gem_num)
|
||||
{
|
||||
cellGem.todo("cellGemCalibrate(gem_num=%d)", gem_num);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake)
|
||||
{
|
||||
gem->controllers[gem_num].calibrated_magnetometer = true;
|
||||
gem->status_flags = CELL_GEM_FLAG_CALIBRATION_OCCURRED | CELL_GEM_FLAG_CALIBRATION_SUCCEEDED;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemClearStatusFlags()
|
||||
s32 cellGemClearStatusFlags(u32 gem_num, u64 mask)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemClearStatusFlags(gem_num=%d, mask=0x%x)", gem_num, mask);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gem->status_flags &= ~mask;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -35,15 +336,38 @@ s32 cellGemConvertVideoStart()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemEnableCameraPitchAngleCorrection()
|
||||
s32 cellGemEnableCameraPitchAngleCorrection(u32 enable_flag)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemEnableCameraPitchAngleCorrection(enable_flag=%d", enable_flag);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
gem->enable_pitch_correction = !!enable_flag;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemEnableMagnetometer()
|
||||
s32 cellGemEnableMagnetometer(u32 gem_num, u32 enable)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemEnableMagnetometer(gem_num=%d, enable=0x%x)", gem_num, enable);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!gem->is_controller_ready(gem_num))
|
||||
{
|
||||
return CELL_GEM_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
gem->controllers[gem_num].enabled_magnetometer = !!enable;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -59,15 +383,43 @@ s32 cellGemEnd()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemFilterState()
|
||||
s32 cellGemFilterState(u32 gem_num, u32 enable)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.warning("cellGemFilterState(gem_num=%d, enable=%d)", gem_num, enable);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gem->controllers[gem_num].enabled_filtering = !!enable;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemForceRGB()
|
||||
s32 cellGemForceRGB(u32 gem_num, float r, float g, float b)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemForceRGB(gem_num=%d, r=%f, g=%f, b=%f)", gem_num, r, g, b);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gem->controllers[gem_num].sphere_rgb = gem_t::gem_color(r, g, b);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -77,15 +429,30 @@ s32 cellGemGetAccelerometerPositionInDevice()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemGetAllTrackableHues()
|
||||
s32 cellGemGetAllTrackableHues(vm::ptr<u8> hues)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemGetAllTrackableHues(hues=*0x%x)");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemGetCameraState()
|
||||
s32 cellGemGetCameraState(vm::ptr<CellGemCameraState> camera_state)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemGetCameraState(camera_state=0x%x)", camera_state);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!camera_state)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
camera_state->exposure_time = 1.0f / 60.0f; // TODO: use correct framerate
|
||||
camera_state->gain = 1.0;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -101,15 +468,68 @@ s32 cellGemGetHuePixels()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemGetImageState()
|
||||
s32 cellGemGetImageState(u32 gem_num, vm::ptr<CellGemImageState> image_state)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemGetImageState(gem_num=%d, image_state=&0x%x)", gem_num, image_state);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake)
|
||||
{
|
||||
auto shared_data = fxm::get_always<gem_camera_shared>();
|
||||
|
||||
image_state->frame_timestamp = shared_data->frame_timestamp.load();
|
||||
image_state->timestamp = image_state->frame_timestamp + 10; // arbitrarily define 10 usecs of frame processing
|
||||
image_state->visible = true;
|
||||
image_state->u = 0;
|
||||
image_state->v = 0;
|
||||
image_state->r = 20;
|
||||
image_state->r_valid = true;
|
||||
image_state->distance = 2 * 1000; // 2 meters away from camera
|
||||
// TODO
|
||||
image_state->projectionx = 1;
|
||||
image_state->projectiony = 1;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemGetInertialState()
|
||||
s32 cellGemGetInertialState(u32 gem_num, u32 state_flag, u64 timestamp, vm::ptr<CellGemInertialState> inertial_state)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemGetInertialState(gem_num=%d, state_flag=%d, timestamp=0x%x, inertial_state=0x%x)", gem_num, state_flag, timestamp, inertial_state);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num) || !inertial_state || !gem->is_controller_ready(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake)
|
||||
{
|
||||
map_to_ds3_input(gem_num, inertial_state->pad.digitalbuttons, inertial_state->pad.analog_T);
|
||||
map_ext_to_ds3_input(gem_num, inertial_state->ext);
|
||||
|
||||
inertial_state->timestamp = gem->timer.GetElapsedTimeInMicroSec();
|
||||
|
||||
inertial_state->counter = gem->inertial_counter++;
|
||||
|
||||
inertial_state->accelerometer[0] = 10;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -124,14 +544,19 @@ s32 cellGemGetInfo(vm::ptr<CellGemInfo> info)
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!info)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
// TODO: Support connecting PlayStation Move controllers
|
||||
info->max_connect = gem->attribute.max_connect;
|
||||
info->now_connect = 0;
|
||||
info->now_connect = gem->connected_controllers;
|
||||
|
||||
for (int i = 0; i < CELL_GEM_MAX_NUM; i++)
|
||||
{
|
||||
info->status[i] = CELL_GEM_STATUS_DISCONNECTED;
|
||||
info->port[i] = 0;
|
||||
info->status[i] = gem->controllers[i].status;
|
||||
info->port[i] = gem->controllers[i].port;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
@@ -149,15 +574,46 @@ s32 cellGemGetMemorySize(s32 max_connect)
|
||||
return max_connect <= 2 ? 0x120000 : 0x140000;
|
||||
}
|
||||
|
||||
s32 cellGemGetRGB()
|
||||
s32 cellGemGetRGB(u32 gem_num, vm::ptr<float> r, vm::ptr<float> g, vm::ptr<float> b)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemGetRGB(gem_num=%d, r=*0x%x, g=*0x%x, b=*0x%x)", gem_num, r, g, b);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num) | !r || !g || !b )
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
auto& sphere_color = gem->controllers[gem_num].sphere_rgb;
|
||||
*r = sphere_color.r;
|
||||
*g = sphere_color.g;
|
||||
*b = sphere_color.b;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemGetRumble()
|
||||
s32 cellGemGetRumble(u32 gem_num, vm::ptr<u8> rumble)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemGetRumble(gem_num=%d, rumble=*0x%x)", gem_num, rumble);
|
||||
auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num) || !rumble)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*rumble = gem->controllers[gem_num].rumble;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -167,17 +623,47 @@ s32 cellGemGetState(u32 gem_num, u32 flag, u64 time_parameter, vm::ptr<CellGemSt
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
// clear out gem_state so no games get any funny ideas about them being connected...
|
||||
std::memset(gem_state.get_ptr(), 0, sizeof(CellGemState));
|
||||
if (!check_gem_num(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (g_cfg.io.move == move_handler::fake)
|
||||
{
|
||||
map_to_ds3_input(gem_num, gem_state->pad.digitalbuttons, gem_state->pad.analog_T);
|
||||
map_ext_to_ds3_input(gem_num, gem_state->ext);
|
||||
|
||||
gem_state->tracking_flags = CELL_GEM_TRACKING_FLAG_POSITION_TRACKED |
|
||||
CELL_GEM_TRACKING_FLAG_VISIBLE;
|
||||
gem_state->timestamp = gem->timer.GetElapsedTimeInMicroSec();
|
||||
|
||||
gem_state->quat[3] = 1.0;
|
||||
}
|
||||
|
||||
return CELL_GEM_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
s32 cellGemGetStatusFlags()
|
||||
s32 cellGemGetStatusFlags(u32 gem_num, vm::ptr<u64> flags)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemGetStatusFlags(gem_num=%d, flags=*0x%x)", gem_num, flags);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num) || !flags)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
*flags = gem->status_flags;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -196,7 +682,6 @@ s32 cellGemHSVtoRGB()
|
||||
s32 cellGemInit(vm::cptr<CellGemAttribute> attribute)
|
||||
{
|
||||
cellGem.warning("cellGemInit(attribute=*0x%x)", attribute);
|
||||
|
||||
const auto gem = fxm::make<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
@@ -204,8 +689,21 @@ s32 cellGemInit(vm::cptr<CellGemAttribute> attribute)
|
||||
return CELL_GEM_ERROR_ALREADY_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!attribute || !attribute->spurs_addr || attribute->max_connect > CELL_GEM_MAX_NUM)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gem->attribute = *attribute;
|
||||
|
||||
for (auto gem_num = 0; gem_num < CELL_GEM_MAX_NUM; gem_num++)
|
||||
{
|
||||
gem->reset_controller(gem_num);
|
||||
}
|
||||
|
||||
// TODO: is this correct?
|
||||
gem->timer.Start();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -215,10 +713,17 @@ s32 cellGemInvalidateCalibration()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemIsTrackableHue()
|
||||
s32 cellGemIsTrackableHue(u32 hue)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
return CELL_OK;
|
||||
cellGem.todo("cellGemIsTrackableHue(hue=%d)", hue);
|
||||
const auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem || hue > 359)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 cellGemPrepareCamera()
|
||||
@@ -227,9 +732,36 @@ s32 cellGemPrepareCamera()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemPrepareVideoConvert()
|
||||
s32 cellGemPrepareVideoConvert(vm::cptr<CellGemVideoConvertAttribute> vc_attribute)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemPrepareVideoConvert(vc_attribute=*0x%x)", vc_attribute);
|
||||
auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!vc_attribute)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const auto vc = *vc_attribute;
|
||||
|
||||
if (!vc_attribute || vc.version == 0 || vc.output_format == 0 ||
|
||||
vc.conversion_flags & CELL_GEM_VIDEO_CONVERT_UNK3 && !vc.buffer_memory)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (vc.video_data_out & 0x1f || vc.buffer_memory & 0xff)
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_ALIGNMENT;
|
||||
}
|
||||
|
||||
gem->vc_attribute = vc;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -239,15 +771,46 @@ s32 cellGemReadExternalPortDeviceInfo()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemReset()
|
||||
s32 cellGemReset(u32 gem_num)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemReset(gem_num=%d)", gem_num);
|
||||
auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gem->reset_controller(gem_num);
|
||||
|
||||
// TODO: is this correct?
|
||||
gem->timer.Start();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemSetRumble()
|
||||
s32 cellGemSetRumble(u32 gem_num, u8 rumble)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemSetRumble(gem_num=%d, rumble=0x%x)", gem_num, rumble);
|
||||
auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
if (!check_gem_num(gem_num))
|
||||
{
|
||||
return CELL_GEM_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
gem->controllers[gem_num].rumble = rumble;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -269,9 +832,16 @@ s32 cellGemUpdateFinish()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGemUpdateStart()
|
||||
s32 cellGemUpdateStart(vm::cptr<void> camera_frame, u64 timestamp)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGem);
|
||||
cellGem.todo("cellGemUpdateStart(camera_frame=*0x%x, timestamp=%d)", camera_frame, timestamp);
|
||||
auto gem = fxm::get<gem_t>();
|
||||
|
||||
if (!gem)
|
||||
{
|
||||
return CELL_GEM_ERROR_UNINITIALIZED;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
static const float CELL_GEM_SPHERE_RADIUS_MM = 22.5f;
|
||||
|
||||
// Error Codes
|
||||
// Error codes
|
||||
enum
|
||||
{
|
||||
CELL_GEM_ERROR_RESOURCE_ALLOCATION_FAILED = 0x80121801,
|
||||
@@ -34,7 +34,7 @@ enum
|
||||
CELL_GEM_NO_EXTERNAL_PORT_DEVICE = 9,
|
||||
};
|
||||
|
||||
// General constents
|
||||
// General constants
|
||||
enum
|
||||
{
|
||||
CELL_GEM_CTRL_CIRCLE = 1 << 5,
|
||||
@@ -59,7 +59,7 @@ enum
|
||||
CELL_GEM_FLAG_CALIBRATION_OCCURRED = 1 << 0,
|
||||
CELL_GEM_FLAG_CALIBRATION_SUCCEEDED = 1 << 1,
|
||||
CELL_GEM_FLAG_CALIBRATION_WARNING_BRIGHT_LIGHTING = 1 << 6,
|
||||
ELL_GEM_FLAG_CALIBRATION_WARNING_MOTION_DETECTED = 1 << 5,
|
||||
CELL_GEM_FLAG_CALIBRATION_WARNING_MOTION_DETECTED = 1 << 5,
|
||||
CELL_GEM_FLAG_CAMERA_PITCH_ANGLE_CHANGED = 1 << 9,
|
||||
CELL_GEM_FLAG_CURRENT_HUE_CONFLICTS_WITH_ENVIRONMENT = 1 << 13,
|
||||
CELL_GEM_FLAG_LIGHTING_CHANGED = 1 << 7,
|
||||
@@ -84,6 +84,15 @@ enum
|
||||
CELL_GEM_VERSION = 2,
|
||||
};
|
||||
|
||||
// Video conversion flags
|
||||
enum
|
||||
{
|
||||
CELL_GEM_VIDEO_CONVERT_UNK1 = 1 << 0,
|
||||
CELL_GEM_VIDEO_CONVERT_UNK2 = 1 << 1,
|
||||
CELL_GEM_VIDEO_CONVERT_UNK3 = 1 << 2,
|
||||
CELL_GEM_VIDEO_CONVERT_UNK4 = 1 << 3,
|
||||
};
|
||||
|
||||
struct CellGemAttribute
|
||||
{
|
||||
be_t<u32> version;
|
||||
@@ -118,9 +127,9 @@ struct CellGemImageState
|
||||
{
|
||||
be_t<u64> frame_timestamp;
|
||||
be_t<u64> timestamp;
|
||||
be_t<f32> u;
|
||||
be_t<f32> v;
|
||||
be_t<f32> r;
|
||||
be_t<f32> u; // horizontal screen position in pixels
|
||||
be_t<f32> v; // vertical screen position in pixels
|
||||
be_t<f32> r; // size of sphere on screen in pixels
|
||||
be_t<f32> projectionx;
|
||||
be_t<f32> projectiony;
|
||||
be_t<f32> distance;
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/lv2/sys_lwmutex.h"
|
||||
#include "Emu/Cell/lv2/sys_lwcond.h"
|
||||
#include "Emu/Cell/lv2/sys_spu.h"
|
||||
#include "cellMusic.h"
|
||||
#include "cellSearch.h"
|
||||
#include "cellSpurs.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
|
||||
|
||||
@@ -23,123 +30,302 @@ enum
|
||||
CELL_MUSIC_DECODE_ERROR_GENERIC = 0x8002C1FF,
|
||||
};
|
||||
|
||||
s32 cellMusicDecodeInitialize()
|
||||
enum
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
CELL_MUSIC_DECODE_EVENT_STATUS_NOTIFICATION = 0,
|
||||
CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT = 1,
|
||||
CELL_MUSIC_DECODE_EVENT_FINALIZE_RESULT = 2,
|
||||
CELL_MUSIC_DECODE_EVENT_SELECT_CONTENTS_RESULT = 3,
|
||||
CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT = 4,
|
||||
CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT = 5,
|
||||
CELL_MUSIC_DECODE_EVENT_UI_NOTIFICATION = 6,
|
||||
CELL_MUSIC_DECODE_EVENT_NEXT_CONTENTS_READY_RESULT = 7,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
CELL_MUSIC_DECODE2_EVENT_STATUS_NOTIFICATION = 0,
|
||||
CELL_MUSIC_DECODE2_EVENT_INITIALIZE_RESULT = 1,
|
||||
CELL_MUSIC_DECODE2_EVENT_FINALIZE_RESULT = 2,
|
||||
CELL_MUSIC_DECODE2_EVENT_SELECT_CONTENTS_RESULT = 3,
|
||||
CELL_MUSIC_DECODE2_EVENT_SET_DECODE_COMMAND_RESULT = 4,
|
||||
CELL_MUSIC_DECODE2_EVENT_SET_SELECTION_CONTEXT_RESULT = 5,
|
||||
CELL_MUSIC_DECODE2_EVENT_UI_NOTIFICATION = 6,
|
||||
CELL_MUSIC_DECODE2_EVENT_NEXT_CONTENTS_READY_RESULT = 7,
|
||||
};
|
||||
|
||||
using CellMusicDecodeCallback = void(u32, vm::ptr<void> param, vm::ptr<void> userData);
|
||||
using CellMusicDecode2Callback = void(u32, vm::ptr<void> param, vm::ptr<void> userData);
|
||||
|
||||
struct music_decode_t
|
||||
{
|
||||
vm::ptr<CellMusicDecodeCallback> func;
|
||||
vm::ptr<void> userData;
|
||||
};
|
||||
|
||||
struct music_decode2_t
|
||||
{
|
||||
vm::ptr<CellMusicDecode2Callback> func;
|
||||
vm::ptr<void> userData;
|
||||
};
|
||||
|
||||
s32 cellMusicDecodeInitialize(s32 mode, u32 container, s32 spuPriority, vm::ptr<CellMusicDecodeCallback> func, vm::ptr<void> userData)
|
||||
{
|
||||
cellMusicDecode.todo("cellMusicDecodeInitialize(mode=0x%x, container=0x%x, spuPriority=0x%x, func=*0x%x, userData=*0x%x)", mode, container, spuPriority, func, userData);
|
||||
|
||||
const auto musicDecode = fxm::make_always<music_decode_t>();
|
||||
musicDecode->func = func;
|
||||
musicDecode->userData = userData;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeInitializeSystemWorkload()
|
||||
s32 cellMusicDecodeInitializeSystemWorkload(s32 mode, u32 container, vm::ptr<CellMusicDecodeCallback> func, vm::ptr<void> userData, s32 spuUsageRate, vm::ptr<CellSpurs> spurs, vm::cptr<u8> priority, vm::cptr<struct CellSpursSystemWorkloadAttribute> attr)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeInitializeSystemWorkload(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x, spuUsageRate=0x%x, spurs=*0x%x, priority=*0x%x, attr=*0x%x)", mode, container, func, userData, spuUsageRate, spurs, priority, attr);
|
||||
|
||||
const auto musicDecode = fxm::make_always<music_decode_t>();
|
||||
musicDecode->func = func;
|
||||
musicDecode->userData = userData;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeFinalize()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeFinalize()");
|
||||
|
||||
const auto musicDecode = fxm::get_always<music_decode_t>();
|
||||
|
||||
if (musicDecode->func)
|
||||
{
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE_EVENT_FINALIZE_RESULT, vm::addr_t(CELL_OK), musicDecode->userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeSelectContents()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeSelectContents()");
|
||||
|
||||
const auto musicDecode = fxm::get_always<music_decode_t>();
|
||||
|
||||
if (!musicDecode->func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE_EVENT_SELECT_CONTENTS_RESULT, vm::addr_t(CELL_OK), musicDecode->userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeSetDecodeCommand()
|
||||
s32 cellMusicDecodeSetDecodeCommand(s32 command)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeSetDecodeCommand(command=0x%x)", command);
|
||||
|
||||
const auto musicDecode = fxm::get_always<music_decode_t>();
|
||||
|
||||
if (!musicDecode->func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(CELL_OK), musicDecode->userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeGetDecodeStatus()
|
||||
s32 cellMusicDecodeGetDecodeStatus(vm::ptr<s32> status)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeGetDecodeStatus(status=*0x%x)", status);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeRead()
|
||||
s32 cellMusicDecodeRead(vm::ptr<void> buf, vm::ptr<u32> startTime, u64 reqSize, vm::ptr<u64> readSize, vm::ptr<s32> position)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeRead(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeGetSelectionContext()
|
||||
s32 cellMusicDecodeGetSelectionContext(vm::ptr<CellMusicSelectionContext> context)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeGetSelectionContext(context=*0x%x)", context);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeSetSelectionContext()
|
||||
s32 cellMusicDecodeSetSelectionContext(vm::ptr<CellMusicSelectionContext> context)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeSetSelectionContext(context=*0x%x)", context);
|
||||
|
||||
const auto musicDecode = fxm::get_always<music_decode_t>();
|
||||
|
||||
if (!musicDecode->func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(CELL_OK), musicDecode->userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeGetContentsId()
|
||||
s32 cellMusicDecodeGetContentsId(vm::ptr<CellSearchContentId> contents_id)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeGetContentsId(contents_id=*0x%x)", contents_id);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeInitialize2()
|
||||
s32 cellMusicDecodeInitialize2(s32 mode, u32 container, s32 spuPriority, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 speed, s32 bufsize)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeInitialize2(mode=0x%x, container=0x%x, spuPriority=0x%x, func=*0x%x, userData=*0x%x, speed=0x%x, bufsize=0x%x)", mode, container, spuPriority, func, userData, speed, bufsize);
|
||||
|
||||
const auto musicDecode = fxm::make_always<music_decode2_t>();
|
||||
musicDecode->func = func;
|
||||
musicDecode->userData = userData;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE2_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeInitialize2SystemWorkload()
|
||||
s32 cellMusicDecodeInitialize2SystemWorkload(s32 mode, u32 container, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 spuUsageRate, s32 bufsize, vm::ptr<CellSpurs> spurs, vm::cptr<u8> priority, vm::cptr<CellSpursSystemWorkloadAttribute> attr)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeInitialize2SystemWorkload(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x, spuUsageRate=0x%x, bufsize=0x%x, spurs=*0x%x, priority=*0x%x, attr=*0x%x)", mode, container, func, userData, spuUsageRate, bufsize, spurs, priority, attr);
|
||||
|
||||
const auto musicDecode = fxm::make_always<music_decode2_t>();
|
||||
musicDecode->func = func;
|
||||
musicDecode->userData = userData;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE2_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeFinalize2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeFinalize2()");
|
||||
|
||||
const auto musicDecode = fxm::get_always<music_decode2_t>();
|
||||
|
||||
if (musicDecode->func)
|
||||
{
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE2_EVENT_FINALIZE_RESULT, vm::addr_t(CELL_OK), musicDecode->userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeSelectContents2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeSelectContents2()");
|
||||
|
||||
const auto musicDecode = fxm::get_always<music_decode2_t>();
|
||||
|
||||
if (!musicDecode->func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE2_EVENT_SELECT_CONTENTS_RESULT, vm::addr_t(CELL_OK), musicDecode->userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeSetDecodeCommand2()
|
||||
s32 cellMusicDecodeSetDecodeCommand2(s32 command)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeSetDecodeCommand2(command=0x%x)", command);
|
||||
|
||||
const auto musicDecode = fxm::get_always<music_decode2_t>();
|
||||
|
||||
if (!musicDecode->func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE2_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(CELL_OK), musicDecode->userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeGetDecodeStatus2()
|
||||
s32 cellMusicDecodeGetDecodeStatus2(vm::ptr<s32> status)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeGetDecodeStatus2(status=*0x%x)", status);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeRead2()
|
||||
s32 cellMusicDecodeRead2(vm::ptr<void> buf, vm::ptr<u32> startTime, u64 reqSize, vm::ptr<u64> readSize, vm::ptr<s32> position)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeRead2(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeGetSelectionContext2()
|
||||
s32 cellMusicDecodeGetSelectionContext2(vm::ptr<CellMusicSelectionContext> context)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeGetSelectionContext2(context=*0x%x)", context);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeSetSelectionContext2()
|
||||
s32 cellMusicDecodeSetSelectionContext2(vm::ptr<CellMusicSelectionContext> context)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeSetSelectionContext2(context=*0x%x)", context);
|
||||
|
||||
const auto musicDecode = fxm::get_always<music_decode2_t>();
|
||||
|
||||
if (!musicDecode->func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
musicDecode->func(ppu, CELL_MUSIC_DECODE2_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(CELL_OK), musicDecode->userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicDecodeGetContentsId2()
|
||||
s32 cellMusicDecodeGetContentsId2(vm::ptr<CellSearchContentId> contents_id )
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicDecode);
|
||||
cellMusicDecode.todo("cellMusicDecodeGetContentsId2(contents_id=*0x%x)", contents_id);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
|
||||
|
||||
@@ -20,33 +21,81 @@ enum
|
||||
CELL_MUSIC_EXPORT_UTIL_ERROR_INITIALIZE = 0x8002c60a,
|
||||
};
|
||||
|
||||
s32 cellMusicExportInitialize()
|
||||
struct CellMusicExportSetParam
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicExport);
|
||||
vm::bptr<char> title;
|
||||
vm::bptr<char> game_title;
|
||||
vm::bptr<char> artist;
|
||||
vm::bptr<char> genre;
|
||||
vm::bptr<char> game_comment;
|
||||
vm::bptr<void> reserved1;
|
||||
vm::bptr<void> reserved2;
|
||||
};
|
||||
|
||||
using CellMusicExportUtilFinishCallback = void(s32 result, vm::ptr<void> userdata);
|
||||
|
||||
error_code cellMusicExportInitialize(u32 version, u32 container, vm::ptr<CellMusicExportUtilFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
cellMusicExport.todo("cellMusicExportInitialize(version=0x%x, container=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, container, funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicExportInitialize2()
|
||||
error_code cellMusicExportInitialize2(u32 version, vm::ptr<CellMusicExportUtilFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicExport);
|
||||
cellMusicExport.todo("cellMusicExportInitialize2(version=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicExportFinalize()
|
||||
error_code cellMusicExportFinalize(vm::ptr<CellMusicExportUtilFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicExport);
|
||||
cellMusicExport.todo("cellMusicExportFinalize(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicExportFromFile()
|
||||
error_code cellMusicExportFromFile(vm::cptr<char> srcHddDir, vm::cptr<char> srcHddFile, vm::ptr<CellMusicExportSetParam> param, vm::ptr<CellMusicExportUtilFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicExport);
|
||||
cellMusicExport.todo("cellMusicExportFromFile(srcHddDir=%s, srcHddFile=%s, param=*0x%x, funcFinish=*0x%x, userdata=*0x%x)", srcHddDir, srcHddFile, param, funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicExportProgress()
|
||||
error_code cellMusicExportProgress(vm::ptr<CellMusicExportUtilFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellMusicExport);
|
||||
cellMusicExport.todo("cellMusicExportProgress(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, 0xFFFF, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -125,20 +125,25 @@ error_code cellNetCtlGetInfo(s32 code, vm::ptr<CellNetCtlInfo> info)
|
||||
{
|
||||
cellNetCtl.todo("cellNetCtlGetInfo(code=0x%x (%s), info=*0x%x)", code, InfoCodeToName(code), info);
|
||||
|
||||
if (code == CELL_NET_CTL_INFO_ETHER_ADDR)
|
||||
{
|
||||
// dummy values set
|
||||
std::memset(info->ether_addr.data, 0xFF, sizeof(info->ether_addr.data));
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (g_cfg.net.net_status == CELL_NET_CTL_STATE_Disconnected)
|
||||
{
|
||||
return CELL_NET_CTL_ERROR_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
if (code == CELL_NET_CTL_INFO_MTU)
|
||||
{
|
||||
info->mtu = 1500;
|
||||
}
|
||||
else if (code == CELL_NET_CTL_INFO_LINK)
|
||||
{
|
||||
if (g_cfg.net.net_status != CELL_NET_CTL_STATE_Disconnected)
|
||||
{
|
||||
info->link = CELL_NET_CTL_LINK_CONNECTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->link = CELL_NET_CTL_LINK_DISCONNECTED;
|
||||
}
|
||||
info->link = CELL_NET_CTL_LINK_CONNECTED;
|
||||
}
|
||||
else if (code == CELL_NET_CTL_INFO_IP_ADDRESS)
|
||||
{
|
||||
@@ -266,35 +271,34 @@ error_code cellGameUpdateTerm()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGameUpdateCheckStartAsync(ppu_thread& ppu, vm::cptr<CellGameUpdateParam> param, vm::ptr<CellGameUpdateCallback> cb_func, vm::ptr<void> userdata)
|
||||
error_code cellGameUpdateCheckStartAsync(vm::cptr<CellGameUpdateParam> param, vm::ptr<CellGameUpdateCallback> cb_func, vm::ptr<void> userdata)
|
||||
{
|
||||
cellNetCtl.todo("cellGameUpdateCheckStartAsync(param=*0x%x, cb_func=*0x%x, userdata=*0x%x)", param, cb_func, userdata);
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
cb_func(ppu, CELL_OK, CELL_OK, userdata);
|
||||
cb_func(ppu, CELL_GAMEUPDATE_RESULT_STATUS_NO_UPDATE, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGameUpdateCheckFinishAsync(ppu_thread& ppu, vm::ptr<CellGameUpdateCallback> cb_func, vm::ptr<void> userdata)
|
||||
error_code cellGameUpdateCheckFinishAsync(vm::ptr<CellGameUpdateCallback> cb_func, vm::ptr<void> userdata)
|
||||
{
|
||||
cellNetCtl.todo("cellGameUpdateCheckFinishAsync(cb_func=*0x%x, userdata=*0x%x)", cb_func, userdata);
|
||||
const u32 PROCESSING_COMPLETE = 5;
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
cb_func(ppu, PROCESSING_COMPLETE, CELL_OK, userdata);
|
||||
cb_func(ppu, CELL_GAMEUPDATE_RESULT_STATUS_FINISHED, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGameUpdateCheckStartWithoutDialogAsync(ppu_thread& ppu, vm::ptr<CellGameUpdateCallback> cb_func, vm::ptr<void> userdata)
|
||||
error_code cellGameUpdateCheckStartWithoutDialogAsync(vm::ptr<CellGameUpdateCallback> cb_func, vm::ptr<void> userdata)
|
||||
{
|
||||
cellNetCtl.todo("cellGameUpdateCheckStartWithoutDialogAsync(cb_func=*0x%x, userdata=*0x%x)", cb_func, userdata);
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
cb_func(ppu, CELL_OK, CELL_OK, userdata);
|
||||
cb_func(ppu, CELL_GAMEUPDATE_RESULT_STATUS_NO_UPDATE, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
return CELL_OK;
|
||||
@@ -306,35 +310,35 @@ error_code cellGameUpdateCheckAbort()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGameUpdateCheckStartAsyncEx(ppu_thread& ppu, vm::cptr<CellGameUpdateParam> param, vm::ptr<CellGameUpdateCallbackEx> cb_func, vm::ptr<void> userdata)
|
||||
error_code cellGameUpdateCheckStartAsyncEx(vm::cptr<CellGameUpdateParam> param, vm::ptr<CellGameUpdateCallbackEx> cb_func, vm::ptr<void> userdata)
|
||||
{
|
||||
cellNetCtl.todo("cellGameUpdateCheckStartAsyncEx(param=*0x%x, cb_func=*0x%x, userdata=*0x%x)", param, cb_func, userdata);
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
cb_func(ppu, vm::make_var(CellGameUpdateResult{ CELL_OK, CELL_OK, 0x0, 0x0}), userdata);
|
||||
cb_func(ppu, vm::make_var(CellGameUpdateResult{ CELL_GAMEUPDATE_RESULT_STATUS_NO_UPDATE, CELL_OK, 0x0, 0x0}), userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGameUpdateCheckFinishAsyncEx(ppu_thread& ppu, vm::ptr<CellGameUpdateCallbackEx> cb_func, vm::ptr<void> userdata)
|
||||
error_code cellGameUpdateCheckFinishAsyncEx(vm::ptr<CellGameUpdateCallbackEx> cb_func, vm::ptr<void> userdata)
|
||||
{
|
||||
cellNetCtl.todo("cellGameUpdateCheckFinishAsyncEx(cb_func=*0x%x, userdata=*0x%x)", cb_func, userdata);
|
||||
const s32 PROCESSING_COMPLETE = 5;
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
cb_func(ppu, vm::make_var(CellGameUpdateResult{ PROCESSING_COMPLETE, CELL_OK, 0x0, 0x0}), userdata);
|
||||
cb_func(ppu, vm::make_var(CellGameUpdateResult{ CELL_GAMEUPDATE_RESULT_STATUS_FINISHED, CELL_OK, 0x0, 0x0}), userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellGameUpdateCheckStartWithoutDialogAsyncEx(ppu_thread& ppu, vm::ptr<CellGameUpdateCallbackEx> cb_func, vm::ptr<void> userdata)
|
||||
error_code cellGameUpdateCheckStartWithoutDialogAsyncEx(vm::ptr<CellGameUpdateCallbackEx> cb_func, vm::ptr<void> userdata)
|
||||
{
|
||||
cellNetCtl.todo("cellGameUpdateCheckStartWithoutDialogAsyncEx(cb_func=*0x%x, userdata=*0x%x)", cb_func, userdata);
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
cb_func(ppu, vm::make_var(CellGameUpdateResult{ CELL_OK, CELL_OK, 0x0, 0x0}), userdata);
|
||||
cb_func(ppu, vm::make_var(CellGameUpdateResult{ CELL_GAMEUPDATE_RESULT_STATUS_NO_UPDATE, CELL_OK, 0x0, 0x0}), userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
return CELL_OK;
|
||||
|
||||
@@ -303,6 +303,18 @@ static const char* InfoCodeToName(s32 code)
|
||||
}
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
CELL_GAMEUPDATE_RESULT_STATUS_NO_UPDATE = 0,
|
||||
CELL_GAMEUPDATE_RESULT_STATUS_UPDATE_FOUND = 1,
|
||||
CELL_GAMEUPDATE_RESULT_STATUS_MAINTENANCE = 2,
|
||||
CELL_GAMEUPDATE_RESULT_STATUS_ERROR = 3,
|
||||
CELL_GAMEUPDATE_RESULT_STATUS_CANCELLED = 4,
|
||||
CELL_GAMEUPDATE_RESULT_STATUS_FINISHED = 5,
|
||||
CELL_GAMEUPDATE_RESULT_STATUS_ABORTED = 6,
|
||||
CELL_GAMEUPDATE_RESULT_STATUS_SYSTEM_UPDATE_NEEDED = 7
|
||||
};
|
||||
|
||||
struct CellGameUpdateResult
|
||||
{
|
||||
be_t<s32> status; // CellGameUpdateResultStatus
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
|
||||
|
||||
@@ -16,42 +17,67 @@ enum
|
||||
CELL_PHOTO_DECODE_ERROR_DECODE = 0x8002c906,
|
||||
};
|
||||
|
||||
// Datatypes
|
||||
struct CellPhotoDecodeSetParam
|
||||
{
|
||||
u32 dstBuffer_addr;
|
||||
u16 width;
|
||||
u16 height;
|
||||
vm::bptr<void> dstBuffer;
|
||||
be_t<u16> width;
|
||||
be_t<u16> height;
|
||||
vm::bptr<void> reserved1;
|
||||
vm::bptr<void> reserved2;
|
||||
};
|
||||
|
||||
struct CellPhotoDecodeReturnParam
|
||||
{
|
||||
u16 width;
|
||||
u16 height;
|
||||
be_t<u16> width;
|
||||
be_t<u16> height;
|
||||
vm::bptr<void> reserved1;
|
||||
vm::bptr<void> reserved2;
|
||||
};
|
||||
|
||||
// Functions
|
||||
s32 cellPhotoDecodeInitialize()
|
||||
using CellPhotoDecodeFinishCallback = void(s32 result, vm::ptr<void> userdata);
|
||||
|
||||
error_code cellPhotoDecodeInitialize(u32 version, u32 container1, u32 container2, vm::ptr<CellPhotoDecodeFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPhotoDecode);
|
||||
cellPhotoDecode.todo("cellPhotoDecodeInitialize(version=0x%x, container1=0x%x, container2=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, container1, container2, funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPhotoDecodeInitialize2()
|
||||
error_code cellPhotoDecodeInitialize2(u32 version, u32 container2, vm::ptr<CellPhotoDecodeFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPhotoDecode);
|
||||
cellPhotoDecode.todo("cellPhotoDecodeInitialize2(version=0x%x, container2=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, container2, funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPhotoDecodeFinalize()
|
||||
error_code cellPhotoDecodeFinalize(vm::ptr<CellPhotoDecodeFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPhotoDecode);
|
||||
cellPhotoDecode.todo("cellPhotoDecodeFinalize(funcFinish=*0x%x, userdata=*0x%x)", funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPhotoDecodeFromFile()
|
||||
error_code cellPhotoDecodeFromFile(vm::cptr<char> srcHddDir, vm::cptr<char> srcHddFile, vm::ptr<CellPhotoDecodeSetParam> set_param, vm::ptr<CellPhotoDecodeReturnParam> return_param)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPhotoDecode);
|
||||
cellPhotoDecode.todo("cellPhotoDecodeFromFile(srcHddDir=%s, srcHddFile=%s, set_param=*0x%x, return_param=*0x%x)", srcHddDir, srcHddFile, set_param, return_param);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/lv2/sys_fs.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
|
||||
|
||||
@@ -16,39 +18,88 @@ enum
|
||||
CELL_PHOTO_IMPORT_ERROR_INITIALIZE = 0x8002c706,
|
||||
};
|
||||
|
||||
// Datatypes
|
||||
struct CellPhotoImportFileDataSub
|
||||
enum
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
//CellPhotoImportFormatType format;
|
||||
//CellPhotoImportTexRot rotate;
|
||||
CELL_PHOTO_IMPORT_HDD_PATH_MAX = 1055,
|
||||
CELL_PHOTO_IMPORT_PHOTO_TITLE_MAX_LENGTH = 64,
|
||||
CELL_PHOTO_IMPORT_GAME_TITLE_MAX_SIZE = 128,
|
||||
CELL_PHOTO_IMPORT_GAME_COMMENT_MAX_SIZE = 1024
|
||||
};
|
||||
|
||||
struct CellPhotoImportFileData
|
||||
enum CellPhotoImportFormatType
|
||||
{
|
||||
char dstFileName; //[CELL_FS_MAX_FS_FILE_NAME_LENGTH];
|
||||
char photo_title; //[CELL_PHOTO_IMPORT_PHOTO_TITLE_MAX_LENGTH*3];
|
||||
char game_title; //[CELL_PHOTO_IMPORT_GAME_TITLE_MAX_SIZE];
|
||||
char game_comment; //[CELL_PHOTO_IMPORT_GAME_COMMENT_MAX_SIZE];
|
||||
CellPhotoImportFileDataSub* data_sub;
|
||||
CELL_PHOTO_IMPORT_FT_UNKNOWN = 0,
|
||||
CELL_PHOTO_IMPORT_FT_JPEG,
|
||||
CELL_PHOTO_IMPORT_FT_PNG,
|
||||
CELL_PHOTO_IMPORT_FT_GIF,
|
||||
CELL_PHOTO_IMPORT_FT_BMP,
|
||||
CELL_PHOTO_IMPORT_FT_TIFF,
|
||||
CELL_PHOTO_IMPORT_FT_MPO,
|
||||
};
|
||||
|
||||
enum CellPhotoImportTexRot
|
||||
{
|
||||
CELL_PHOTO_IMPORT_TEX_ROT_0 = 0,
|
||||
CELL_PHOTO_IMPORT_TEX_ROT_90,
|
||||
CELL_PHOTO_IMPORT_TEX_ROT_180,
|
||||
CELL_PHOTO_IMPORT_TEX_ROT_270,
|
||||
};
|
||||
|
||||
struct CellPhotoImportFileDataSub
|
||||
{
|
||||
be_t<s32> width;
|
||||
be_t<s32> height;
|
||||
be_t<CellPhotoImportFormatType> format;
|
||||
be_t<CellPhotoImportTexRot> rotate;
|
||||
};
|
||||
|
||||
struct CellPhotoImportFileData
|
||||
{
|
||||
char dstFileName[CELL_FS_MAX_FS_FILE_NAME_LENGTH];
|
||||
char photo_title[CELL_PHOTO_IMPORT_PHOTO_TITLE_MAX_LENGTH * 3];
|
||||
char game_title[CELL_PHOTO_IMPORT_GAME_TITLE_MAX_SIZE];
|
||||
char game_comment[CELL_PHOTO_IMPORT_GAME_COMMENT_MAX_SIZE];
|
||||
char padding;
|
||||
vm::bptr<CellPhotoImportFileDataSub> data_sub;
|
||||
vm::bptr<void> reserved;
|
||||
};
|
||||
|
||||
struct CellPhotoImportSetParam
|
||||
{
|
||||
unsigned int fileSizeMax;
|
||||
{
|
||||
be_t<u32> fileSizeMax;
|
||||
vm::bptr<void> reserved1;
|
||||
vm::bptr<void> reserved2;
|
||||
};
|
||||
|
||||
// Functions
|
||||
s32 cellPhotoImport()
|
||||
using CellPhotoImportFinishCallback = void(s32 result, vm::ptr<CellPhotoImportFileData> filedata, vm::ptr<void> userdata);
|
||||
|
||||
error_code cellPhotoImport(u32 version, vm::cptr<char> dstHddPath, vm::ptr<CellPhotoImportSetParam> param, u32 container, vm::ptr<CellPhotoImportFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPhotoImportUtil);
|
||||
cellPhotoImportUtil.todo("cellPhotoImport(version=0x%x, dstHddPath=%s, param=*0x%x, container=0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, dstHddPath, param, container, funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
vm::var<CellPhotoImportFileData> filedata;
|
||||
filedata->data_sub = vm::var<CellPhotoImportFileDataSub>();
|
||||
funcFinish(ppu, CELL_OK, filedata, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPhotoImport2()
|
||||
error_code cellPhotoImport2(u32 version, vm::cptr<char> dstHddPath, vm::ptr<CellPhotoImportSetParam> param, vm::ptr<CellPhotoImportFinishCallback> funcFinish, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPhotoImportUtil);
|
||||
cellPhotoImportUtil.todo("cellPhotoImport2(version=0x%x, dstHddPath=%s, param=*0x%x, funcFinish=*0x%x, userdata=*0x%x)", version, dstHddPath, param, funcFinish, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
vm::var<CellPhotoImportFileData> filedata;
|
||||
filedata->data_sub = vm::var<CellPhotoImportFileDataSub>();
|
||||
funcFinish(ppu, CELL_OK, filedata, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
logs::channel cellPrint("cellPrint");
|
||||
|
||||
@@ -16,87 +19,131 @@ enum
|
||||
CELL_PRINT_ERROR_CANCELED_BY_PRINTER = 0x8002c408,
|
||||
};
|
||||
|
||||
s32 cellSysutilPrintInit()
|
||||
struct CellPrintLoadParam
|
||||
{
|
||||
be_t<u32> mode;
|
||||
u8 reserved[32];
|
||||
};
|
||||
|
||||
struct CellPrintStatus
|
||||
{
|
||||
be_t<s32> status;
|
||||
be_t<s32> errorStatus;
|
||||
be_t<s32> continueEnabled;
|
||||
u8 reserved[32];
|
||||
};
|
||||
|
||||
using CellPrintCallback = void(s32 result, vm::ptr<void> userdata);
|
||||
|
||||
error_code cellSysutilPrintInit()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellSysutilPrintShutdown()
|
||||
error_code cellSysutilPrintShutdown()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintLoadAsync()
|
||||
error_code cellPrintLoadAsync(vm::ptr<CellPrintCallback> function, vm::ptr<void> userdata, vm::cptr<CellPrintLoadParam> param, u32 container)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintLoadAsync(function=*0x%x, userdata=*0x%x, param=*0x%x, container=0x%x)", function, userdata, param, container);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
function(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintLoadAsync2()
|
||||
error_code cellPrintLoadAsync2(vm::ptr<CellPrintCallback> function, vm::ptr<void> userdata, vm::cptr<CellPrintLoadParam> param)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintLoadAsync2(function=*0x%x, userdata=*0x%x, param=*0x%x)", function, userdata, param);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
function(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintUnloadAsync()
|
||||
error_code cellPrintUnloadAsync(vm::ptr<CellPrintCallback> function, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintUnloadAsync(function=*0x%x, userdata=*0x%x)", function, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
function(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintGetStatus()
|
||||
error_code cellPrintGetStatus(vm::ptr<CellPrintStatus> status)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintGetStatus(status=*0x%x)", status);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintOpenConfig()
|
||||
error_code cellPrintOpenConfig(vm::ptr<CellPrintCallback> function, vm::ptr<void> userdata)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintOpenConfig(function=*0x%x, userdata=*0x%x)", function, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
function(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintGetPrintableArea()
|
||||
error_code cellPrintGetPrintableArea(vm::ptr<s32> pixelWidth, vm::ptr<s32> pixelHeight)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintGetPrintableArea(pixelWidth=*0x%x, pixelHeight=*0x%x)", pixelWidth, pixelHeight);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintStartJob()
|
||||
error_code cellPrintStartJob(s32 totalPage, s32 colorFormat)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintStartJob(totalPage=0x%x, colorFormat=0x%x)", totalPage, colorFormat);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintEndJob()
|
||||
error_code cellPrintEndJob()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintEndJob()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintCancelJob()
|
||||
error_code cellPrintCancelJob()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintCancelJob()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintStartPage()
|
||||
error_code cellPrintStartPage()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintStartPage()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintEndPage()
|
||||
error_code cellPrintEndPage()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintEndPage()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPrintSendBand()
|
||||
error_code cellPrintSendBand(vm::cptr<u8> buff, s32 buffsize, vm::ptr<s32> sendsize)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellPrint);
|
||||
cellPrint.todo("cellPrintSendBand(buff=*0x%x, buffsize=0x%x, sendsize=*0x%x)", buff, buffsize, sendsize);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ u32 cellSpursModulePollStatus(SPUThread& spu, u32* status)
|
||||
void cellSpursModuleExit(SPUThread& spu)
|
||||
{
|
||||
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
|
||||
spu.pc = ctxt->exitToKernelAddr - 4;
|
||||
spu.pc = ctxt->exitToKernelAddr;
|
||||
throw SpursModuleExit();
|
||||
}
|
||||
|
||||
@@ -659,7 +659,7 @@ void spursKernelDispatchWorkload(SPUThread& spu, u64 widAndPollStatus)
|
||||
spu.gpr[3]._u32[3] = 0x100;
|
||||
spu.gpr[4]._u64[1] = wklInfo->arg;
|
||||
spu.gpr[5]._u32[3] = pollStatus;
|
||||
spu.pc = 0xA00 - 4;
|
||||
spu.pc = 0xA00;
|
||||
}
|
||||
|
||||
// SPURS kernel workload exit
|
||||
@@ -1404,7 +1404,7 @@ void spursTasksetResumeTask(SPUThread& spu)
|
||||
spu.gpr[80 + i] = ctxt->savedContextR80ToR127[i];
|
||||
}
|
||||
|
||||
spu.pc = spu.gpr[0]._u32[3] - 4;
|
||||
spu.pc = spu.gpr[0]._u32[3];
|
||||
}
|
||||
|
||||
// Start a task
|
||||
@@ -1422,7 +1422,7 @@ void spursTasksetStartTask(SPUThread& spu, CellSpursTaskArgument& taskArgs)
|
||||
spu.gpr[i].clear();
|
||||
}
|
||||
|
||||
spu.pc = ctxt->savedContextLr.value()._u32[3] - 4;
|
||||
spu.pc = ctxt->savedContextLr.value()._u32[3];
|
||||
}
|
||||
|
||||
// Process a request and update the state of the taskset
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "cellSysutil.h"
|
||||
|
||||
|
||||
extern logs::channel cellSysutil;
|
||||
@@ -44,18 +44,39 @@ void fmt_class_string<CellStorageError>::format(std::string& out, u64 arg)
|
||||
error_code cellStorageDataImportMove(u32 version, vm::ptr<char> srcMediaFile, vm::ptr<char> dstHddDir, vm::ptr<CellStorageDataSetParam> param, vm::ptr<CellStorageDataFinishCallback> funcFinish, u32 container, vm::ptr<void> userdata)
|
||||
{
|
||||
cellSysutil.todo("cellStorageDataImportMove(version=0x%x, srcMediaFile=%s, dstHddDir=%s, param=*0x%x, funcFinish=*0x%x, container=0x%x, userdata=*0x%x)", version, srcMediaFile, dstHddDir, param, funcFinish, container, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellStorageDataImport(u32 version, vm::ptr<char> srcMediaFile, vm::ptr<char> dstHddDir, vm::ptr<CellStorageDataSetParam> param, vm::ptr<CellStorageDataFinishCallback> funcFinish, u32 container, vm::ptr<void> userdata)
|
||||
{
|
||||
cellSysutil.todo("cellStorageDataImport(version=0x%x, srcMediaFile=%s, dstHddDir=%s, param=*0x%x, funcFinish=*0x%x, container=0x%x, userdata=*0x%x)", version, srcMediaFile, dstHddDir, param, funcFinish, container, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellStorageDataExport(u32 version, vm::ptr<char> srcHddFile, vm::ptr<char> dstMediaDir, vm::ptr<CellStorageDataSetParam> param, vm::ptr<CellStorageDataFinishCallback> funcFinish, u32 container, vm::ptr<void> userdata)
|
||||
{
|
||||
cellSysutil.todo("cellStorageDataExport(version=0x%x, srcHddFile=%s, dstMediaDir=%s, param=*0x%x, funcFinish=*0x%x, container=0x%x, userdata=*0x%x)", version, srcHddFile, dstMediaDir, param, funcFinish, container, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
funcFinish(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
#include "cellSysconf.h"
|
||||
|
||||
@@ -14,6 +15,13 @@ s32 cellSysconfAbort()
|
||||
s32 cellSysconfOpen(u32 type, vm::ptr<CellSysconfCallback> func, vm::ptr<void> userdata, vm::ptr<void> extparam, u32 id)
|
||||
{
|
||||
cellSysconf.todo("cellSysconfOpen(type=%d, func=*0x%x, userdata=*0x%x, extparam=*0x%x, id=%d)", type, func, userdata, extparam, id);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
func(ppu, CELL_OK, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
|
||||
#include "cellSysutil.h"
|
||||
|
||||
@@ -46,6 +47,22 @@ extern void sysutil_register_cb(std::function<s32(ppu_thread&)>&& cb)
|
||||
cbm->registered.push(std::move(cb));
|
||||
}
|
||||
|
||||
extern s32 sysutil_register_cb_wait(ppu_thread& caller, std::function<s32(ppu_thread&)>&& cb) {
|
||||
atomic_t<bool> wait{true};
|
||||
u32 rtn = -1;
|
||||
|
||||
sysutil_register_cb([&](ppu_thread& ppu) -> s32 {
|
||||
rtn = cb(ppu);
|
||||
wait = false;
|
||||
caller.notify();
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
lv2_obj::sleep(caller);
|
||||
while (wait && !Emu.IsStopped()) thread_ctrl::wait();
|
||||
return rtn;
|
||||
}
|
||||
|
||||
extern void sysutil_send_system_cmd(u64 status, u64 param)
|
||||
{
|
||||
if (const auto cbm = fxm::get<sysutil_cb_manager>())
|
||||
@@ -198,6 +215,10 @@ s32 cellSysutilGetSystemParamInt(CellSysutilParamId id, vm::ptr<s32> value)
|
||||
*value = 0;
|
||||
break;
|
||||
|
||||
case CELL_SYSUTIL_SYSTEMPARAM_ID_MAGNETOMETER:
|
||||
*value = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
@@ -301,7 +322,7 @@ s32 cellSysCacheMount(vm::ptr<CellSysCacheParam> param)
|
||||
|
||||
const std::string& cache_id = param->cacheId;
|
||||
verify(HERE), cache_id.size() < sizeof(param->cacheId);
|
||||
|
||||
|
||||
const std::string& cache_path = "/dev_hdd1/cache/" + cache_id + '/';
|
||||
strcpy_trunc(param->getCachePath, cache_path);
|
||||
|
||||
@@ -329,7 +350,7 @@ s32 cellSysutilEnableBgmPlaybackEx(vm::ptr<CellSysutilBgmPlaybackExtraParam> par
|
||||
cellSysutil.warning("cellSysutilEnableBgmPlaybackEx(param=*0x%x)", param);
|
||||
|
||||
// TODO
|
||||
g_bgm_playback_enabled = true;
|
||||
g_bgm_playback_enabled = true;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -723,12 +744,12 @@ DECLARE(ppu_module_manager::cellSysutil)("cellSysutil", []()
|
||||
REG_FUNC(cellSysutil, _ZN8cxmlutil16CheckElementNameERKN4cxml7ElementEPKc);
|
||||
REG_FUNC(cellSysutil, _ZN8cxmlutil16FindChildElementERKN4cxml7ElementEPKcS5_S5_);
|
||||
REG_FUNC(cellSysutil, _ZN8cxmlutil7GetFileERKN4cxml7ElementEPKcPNS0_4FileE);
|
||||
|
||||
|
||||
REG_FUNC(cellSysutil, _ZN16sysutil_cxmlutil11FixedMemory3EndEi);
|
||||
REG_FUNC(cellSysutil, _ZN16sysutil_cxmlutil11FixedMemory5BeginEi);
|
||||
REG_FUNC(cellSysutil, _ZN16sysutil_cxmlutil11FixedMemory8AllocateEN4cxml14AllocationTypeEPvS3_jPS3_Pj);
|
||||
REG_FUNC(cellSysutil, _ZN16sysutil_cxmlutil12PacketWriter5WriteEPKvjPv);
|
||||
REG_FUNC(cellSysutil, _ZN16sysutil_cxmlutil12PacketWriterC1EiiRN4cxml8DocumentE);
|
||||
|
||||
|
||||
REG_FNID(cellSysutil, 0xE1EC7B6A, cellSysutil_E1EC7B6A);
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ enum CellSysutilParamId: s32
|
||||
CELL_SYSUTIL_SYSTEMPARAM_ID_JAPANESE_KEYBOARD_ENTRY_METHOD = 0x0154,
|
||||
CELL_SYSUTIL_SYSTEMPARAM_ID_CHINESE_KEYBOARD_ENTRY_METHOD = 0x0155,
|
||||
CELL_SYSUTIL_SYSTEMPARAM_ID_PAD_AUTOOFF = 0x0156,
|
||||
CELL_SYSUTIL_SYSTEMPARAM_ID_MAGNETOMETER = 0x0157,
|
||||
|
||||
// Strings
|
||||
CELL_SYSUTIL_SYSTEMPARAM_ID_NICKNAME = 0x0113,
|
||||
@@ -198,3 +199,4 @@ struct CellSysCacheParam
|
||||
|
||||
extern void sysutil_register_cb(std::function<s32(ppu_thread&)>&&);
|
||||
extern void sysutil_send_system_cmd(u64 status, u64 param);
|
||||
extern s32 sysutil_register_cb_wait(ppu_thread&, std::function<s32(ppu_thread&)>&&);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "cellUserInfo.h"
|
||||
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
logs::channel cellUserInfo("cellUserInfo");
|
||||
|
||||
@@ -68,12 +69,28 @@ error_code cellUserInfoGetStat(u32 id, vm::ptr<CellUserInfoUserStat> stat)
|
||||
error_code cellUserInfoSelectUser_ListType(vm::ptr<CellUserInfoTypeSet> listType, vm::ptr<CellUserInfoFinishCallback> funcSelect, u32 container, vm::ptr<void> userdata)
|
||||
{
|
||||
cellUserInfo.todo("cellUserInfoSelectUser_ListType(listType=*0x%x, funcSelect=*0x%x, container=0x%x, userdata=*0x%x)", listType, funcSelect, container, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
vm::var<CellUserInfoUserStat> selectUser;
|
||||
funcSelect(ppu, CELL_OK, selectUser, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellUserInfoSelectUser_SetList(vm::ptr<CellUserInfoListSet> setList, vm::ptr<CellUserInfoFinishCallback> funcSelect, u32 container, vm::ptr<void> userdata)
|
||||
{
|
||||
cellUserInfo.todo("cellUserInfoSelectUser_SetList(setList=*0x%x, funcSelect=*0x%x, container=0x%x, userdata=*0x%x)", setList, funcSelect, container, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
{
|
||||
vm::var<CellUserInfoUserStat> selectUser;
|
||||
funcSelect(ppu, CELL_OK, selectUser, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,11 @@ s32 sceNpDrmVerifyUpgradeLicense(vm::cptr<char> content_id)
|
||||
{
|
||||
sceNp.warning("sceNpDrmVerifyUpgradeLicense(content_id=%s)", content_id);
|
||||
|
||||
if (!content_id)
|
||||
{
|
||||
return SCE_NP_DRM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (!fs::is_file(vfs::get("/dev_hdd0/home/00000001/exdata/") + content_id.get_ptr() + ".rap"))
|
||||
{
|
||||
// Game hasn't been purchased therefore no RAP file present
|
||||
@@ -154,6 +159,11 @@ s32 sceNpDrmVerifyUpgradeLicense2(vm::cptr<char> content_id)
|
||||
{
|
||||
sceNp.warning("sceNpDrmVerifyUpgradeLicense2(content_id=%s)", content_id);
|
||||
|
||||
if (!content_id)
|
||||
{
|
||||
return SCE_NP_DRM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (!fs::is_file(vfs::get("/dev_hdd0/home/00000001/exdata/") + content_id.get_ptr() + ".rap"))
|
||||
{
|
||||
// Game hasn't been purchased therefore no RAP file present
|
||||
|
||||
@@ -31,7 +31,7 @@ s32 sceNpMatching2Init(u32 poolsize, s32 priority)
|
||||
{
|
||||
sceNp2.todo("sceNpMatching2Init(poolsize=0x%x, priority=%d)", poolsize, priority);
|
||||
|
||||
return SCE_NP_MATCHING2_ERROR_TIMEDOUT;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 sceNpMatching2Init2(u32 poolsize, s32 priority, vm::ptr<SceNpMatching2UtilityInitParam> param)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/Modules/cellSysutil.h"
|
||||
|
||||
#include "restore_new.h"
|
||||
#include "Utilities/rXml.h"
|
||||
@@ -196,9 +197,9 @@ error_code sceNpTrophyCreateContext(vm::ptr<u32> context, vm::cptr<SceNpCommunic
|
||||
}
|
||||
|
||||
// open trophy pack file
|
||||
fs::file stream(vfs::get("/dev_hdd0/game/" + Emu.GetTitleID() + "/TROPDIR/" + name + "/TROPHY.TRP"));
|
||||
fs::file stream(vfs::get(Emu.GetDir() + "TROPDIR/" + name + "/TROPHY.TRP"));
|
||||
|
||||
if (!stream && (Emu.GetCat() == "DG" || Emu.GetCat() == "GD"))
|
||||
if (!stream && Emu.GetCat() == "GD")
|
||||
{
|
||||
stream.open(vfs::get("/dev_bdvd/PS3_GAME/TROPDIR/" + name + "/TROPHY.TRP"));
|
||||
}
|
||||
@@ -315,16 +316,23 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
|
||||
tropusr->Load(trophyUsrPath, trophyConfPath);
|
||||
ctxt->tropusr.reset(tropusr);
|
||||
|
||||
// TODO: Callbacks
|
||||
if (statusCb(ppu, context, SCE_NP_TROPHY_STATUS_INSTALLED, 100, 100, arg) < 0)
|
||||
{
|
||||
return SCE_NP_TROPHY_ERROR_PROCESSING_ABORTED;
|
||||
}
|
||||
s32 rtn = sysutil_register_cb_wait(ppu, [=](ppu_thread& ppu) -> s32 {
|
||||
return statusCb(ppu, context, SCE_NP_TROPHY_STATUS_INSTALLED, 100, 100, arg);
|
||||
});
|
||||
|
||||
if (statusCb(ppu, context, SCE_NP_TROPHY_STATUS_PROCESSING_COMPLETE, 100, 100, arg) < 0)
|
||||
{
|
||||
return SCE_NP_TROPHY_ERROR_PROCESSING_ABORTED;
|
||||
}
|
||||
if (rtn < 0)
|
||||
return SCE_NP_TROPHY_ERROR_PROCESSING_ABORTED;
|
||||
|
||||
rtn = sysutil_register_cb_wait(ppu, [=](ppu_thread& ppu) -> s32 {
|
||||
return statusCb(ppu, context, SCE_NP_TROPHY_STATUS_PROCESSING_COMPLETE, 100, 100, arg);
|
||||
});
|
||||
|
||||
if (rtn < 0)
|
||||
return SCE_NP_TROPHY_ERROR_PROCESSING_ABORTED;
|
||||
|
||||
rtn = sysutil_register_cb_wait(ppu, [=](ppu_thread& ppu) -> s32 {
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,13 @@
|
||||
#include "Emu/Cell/lv2/sys_interrupt.h"
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
#include "Emu/Cell/lv2/sys_ss.h"
|
||||
#include "Emu/Cell/lv2/sys_tty.h"
|
||||
#include "sysPrxForUser.h"
|
||||
|
||||
logs::channel sysPrxForUser("sysPrxForUser");
|
||||
|
||||
extern u64 get_system_time();
|
||||
|
||||
extern fs::file g_tty;
|
||||
|
||||
vm::gvar<s32> sys_prx_version; // ???
|
||||
vm::gvar<vm::ptr<void()>> g_ppu_atexitspawn;
|
||||
vm::gvar<vm::ptr<void()>> g_ppu_at_Exitspawn;
|
||||
@@ -97,20 +96,16 @@ s32 console_getc()
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
}
|
||||
|
||||
s32 console_putc()
|
||||
void console_putc(char ch)
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
sysPrxForUser.trace("console_putc(ch=0x%x)", ch);
|
||||
sys_tty_write(0, vm::var<char>(ch), 1, vm::var<u32>{});
|
||||
}
|
||||
|
||||
s32 console_write(vm::ptr<char> data, u32 len)
|
||||
error_code console_write(vm::ptr<char> data, u32 len)
|
||||
{
|
||||
sysPrxForUser.warning("console_write(data=*0x%x, len=%d)", data, len);
|
||||
|
||||
if (g_tty)
|
||||
{
|
||||
g_tty.write(data.get_ptr(), len);
|
||||
}
|
||||
|
||||
sysPrxForUser.trace("console_write(data=*0x%x, len=%d)", data, len);
|
||||
sys_tty_write(0, data, len, vm::var<u32>{});
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/lv2/sys_tty.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Utilities/cfmt.h"
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
|
||||
extern logs::channel sysPrxForUser;
|
||||
|
||||
extern fs::file g_tty;
|
||||
|
||||
// cfmt implementation (TODO)
|
||||
|
||||
using qsortcmp = s32(vm::cptr<void> e1, vm::cptr<void> e2);
|
||||
@@ -420,10 +417,9 @@ s32 _sys_printf(ppu_thread& ppu, vm::cptr<char> fmt, ppu_va_args_t va_args)
|
||||
{
|
||||
sysPrxForUser.warning("_sys_printf(fmt=%s, ...)", fmt);
|
||||
|
||||
if (g_tty)
|
||||
{
|
||||
g_tty.write(ps3_fmt(ppu, fmt, va_args.count));
|
||||
}
|
||||
const auto buf = vm::make_str(ps3_fmt(ppu, fmt, va_args.count));
|
||||
|
||||
sys_tty_write(0, buf, buf.get_count() - 1, vm::var<u32>{});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1436,7 +1436,7 @@ void ppu_module::analyse(u32 lib_toc, u32 entry)
|
||||
|
||||
void ppu_acontext::UNK(ppu_opcode_t op)
|
||||
{
|
||||
std::fill_n(gpr, 32, spec_gpr{});
|
||||
std::fill_n(gpr, 32, spec_gpr());
|
||||
LOG_ERROR(PPU, "Unknown/Illegal opcode: 0x%08x at 0x%x" HERE, op.opcode, cia);
|
||||
}
|
||||
|
||||
@@ -2563,7 +2563,7 @@ void ppu_acontext::MULHW(ppu_opcode_t op)
|
||||
|
||||
void ppu_acontext::LDARX(ppu_opcode_t op)
|
||||
{
|
||||
gpr[op.rd] = {};
|
||||
gpr[op.rd] = spec_gpr();
|
||||
}
|
||||
|
||||
void ppu_acontext::DCBF(ppu_opcode_t op)
|
||||
@@ -2879,7 +2879,7 @@ void ppu_acontext::LVRX(ppu_opcode_t op)
|
||||
|
||||
void ppu_acontext::LSWI(ppu_opcode_t op)
|
||||
{
|
||||
std::fill_n(gpr, 32, spec_gpr{});
|
||||
std::fill_n(gpr, 32, spec_gpr());
|
||||
}
|
||||
|
||||
void ppu_acontext::LFSUX(ppu_opcode_t op)
|
||||
@@ -3106,7 +3106,7 @@ void ppu_acontext::STHU(ppu_opcode_t op)
|
||||
|
||||
void ppu_acontext::LMW(ppu_opcode_t op)
|
||||
{
|
||||
std::fill_n(gpr, 32, spec_gpr{});
|
||||
std::fill_n(gpr, 32, spec_gpr());
|
||||
}
|
||||
|
||||
void ppu_acontext::STMW(ppu_opcode_t op)
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include "llvm/Transforms/IPO.h"
|
||||
#include "llvm/Transforms/Vectorize.h"
|
||||
|
||||
#ifdef POLLY_AVAILABLE
|
||||
#include <polly/RegisterPasses.h>
|
||||
#include <polly/ScopDetection.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
@@ -296,7 +302,7 @@ static bool ppu_break(ppu_thread& ppu, ppu_opcode_t op)
|
||||
// Pause and wait if necessary
|
||||
bool status = ppu.state.test_and_set(cpu_flag::dbg_pause);
|
||||
#ifdef WITH_GDB_DEBUGGER
|
||||
fxm::get<GDBDebugServer>()->notify();
|
||||
fxm::get<GDBDebugServer>()->pause_from(&ppu);
|
||||
#endif
|
||||
if (!status && ppu.check_state())
|
||||
{
|
||||
@@ -575,6 +581,11 @@ void ppu_thread::cpu_task()
|
||||
cmd_pop(), lv2_obj::sleep(*this);
|
||||
break;
|
||||
}
|
||||
case ppu_cmd::reset_stack:
|
||||
{
|
||||
cmd_pop(), gpr[1] = ::align(stack_addr + stack_size, 0x200) - 0x200;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fmt::throw_exception("Unknown ppu_cmd(0x%x)" HERE, (u32)type);
|
||||
@@ -1464,6 +1475,11 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef POLLY_AVAILABLE
|
||||
PassRegistry &Registry = *PassRegistry::getPassRegistry();
|
||||
polly::initializePollyPasses(Registry);
|
||||
initializeAnalysis(Registry);
|
||||
#endif
|
||||
std::shared_ptr<MsgDialogBase> dlg;
|
||||
|
||||
{
|
||||
@@ -1481,6 +1497,9 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co
|
||||
//pm.add(createLoopInstSimplifyPass());
|
||||
//pm.add(createNewGVNPass());
|
||||
pm.add(createDeadStoreEliminationPass());
|
||||
#ifdef POLLY_AVAILABLE
|
||||
polly::registerPollyPasses(pm);
|
||||
#endif
|
||||
//pm.add(createSCCPPass());
|
||||
//pm.add(createReassociatePass());
|
||||
//pm.add(createInstructionCombiningPass());
|
||||
|
||||
@@ -16,6 +16,7 @@ enum class ppu_cmd : u32
|
||||
hle_call, // Execute function by index (arg)
|
||||
initialize, // ppu_initialize()
|
||||
sleep,
|
||||
reset_stack, // resets stack address
|
||||
};
|
||||
|
||||
// Formatting helper
|
||||
|
||||
@@ -14,7 +14,7 @@ const ppu_decoder<PPUTranslator> s_ppu_decoder;
|
||||
PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, const ppu_module& info)
|
||||
: cpu_translator(context, module, 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;
|
||||
|
||||
@@ -15,7 +15,7 @@ class PPUTranslator final : public cpu_translator
|
||||
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;
|
||||
|
||||
// LLVM function
|
||||
llvm::Function* m_function;
|
||||
@@ -297,7 +297,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...});
|
||||
@@ -307,7 +307,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
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Utilities/sysinfo.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
|
||||
#define ASMJIT_STATIC
|
||||
#define ASMJIT_DEBUG
|
||||
@@ -340,18 +341,13 @@ void spu_recompiler::InterpreterCall(spu_opcode_t op)
|
||||
{
|
||||
// TODO: check correctness
|
||||
|
||||
const u32 old_pc = _spu->pc;
|
||||
|
||||
if (test(_spu->state) && _spu->check_state())
|
||||
{
|
||||
return 0x2000000 | _spu->pc;
|
||||
}
|
||||
|
||||
_func(*_spu, { opcode });
|
||||
|
||||
if (old_pc != _spu->pc)
|
||||
if (UNLIKELY(!_func(*_spu, {opcode})))
|
||||
{
|
||||
_spu->pc += 4;
|
||||
return 0x2000000 | _spu->pc;
|
||||
}
|
||||
|
||||
@@ -2874,7 +2870,7 @@ void spu_recompiler::BRZ(spu_opcode_t op)
|
||||
{
|
||||
const u32 target = spu_branch_target(m_pos, op.i16);
|
||||
|
||||
if (target == m_pos) fmt::throw_exception("Branch-to-self (0x%05x)" HERE, target);
|
||||
if (target == m_pos) std::this_thread::yield();
|
||||
|
||||
c->cmp(SPU_OFF_32(gpr, op.rt, &v128::_u32, 3), 0);
|
||||
|
||||
@@ -2920,7 +2916,7 @@ void spu_recompiler::BRNZ(spu_opcode_t op)
|
||||
{
|
||||
const u32 target = spu_branch_target(m_pos, op.i16);
|
||||
|
||||
if (target == m_pos) fmt::throw_exception("Branch-to-self (0x%05x)" HERE, target);
|
||||
if (target == m_pos) std::this_thread::yield();
|
||||
|
||||
c->cmp(SPU_OFF_32(gpr, op.rt, &v128::_u32, 3), 0);
|
||||
|
||||
@@ -2945,7 +2941,7 @@ void spu_recompiler::BRHZ(spu_opcode_t op)
|
||||
{
|
||||
const u32 target = spu_branch_target(m_pos, op.i16);
|
||||
|
||||
if (target == m_pos) fmt::throw_exception("Branch-to-self (0x%05x)" HERE, target);
|
||||
if (target == m_pos) std::this_thread::yield();
|
||||
|
||||
c->cmp(SPU_OFF_16(gpr, op.rt, &v128::_u16, 6), 0);
|
||||
|
||||
@@ -2970,7 +2966,7 @@ void spu_recompiler::BRHNZ(spu_opcode_t op)
|
||||
{
|
||||
const u32 target = spu_branch_target(m_pos, op.i16);
|
||||
|
||||
if (target == m_pos) fmt::throw_exception("Branch-to-self (0x%05x)" HERE, target);
|
||||
if (target == m_pos) std::this_thread::yield();
|
||||
|
||||
c->cmp(SPU_OFF_16(gpr, op.rt, &v128::_u16, 6), 0);
|
||||
|
||||
@@ -3016,7 +3012,7 @@ void spu_recompiler::BRA(spu_opcode_t op)
|
||||
{
|
||||
const u32 target = spu_branch_target(0, op.i16);
|
||||
|
||||
if (target == m_pos) fmt::throw_exception("Branch-to-self (0x%05x)" HERE, target);
|
||||
if (target == m_pos) std::this_thread::yield();
|
||||
|
||||
if (labels[target / 4].isValid())
|
||||
{
|
||||
@@ -3061,7 +3057,7 @@ void spu_recompiler::BRASL(spu_opcode_t op)
|
||||
{
|
||||
const u32 target = spu_branch_target(0, op.i16);
|
||||
|
||||
if (target == m_pos) fmt::throw_exception("Branch-to-self (0x%05x)" HERE, target);
|
||||
if (target == m_pos) std::this_thread::yield();
|
||||
|
||||
const XmmLink& vr = XmmAlloc();
|
||||
c->movdqa(vr, XmmConst(_mm_set_epi32(spu_branch_target(m_pos + 4), 0, 0, 0)));
|
||||
@@ -3116,7 +3112,7 @@ void spu_recompiler::BRSL(spu_opcode_t op)
|
||||
{
|
||||
const u32 target = spu_branch_target(m_pos, op.i16);
|
||||
|
||||
if (target == m_pos) fmt::throw_exception("Branch-to-self (0x%05x)" HERE, target);
|
||||
if (target == m_pos) std::this_thread::yield();
|
||||
|
||||
const XmmLink& vr = XmmAlloc();
|
||||
c->movdqa(vr, XmmConst(_mm_set_epi32(spu_branch_target(m_pos + 4), 0, 0, 0)));
|
||||
|
||||
+511
-285
File diff suppressed because it is too large
Load Diff
+239
-239
@@ -4,258 +4,258 @@
|
||||
|
||||
class SPUThread;
|
||||
|
||||
using spu_inter_func_t = void(*)(SPUThread& spu, spu_opcode_t op);
|
||||
using spu_inter_func_t = bool(*)(SPUThread& spu, spu_opcode_t op);
|
||||
|
||||
struct spu_interpreter
|
||||
{
|
||||
static void UNK(SPUThread&, spu_opcode_t);
|
||||
static bool UNK(SPUThread&, spu_opcode_t);
|
||||
static void set_interrupt_status(SPUThread&, spu_opcode_t);
|
||||
|
||||
static void STOP(SPUThread&, spu_opcode_t);
|
||||
static void LNOP(SPUThread&, spu_opcode_t);
|
||||
static void SYNC(SPUThread&, spu_opcode_t);
|
||||
static void DSYNC(SPUThread&, spu_opcode_t);
|
||||
static void MFSPR(SPUThread&, spu_opcode_t);
|
||||
static void RDCH(SPUThread&, spu_opcode_t);
|
||||
static void RCHCNT(SPUThread&, spu_opcode_t);
|
||||
static void SF(SPUThread&, spu_opcode_t);
|
||||
static void OR(SPUThread&, spu_opcode_t);
|
||||
static void BG(SPUThread&, spu_opcode_t);
|
||||
static void SFH(SPUThread&, spu_opcode_t);
|
||||
static void NOR(SPUThread&, spu_opcode_t);
|
||||
static void ABSDB(SPUThread&, spu_opcode_t);
|
||||
static void ROT(SPUThread&, spu_opcode_t);
|
||||
static void ROTM(SPUThread&, spu_opcode_t);
|
||||
static void ROTMA(SPUThread&, spu_opcode_t);
|
||||
static void SHL(SPUThread&, spu_opcode_t);
|
||||
static void ROTH(SPUThread&, spu_opcode_t);
|
||||
static void ROTHM(SPUThread&, spu_opcode_t);
|
||||
static void ROTMAH(SPUThread&, spu_opcode_t);
|
||||
static void SHLH(SPUThread&, spu_opcode_t);
|
||||
static void ROTI(SPUThread&, spu_opcode_t);
|
||||
static void ROTMI(SPUThread&, spu_opcode_t);
|
||||
static void ROTMAI(SPUThread&, spu_opcode_t);
|
||||
static void SHLI(SPUThread&, spu_opcode_t);
|
||||
static void ROTHI(SPUThread&, spu_opcode_t);
|
||||
static void ROTHMI(SPUThread&, spu_opcode_t);
|
||||
static void ROTMAHI(SPUThread&, spu_opcode_t);
|
||||
static void SHLHI(SPUThread&, spu_opcode_t);
|
||||
static void A(SPUThread&, spu_opcode_t);
|
||||
static void AND(SPUThread&, spu_opcode_t);
|
||||
static void CG(SPUThread&, spu_opcode_t);
|
||||
static void AH(SPUThread&, spu_opcode_t);
|
||||
static void NAND(SPUThread&, spu_opcode_t);
|
||||
static void AVGB(SPUThread&, spu_opcode_t);
|
||||
static void MTSPR(SPUThread&, spu_opcode_t);
|
||||
static void WRCH(SPUThread&, spu_opcode_t);
|
||||
static void BIZ(SPUThread&, spu_opcode_t);
|
||||
static void BINZ(SPUThread&, spu_opcode_t);
|
||||
static void BIHZ(SPUThread&, spu_opcode_t);
|
||||
static void BIHNZ(SPUThread&, spu_opcode_t);
|
||||
static void STOPD(SPUThread&, spu_opcode_t);
|
||||
static void STQX(SPUThread&, spu_opcode_t);
|
||||
static void BI(SPUThread&, spu_opcode_t);
|
||||
static void BISL(SPUThread&, spu_opcode_t);
|
||||
static void IRET(SPUThread&, spu_opcode_t);
|
||||
static void BISLED(SPUThread&, spu_opcode_t);
|
||||
static void HBR(SPUThread&, spu_opcode_t);
|
||||
static void GB(SPUThread&, spu_opcode_t);
|
||||
static void GBH(SPUThread&, spu_opcode_t);
|
||||
static void GBB(SPUThread&, spu_opcode_t);
|
||||
static void FSM(SPUThread&, spu_opcode_t);
|
||||
static void FSMH(SPUThread&, spu_opcode_t);
|
||||
static void FSMB(SPUThread&, spu_opcode_t);
|
||||
static void LQX(SPUThread&, spu_opcode_t);
|
||||
static void CBX(SPUThread&, spu_opcode_t);
|
||||
static void CHX(SPUThread&, spu_opcode_t);
|
||||
static void CWX(SPUThread&, spu_opcode_t);
|
||||
static void CDX(SPUThread&, spu_opcode_t);
|
||||
static void ROTQBI(SPUThread&, spu_opcode_t);
|
||||
static void ROTQMBI(SPUThread&, spu_opcode_t);
|
||||
static void SHLQBI(SPUThread&, spu_opcode_t);
|
||||
static void ORX(SPUThread&, spu_opcode_t);
|
||||
static void CBD(SPUThread&, spu_opcode_t);
|
||||
static void CHD(SPUThread&, spu_opcode_t);
|
||||
static void CWD(SPUThread&, spu_opcode_t);
|
||||
static void CDD(SPUThread&, spu_opcode_t);
|
||||
static void ROTQBII(SPUThread&, spu_opcode_t);
|
||||
static void ROTQMBII(SPUThread&, spu_opcode_t);
|
||||
static void SHLQBII(SPUThread&, spu_opcode_t);
|
||||
static void NOP(SPUThread&, spu_opcode_t);
|
||||
static void CGT(SPUThread&, spu_opcode_t);
|
||||
static void XOR(SPUThread&, spu_opcode_t);
|
||||
static void CGTH(SPUThread&, spu_opcode_t);
|
||||
static void EQV(SPUThread&, spu_opcode_t);
|
||||
static void CGTB(SPUThread&, spu_opcode_t);
|
||||
static void SUMB(SPUThread&, spu_opcode_t);
|
||||
static void HGT(SPUThread&, spu_opcode_t);
|
||||
static void CLZ(SPUThread&, spu_opcode_t);
|
||||
static void XSWD(SPUThread&, spu_opcode_t);
|
||||
static void XSHW(SPUThread&, spu_opcode_t);
|
||||
static void CNTB(SPUThread&, spu_opcode_t);
|
||||
static void XSBH(SPUThread&, spu_opcode_t);
|
||||
static void CLGT(SPUThread&, spu_opcode_t);
|
||||
static void ANDC(SPUThread&, spu_opcode_t);
|
||||
static void CLGTH(SPUThread&, spu_opcode_t);
|
||||
static void ORC(SPUThread&, spu_opcode_t);
|
||||
static void CLGTB(SPUThread&, spu_opcode_t);
|
||||
static void HLGT(SPUThread&, spu_opcode_t);
|
||||
static void CEQ(SPUThread&, spu_opcode_t);
|
||||
static void MPYHHU(SPUThread&, spu_opcode_t);
|
||||
static void ADDX(SPUThread&, spu_opcode_t);
|
||||
static void SFX(SPUThread&, spu_opcode_t);
|
||||
static void CGX(SPUThread&, spu_opcode_t);
|
||||
static void BGX(SPUThread&, spu_opcode_t);
|
||||
static void MPYHHA(SPUThread&, spu_opcode_t);
|
||||
static void MPYHHAU(SPUThread&, spu_opcode_t);
|
||||
static void MPY(SPUThread&, spu_opcode_t);
|
||||
static void MPYH(SPUThread&, spu_opcode_t);
|
||||
static void MPYHH(SPUThread&, spu_opcode_t);
|
||||
static void MPYS(SPUThread&, spu_opcode_t);
|
||||
static void CEQH(SPUThread&, spu_opcode_t);
|
||||
static void MPYU(SPUThread&, spu_opcode_t);
|
||||
static void CEQB(SPUThread&, spu_opcode_t);
|
||||
static void HEQ(SPUThread&, spu_opcode_t);
|
||||
static void BRZ(SPUThread&, spu_opcode_t);
|
||||
static void STQA(SPUThread&, spu_opcode_t);
|
||||
static void BRNZ(SPUThread&, spu_opcode_t);
|
||||
static void BRHZ(SPUThread&, spu_opcode_t);
|
||||
static void BRHNZ(SPUThread&, spu_opcode_t);
|
||||
static void STQR(SPUThread&, spu_opcode_t);
|
||||
static void BRA(SPUThread&, spu_opcode_t);
|
||||
static void LQA(SPUThread&, spu_opcode_t);
|
||||
static void BRASL(SPUThread&, spu_opcode_t);
|
||||
static void BR(SPUThread&, spu_opcode_t);
|
||||
static void FSMBI(SPUThread&, spu_opcode_t);
|
||||
static void BRSL(SPUThread&, spu_opcode_t);
|
||||
static void LQR(SPUThread&, spu_opcode_t);
|
||||
static void IL(SPUThread&, spu_opcode_t);
|
||||
static void ILHU(SPUThread&, spu_opcode_t);
|
||||
static void ILH(SPUThread&, spu_opcode_t);
|
||||
static void IOHL(SPUThread&, spu_opcode_t);
|
||||
static void ORI(SPUThread&, spu_opcode_t);
|
||||
static void ORHI(SPUThread&, spu_opcode_t);
|
||||
static void ORBI(SPUThread&, spu_opcode_t);
|
||||
static void SFI(SPUThread&, spu_opcode_t);
|
||||
static void SFHI(SPUThread&, spu_opcode_t);
|
||||
static void ANDI(SPUThread&, spu_opcode_t);
|
||||
static void ANDHI(SPUThread&, spu_opcode_t);
|
||||
static void ANDBI(SPUThread&, spu_opcode_t);
|
||||
static void AI(SPUThread&, spu_opcode_t);
|
||||
static void AHI(SPUThread&, spu_opcode_t);
|
||||
static void STQD(SPUThread&, spu_opcode_t);
|
||||
static void LQD(SPUThread&, spu_opcode_t);
|
||||
static void XORI(SPUThread&, spu_opcode_t);
|
||||
static void XORHI(SPUThread&, spu_opcode_t);
|
||||
static void XORBI(SPUThread&, spu_opcode_t);
|
||||
static void CGTI(SPUThread&, spu_opcode_t);
|
||||
static void CGTHI(SPUThread&, spu_opcode_t);
|
||||
static void CGTBI(SPUThread&, spu_opcode_t);
|
||||
static void HGTI(SPUThread&, spu_opcode_t);
|
||||
static void CLGTI(SPUThread&, spu_opcode_t);
|
||||
static void CLGTHI(SPUThread&, spu_opcode_t);
|
||||
static void CLGTBI(SPUThread&, spu_opcode_t);
|
||||
static void HLGTI(SPUThread&, spu_opcode_t);
|
||||
static void MPYI(SPUThread&, spu_opcode_t);
|
||||
static void MPYUI(SPUThread&, spu_opcode_t);
|
||||
static void CEQI(SPUThread&, spu_opcode_t);
|
||||
static void CEQHI(SPUThread&, spu_opcode_t);
|
||||
static void CEQBI(SPUThread&, spu_opcode_t);
|
||||
static void HEQI(SPUThread&, spu_opcode_t);
|
||||
static void HBRA(SPUThread&, spu_opcode_t);
|
||||
static void HBRR(SPUThread&, spu_opcode_t);
|
||||
static void ILA(SPUThread&, spu_opcode_t);
|
||||
static void SELB(SPUThread&, spu_opcode_t);
|
||||
static void MPYA(SPUThread&, spu_opcode_t);
|
||||
static void DFCGT(SPUThread&, spu_opcode_t);
|
||||
static void DFCMGT(SPUThread&, spu_opcode_t);
|
||||
static void DFTSV(SPUThread&, spu_opcode_t);
|
||||
static void DFCEQ(SPUThread&, spu_opcode_t);
|
||||
static void DFCMEQ(SPUThread&, spu_opcode_t);
|
||||
static bool STOP(SPUThread&, spu_opcode_t);
|
||||
static bool LNOP(SPUThread&, spu_opcode_t);
|
||||
static bool SYNC(SPUThread&, spu_opcode_t);
|
||||
static bool DSYNC(SPUThread&, spu_opcode_t);
|
||||
static bool MFSPR(SPUThread&, spu_opcode_t);
|
||||
static bool RDCH(SPUThread&, spu_opcode_t);
|
||||
static bool RCHCNT(SPUThread&, spu_opcode_t);
|
||||
static bool SF(SPUThread&, spu_opcode_t);
|
||||
static bool OR(SPUThread&, spu_opcode_t);
|
||||
static bool BG(SPUThread&, spu_opcode_t);
|
||||
static bool SFH(SPUThread&, spu_opcode_t);
|
||||
static bool NOR(SPUThread&, spu_opcode_t);
|
||||
static bool ABSDB(SPUThread&, spu_opcode_t);
|
||||
static bool ROT(SPUThread&, spu_opcode_t);
|
||||
static bool ROTM(SPUThread&, spu_opcode_t);
|
||||
static bool ROTMA(SPUThread&, spu_opcode_t);
|
||||
static bool SHL(SPUThread&, spu_opcode_t);
|
||||
static bool ROTH(SPUThread&, spu_opcode_t);
|
||||
static bool ROTHM(SPUThread&, spu_opcode_t);
|
||||
static bool ROTMAH(SPUThread&, spu_opcode_t);
|
||||
static bool SHLH(SPUThread&, spu_opcode_t);
|
||||
static bool ROTI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTMI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTMAI(SPUThread&, spu_opcode_t);
|
||||
static bool SHLI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTHI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTHMI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTMAHI(SPUThread&, spu_opcode_t);
|
||||
static bool SHLHI(SPUThread&, spu_opcode_t);
|
||||
static bool A(SPUThread&, spu_opcode_t);
|
||||
static bool AND(SPUThread&, spu_opcode_t);
|
||||
static bool CG(SPUThread&, spu_opcode_t);
|
||||
static bool AH(SPUThread&, spu_opcode_t);
|
||||
static bool NAND(SPUThread&, spu_opcode_t);
|
||||
static bool AVGB(SPUThread&, spu_opcode_t);
|
||||
static bool MTSPR(SPUThread&, spu_opcode_t);
|
||||
static bool WRCH(SPUThread&, spu_opcode_t);
|
||||
static bool BIZ(SPUThread&, spu_opcode_t);
|
||||
static bool BINZ(SPUThread&, spu_opcode_t);
|
||||
static bool BIHZ(SPUThread&, spu_opcode_t);
|
||||
static bool BIHNZ(SPUThread&, spu_opcode_t);
|
||||
static bool STOPD(SPUThread&, spu_opcode_t);
|
||||
static bool STQX(SPUThread&, spu_opcode_t);
|
||||
static bool BI(SPUThread&, spu_opcode_t);
|
||||
static bool BISL(SPUThread&, spu_opcode_t);
|
||||
static bool IRET(SPUThread&, spu_opcode_t);
|
||||
static bool BISLED(SPUThread&, spu_opcode_t);
|
||||
static bool HBR(SPUThread&, spu_opcode_t);
|
||||
static bool GB(SPUThread&, spu_opcode_t);
|
||||
static bool GBH(SPUThread&, spu_opcode_t);
|
||||
static bool GBB(SPUThread&, spu_opcode_t);
|
||||
static bool FSM(SPUThread&, spu_opcode_t);
|
||||
static bool FSMH(SPUThread&, spu_opcode_t);
|
||||
static bool FSMB(SPUThread&, spu_opcode_t);
|
||||
static bool LQX(SPUThread&, spu_opcode_t);
|
||||
static bool CBX(SPUThread&, spu_opcode_t);
|
||||
static bool CHX(SPUThread&, spu_opcode_t);
|
||||
static bool CWX(SPUThread&, spu_opcode_t);
|
||||
static bool CDX(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQBI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQMBI(SPUThread&, spu_opcode_t);
|
||||
static bool SHLQBI(SPUThread&, spu_opcode_t);
|
||||
static bool ORX(SPUThread&, spu_opcode_t);
|
||||
static bool CBD(SPUThread&, spu_opcode_t);
|
||||
static bool CHD(SPUThread&, spu_opcode_t);
|
||||
static bool CWD(SPUThread&, spu_opcode_t);
|
||||
static bool CDD(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQBII(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQMBII(SPUThread&, spu_opcode_t);
|
||||
static bool SHLQBII(SPUThread&, spu_opcode_t);
|
||||
static bool NOP(SPUThread&, spu_opcode_t);
|
||||
static bool CGT(SPUThread&, spu_opcode_t);
|
||||
static bool XOR(SPUThread&, spu_opcode_t);
|
||||
static bool CGTH(SPUThread&, spu_opcode_t);
|
||||
static bool EQV(SPUThread&, spu_opcode_t);
|
||||
static bool CGTB(SPUThread&, spu_opcode_t);
|
||||
static bool SUMB(SPUThread&, spu_opcode_t);
|
||||
static bool HGT(SPUThread&, spu_opcode_t);
|
||||
static bool CLZ(SPUThread&, spu_opcode_t);
|
||||
static bool XSWD(SPUThread&, spu_opcode_t);
|
||||
static bool XSHW(SPUThread&, spu_opcode_t);
|
||||
static bool CNTB(SPUThread&, spu_opcode_t);
|
||||
static bool XSBH(SPUThread&, spu_opcode_t);
|
||||
static bool CLGT(SPUThread&, spu_opcode_t);
|
||||
static bool ANDC(SPUThread&, spu_opcode_t);
|
||||
static bool CLGTH(SPUThread&, spu_opcode_t);
|
||||
static bool ORC(SPUThread&, spu_opcode_t);
|
||||
static bool CLGTB(SPUThread&, spu_opcode_t);
|
||||
static bool HLGT(SPUThread&, spu_opcode_t);
|
||||
static bool CEQ(SPUThread&, spu_opcode_t);
|
||||
static bool MPYHHU(SPUThread&, spu_opcode_t);
|
||||
static bool ADDX(SPUThread&, spu_opcode_t);
|
||||
static bool SFX(SPUThread&, spu_opcode_t);
|
||||
static bool CGX(SPUThread&, spu_opcode_t);
|
||||
static bool BGX(SPUThread&, spu_opcode_t);
|
||||
static bool MPYHHA(SPUThread&, spu_opcode_t);
|
||||
static bool MPYHHAU(SPUThread&, spu_opcode_t);
|
||||
static bool MPY(SPUThread&, spu_opcode_t);
|
||||
static bool MPYH(SPUThread&, spu_opcode_t);
|
||||
static bool MPYHH(SPUThread&, spu_opcode_t);
|
||||
static bool MPYS(SPUThread&, spu_opcode_t);
|
||||
static bool CEQH(SPUThread&, spu_opcode_t);
|
||||
static bool MPYU(SPUThread&, spu_opcode_t);
|
||||
static bool CEQB(SPUThread&, spu_opcode_t);
|
||||
static bool HEQ(SPUThread&, spu_opcode_t);
|
||||
static bool BRZ(SPUThread&, spu_opcode_t);
|
||||
static bool STQA(SPUThread&, spu_opcode_t);
|
||||
static bool BRNZ(SPUThread&, spu_opcode_t);
|
||||
static bool BRHZ(SPUThread&, spu_opcode_t);
|
||||
static bool BRHNZ(SPUThread&, spu_opcode_t);
|
||||
static bool STQR(SPUThread&, spu_opcode_t);
|
||||
static bool BRA(SPUThread&, spu_opcode_t);
|
||||
static bool LQA(SPUThread&, spu_opcode_t);
|
||||
static bool BRASL(SPUThread&, spu_opcode_t);
|
||||
static bool BR(SPUThread&, spu_opcode_t);
|
||||
static bool FSMBI(SPUThread&, spu_opcode_t);
|
||||
static bool BRSL(SPUThread&, spu_opcode_t);
|
||||
static bool LQR(SPUThread&, spu_opcode_t);
|
||||
static bool IL(SPUThread&, spu_opcode_t);
|
||||
static bool ILHU(SPUThread&, spu_opcode_t);
|
||||
static bool ILH(SPUThread&, spu_opcode_t);
|
||||
static bool IOHL(SPUThread&, spu_opcode_t);
|
||||
static bool ORI(SPUThread&, spu_opcode_t);
|
||||
static bool ORHI(SPUThread&, spu_opcode_t);
|
||||
static bool ORBI(SPUThread&, spu_opcode_t);
|
||||
static bool SFI(SPUThread&, spu_opcode_t);
|
||||
static bool SFHI(SPUThread&, spu_opcode_t);
|
||||
static bool ANDI(SPUThread&, spu_opcode_t);
|
||||
static bool ANDHI(SPUThread&, spu_opcode_t);
|
||||
static bool ANDBI(SPUThread&, spu_opcode_t);
|
||||
static bool AI(SPUThread&, spu_opcode_t);
|
||||
static bool AHI(SPUThread&, spu_opcode_t);
|
||||
static bool STQD(SPUThread&, spu_opcode_t);
|
||||
static bool LQD(SPUThread&, spu_opcode_t);
|
||||
static bool XORI(SPUThread&, spu_opcode_t);
|
||||
static bool XORHI(SPUThread&, spu_opcode_t);
|
||||
static bool XORBI(SPUThread&, spu_opcode_t);
|
||||
static bool CGTI(SPUThread&, spu_opcode_t);
|
||||
static bool CGTHI(SPUThread&, spu_opcode_t);
|
||||
static bool CGTBI(SPUThread&, spu_opcode_t);
|
||||
static bool HGTI(SPUThread&, spu_opcode_t);
|
||||
static bool CLGTI(SPUThread&, spu_opcode_t);
|
||||
static bool CLGTHI(SPUThread&, spu_opcode_t);
|
||||
static bool CLGTBI(SPUThread&, spu_opcode_t);
|
||||
static bool HLGTI(SPUThread&, spu_opcode_t);
|
||||
static bool MPYI(SPUThread&, spu_opcode_t);
|
||||
static bool MPYUI(SPUThread&, spu_opcode_t);
|
||||
static bool CEQI(SPUThread&, spu_opcode_t);
|
||||
static bool CEQHI(SPUThread&, spu_opcode_t);
|
||||
static bool CEQBI(SPUThread&, spu_opcode_t);
|
||||
static bool HEQI(SPUThread&, spu_opcode_t);
|
||||
static bool HBRA(SPUThread&, spu_opcode_t);
|
||||
static bool HBRR(SPUThread&, spu_opcode_t);
|
||||
static bool ILA(SPUThread&, spu_opcode_t);
|
||||
static bool SELB(SPUThread&, spu_opcode_t);
|
||||
static bool MPYA(SPUThread&, spu_opcode_t);
|
||||
static bool DFCGT(SPUThread&, spu_opcode_t);
|
||||
static bool DFCMGT(SPUThread&, spu_opcode_t);
|
||||
static bool DFTSV(SPUThread&, spu_opcode_t);
|
||||
static bool DFCEQ(SPUThread&, spu_opcode_t);
|
||||
static bool DFCMEQ(SPUThread&, spu_opcode_t);
|
||||
};
|
||||
|
||||
struct spu_interpreter_fast final : spu_interpreter
|
||||
{
|
||||
static void ROTQBYBI(SPUThread&, spu_opcode_t);
|
||||
static void ROTQMBYBI(SPUThread&, spu_opcode_t);
|
||||
static void SHLQBYBI(SPUThread&, spu_opcode_t);
|
||||
static void ROTQBY(SPUThread&, spu_opcode_t);
|
||||
static void ROTQMBY(SPUThread&, spu_opcode_t);
|
||||
static void SHLQBY(SPUThread&, spu_opcode_t);
|
||||
static void ROTQBYI(SPUThread&, spu_opcode_t);
|
||||
static void ROTQMBYI(SPUThread&, spu_opcode_t);
|
||||
static void SHLQBYI(SPUThread&, spu_opcode_t);
|
||||
static void SHUFB(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQBYBI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQMBYBI(SPUThread&, spu_opcode_t);
|
||||
static bool SHLQBYBI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQBY(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQMBY(SPUThread&, spu_opcode_t);
|
||||
static bool SHLQBY(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQBYI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQMBYI(SPUThread&, spu_opcode_t);
|
||||
static bool SHLQBYI(SPUThread&, spu_opcode_t);
|
||||
static bool SHUFB(SPUThread&, spu_opcode_t);
|
||||
|
||||
static void FREST(SPUThread&, spu_opcode_t);
|
||||
static void FRSQEST(SPUThread&, spu_opcode_t);
|
||||
static void FCGT(SPUThread&, spu_opcode_t);
|
||||
static void FA(SPUThread&, spu_opcode_t);
|
||||
static void FS(SPUThread&, spu_opcode_t);
|
||||
static void FM(SPUThread&, spu_opcode_t);
|
||||
static void FCMGT(SPUThread&, spu_opcode_t);
|
||||
static void DFA(SPUThread&, spu_opcode_t);
|
||||
static void DFS(SPUThread&, spu_opcode_t);
|
||||
static void DFM(SPUThread&, spu_opcode_t);
|
||||
static void DFMA(SPUThread&, spu_opcode_t);
|
||||
static void DFMS(SPUThread&, spu_opcode_t);
|
||||
static void DFNMS(SPUThread&, spu_opcode_t);
|
||||
static void DFNMA(SPUThread&, spu_opcode_t);
|
||||
static void FSCRRD(SPUThread&, spu_opcode_t);
|
||||
static void FESD(SPUThread&, spu_opcode_t);
|
||||
static void FRDS(SPUThread&, spu_opcode_t);
|
||||
static void FSCRWR(SPUThread&, spu_opcode_t);
|
||||
static void FCEQ(SPUThread&, spu_opcode_t);
|
||||
static void FCMEQ(SPUThread&, spu_opcode_t);
|
||||
static void FI(SPUThread&, spu_opcode_t);
|
||||
static void CFLTS(SPUThread&, spu_opcode_t);
|
||||
static void CFLTU(SPUThread&, spu_opcode_t);
|
||||
static void CSFLT(SPUThread&, spu_opcode_t);
|
||||
static void CUFLT(SPUThread&, spu_opcode_t);
|
||||
static void FNMS(SPUThread&, spu_opcode_t);
|
||||
static void FMA(SPUThread&, spu_opcode_t);
|
||||
static void FMS(SPUThread&, spu_opcode_t);
|
||||
static bool FREST(SPUThread&, spu_opcode_t);
|
||||
static bool FRSQEST(SPUThread&, spu_opcode_t);
|
||||
static bool FCGT(SPUThread&, spu_opcode_t);
|
||||
static bool FA(SPUThread&, spu_opcode_t);
|
||||
static bool FS(SPUThread&, spu_opcode_t);
|
||||
static bool FM(SPUThread&, spu_opcode_t);
|
||||
static bool FCMGT(SPUThread&, spu_opcode_t);
|
||||
static bool DFA(SPUThread&, spu_opcode_t);
|
||||
static bool DFS(SPUThread&, spu_opcode_t);
|
||||
static bool DFM(SPUThread&, spu_opcode_t);
|
||||
static bool DFMA(SPUThread&, spu_opcode_t);
|
||||
static bool DFMS(SPUThread&, spu_opcode_t);
|
||||
static bool DFNMS(SPUThread&, spu_opcode_t);
|
||||
static bool DFNMA(SPUThread&, spu_opcode_t);
|
||||
static bool FSCRRD(SPUThread&, spu_opcode_t);
|
||||
static bool FESD(SPUThread&, spu_opcode_t);
|
||||
static bool FRDS(SPUThread&, spu_opcode_t);
|
||||
static bool FSCRWR(SPUThread&, spu_opcode_t);
|
||||
static bool FCEQ(SPUThread&, spu_opcode_t);
|
||||
static bool FCMEQ(SPUThread&, spu_opcode_t);
|
||||
static bool FI(SPUThread&, spu_opcode_t);
|
||||
static bool CFLTS(SPUThread&, spu_opcode_t);
|
||||
static bool CFLTU(SPUThread&, spu_opcode_t);
|
||||
static bool CSFLT(SPUThread&, spu_opcode_t);
|
||||
static bool CUFLT(SPUThread&, spu_opcode_t);
|
||||
static bool FNMS(SPUThread&, spu_opcode_t);
|
||||
static bool FMA(SPUThread&, spu_opcode_t);
|
||||
static bool FMS(SPUThread&, spu_opcode_t);
|
||||
};
|
||||
|
||||
struct spu_interpreter_precise final : spu_interpreter
|
||||
{
|
||||
static void ROTQBYBI(SPUThread&, spu_opcode_t);
|
||||
static void ROTQMBYBI(SPUThread&, spu_opcode_t);
|
||||
static void SHLQBYBI(SPUThread&, spu_opcode_t);
|
||||
static void ROTQBY(SPUThread&, spu_opcode_t);
|
||||
static void ROTQMBY(SPUThread&, spu_opcode_t);
|
||||
static void SHLQBY(SPUThread&, spu_opcode_t);
|
||||
static void ROTQBYI(SPUThread&, spu_opcode_t);
|
||||
static void ROTQMBYI(SPUThread&, spu_opcode_t);
|
||||
static void SHLQBYI(SPUThread&, spu_opcode_t);
|
||||
static void SHUFB(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQBYBI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQMBYBI(SPUThread&, spu_opcode_t);
|
||||
static bool SHLQBYBI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQBY(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQMBY(SPUThread&, spu_opcode_t);
|
||||
static bool SHLQBY(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQBYI(SPUThread&, spu_opcode_t);
|
||||
static bool ROTQMBYI(SPUThread&, spu_opcode_t);
|
||||
static bool SHLQBYI(SPUThread&, spu_opcode_t);
|
||||
static bool SHUFB(SPUThread&, spu_opcode_t);
|
||||
|
||||
static void FREST(SPUThread&, spu_opcode_t);
|
||||
static void FRSQEST(SPUThread&, spu_opcode_t);
|
||||
static void FCGT(SPUThread&, spu_opcode_t);
|
||||
static void FA(SPUThread&, spu_opcode_t);
|
||||
static void FS(SPUThread&, spu_opcode_t);
|
||||
static void FM(SPUThread&, spu_opcode_t);
|
||||
static void FCMGT(SPUThread&, spu_opcode_t);
|
||||
static void DFA(SPUThread&, spu_opcode_t);
|
||||
static void DFS(SPUThread&, spu_opcode_t);
|
||||
static void DFM(SPUThread&, spu_opcode_t);
|
||||
static void DFMA(SPUThread&, spu_opcode_t);
|
||||
static void DFMS(SPUThread&, spu_opcode_t);
|
||||
static void DFNMS(SPUThread&, spu_opcode_t);
|
||||
static void DFNMA(SPUThread&, spu_opcode_t);
|
||||
static void FSCRRD(SPUThread&, spu_opcode_t);
|
||||
static void FESD(SPUThread&, spu_opcode_t);
|
||||
static void FRDS(SPUThread&, spu_opcode_t);
|
||||
static void FSCRWR(SPUThread&, spu_opcode_t);
|
||||
static void FCEQ(SPUThread&, spu_opcode_t);
|
||||
static void FCMEQ(SPUThread&, spu_opcode_t);
|
||||
static void FI(SPUThread&, spu_opcode_t);
|
||||
static void CFLTS(SPUThread&, spu_opcode_t);
|
||||
static void CFLTU(SPUThread&, spu_opcode_t);
|
||||
static void CSFLT(SPUThread&, spu_opcode_t);
|
||||
static void CUFLT(SPUThread&, spu_opcode_t);
|
||||
static void FNMS(SPUThread&, spu_opcode_t);
|
||||
static void FMA(SPUThread&, spu_opcode_t);
|
||||
static void FMS(SPUThread&, spu_opcode_t);
|
||||
static bool FREST(SPUThread&, spu_opcode_t);
|
||||
static bool FRSQEST(SPUThread&, spu_opcode_t);
|
||||
static bool FCGT(SPUThread&, spu_opcode_t);
|
||||
static bool FA(SPUThread&, spu_opcode_t);
|
||||
static bool FS(SPUThread&, spu_opcode_t);
|
||||
static bool FM(SPUThread&, spu_opcode_t);
|
||||
static bool FCMGT(SPUThread&, spu_opcode_t);
|
||||
static bool DFA(SPUThread&, spu_opcode_t);
|
||||
static bool DFS(SPUThread&, spu_opcode_t);
|
||||
static bool DFM(SPUThread&, spu_opcode_t);
|
||||
static bool DFMA(SPUThread&, spu_opcode_t);
|
||||
static bool DFMS(SPUThread&, spu_opcode_t);
|
||||
static bool DFNMS(SPUThread&, spu_opcode_t);
|
||||
static bool DFNMA(SPUThread&, spu_opcode_t);
|
||||
static bool FSCRRD(SPUThread&, spu_opcode_t);
|
||||
static bool FESD(SPUThread&, spu_opcode_t);
|
||||
static bool FRDS(SPUThread&, spu_opcode_t);
|
||||
static bool FSCRWR(SPUThread&, spu_opcode_t);
|
||||
static bool FCEQ(SPUThread&, spu_opcode_t);
|
||||
static bool FCMEQ(SPUThread&, spu_opcode_t);
|
||||
static bool FI(SPUThread&, spu_opcode_t);
|
||||
static bool CFLTS(SPUThread&, spu_opcode_t);
|
||||
static bool CFLTU(SPUThread&, spu_opcode_t);
|
||||
static bool CSFLT(SPUThread&, spu_opcode_t);
|
||||
static bool CUFLT(SPUThread&, spu_opcode_t);
|
||||
static bool FNMS(SPUThread&, spu_opcode_t);
|
||||
static bool FMA(SPUThread&, spu_opcode_t);
|
||||
static bool FMS(SPUThread&, spu_opcode_t);
|
||||
};
|
||||
|
||||
@@ -32,6 +32,7 @@ const bool s_use_ssse3 =
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#define _mm_shuffle_epi8
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@@ -388,25 +389,75 @@ void SPUThread::cpu_task()
|
||||
g_cfg.core.spu_decoder == spu_decoder_type::fast ? &g_spu_interpreter_fast.get_table() :
|
||||
(fmt::throw_exception<std::logic_error>("Invalid SPU decoder"), nullptr));
|
||||
|
||||
// LS base address
|
||||
const auto base = vm::_ptr<const u32>(offset);
|
||||
// LS pointer
|
||||
const auto base = vm::_ptr<const u8>(offset);
|
||||
const auto bswap4 = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
|
||||
|
||||
v128 _op;
|
||||
using func_t = decltype(&spu_interpreter::UNK);
|
||||
func_t func0, func1, func2, func3, func4, func5;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!test(state))
|
||||
if (UNLIKELY(test(state)))
|
||||
{
|
||||
// Read opcode
|
||||
const u32 op = base[pc / 4];
|
||||
if (check_state()) return;
|
||||
|
||||
// Call interpreter function
|
||||
table[spu_decode(op)](*this, { op });
|
||||
|
||||
// Next instruction
|
||||
pc += 4;
|
||||
// Decode single instruction (may be step)
|
||||
const u32 op = *reinterpret_cast<const be_t<u32>*>(base + pc);
|
||||
if (table[spu_decode(op)](*this, {op})) { pc += 4; }
|
||||
continue;
|
||||
}
|
||||
|
||||
if (check_state()) return;
|
||||
if (pc % 16 || !s_use_ssse3)
|
||||
{
|
||||
// Unaligned
|
||||
const u32 op = *reinterpret_cast<const be_t<u32>*>(base + pc);
|
||||
if (table[spu_decode(op)](*this, {op})) { pc += 4; }
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reinitialize
|
||||
_op.vi = _mm_shuffle_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(base + pc)), bswap4);
|
||||
func0 = table[spu_decode(_op._u32[0])];
|
||||
func1 = table[spu_decode(_op._u32[1])];
|
||||
func2 = table[spu_decode(_op._u32[2])];
|
||||
func3 = table[spu_decode(_op._u32[3])];
|
||||
|
||||
while (LIKELY(func0(*this, {_op._u32[0]})))
|
||||
{
|
||||
pc += 4;
|
||||
if (LIKELY(func1(*this, {_op._u32[1]})))
|
||||
{
|
||||
pc += 4;
|
||||
u32 op2 = _op._u32[2];
|
||||
u32 op3 = _op._u32[3];
|
||||
_op.vi = _mm_shuffle_epi8(_mm_load_si128(reinterpret_cast<const __m128i*>(base + pc + 8)), bswap4);
|
||||
func0 = table[spu_decode(_op._u32[0])];
|
||||
func1 = table[spu_decode(_op._u32[1])];
|
||||
func4 = table[spu_decode(_op._u32[2])];
|
||||
func5 = table[spu_decode(_op._u32[3])];
|
||||
if (LIKELY(func2(*this, {op2})))
|
||||
{
|
||||
pc += 4;
|
||||
if (LIKELY(func3(*this, {op3})))
|
||||
{
|
||||
pc += 4;
|
||||
func2 = func4;
|
||||
func3 = func5;
|
||||
|
||||
if (UNLIKELY(test(state)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ void lv2_int_serv::exec()
|
||||
{
|
||||
thread->cmd_list
|
||||
({
|
||||
{ ppu_cmd::reset_stack, 0 },
|
||||
{ ppu_cmd::set_args, 2 }, arg1, arg2,
|
||||
{ ppu_cmd::lle_call, 2 },
|
||||
{ ppu_cmd::sleep, 0 }
|
||||
|
||||
@@ -290,8 +290,8 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr<sys_exit2_param> ar
|
||||
|
||||
if (Emu.GetCat() == "DG" || Emu.GetCat() == "GD")
|
||||
disc = vfs::get("/dev_bdvd/");
|
||||
else if (Emu.GetTitleID().size())
|
||||
disc = vfs::get("/dev_hdd0/game/" + Emu.GetTitleID() + "/");
|
||||
if (disc.empty() && Emu.GetTitleID().size())
|
||||
disc = vfs::get(Emu.GetDir());
|
||||
|
||||
vm::temporary_unlock(ppu);
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#include "stdafx.h"
|
||||
#include "sys_tty.h"
|
||||
|
||||
|
||||
|
||||
logs::channel sys_tty("sys_tty");
|
||||
|
||||
extern fs::file g_tty;
|
||||
extern atomic_t<s64> g_tty_size;
|
||||
|
||||
error_code sys_tty_read(s32 ch, vm::ptr<char> buf, u32 len, vm::ptr<u32> preadlen)
|
||||
{
|
||||
@@ -28,7 +27,10 @@ error_code sys_tty_write(s32 ch, vm::cptr<char> buf, u32 len, vm::ptr<u32> pwrit
|
||||
|
||||
if (written_len > 0 && g_tty)
|
||||
{
|
||||
// Lock size by making it negative
|
||||
g_tty_size -= (1ll << 48);
|
||||
g_tty.write(buf.get_ptr(), len);
|
||||
g_tty_size += (1ll << 48) + len;
|
||||
}
|
||||
|
||||
if (!pwritelen)
|
||||
|
||||
@@ -525,7 +525,7 @@ void write_vertex_array_data_to_buffer(gsl::span<gsl::byte> raw_dst_span, gsl::s
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
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)
|
||||
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, u32 base_index)
|
||||
{
|
||||
T min_index = -1;
|
||||
T max_index = 0;
|
||||
@@ -535,6 +535,7 @@ std::tuple<T, T, u32> upload_untouched(gsl::span<to_be_t<const T>> src, gsl::spa
|
||||
u32 dst_idx = 0;
|
||||
for (T index : src)
|
||||
{
|
||||
index = (base_index + index) & 0x000fffff;
|
||||
if (is_primitive_restart_enabled && index == primitive_restart_index)
|
||||
{
|
||||
// List types do not need primitive restart. Just skip over this instead
|
||||
@@ -780,19 +781,19 @@ namespace
|
||||
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)
|
||||
std::function<bool(rsx::primitive_type)> expands, u32 base_index)
|
||||
{
|
||||
u32 first;
|
||||
u32 count;
|
||||
std::tie(first, count) = get_first_count_from_draw_indexed_clause(first_count_arguments);
|
||||
|
||||
if (!expands(draw_mode)) return upload_untouched<T>(src, dst, restart_index_enabled, restart_index);
|
||||
if (!expands(draw_mode)) return upload_untouched<T>(src, dst, restart_index_enabled, restart_index, base_index);
|
||||
|
||||
switch (draw_mode)
|
||||
{
|
||||
case rsx::primitive_type::line_loop:
|
||||
{
|
||||
const auto &returnvalue = upload_untouched<T>(src, dst, restart_index_enabled, restart_index);
|
||||
const auto &returnvalue = upload_untouched<T>(src, dst, restart_index_enabled, restart_index, base_index);
|
||||
dst[count] = src[0];
|
||||
return returnvalue;
|
||||
}
|
||||
@@ -810,16 +811,16 @@ namespace
|
||||
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)
|
||||
std::function<bool(rsx::primitive_type)> expands, u32 base_index)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case rsx::index_array_type::u16:
|
||||
return write_index_array_data_to_buffer_impl<u16>(as_span_workaround<u16>(dst),
|
||||
gsl::as_span<const be_t<u16>>(src), draw_mode, restart_index_enabled, restart_index, first_count_arguments, expands);
|
||||
gsl::as_span<const be_t<u16>>(src), draw_mode, restart_index_enabled, restart_index, first_count_arguments, expands, base_index);
|
||||
case rsx::index_array_type::u32:
|
||||
return write_index_array_data_to_buffer_impl<u32>(as_span_workaround<u32>(dst),
|
||||
gsl::as_span<const be_t<u32>>(src), draw_mode, restart_index_enabled, restart_index, first_count_arguments, expands);
|
||||
gsl::as_span<const be_t<u32>>(src), draw_mode, restart_index_enabled, restart_index, first_count_arguments, expands, base_index);
|
||||
}
|
||||
fmt::throw_exception("Unknown index type" HERE);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ u32 get_index_type_size(rsx::index_array_type type);
|
||||
*/
|
||||
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);
|
||||
std::function<bool(rsx::primitive_type)> expands, u32 base_index = 0);
|
||||
|
||||
/**
|
||||
* Write index data needed to emulate non indexed non native primitive mode.
|
||||
|
||||
@@ -111,7 +111,7 @@ void FragmentProgramDecompiler::SetDst(std::string code, bool append_mask)
|
||||
}
|
||||
|
||||
u32 reg_index = dst.fp16 ? dst.dest_reg >> 1 : dst.dest_reg;
|
||||
temp_registers[reg_index].tag(dst.dest_reg, !!dst.fp16);
|
||||
temp_registers[reg_index].tag(dst.dest_reg, !!dst.fp16, dst.mask_x, dst.mask_y, dst.mask_z, dst.mask_w);
|
||||
}
|
||||
|
||||
void FragmentProgramDecompiler::AddFlowOp(std::string code)
|
||||
@@ -283,14 +283,15 @@ std::string FragmentProgramDecompiler::Format(const std::string& code, bool igno
|
||||
{ "$_i", [this]() -> std::string {return std::to_string(dst.tex_num);} },
|
||||
{ "$m", std::bind(std::mem_fn(&FragmentProgramDecompiler::GetMask), this) },
|
||||
{ "$ifcond ", [this]() -> std::string
|
||||
{
|
||||
const std::string& cond = GetCond();
|
||||
if (cond == "true") return "";
|
||||
return "if(" + cond + ") ";
|
||||
}
|
||||
{
|
||||
const std::string& cond = GetCond();
|
||||
if (cond == "true") return "";
|
||||
return "if(" + cond + ") ";
|
||||
}
|
||||
},
|
||||
{ "$cond", std::bind(std::mem_fn(&FragmentProgramDecompiler::GetCond), this) },
|
||||
{ "$_c", std::bind(std::mem_fn(&FragmentProgramDecompiler::AddConst), this) }
|
||||
{ "$_c", std::bind(std::mem_fn(&FragmentProgramDecompiler::AddConst), this) },
|
||||
{ "$float4", [this]() -> std::string { return getFloatTypeName(4); } }
|
||||
};
|
||||
|
||||
if (!ignore_redirects)
|
||||
@@ -407,20 +408,13 @@ template<typename T> std::string FragmentProgramDecompiler::GetSRC(T src)
|
||||
dst.opcode == RSX_FP_OPCODE_UPB ||
|
||||
dst.opcode == RSX_FP_OPCODE_UPG)
|
||||
{
|
||||
//TODO: Implement aliased gather for half floats
|
||||
bool xy_read = false;
|
||||
bool zw_read = false;
|
||||
|
||||
if (src.swizzle_x < 2 || src.swizzle_y < 2 || src.swizzle_z < 2 || src.swizzle_w < 2)
|
||||
xy_read = true;
|
||||
if (src.swizzle_x > 1 || src.swizzle_y > 1 || src.swizzle_z > 1 || src.swizzle_w > 1)
|
||||
zw_read = true;
|
||||
|
||||
auto ® = temp_registers[src.tmp_reg_index];
|
||||
if (reg.requires_gather(xy_read, zw_read))
|
||||
if (reg.requires_gather(src.swizzle_x))
|
||||
{
|
||||
properties.has_gather_op = true;
|
||||
AddCode(reg.gather_r());
|
||||
AddReg(src.tmp_reg_index, src.fp16);
|
||||
ret = getFloatTypeName(4) + reg.gather_r();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -502,79 +496,6 @@ template<typename T> std::string FragmentProgramDecompiler::GetSRC(T src)
|
||||
|
||||
std::string FragmentProgramDecompiler::BuildCode()
|
||||
{
|
||||
//Scan if any outputs are available
|
||||
const bool use_32_bit_exports = !!(m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS);
|
||||
const bool exports_depth = !!(m_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT);
|
||||
const std::set<std::string> output_values =
|
||||
{
|
||||
(use_32_bit_exports) ? "r0" : "h0",
|
||||
"r1",
|
||||
(use_32_bit_exports) ? "r2" : "h4",
|
||||
(use_32_bit_exports) ? "r3" : "h6",
|
||||
(use_32_bit_exports) ? "r4" : "h8",
|
||||
};
|
||||
|
||||
bool gather_output_registers = true;
|
||||
const auto float4_name = getFloatTypeName(4);
|
||||
for (auto &v : output_values)
|
||||
{
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, float4_name, v))
|
||||
{
|
||||
gather_output_registers = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Explicitly discard on encountering null shaders
|
||||
if (gather_output_registers)
|
||||
{
|
||||
bool has_any_output = false;
|
||||
bool first_output_exists = false;
|
||||
|
||||
if (use_32_bit_exports || exports_depth)
|
||||
{
|
||||
for (int reg = 0; reg < 5; ++reg)
|
||||
{
|
||||
if (reg == 1 && !exports_depth)
|
||||
continue;
|
||||
|
||||
const std::string half_register = "h" + std::to_string(reg + 1);
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, float4_name, half_register))
|
||||
{
|
||||
has_any_output = true;
|
||||
if (!reg) first_output_exists = true;
|
||||
|
||||
const std::string this_register = "r" + std::to_string(reg);
|
||||
AddReg(reg, 0);
|
||||
AddCode("//Register gather because output was not specified");
|
||||
AddCode(this_register + ".zw = gather(" + half_register + ");");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_any_output)
|
||||
{
|
||||
properties.has_no_output = true;
|
||||
|
||||
LOG_ERROR(RSX, "Invalid fragment shader: No output register was updated!");
|
||||
|
||||
//Comment out main block as it is now useless
|
||||
main = "/*\n" + main + "*/\n";
|
||||
AddCode("//No output, manually abort writes (nvidia+vulkan writes garbage otherwise)");
|
||||
AddCode("discard;");
|
||||
}
|
||||
else
|
||||
{
|
||||
//Requires gather operation for output...
|
||||
properties.has_gather_op = true;
|
||||
|
||||
if (!first_output_exists)
|
||||
{
|
||||
LOG_WARNING(RSX, "Fragment shader does not write to first RTT and has no explicit output registers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream OS;
|
||||
insertHeader(OS);
|
||||
OS << "\n";
|
||||
@@ -836,10 +757,27 @@ std::string FragmentProgramDecompiler::Decompile()
|
||||
|
||||
int forced_unit = FORCE_NONE;
|
||||
|
||||
//Add the output registers. They are statically written to and have guaranteed initialization (except r1.z which == wpos.z)
|
||||
//This can be used instead of an explicit clear pass in some games (Motorstorm)
|
||||
if (m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS)
|
||||
{
|
||||
AddReg(0, CELL_GCM_FALSE);
|
||||
AddReg(2, CELL_GCM_FALSE);
|
||||
AddReg(3, CELL_GCM_FALSE);
|
||||
AddReg(4, CELL_GCM_FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddReg(0, CELL_GCM_TRUE);
|
||||
AddReg(4, CELL_GCM_TRUE);
|
||||
AddReg(6, CELL_GCM_TRUE);
|
||||
AddReg(8, CELL_GCM_TRUE);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
for (auto found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size);
|
||||
found != m_end_offsets.end();
|
||||
found != m_end_offsets.end();
|
||||
found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size))
|
||||
{
|
||||
m_end_offsets.erase(found);
|
||||
@@ -849,7 +787,7 @@ std::string FragmentProgramDecompiler::Decompile()
|
||||
}
|
||||
|
||||
for (auto found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size);
|
||||
found != m_else_offsets.end();
|
||||
found != m_else_offsets.end();
|
||||
found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size))
|
||||
{
|
||||
m_else_offsets.erase(found);
|
||||
|
||||
@@ -24,25 +24,41 @@ class FragmentProgramDecompiler
|
||||
bool aliased_r0 = false;
|
||||
bool aliased_h0 = false;
|
||||
bool aliased_h1 = false;
|
||||
bool last_write_half = false;
|
||||
bool last_write_half[4] = { false, false, false, false };
|
||||
|
||||
u32 real_index = UINT32_MAX;
|
||||
|
||||
void tag(u32 index, bool half_register)
|
||||
void tag(u32 index, bool half_register, bool x, bool y, bool z, bool w)
|
||||
{
|
||||
if (half_register)
|
||||
{
|
||||
last_write_half = true;
|
||||
|
||||
if (index & 1)
|
||||
{
|
||||
if (x) last_write_half[2] = true;
|
||||
if (y) last_write_half[2] = true;
|
||||
if (z) last_write_half[3] = true;
|
||||
if (w) last_write_half[3] = true;
|
||||
|
||||
aliased_h1 = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x) last_write_half[0] = true;
|
||||
if (y) last_write_half[0] = true;
|
||||
if (z) last_write_half[1] = true;
|
||||
if (w) last_write_half[1] = true;
|
||||
|
||||
aliased_h0 = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x) last_write_half[0] = false;
|
||||
if (y) last_write_half[1] = false;
|
||||
if (z) last_write_half[2] = false;
|
||||
if (w) last_write_half[3] = false;
|
||||
|
||||
aliased_r0 = true;
|
||||
last_write_half = false;
|
||||
}
|
||||
|
||||
if (real_index == UINT32_MAX)
|
||||
@@ -54,12 +70,19 @@ class FragmentProgramDecompiler
|
||||
}
|
||||
}
|
||||
|
||||
bool requires_gather(bool xy, bool zw) const
|
||||
bool requires_gather(u8 channel) const
|
||||
{
|
||||
//Data fetched from the single precision register requires merging of the two half registers
|
||||
//TODO: Check individual swizzle channels
|
||||
if ((aliased_h0 && xy) || (aliased_h1 && zw))
|
||||
return last_write_half;
|
||||
verify(HERE), channel < 4;
|
||||
if (aliased_h0 && channel < 2)
|
||||
{
|
||||
return last_write_half[channel];
|
||||
}
|
||||
|
||||
if (aliased_h1 && channel > 1)
|
||||
{
|
||||
return last_write_half[channel];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -67,7 +90,7 @@ class FragmentProgramDecompiler
|
||||
bool requires_split(u32 /*index*/) const
|
||||
{
|
||||
//Data fetched from any of the two half registers requires sync with the full register
|
||||
if (!last_write_half && aliased_r0)
|
||||
if (!(last_write_half[0] || last_write_half[1]) && aliased_r0)
|
||||
{
|
||||
//r0 has been written to
|
||||
//TODO: Check for specific elements in real32 register
|
||||
@@ -85,15 +108,12 @@ class FragmentProgramDecompiler
|
||||
std::string ret = "//Invalid gather";
|
||||
|
||||
if (aliased_h0 && aliased_h1)
|
||||
ret = reg + " = gather(" + h0 + ", " + h1 + ");";
|
||||
ret = "(gather(" + h0 + ", " + h1 + "))";
|
||||
else if (aliased_h0)
|
||||
ret = reg + ".xy = gather(" + h0 + ");";
|
||||
ret = "(gather(" + h0 + "), " + reg + ".zw)";
|
||||
else if (aliased_h1)
|
||||
ret = reg + ".zw = gather(" + h1 + ");";
|
||||
ret = "(" + reg + ".xy, gather(" + h1 + "))";
|
||||
|
||||
last_write_half = false;
|
||||
aliased_h0 = false;
|
||||
aliased_h1 = false;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -444,15 +444,15 @@ namespace glsl
|
||||
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
|
||||
return "textureProj($t, $0 * vec4(texture_parameters[$_i].xy, 1., 1.), $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)";
|
||||
return "texture($t, $0.xyz * vec3(texture_parameters[$_i].xy, 1.))";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SHADOW2D_PROJ:
|
||||
return "textureProj($t, $0, $1.x)"; // Note: $1.x is bias
|
||||
return "textureProj($t, $0 * vec4(texture_parameters[$_i].xy, 1., 1.), $1.x)"; // Note: $1.x is bias
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE:
|
||||
return "texture($t, $0.xyz)";
|
||||
case FUNCTION::FUNCTION_TEXTURE_SAMPLECUBE_PROJ:
|
||||
|
||||
@@ -78,6 +78,7 @@ namespace program_hash_util
|
||||
* - static void recompile_fragment_program(RSXFragmentProgram *RSXFP, FragmentProgramData& fragmentProgramData, size_t ID);
|
||||
* - static void recompile_vertex_program(RSXVertexProgram *RSXVP, VertexProgramData& vertexProgramData, size_t ID);
|
||||
* - static PipelineData build_program(VertexProgramData &vertexProgramData, FragmentProgramData &fragmentProgramData, const PipelineProperties &pipelineProperties, const ExtraData& extraData);
|
||||
* - static void validate_pipeline_properties(const VertexProgramData &vertexProgramData, const FragmentProgramData &fragmentProgramData, PipelineProperties& props);
|
||||
*/
|
||||
template<typename backend_traits>
|
||||
class program_state_cache
|
||||
@@ -261,7 +262,7 @@ public:
|
||||
pipeline_storage_type& getGraphicPipelineState(
|
||||
const RSXVertexProgram& vertexShader,
|
||||
const RSXFragmentProgram& fragmentShader,
|
||||
const pipeline_properties& pipelineProperties,
|
||||
pipeline_properties& pipelineProperties,
|
||||
Args&& ...args
|
||||
)
|
||||
{
|
||||
@@ -273,6 +274,7 @@ public:
|
||||
bool already_existing_fragment_program = std::get<1>(fp_search);
|
||||
bool already_existing_vertex_program = std::get<1>(vp_search);
|
||||
|
||||
backend_traits::validate_pipeline_properties(vertex_program, fragment_program, pipelineProperties);
|
||||
pipeline_key key = { vertex_program.id, fragment_program.id, pipelineProperties };
|
||||
|
||||
if (already_existing_fragment_program && already_existing_vertex_program)
|
||||
@@ -305,7 +307,7 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fill_fragment_constants_buffer(gsl::span<f32, gsl::dynamic_range> dst_buffer, const RSXFragmentProgram &fragment_program) const
|
||||
void fill_fragment_constants_buffer(gsl::span<f32, gsl::dynamic_range> dst_buffer, const RSXFragmentProgram &fragment_program, bool sanitize = false) const
|
||||
{
|
||||
const auto I = m_fragment_shader_cache.find(fragment_program);
|
||||
if (I == m_fragment_shader_cache.end())
|
||||
@@ -345,6 +347,13 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sanitize)
|
||||
{
|
||||
//Lower NaNs to 0
|
||||
const auto mask = _mm_cmpunord_ps((__m128&)shuffled_vector, _mm_set1_ps(1.f));
|
||||
const auto result = _mm_andnot_ps(mask, (__m128&)shuffled_vector);
|
||||
_mm_stream_si128((__m128i*)dst, (__m128i&)result);
|
||||
}
|
||||
else
|
||||
{
|
||||
_mm_stream_si128((__m128i*)dst, shuffled_vector);
|
||||
|
||||
@@ -131,4 +131,9 @@ public:
|
||||
else
|
||||
fmt::throw_exception("m_put_pos == m_get_pos!" HERE);
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,6 +35,20 @@ namespace rsx
|
||||
{}
|
||||
};
|
||||
|
||||
template <typename surface_type>
|
||||
struct surface_overlap_info_t
|
||||
{
|
||||
surface_type surface = nullptr;
|
||||
bool is_depth = false;
|
||||
|
||||
u16 src_x = 0;
|
||||
u16 src_y = 0;
|
||||
u16 dst_x = 0;
|
||||
u16 dst_y = 0;
|
||||
u16 width = 0;
|
||||
u16 height = 0;
|
||||
};
|
||||
|
||||
struct surface_format_info
|
||||
{
|
||||
u32 surface_width;
|
||||
@@ -50,22 +64,11 @@ namespace rsx
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -120,6 +123,7 @@ namespace rsx
|
||||
using command_list_type = typename Traits::command_list_type;
|
||||
using download_buffer_object = typename Traits::download_buffer_object;
|
||||
using surface_subresource = surface_subresource_storage<surface_type>;
|
||||
using surface_overlap_info = surface_overlap_info_t<surface_type>;
|
||||
|
||||
std::unordered_map<u32, surface_storage_type> m_render_targets_storage = {};
|
||||
std::unordered_map<u32, surface_storage_type> m_depth_stencil_storage = {};
|
||||
@@ -757,29 +761,17 @@ namespace rsx
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
else if (crop && info.surface_width > x_offset && info.surface_height > y_offset)
|
||||
{
|
||||
if (crop) //Forcefully fit the requested region by clipping and scaling
|
||||
{
|
||||
u16 remaining_width = info.surface_width - x_offset;
|
||||
u16 remaining_height = info.surface_height - y_offset;
|
||||
//Forcefully fit the requested region by clipping and scaling
|
||||
u16 remaining_width = info.surface_width - x_offset;
|
||||
u16 remaining_height = info.surface_height - y_offset;
|
||||
|
||||
w = std::min(real_width, remaining_width);
|
||||
h = std::min(real_height, remaining_height);
|
||||
clipped = true;
|
||||
w = std::min(real_width, remaining_width);
|
||||
h = std::min(real_height, remaining_height);
|
||||
clipped = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 = real_height;
|
||||
clipped = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,5 +838,61 @@ namespace rsx
|
||||
|
||||
return{};
|
||||
}
|
||||
|
||||
std::vector<surface_overlap_info> get_merged_texture_memory_region(u32 texaddr, u32 required_width, u32 required_height, u32 required_pitch, u32 bpp)
|
||||
{
|
||||
std::vector<surface_overlap_info> result;
|
||||
const u32 limit = texaddr + (required_pitch * required_height);
|
||||
|
||||
auto process_list_function = [&](std::unordered_map<u32, surface_storage_type>& data, bool is_depth)
|
||||
{
|
||||
for (auto &tex_info : data)
|
||||
{
|
||||
auto this_address = std::get<0>(tex_info);
|
||||
if (this_address > limit)
|
||||
continue;
|
||||
|
||||
auto surface = std::get<1>(tex_info).get();
|
||||
const auto pitch = surface->get_rsx_pitch();
|
||||
if (pitch != required_pitch)
|
||||
continue;
|
||||
|
||||
const auto texture_size = pitch * surface->get_surface_height();
|
||||
if ((this_address + texture_size) <= texaddr)
|
||||
continue;
|
||||
|
||||
surface_overlap_info info;
|
||||
info.surface = surface;
|
||||
info.is_depth = is_depth;
|
||||
|
||||
if (this_address < texaddr)
|
||||
{
|
||||
auto offset = texaddr - this_address;
|
||||
info.src_y = (offset / required_pitch);
|
||||
info.src_x = (offset % required_pitch) / bpp;
|
||||
info.dst_x = 0;
|
||||
info.dst_y = 0;
|
||||
info.width = std::min<u32>(required_width, surface->get_surface_width() - info.src_x);
|
||||
info.height = std::min<u32>(required_height, surface->get_surface_height() - info.src_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto offset = this_address - texaddr;
|
||||
info.src_x = 0;
|
||||
info.src_y = 0;
|
||||
info.dst_y = (offset / required_pitch);
|
||||
info.dst_x = (offset % required_pitch) / bpp;
|
||||
info.width = std::min<u32>(surface->get_surface_width(), required_width - info.dst_x);
|
||||
info.height = std::min<u32>(surface->get_surface_height(), required_height - info.dst_y);
|
||||
}
|
||||
|
||||
result.push_back(info);
|
||||
}
|
||||
};
|
||||
|
||||
process_list_function(m_render_targets_storage, false);
|
||||
process_list_function(m_depth_stencil_storage, true);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -251,10 +251,22 @@ namespace rsx
|
||||
u32 address_range = 0;
|
||||
};
|
||||
|
||||
struct copy_region_descriptor
|
||||
{
|
||||
image_resource_type src;
|
||||
u16 src_x;
|
||||
u16 src_y;
|
||||
u16 dst_x;
|
||||
u16 dst_y;
|
||||
u16 w;
|
||||
u16 h;
|
||||
};
|
||||
|
||||
struct deferred_subresource
|
||||
{
|
||||
image_resource_type external_handle = 0;
|
||||
std::array<image_resource_type, 6> external_cubemap_sources;
|
||||
std::vector<copy_region_descriptor> sections_to_copy;
|
||||
u32 base_address = 0;
|
||||
u32 gcm_format = 0;
|
||||
u16 x = 0;
|
||||
@@ -262,6 +274,8 @@ namespace rsx
|
||||
u16 width = 0;
|
||||
u16 height = 0;
|
||||
bool is_cubemap = false;
|
||||
bool is_copy_cmd = false;
|
||||
bool update_cached = false;
|
||||
|
||||
deferred_subresource()
|
||||
{}
|
||||
@@ -339,8 +353,8 @@ namespace rsx
|
||||
|
||||
std::unordered_map<u32, framebuffer_memory_characteristics> m_cache_miss_statistics_table;
|
||||
|
||||
//Set when a hw blit engine incompatibility is detected
|
||||
bool blit_engine_incompatibility_warning_raised = false;
|
||||
//Map of messages to only emit once
|
||||
std::unordered_map<std::string, bool> m_once_only_messages_map;
|
||||
|
||||
//Set when a shader read-only texture data suddenly becomes contested, usually by fbo memory
|
||||
bool read_only_tex_invalidate = false;
|
||||
@@ -357,6 +371,7 @@ namespace rsx
|
||||
//Other statistics
|
||||
std::atomic<u32> m_num_flush_requests = { 0 };
|
||||
std::atomic<u32> m_num_cache_misses = { 0 };
|
||||
std::atomic<u32> m_num_cache_mispredictions = { 0 };
|
||||
|
||||
/* Helpers */
|
||||
virtual void free_texture_section(section_storage_type&) = 0;
|
||||
@@ -370,6 +385,9 @@ namespace rsx
|
||||
virtual void set_up_remap_vector(section_storage_type& section, const std::pair<std::array<u8, 4>, std::array<u8, 4>>& remap_vector) = 0;
|
||||
virtual void insert_texture_barrier(commandbuffer_type&, image_storage_type* tex) = 0;
|
||||
virtual image_view_type generate_cubemap_from_images(commandbuffer_type&, u32 gcm_format, u16 size, const std::array<image_resource_type, 6>& sources) = 0;
|
||||
virtual image_view_type generate_atlas_from_images(commandbuffer_type&, u32 gcm_format, u16 width, u16 height, const std::vector<copy_region_descriptor>& sections_to_copy) = 0;
|
||||
virtual void update_image_contents(commandbuffer_type&, image_view_type dst, image_resource_type src, u16 width, u16 height) = 0;
|
||||
virtual bool render_target_format_is_compatible(image_storage_type* tex, u32 gcm_format) = 0;
|
||||
|
||||
constexpr u32 get_block_size() const { return 0x1000000; }
|
||||
inline u32 get_block_address(u32 address) const { return (address & ~0xFFFFFF); }
|
||||
@@ -379,6 +397,33 @@ namespace rsx
|
||||
m_cache_update_tag++;
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
void emit_once(bool error, const char* fmt, Args&&... params)
|
||||
{
|
||||
const std::string message = fmt::format(fmt, std::forward<Args>(params)...);
|
||||
if (m_once_only_messages_map.find(message) != m_once_only_messages_map.end())
|
||||
return;
|
||||
|
||||
if (error)
|
||||
logs::RSX.error(message.c_str());
|
||||
else
|
||||
logs::RSX.warning(message.c_str());
|
||||
|
||||
m_once_only_messages_map[message] = true;
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
void err_once(const char* fmt, Args&&... params)
|
||||
{
|
||||
emit_once(true, fmt, std::forward<Args>(params)...);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
void warn_once(const char* fmt, Args&&... params)
|
||||
{
|
||||
emit_once(false, fmt, std::forward<Args>(params)...);
|
||||
}
|
||||
|
||||
private:
|
||||
//Internal implementation methods and helpers
|
||||
|
||||
@@ -1214,14 +1259,19 @@ namespace rsx
|
||||
found_desc.width != desc.width || found_desc.height != desc.height)
|
||||
continue;
|
||||
|
||||
if (desc.update_cached)
|
||||
update_image_contents(cmd, It->second.second, desc.external_handle, desc.width, desc.height);
|
||||
|
||||
return It->second.second;
|
||||
}
|
||||
|
||||
image_view_type result = 0;
|
||||
if (!desc.is_cubemap)
|
||||
result = create_temporary_subresource_view(cmd, &desc.external_handle, desc.gcm_format, desc.x, desc.y, desc.width, desc.height);
|
||||
else
|
||||
if (desc.is_copy_cmd)
|
||||
result = generate_atlas_from_images(cmd, desc.gcm_format, desc.width, desc.height, desc.sections_to_copy);
|
||||
else if (desc.is_cubemap)
|
||||
result = generate_cubemap_from_images(cmd, desc.gcm_format, desc.width, desc.external_cubemap_sources);
|
||||
else
|
||||
result = create_temporary_subresource_view(cmd, &desc.external_handle, desc.gcm_format, desc.x, desc.y, desc.width, desc.height);
|
||||
|
||||
if (result)
|
||||
{
|
||||
@@ -1238,7 +1288,7 @@ namespace rsx
|
||||
|
||||
template <typename render_target_type, typename surface_store_type>
|
||||
sampled_image_descriptor process_framebuffer_resource(commandbuffer_type& cmd, render_target_type texptr, u32 texaddr, u32 gcm_format, surface_store_type& m_rtts,
|
||||
u16 tex_width, u16 tex_height, rsx::texture_dimension_extended extended_dimension, bool is_depth)
|
||||
u16 tex_width, u16 tex_height, u16 tex_pitch, rsx::texture_dimension_extended extended_dimension, bool is_depth)
|
||||
{
|
||||
const u32 format = gcm_format & ~(CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN);
|
||||
const auto surface_width = texptr->get_surface_width();
|
||||
@@ -1311,28 +1361,6 @@ namespace rsx
|
||||
u32 internal_height = tex_height;
|
||||
get_native_dimensions(internal_width, internal_height, texptr);
|
||||
|
||||
if (internal_width > surface_width || internal_height > surface_height)
|
||||
{
|
||||
//An AA flag is likely missing
|
||||
//HACK
|
||||
auto aa_mode = texptr->aa_mode;
|
||||
if ((internal_width >> 1) == surface_width)
|
||||
{
|
||||
if (internal_height > surface_height)
|
||||
texptr->aa_mode = rsx::surface_antialiasing::square_centered_4_samples;
|
||||
else
|
||||
texptr->aa_mode = rsx::surface_antialiasing::diagonal_centered_2_samples;
|
||||
|
||||
internal_width = tex_width;
|
||||
internal_height = tex_height;
|
||||
get_native_dimensions(internal_width, internal_height, texptr);
|
||||
}
|
||||
|
||||
internal_width = std::min(internal_width, (u32)surface_width);
|
||||
internal_height = std::min(internal_height, (u32)surface_height);
|
||||
texptr->aa_mode = aa_mode;
|
||||
}
|
||||
|
||||
const bool unnormalized = (gcm_format & CELL_GCM_TEXTURE_UN) != 0;
|
||||
f32 scale_x = (unnormalized)? (1.f / tex_width) : 1.f;
|
||||
f32 scale_y = (unnormalized)? (1.f / tex_height) : 1.f;
|
||||
@@ -1343,9 +1371,54 @@ namespace rsx
|
||||
scale_y = 0.f;
|
||||
}
|
||||
|
||||
bool requires_processing = surface_width != internal_width || surface_height != internal_height;
|
||||
if (internal_width > surface_width || internal_height > surface_height)
|
||||
{
|
||||
auto bpp = get_format_block_size_in_bytes(format);
|
||||
auto overlapping = m_rtts.get_merged_texture_memory_region(texaddr, tex_width, tex_height, tex_pitch, bpp);
|
||||
|
||||
if (overlapping.size() > 1)
|
||||
{
|
||||
const auto w = rsx::apply_resolution_scale(internal_width, true);
|
||||
const auto h = rsx::apply_resolution_scale(internal_height, true);
|
||||
|
||||
sampled_image_descriptor result = { texptr->get_surface(), texaddr, format, 0, 0, w, h,
|
||||
texture_upload_context::framebuffer_storage, is_depth, scale_x, scale_y,
|
||||
rsx::texture_dimension_extended::texture_dimension_2d };
|
||||
|
||||
result.external_subresource_desc.is_copy_cmd = true;
|
||||
result.external_subresource_desc.sections_to_copy.reserve(overlapping.size());
|
||||
|
||||
for (auto §ion : overlapping)
|
||||
{
|
||||
result.external_subresource_desc.sections_to_copy.push_back
|
||||
({
|
||||
section.surface->get_surface(),
|
||||
rsx::apply_resolution_scale(section.src_x, true),
|
||||
rsx::apply_resolution_scale(section.src_y, true),
|
||||
rsx::apply_resolution_scale(section.dst_x, true),
|
||||
rsx::apply_resolution_scale(section.dst_y, true),
|
||||
rsx::apply_resolution_scale(section.width, true),
|
||||
rsx::apply_resolution_scale(section.height, true)
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool requires_processing = surface_width > internal_width || surface_height > internal_height;
|
||||
bool update_subresource_cache = false;
|
||||
if (!requires_processing)
|
||||
{
|
||||
//NOTE: The scale also accounts for sampling outside the RTT region, e.g render to one quadrant but send whole texture for sampling
|
||||
//In these cases, internal dimensions will exceed available surface dimensions. Account for the missing information using scaling (missing data will result in border color)
|
||||
//TODO: Proper gather and stitching without performance loss
|
||||
if (internal_width > surface_width)
|
||||
scale_x *= ((f32)internal_width / surface_width);
|
||||
|
||||
if (internal_height > surface_height)
|
||||
scale_y *= ((f32)internal_height / surface_height);
|
||||
|
||||
if (!is_depth)
|
||||
{
|
||||
for (const auto& tex : m_rtts.m_bound_render_targets)
|
||||
@@ -1356,6 +1429,7 @@ namespace rsx
|
||||
{
|
||||
LOG_WARNING(RSX, "Attempting to sample a currently bound render target @ 0x%x", texaddr);
|
||||
requires_processing = true;
|
||||
update_subresource_cache = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -1375,6 +1449,7 @@ namespace rsx
|
||||
{
|
||||
LOG_WARNING(RSX, "Attempting to sample a currently bound depth surface @ 0x%x", texaddr);
|
||||
requires_processing = true;
|
||||
update_subresource_cache = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1385,12 +1460,22 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
if (!requires_processing)
|
||||
{
|
||||
//Check if we need to do anything about the formats
|
||||
requires_processing = !render_target_format_is_compatible(texptr, format);
|
||||
}
|
||||
|
||||
if (requires_processing)
|
||||
{
|
||||
const auto w = rsx::apply_resolution_scale(internal_width, true);
|
||||
const auto h = rsx::apply_resolution_scale(internal_height, true);
|
||||
return{ texptr->get_surface(), texaddr, format, 0, 0, w, h, texture_upload_context::framebuffer_storage,
|
||||
|
||||
sampled_image_descriptor result = { texptr->get_surface(), texaddr, format, 0, 0, w, h, texture_upload_context::framebuffer_storage,
|
||||
is_depth, scale_x, scale_y, rsx::texture_dimension_extended::texture_dimension_2d };
|
||||
|
||||
result.external_subresource_desc.update_cached = update_subresource_cache;
|
||||
return result;
|
||||
}
|
||||
|
||||
return{ texptr->get_view(), texture_upload_context::framebuffer_storage, is_depth, scale_x, scale_y, rsx::texture_dimension_extended::texture_dimension_2d };
|
||||
@@ -1413,8 +1498,9 @@ namespace rsx
|
||||
const auto extended_dimension = tex.get_extended_texture_dimension();
|
||||
u16 depth = 0;
|
||||
u16 tex_height = (u16)tex.height();
|
||||
u16 tex_pitch = tex.pitch();
|
||||
const u16 tex_width = tex.width();
|
||||
u16 tex_pitch = is_compressed_format? (u16)(get_texture_size(tex) / tex_height) : tex.pitch(); //NOTE: Compressed textures dont have a real pitch (tex_size = (w*h)/6)
|
||||
if (tex_pitch == 0) tex_pitch = tex_width * get_format_block_size_in_bytes(format);
|
||||
|
||||
switch (extended_dimension)
|
||||
{
|
||||
@@ -1439,9 +1525,9 @@ namespace rsx
|
||||
//TODO: When framebuffer Y compression is properly handled, this section can be removed. A more accurate framebuffer storage check exists below this block
|
||||
if (auto texptr = m_rtts.get_texture_from_render_target_if_applicable(texaddr))
|
||||
{
|
||||
if (test_framebuffer(texaddr + texptr->raster_address_offset))
|
||||
if (test_framebuffer(texaddr))
|
||||
{
|
||||
return process_framebuffer_resource(cmd, texptr, texaddr, tex.format(), m_rtts, tex_width, tex_height, extended_dimension, false);
|
||||
return process_framebuffer_resource(cmd, texptr, texaddr, tex.format(), m_rtts, tex_width, tex_height, tex_pitch, extended_dimension, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1452,9 +1538,9 @@ namespace rsx
|
||||
|
||||
if (auto texptr = m_rtts.get_texture_from_depth_stencil_if_applicable(texaddr))
|
||||
{
|
||||
if (test_framebuffer(texaddr + texptr->raster_address_offset))
|
||||
if (test_framebuffer(texaddr))
|
||||
{
|
||||
return process_framebuffer_resource(cmd, texptr, texaddr, tex.format(), m_rtts, tex_width, tex_height, extended_dimension, true);
|
||||
return process_framebuffer_resource(cmd, texptr, texaddr, tex.format(), m_rtts, tex_width, tex_height, tex_pitch, extended_dimension, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1464,9 +1550,6 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
tex_pitch = is_compressed_format? (u16)(get_texture_size(tex) / tex_height) : tex_pitch; //NOTE: Compressed textures dont have a real pitch (tex_size = (w*h)/6)
|
||||
if (tex_pitch == 0) tex_pitch = tex_width * get_format_block_size_in_bytes(format);
|
||||
|
||||
const bool unnormalized = (tex.format() & CELL_GCM_TEXTURE_UN) != 0;
|
||||
f32 scale_x = (unnormalized) ? (1.f / tex_width) : 1.f;
|
||||
f32 scale_y = (unnormalized) ? (1.f / tex_height) : 1.f;
|
||||
@@ -1483,12 +1566,11 @@ namespace rsx
|
||||
* a bound render target. We can bypass the expensive download in this case
|
||||
*/
|
||||
|
||||
//TODO: Take framebuffer Y compression into account
|
||||
const auto rsc = m_rtts.get_surface_subresource_if_applicable(texaddr, tex_width, tex_height, tex_pitch);
|
||||
if (rsc.surface)
|
||||
{
|
||||
//TODO: Check that this region is not cpu-dirty before doing a copy
|
||||
if (!test_framebuffer(rsc.base_address + rsc.surface->raster_address_offset))
|
||||
if (!test_framebuffer(rsc.base_address))
|
||||
{
|
||||
m_rtts.invalidate_surface_address(rsc.base_address, rsc.is_depth_surface);
|
||||
invalidate_address(rsc.base_address, false, true, std::forward<Args>(extras)...);
|
||||
@@ -1562,7 +1644,7 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
if ((!blit_engine_incompatibility_warning_raised && g_cfg.video.use_gpu_texture_scaling) || is_hw_blit_engine_compatible(format))
|
||||
if (is_hw_blit_engine_compatible(format))
|
||||
{
|
||||
//Find based on range instead
|
||||
auto overlapping_surfaces = find_texture_from_range(texaddr, tex_size);
|
||||
@@ -1593,14 +1675,6 @@ namespace rsx
|
||||
break;
|
||||
}
|
||||
|
||||
if (!blit_engine_incompatibility_warning_raised && !is_hw_blit_engine_compatible(format))
|
||||
{
|
||||
LOG_ERROR(RSX, "Format 0x%X is not compatible with the hardware blit acceleration."
|
||||
" Consider turning off GPU texture scaling in the options to partially handle textures on your CPU.", format);
|
||||
blit_engine_incompatibility_warning_raised = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (surface->get_sampler_status() != rsx::texture_sampler_status::status_ready)
|
||||
set_up_remap_vector(*surface, tex.decoded_remap());
|
||||
|
||||
@@ -1620,13 +1694,24 @@ namespace rsx
|
||||
auto subresources_layout = get_subresources_layout(tex);
|
||||
auto remap_vector = tex.decoded_remap();
|
||||
|
||||
bool is_depth_format = false;
|
||||
switch (format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_DEPTH16:
|
||||
case CELL_GCM_TEXTURE_DEPTH16_FLOAT:
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8:
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT:
|
||||
is_depth_format = true;
|
||||
break;
|
||||
}
|
||||
|
||||
//Invalidate with writing=false, discard=false, rebuild=false, native_flush=true
|
||||
invalidate_range_impl_base(texaddr, tex_size, false, false, false, true, std::forward<Args>(extras)...);
|
||||
|
||||
m_texture_memory_in_use += (tex_pitch * tex_height);
|
||||
return{ upload_image_from_cpu(cmd, texaddr, tex_width, tex_height, depth, tex.get_exact_mipmap_count(), tex_pitch, format,
|
||||
texture_upload_context::shader_read, subresources_layout, extended_dimension, is_swizzled, remap_vector)->get_raw_view(),
|
||||
texture_upload_context::shader_read, false, scale_x, scale_y, extended_dimension };
|
||||
texture_upload_context::shader_read, is_depth_format, scale_x, scale_y, extended_dimension };
|
||||
}
|
||||
|
||||
template <typename surface_store_type, typename blitter_type, typename ...Args>
|
||||
@@ -1678,14 +1763,14 @@ namespace rsx
|
||||
src_is_render_target = false;
|
||||
}
|
||||
|
||||
if (src_is_render_target && !test_framebuffer(src_subres.base_address + src_subres.surface->raster_address_offset))
|
||||
if (src_is_render_target && !test_framebuffer(src_subres.base_address))
|
||||
{
|
||||
m_rtts.invalidate_surface_address(src_subres.base_address, src_subres.is_depth_surface);
|
||||
invalidate_address(src_subres.base_address, false, true, std::forward<Args>(extras)...);
|
||||
src_is_render_target = false;
|
||||
}
|
||||
|
||||
if (dst_is_render_target && !test_framebuffer(dst_subres.base_address + dst_subres.surface->raster_address_offset))
|
||||
if (dst_is_render_target && !test_framebuffer(dst_subres.base_address))
|
||||
{
|
||||
m_rtts.invalidate_surface_address(dst_subres.base_address, dst_subres.is_depth_surface);
|
||||
invalidate_address(dst_subres.base_address, false, true, std::forward<Args>(extras)...);
|
||||
@@ -1982,6 +2067,11 @@ namespace rsx
|
||||
cached_dest->reprotect(utils::protection::no);
|
||||
m_cache[get_block_address(cached_dest->get_section_base())].notify();
|
||||
}
|
||||
else if (cached_dest->is_synchronized())
|
||||
{
|
||||
//Prematurely read back
|
||||
m_num_cache_mispredictions++;
|
||||
}
|
||||
|
||||
cached_dest->touch();
|
||||
}
|
||||
@@ -2041,6 +2131,7 @@ namespace rsx
|
||||
{
|
||||
m_num_flush_requests.store(0u);
|
||||
m_num_cache_misses.store(0u);
|
||||
m_num_cache_mispredictions.store(0u);
|
||||
}
|
||||
|
||||
virtual const u32 get_unreleased_textures_count() const
|
||||
@@ -2058,6 +2149,11 @@ namespace rsx
|
||||
return m_num_flush_requests;
|
||||
}
|
||||
|
||||
virtual u32 get_num_cache_mispredictions() const
|
||||
{
|
||||
return m_num_cache_mispredictions;
|
||||
}
|
||||
|
||||
virtual f32 get_cache_miss_ratio() const
|
||||
{
|
||||
const auto num_flushes = m_num_flush_requests.load();
|
||||
|
||||
@@ -306,12 +306,8 @@ void D3D12FragmentDecompiler::insertMainEnd(std::stringstream & OS)
|
||||
{
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", "r1"))
|
||||
{
|
||||
/**
|
||||
* Note: Naruto Shippuden : Ultimate Ninja Storm 2 sets CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS in a shader
|
||||
* but it writes depth in r1.z and not h2.z.
|
||||
* Maybe there's a different flag for depth ?
|
||||
*/
|
||||
// OS << " Out.depth = " << ((m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) ? "r1.z;" : "h2.z;") << "\n";
|
||||
//Depth writes are always from a fp32 register. See issues section on nvidia's NV_fragment_program spec
|
||||
//https://www.khronos.org/registry/OpenGL/extensions/NV/NV_fragment_program.txt
|
||||
OS << " Out.depth = r1.z;\n";
|
||||
}
|
||||
else
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
void data_cache::store_and_protect_data(u64 key, u32 start, size_t size, u8 format, size_t w, size_t h, size_t d, size_t m, ComPtr<ID3D12Resource> data)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mut);
|
||||
std::lock_guard<shared_mutex> lock(m_mut);
|
||||
m_address_to_data[key] = std::make_pair(texture_entry(format, w, h, d, m), data);
|
||||
protect_data(key, start, size);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ void data_cache::protect_data(u64 key, u32 start, size_t size)
|
||||
bool data_cache::invalidate_address(u32 addr)
|
||||
{
|
||||
// In case 2 threads write to texture memory
|
||||
std::lock_guard<std::mutex> lock(m_mut);
|
||||
std::lock_guard<shared_mutex> lock(m_mut);
|
||||
bool handled = false;
|
||||
auto It = m_protected_ranges.begin(), E = m_protected_ranges.end();
|
||||
for (; It != E;)
|
||||
@@ -49,7 +49,7 @@ bool data_cache::invalidate_address(u32 addr)
|
||||
|
||||
std::pair<texture_entry, ComPtr<ID3D12Resource> > *data_cache::find_data_if_available(u64 key)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mut);
|
||||
std::lock_guard<shared_mutex> lock(m_mut);
|
||||
auto It = m_address_to_data.find(key);
|
||||
if (It == m_address_to_data.end())
|
||||
return nullptr;
|
||||
@@ -58,7 +58,7 @@ std::pair<texture_entry, ComPtr<ID3D12Resource> > *data_cache::find_data_if_avai
|
||||
|
||||
void data_cache::unprotect_all()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mut);
|
||||
std::lock_guard<shared_mutex> lock(m_mut);
|
||||
for (auto &protectedTexture : m_protected_ranges)
|
||||
{
|
||||
u32 protectedRangeStart = std::get<1>(protectedTexture), protectedRangeSize = std::get<2>(protectedTexture);
|
||||
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
* Memory protection fault catch can be generated by any thread and
|
||||
* modifies it.
|
||||
*/
|
||||
std::mutex m_mut;
|
||||
shared_mutex m_mut;
|
||||
|
||||
std::unordered_map<u64, std::pair<texture_entry, ComPtr<ID3D12Resource>> > m_address_to_data; // Storage
|
||||
std::list <std::tuple<u64, u32, u32> > m_protected_ranges; // address, start of protected range, size of protected range
|
||||
|
||||
@@ -145,6 +145,11 @@ struct D3D12Traits
|
||||
vertexProgramData.id = (u32)ID;
|
||||
}
|
||||
|
||||
static
|
||||
void validate_pipeline_properties(const vertex_program_type&, const fragment_program_type&, pipeline_properties&)
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
pipeline_storage_type build_pipeline(
|
||||
const vertex_program_type &vertexProgramData, const fragment_program_type &fragmentProgramData, const pipeline_properties &pipelineProperties,
|
||||
|
||||
@@ -348,6 +348,26 @@ void GLFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
|
||||
}
|
||||
}
|
||||
|
||||
OS << "}\n\n";
|
||||
|
||||
OS << "void main()\n";
|
||||
OS << "{\n";
|
||||
|
||||
std::string parameters = "";
|
||||
for (auto ®_name : output_values)
|
||||
{
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", reg_name))
|
||||
{
|
||||
if (parameters.length())
|
||||
parameters += ", ";
|
||||
|
||||
parameters += reg_name;
|
||||
OS << " vec4 " << reg_name << " = vec4(0.);\n";
|
||||
}
|
||||
}
|
||||
|
||||
OS << "\n" << " fs_main(" + parameters + ");\n\n";
|
||||
|
||||
if (!first_output_name.empty())
|
||||
{
|
||||
auto make_comparison_test = [](rsx::comparison_function compare_func, const std::string &test, const std::string &a, const std::string &b) -> std::string
|
||||
@@ -381,29 +401,9 @@ void GLFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
|
||||
}
|
||||
}
|
||||
|
||||
OS << " if (alpha_test != 0 && !comparison_passes(" << first_output_name << ".a, alpha_ref, alpha_func)) discard;\n";
|
||||
OS << " if (alpha_test != 0 && !comparison_passes(" << first_output_name << ".a, alpha_ref, alpha_func)) discard;\n\n";
|
||||
}
|
||||
|
||||
OS << "}\n\n";
|
||||
|
||||
OS << "void main()\n";
|
||||
OS << "{\n";
|
||||
|
||||
std::string parameters = "";
|
||||
for (auto ®_name : output_values)
|
||||
{
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", reg_name))
|
||||
{
|
||||
if (parameters.length())
|
||||
parameters += ", ";
|
||||
|
||||
parameters += reg_name;
|
||||
OS << " vec4 " << reg_name << " = vec4(0.);\n";
|
||||
}
|
||||
}
|
||||
|
||||
OS << "\n" << " fs_main(" + parameters + ");\n\n";
|
||||
|
||||
//Append the color output assignments
|
||||
OS << color_output_block;
|
||||
|
||||
@@ -411,11 +411,8 @@ void GLFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
|
||||
{
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", "r1"))
|
||||
{
|
||||
/** Note: Naruto Shippuden : Ultimate Ninja Storm 2 sets CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS in a shader
|
||||
* but it writes depth in r1.z and not h2.z.
|
||||
* Maybe there's a different flag for depth ?
|
||||
*/
|
||||
//OS << ((m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) ? "\tgl_FragDepth = r1.z;\n" : "\tgl_FragDepth = h0.z;\n") << "\n";
|
||||
//Depth writes are always from a fp32 register. See issues section on nvidia's NV_fragment_program spec
|
||||
//https://www.khronos.org/registry/OpenGL/extensions/NV/NV_fragment_program.txt
|
||||
OS << " gl_FragDepth = r1.z;\n";
|
||||
}
|
||||
else
|
||||
|
||||
+104
-61
@@ -209,7 +209,7 @@ void GLGSRender::end()
|
||||
{
|
||||
std::chrono::time_point<steady_clock> textures_start = steady_clock::now();
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
std::lock_guard<shared_mutex> lock(m_sampler_mutex);
|
||||
void* unused = nullptr;
|
||||
bool update_framebuffer_sourced = false;
|
||||
|
||||
@@ -598,6 +598,7 @@ void GLGSRender::set_viewport()
|
||||
void GLGSRender::on_init_thread()
|
||||
{
|
||||
GSRender::on_init_thread();
|
||||
zcull_ctrl.reset(static_cast<::rsx::reports::ZCULL_control*>(this));
|
||||
|
||||
gl::init();
|
||||
|
||||
@@ -736,14 +737,16 @@ void GLGSRender::on_init_thread()
|
||||
m_index_ring_buffer.reset(new gl::ring_buffer());
|
||||
}
|
||||
|
||||
m_attrib_ring_buffer->create(gl::buffer::target::texture, std::min<GLsizeiptr>(m_max_texbuffer_size, 256 * 0x100000));
|
||||
m_index_ring_buffer->create(gl::buffer::target::element_array, std::min<GLsizeiptr>(m_max_texbuffer_size, 64 * 0x100000));
|
||||
m_transform_constants_buffer->create(gl::buffer::target::uniform, std::min<GLsizeiptr>(m_max_texbuffer_size, 16 * 0x100000));
|
||||
m_fragment_constants_buffer->create(gl::buffer::target::uniform, std::min<GLsizeiptr>(m_max_texbuffer_size, 16 * 0x100000));
|
||||
m_vertex_state_buffer->create(gl::buffer::target::uniform, std::min<GLsizeiptr>(m_max_texbuffer_size, 16 * 0x100000));
|
||||
m_attrib_ring_buffer->create(gl::buffer::target::texture, 256 * 0x100000);
|
||||
m_index_ring_buffer->create(gl::buffer::target::element_array, 64 * 0x100000);
|
||||
m_transform_constants_buffer->create(gl::buffer::target::uniform, 64 * 0x100000);
|
||||
m_fragment_constants_buffer->create(gl::buffer::target::uniform, 16 * 0x100000);
|
||||
m_vertex_state_buffer->create(gl::buffer::target::uniform, 16 * 0x100000);
|
||||
|
||||
m_gl_persistent_stream_buffer.copy_from(*m_attrib_ring_buffer, GL_R8UI, 0, (u32)m_attrib_ring_buffer->size());
|
||||
m_gl_volatile_stream_buffer.copy_from(*m_attrib_ring_buffer, GL_R8UI, 0, (u32)m_attrib_ring_buffer->size());
|
||||
m_persistent_stream_view.update(m_attrib_ring_buffer.get(), 0, m_max_texbuffer_size);
|
||||
m_volatile_stream_view.update(m_attrib_ring_buffer.get(), 0, m_max_texbuffer_size);
|
||||
m_gl_persistent_stream_buffer.copy_from(m_persistent_stream_view);
|
||||
m_gl_volatile_stream_buffer.copy_from(m_volatile_stream_view);
|
||||
|
||||
m_vao.element_array_buffer = *m_index_ring_buffer;
|
||||
|
||||
@@ -766,7 +769,7 @@ void GLGSRender::on_init_thread()
|
||||
for (u32 i = 0; i < occlusion_query_count; ++i)
|
||||
{
|
||||
GLuint handle = 0;
|
||||
auto &query = occlusion_query_data[i];
|
||||
auto &query = m_occlusion_query_data[i];
|
||||
glGenQueries(1, &handle);
|
||||
|
||||
query.driver_handle = (u64)handle;
|
||||
@@ -851,6 +854,8 @@ void GLGSRender::on_init_thread()
|
||||
|
||||
void GLGSRender::on_exit()
|
||||
{
|
||||
zcull_ctrl.release();
|
||||
|
||||
m_prog_buffer.clear();
|
||||
|
||||
if (draw_fbo)
|
||||
@@ -918,7 +923,7 @@ void GLGSRender::on_exit()
|
||||
|
||||
for (u32 i = 0; i < occlusion_query_count; ++i)
|
||||
{
|
||||
auto &query = occlusion_query_data[i];
|
||||
auto &query = m_occlusion_query_data[i];
|
||||
query.active = false;
|
||||
query.pending = false;
|
||||
|
||||
@@ -1034,7 +1039,7 @@ bool GLGSRender::check_program_state()
|
||||
return (rsx::method_registers.shader_program_address() != 0);
|
||||
}
|
||||
|
||||
void GLGSRender::load_program(const vertex_upload_info& upload_info)
|
||||
void GLGSRender::load_program(const gl::vertex_upload_info& upload_info)
|
||||
{
|
||||
get_current_fragment_program(fs_sampler_state);
|
||||
verify(HERE), current_fragment_program.valid;
|
||||
@@ -1110,7 +1115,10 @@ void GLGSRender::load_program(const vertex_upload_info& upload_info)
|
||||
buf = static_cast<u8*>(mapping.first);
|
||||
fragment_constants_offset = mapping.second;
|
||||
if (fragment_constants_size)
|
||||
m_prog_buffer.fill_fragment_constants_buffer({ reinterpret_cast<float*>(buf), gsl::narrow<int>(fragment_constants_size) }, fragment_program);
|
||||
{
|
||||
m_prog_buffer.fill_fragment_constants_buffer({ reinterpret_cast<float*>(buf), gsl::narrow<int>(fragment_constants_size) },
|
||||
fragment_program, gl::get_driver_caps().vendor_NVIDIA);
|
||||
}
|
||||
|
||||
// Fragment state
|
||||
fill_fragment_state_buffer(buf+fragment_constants_size, fragment_program);
|
||||
@@ -1158,20 +1166,6 @@ void GLGSRender::update_draw_state()
|
||||
|
||||
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()),
|
||||
@@ -1193,9 +1187,52 @@ void GLGSRender::update_draw_state()
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
bool mrt_blend_enabled[] =
|
||||
{
|
||||
rsx::method_registers.blend_enabled(),
|
||||
rsx::method_registers.blend_enabled_surface_1(),
|
||||
rsx::method_registers.blend_enabled_surface_2(),
|
||||
rsx::method_registers.blend_enabled_surface_3()
|
||||
};
|
||||
|
||||
bool blend_equation_override = false;
|
||||
if (rsx::method_registers.msaa_alpha_to_coverage_enabled() &&
|
||||
!rsx::method_registers.alpha_test_enabled())
|
||||
{
|
||||
if (rsx::method_registers.msaa_enabled() &&
|
||||
rsx::method_registers.msaa_sample_mask() &&
|
||||
rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::center_1_sample)
|
||||
{
|
||||
//fake alpha-to-coverage
|
||||
//blend used in conjunction with alpha test to fake order-independent edge transparency
|
||||
mrt_blend_enabled[0] = mrt_blend_enabled[1] = mrt_blend_enabled[2] = mrt_blend_enabled[3] = true;
|
||||
blend_equation_override = true;
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
}
|
||||
}
|
||||
|
||||
if (mrt_blend_enabled[0] || mrt_blend_enabled[1] || mrt_blend_enabled[2] || mrt_blend_enabled[3])
|
||||
{
|
||||
if (!blend_equation_override)
|
||||
{
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
gl_state.enablei(mrt_blend_enabled[0], GL_BLEND, 0);
|
||||
gl_state.enablei(mrt_blend_enabled[1], GL_BLEND, 1);
|
||||
gl_state.enablei(mrt_blend_enabled[2], GL_BLEND, 2);
|
||||
gl_state.enablei(mrt_blend_enabled[3], GL_BLEND, 3);
|
||||
|
||||
if (gl_state.enable(rsx::method_registers.logic_op_enabled(), GL_COLOR_LOGIC_OP))
|
||||
{
|
||||
@@ -1365,19 +1402,21 @@ void GLGSRender::flip(int buffer)
|
||||
gl::screen.bind();
|
||||
glViewport(0, 0, m_frame->client_width(), m_frame->client_height());
|
||||
|
||||
m_text_printer.print_text(0, 0, m_frame->client_width(), m_frame->client_height(), "draw calls: " + std::to_string(m_draw_calls));
|
||||
m_text_printer.print_text(0, 18, m_frame->client_width(), m_frame->client_height(), "draw call setup: " + std::to_string(m_begin_time) + "us");
|
||||
m_text_printer.print_text(0, 36, m_frame->client_width(), m_frame->client_height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
|
||||
m_text_printer.print_text(0, 54, m_frame->client_width(), m_frame->client_height(), "textures upload time: " + std::to_string(m_textures_upload_time) + "us");
|
||||
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");
|
||||
m_text_printer.print_text(0, 0, m_frame->client_width(), m_frame->client_height(), "RSX Load: " + std::to_string(get_load()) + "%");
|
||||
m_text_printer.print_text(0, 18, m_frame->client_width(), m_frame->client_height(), "draw calls: " + std::to_string(m_draw_calls));
|
||||
m_text_printer.print_text(0, 36, m_frame->client_width(), m_frame->client_height(), "draw call setup: " + std::to_string(m_begin_time) + "us");
|
||||
m_text_printer.print_text(0, 54, m_frame->client_width(), m_frame->client_height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
|
||||
m_text_printer.print_text(0, 72, m_frame->client_width(), m_frame->client_height(), "textures upload time: " + std::to_string(m_textures_upload_time) + "us");
|
||||
m_text_printer.print_text(0, 90, 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);
|
||||
auto num_flushes = m_gl_texture_cache.get_num_flush_requests();
|
||||
auto cache_miss_ratio = (u32)ceil(m_gl_texture_cache.get_cache_miss_ratio() * 100);
|
||||
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_text_printer.print_text(0, 144, m_frame->client_width(), m_frame->client_height(), "Flush requests: " + std::to_string(num_flushes) + " (" + std::to_string(cache_miss_ratio) + "% hard faults)");
|
||||
const auto num_dirty_textures = m_gl_texture_cache.get_unreleased_textures_count();
|
||||
const auto texture_memory_size = m_gl_texture_cache.get_texture_memory_in_use() / (1024 * 1024);
|
||||
const auto num_flushes = m_gl_texture_cache.get_num_flush_requests();
|
||||
const auto num_mispredict = m_gl_texture_cache.get_num_cache_mispredictions();
|
||||
const auto cache_miss_ratio = (u32)ceil(m_gl_texture_cache.get_cache_miss_ratio() * 100);
|
||||
m_text_printer.print_text(0, 126, m_frame->client_width(), m_frame->client_height(), "Unreleased textures: " + std::to_string(num_dirty_textures));
|
||||
m_text_printer.print_text(0, 144, m_frame->client_width(), m_frame->client_height(), "Texture memory: " + std::to_string(texture_memory_size) + "M");
|
||||
m_text_printer.print_text(0, 162, m_frame->client_width(), m_frame->client_height(), fmt::format("Flush requests: %d (%d%% hard faults, %d mispedictions)", num_flushes, cache_miss_ratio, num_mispredict));
|
||||
}
|
||||
|
||||
m_frame->flip(m_context);
|
||||
@@ -1399,14 +1438,6 @@ void GLGSRender::flip(int buffer)
|
||||
m_textures_upload_time = 0;
|
||||
}
|
||||
|
||||
|
||||
u64 GLGSRender::timestamp() const
|
||||
{
|
||||
GLint64 result;
|
||||
glGetInteger64v(GL_TIMESTAMP, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GLGSRender::on_access_violation(u32 address, bool is_writing)
|
||||
{
|
||||
bool can_flush = (std::this_thread::get_id() == m_thread_id);
|
||||
@@ -1416,7 +1447,7 @@ bool GLGSRender::on_access_violation(u32 address, bool is_writing)
|
||||
return false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
std::lock_guard<shared_mutex> lock(m_sampler_mutex);
|
||||
m_samplers_dirty.store(true);
|
||||
}
|
||||
|
||||
@@ -1444,7 +1475,7 @@ void GLGSRender::on_notify_memory_unmapped(u32 address_base, u32 size)
|
||||
{
|
||||
m_gl_texture_cache.purge_dirty();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
std::lock_guard<shared_mutex> lock(m_sampler_mutex);
|
||||
m_samplers_dirty.store(true);
|
||||
}
|
||||
}
|
||||
@@ -1456,7 +1487,7 @@ void GLGSRender::do_local_task(bool /*idle*/)
|
||||
|
||||
if (!work_queue.empty())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queue_guard);
|
||||
std::lock_guard<shared_mutex> lock(queue_guard);
|
||||
|
||||
work_queue.remove_if([](work_item &q) { return q.received; });
|
||||
|
||||
@@ -1497,7 +1528,7 @@ void GLGSRender::do_local_task(bool /*idle*/)
|
||||
|
||||
work_item& GLGSRender::post_flush_request(u32 address, gl::texture_cache::thrashed_set& flush_data)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queue_guard);
|
||||
std::lock_guard<shared_mutex> lock(queue_guard);
|
||||
|
||||
work_queue.emplace_back();
|
||||
work_item &result = work_queue.back();
|
||||
@@ -1529,31 +1560,43 @@ void GLGSRender::notify_tile_unbound(u32 tile)
|
||||
//m_rtts.invalidate_surface_address(addr, false);
|
||||
}
|
||||
|
||||
void GLGSRender::begin_occlusion_query(rsx::occlusion_query_info* query)
|
||||
void GLGSRender::begin_occlusion_query(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
query->result = 0;
|
||||
glBeginQuery(GL_ANY_SAMPLES_PASSED, (GLuint)query->driver_handle);
|
||||
}
|
||||
|
||||
void GLGSRender::end_occlusion_query(rsx::occlusion_query_info* query)
|
||||
void GLGSRender::end_occlusion_query(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
glEndQuery(GL_ANY_SAMPLES_PASSED);
|
||||
if (query->num_draws)
|
||||
glEndQuery(GL_ANY_SAMPLES_PASSED);
|
||||
}
|
||||
|
||||
bool GLGSRender::check_occlusion_query_status(rsx::occlusion_query_info* query)
|
||||
bool GLGSRender::check_occlusion_query_status(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
if (!query->num_draws)
|
||||
return true;
|
||||
|
||||
GLint status = GL_TRUE;
|
||||
glGetQueryObjectiv((GLuint)query->driver_handle, GL_QUERY_RESULT_AVAILABLE, &status);
|
||||
|
||||
return status != GL_FALSE;
|
||||
}
|
||||
|
||||
void GLGSRender::get_occlusion_query_result(rsx::occlusion_query_info* query)
|
||||
void GLGSRender::get_occlusion_query_result(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
GLint result;
|
||||
glGetQueryObjectiv((GLuint)query->driver_handle, GL_QUERY_RESULT, &result);
|
||||
if (query->num_draws)
|
||||
{
|
||||
GLint result;
|
||||
glGetQueryObjectiv((GLuint)query->driver_handle, GL_QUERY_RESULT, &result);
|
||||
|
||||
query->result += result;
|
||||
query->result += result;
|
||||
}
|
||||
}
|
||||
|
||||
void GLGSRender::discard_occlusion_query(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
glEndQuery(GL_ANY_SAMPLES_PASSED);
|
||||
}
|
||||
|
||||
void GLGSRender::shell_do_cleanup()
|
||||
|
||||
@@ -21,6 +21,16 @@ namespace gl
|
||||
using null_vertex_cache = vertex_cache;
|
||||
|
||||
using shader_cache = rsx::shaders_cache<void*, GLProgramBuffer>;
|
||||
|
||||
struct vertex_upload_info
|
||||
{
|
||||
u32 vertex_draw_count;
|
||||
u32 allocated_vertex_count;
|
||||
u32 vertex_index_base;
|
||||
u32 persistent_mapping_offset;
|
||||
u32 volatile_mapping_offset;
|
||||
std::optional<std::tuple<GLenum, u32> > index_info;
|
||||
};
|
||||
}
|
||||
|
||||
struct work_item
|
||||
@@ -88,7 +98,7 @@ struct driver_state
|
||||
return !!test;
|
||||
}
|
||||
|
||||
const bool test_property(GLenum property, u32 test) const
|
||||
inline bool test_property(GLenum property, u32 test) const
|
||||
{
|
||||
auto found = properties.find(property);
|
||||
if (found == properties.end())
|
||||
@@ -255,17 +265,7 @@ struct driver_state
|
||||
}
|
||||
};
|
||||
|
||||
struct vertex_upload_info
|
||||
{
|
||||
u32 vertex_draw_count;
|
||||
u32 allocated_vertex_count;
|
||||
u32 vertex_index_base;
|
||||
u32 persistent_mapping_offset;
|
||||
u32 volatile_mapping_offset;
|
||||
std::optional<std::tuple<GLenum, u32> > index_info;
|
||||
};
|
||||
|
||||
class GLGSRender : public GSRender
|
||||
class GLGSRender : public GSRender, public ::rsx::reports::ZCULL_control
|
||||
{
|
||||
private:
|
||||
GLFragmentProgram m_fragment_prog;
|
||||
@@ -279,6 +279,8 @@ private:
|
||||
|
||||
gl::texture_cache m_gl_texture_cache;
|
||||
|
||||
gl::buffer_view m_persistent_stream_view;
|
||||
gl::buffer_view m_volatile_stream_view;
|
||||
gl::texture m_gl_persistent_stream_buffer;
|
||||
gl::texture m_gl_volatile_stream_buffer;
|
||||
|
||||
@@ -309,7 +311,7 @@ private:
|
||||
|
||||
std::vector<u64> m_overlay_cleanup_requests;
|
||||
|
||||
std::mutex queue_guard;
|
||||
shared_mutex queue_guard;
|
||||
std::list<work_item> work_queue;
|
||||
|
||||
bool flush_draw_buffers = false;
|
||||
@@ -325,7 +327,7 @@ private:
|
||||
//vaos are mandatory for core profile
|
||||
gl::vao m_vao;
|
||||
|
||||
std::mutex m_sampler_mutex;
|
||||
shared_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 = {};
|
||||
@@ -340,14 +342,14 @@ private:
|
||||
driver_state gl_state;
|
||||
|
||||
// Return element to draw and in case of indexed draw index type and offset in index buffer
|
||||
vertex_upload_info set_vertex_buffer();
|
||||
gl::vertex_upload_info set_vertex_buffer();
|
||||
rsx::vertex_input_layout m_vertex_layout = {};
|
||||
|
||||
void clear_surface(u32 arg);
|
||||
void init_buffers(rsx::framebuffer_creation_context context, bool skip_reading = false);
|
||||
|
||||
bool check_program_state();
|
||||
void load_program(const vertex_upload_info& upload_info);
|
||||
void load_program(const gl::vertex_upload_info& upload_info);
|
||||
|
||||
void update_draw_state();
|
||||
|
||||
@@ -361,10 +363,11 @@ public:
|
||||
|
||||
bool scaled_image_from_memory(rsx::blit_src_info& src_info, rsx::blit_dst_info& dst_info, bool interpolate) override;
|
||||
|
||||
void begin_occlusion_query(rsx::occlusion_query_info* query) override;
|
||||
void end_occlusion_query(rsx::occlusion_query_info* query) override;
|
||||
bool check_occlusion_query_status(rsx::occlusion_query_info* query) override;
|
||||
void get_occlusion_query_result(rsx::occlusion_query_info* query) override;
|
||||
void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
void end_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
bool check_occlusion_query_status(rsx::reports::occlusion_query_info* query) override;
|
||||
void get_occlusion_query_result(rsx::reports::occlusion_query_info* query) override;
|
||||
void discard_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
|
||||
protected:
|
||||
void begin() override;
|
||||
@@ -374,7 +377,6 @@ protected:
|
||||
void on_exit() override;
|
||||
bool do_method(u32 id, u32 arg) override;
|
||||
void flip(int buffer) override;
|
||||
u64 timestamp() const override;
|
||||
|
||||
void do_local_task(bool idle) override;
|
||||
|
||||
|
||||
@@ -85,8 +85,9 @@ namespace gl
|
||||
bool ARB_texture_barrier_supported = false;
|
||||
bool NV_texture_barrier_supported = false;
|
||||
bool initialized = false;
|
||||
bool vendor_INTEL = false;
|
||||
bool vendor_AMD = false;
|
||||
bool vendor_INTEL = false; //has broken GLSL compiler
|
||||
bool vendor_AMD = false; //has broken ARB_multidraw
|
||||
bool vendor_NVIDIA = false; //has NaN poisoning issues
|
||||
|
||||
void initialize()
|
||||
{
|
||||
@@ -104,35 +105,35 @@ namespace gl
|
||||
if (ext_name == "GL_ARB_shader_draw_parameters")
|
||||
{
|
||||
ARB_shader_draw_parameters_supported = true;
|
||||
find_count --;
|
||||
find_count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ext_name == "GL_EXT_direct_state_access")
|
||||
{
|
||||
EXT_dsa_supported = true;
|
||||
find_count --;
|
||||
find_count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ext_name == "GL_ARB_direct_state_access")
|
||||
{
|
||||
ARB_dsa_supported = true;
|
||||
find_count --;
|
||||
find_count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ext_name == "GL_ARB_buffer_storage")
|
||||
{
|
||||
ARB_buffer_storage_supported = true;
|
||||
find_count --;
|
||||
find_count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ext_name == "GL_ARB_texture_buffer_object")
|
||||
{
|
||||
ARB_texture_buffer_supported = true;
|
||||
find_count --;
|
||||
find_count--;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -195,6 +196,10 @@ namespace gl
|
||||
if (!EXT_dsa_supported && glGetTextureImageEXT && glTextureBufferRangeEXT)
|
||||
EXT_dsa_supported = true;
|
||||
}
|
||||
else if (vendor_string.find("nvidia") != std::string::npos)
|
||||
{
|
||||
vendor_NVIDIA = true;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else if (vendor_string.find("amd") != std::string::npos || vendor_string.find("ati") != std::string::npos)
|
||||
{
|
||||
@@ -864,7 +869,7 @@ namespace gl
|
||||
buffer::create();
|
||||
|
||||
glBindBuffer((GLenum)m_target, m_id);
|
||||
glBufferStorage((GLenum)m_target, size, data, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_CLIENT_STORAGE_BIT | GL_MAP_COHERENT_BIT);
|
||||
glBufferStorage((GLenum)m_target, size, data, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
||||
m_memory_mapping = glMapBufferRange((GLenum)m_target, 0, size, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
|
||||
|
||||
verify(HERE), m_memory_mapping != nullptr;
|
||||
@@ -886,15 +891,19 @@ namespace gl
|
||||
if ((offset + alloc_size) > m_size)
|
||||
{
|
||||
if (!m_fence.is_empty())
|
||||
{
|
||||
m_fence.wait_for_signal();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR(RSX, "OOM Error: Ring buffer was likely being used without notify() being called");
|
||||
glFinish();
|
||||
}
|
||||
|
||||
m_data_loc = 0;
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
if (!m_data_loc)
|
||||
m_fence.reset();
|
||||
|
||||
//Align data loc to 256; allows some "guard" region so we dont trample our own data inadvertently
|
||||
m_data_loc = align(offset + alloc_size, 256);
|
||||
return std::make_pair(((char*)m_memory_mapping) + offset, offset);
|
||||
@@ -928,7 +937,8 @@ namespace gl
|
||||
//Notification of a draw command
|
||||
virtual void notify()
|
||||
{
|
||||
if (m_fence.is_empty())
|
||||
//Insert fence about 25% into the buffer
|
||||
if (m_fence.is_empty() && (m_data_loc > (m_size >> 2)))
|
||||
m_fence.reset();
|
||||
}
|
||||
};
|
||||
@@ -1046,6 +1056,69 @@ namespace gl
|
||||
void notify() override {}
|
||||
};
|
||||
|
||||
class buffer_view
|
||||
{
|
||||
buffer* m_buffer = nullptr;
|
||||
u32 m_offset = 0;
|
||||
u32 m_range = 0;
|
||||
GLenum m_format = GL_R8UI;
|
||||
|
||||
public:
|
||||
buffer_view(buffer *_buffer, u32 offset, u32 range, GLenum format = GL_R8UI)
|
||||
: m_buffer(_buffer), m_offset(offset), m_range(range), m_format(format)
|
||||
{}
|
||||
|
||||
buffer_view()
|
||||
{}
|
||||
|
||||
void update(buffer *_buffer, u32 offset, u32 range, GLenum format = GL_R8UI)
|
||||
{
|
||||
m_buffer = _buffer;
|
||||
m_offset = offset;
|
||||
m_range = range;
|
||||
m_format = format;
|
||||
}
|
||||
|
||||
u32 offset() const
|
||||
{
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
u32 range() const
|
||||
{
|
||||
return m_range;
|
||||
}
|
||||
|
||||
u32 format() const
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
buffer* value() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
bool in_range(u32 address, u32 size, u32& new_offset) const
|
||||
{
|
||||
if (address < m_offset)
|
||||
return false;
|
||||
|
||||
const u32 _offset = address - m_offset;
|
||||
if (m_range < _offset)
|
||||
return false;
|
||||
|
||||
const auto remaining = m_range - _offset;
|
||||
if (size <= remaining)
|
||||
{
|
||||
new_offset = _offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class vao
|
||||
{
|
||||
template<buffer::target BindId, uint GetStateId>
|
||||
@@ -1681,6 +1754,11 @@ namespace gl
|
||||
__glcheck glTextureBufferRange(id(), gl_format_type, buf.id(), offset, length);
|
||||
}
|
||||
|
||||
void copy_from(buffer_view &view)
|
||||
{
|
||||
copy_from(*view.value(), view.format(), view.offset(), view.range());
|
||||
}
|
||||
|
||||
void copy_from(const buffer& buf, texture::format format, texture::type type, class pixel_unpack_settings pixel_settings)
|
||||
{
|
||||
buffer::save_binding_state save_buffer(buffer::target::pixel_unpack, buf);
|
||||
@@ -2765,17 +2843,4 @@ namespace gl
|
||||
set_id(0);
|
||||
}
|
||||
};
|
||||
|
||||
class buffer_view : public buffer
|
||||
{
|
||||
public:
|
||||
buffer_view(GLuint id) : buffer(id)
|
||||
{
|
||||
}
|
||||
|
||||
~buffer_view()
|
||||
{
|
||||
set_id(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ struct GLTraits
|
||||
vertexProgramData.Compile();
|
||||
}
|
||||
|
||||
static
|
||||
void validate_pipeline_properties(const vertex_program_type&, const fragment_program_type&, pipeline_properties&)
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
pipeline_storage_type build_pipeline(const vertex_program_type &vertexProgramData, const fragment_program_type &fragmentProgramData, const pipeline_properties&)
|
||||
{
|
||||
|
||||
@@ -284,6 +284,47 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk
|
||||
return;
|
||||
}
|
||||
|
||||
const auto aa_mode = rsx::method_registers.surface_antialias();
|
||||
const auto bpp = get_format_block_size_in_bytes(surface_format);
|
||||
const u32 aa_factor = (aa_mode == rsx::surface_antialiasing::center_1_sample || aa_mode == rsx::surface_antialiasing::diagonal_centered_2_samples) ? 1 : 2;
|
||||
|
||||
//Window (raster) offsets
|
||||
const auto window_offset_x = rsx::method_registers.window_offset_x();
|
||||
const auto window_offset_y = rsx::method_registers.window_offset_y();
|
||||
const auto window_clip_width = rsx::method_registers.window_clip_horizontal();
|
||||
const auto window_clip_height = rsx::method_registers.window_clip_vertical();
|
||||
|
||||
if (window_offset_x || window_offset_y)
|
||||
{
|
||||
//Window offset is what affects the raster position!
|
||||
//Tested with Turbo: Super stunt squad that only changes the window offset to declare new framebuffers
|
||||
//Sampling behavior clearly indicates the addresses are expected to have changed
|
||||
if (auto clip_type = rsx::method_registers.window_clip_type())
|
||||
LOG_ERROR(RSX, "Unknown window clip type 0x%X" HERE, clip_type);
|
||||
|
||||
for (const auto &index : rsx::utility::get_rtt_indexes(target))
|
||||
{
|
||||
if (surface_addresses[index])
|
||||
{
|
||||
const u32 window_offset_bytes = (std::max<u32>(pitchs[index], clip_horizontal * aa_factor * bpp) * window_offset_y) + ((aa_factor * bpp) * window_offset_x);
|
||||
surface_addresses[index] += window_offset_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
if (depth_address)
|
||||
{
|
||||
const auto depth_bpp = depth_format == rsx::surface_depth_format::z16 ? 2 : 4;
|
||||
depth_address += (std::max<u32>(zeta_pitch, clip_horizontal * aa_factor * depth_bpp) * window_offset_y) + ((aa_factor * depth_bpp) * window_offset_x);
|
||||
}
|
||||
}
|
||||
|
||||
if ((window_clip_width && window_clip_width < clip_horizontal) ||
|
||||
(window_clip_height && window_clip_height < clip_vertical))
|
||||
{
|
||||
LOG_ERROR(RSX, "Unexpected window clip dimensions: window_clip=%dx%d, surface_clip=%dx%d",
|
||||
window_clip_width, window_clip_height, clip_horizontal, clip_vertical);
|
||||
}
|
||||
|
||||
if (draw_fbo)
|
||||
{
|
||||
bool really_changed = false;
|
||||
@@ -323,9 +364,6 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk
|
||||
|
||||
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);
|
||||
const u32 aa_factor = (aa_mode == rsx::surface_antialiasing::center_1_sample || aa_mode == rsx::surface_antialiasing::diagonal_centered_2_samples) ? 1 : 2;
|
||||
|
||||
for (int i = 0; i < rsx::limits::color_buffers_count; ++i)
|
||||
{
|
||||
@@ -351,10 +389,8 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk
|
||||
|
||||
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]);
|
||||
|
||||
m_gl_texture_cache.tag_framebuffer(surface_addresses[i] + rtt->raster_address_offset);
|
||||
m_gl_texture_cache.tag_framebuffer(surface_addresses[i]);
|
||||
}
|
||||
else
|
||||
m_surface_info[i] = {};
|
||||
@@ -372,13 +408,8 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk
|
||||
}
|
||||
|
||||
auto ds = std::get<1>(m_rtts.m_bound_depth_stencil);
|
||||
u8 texel_size = 2;
|
||||
|
||||
if (depth_format == rsx::surface_depth_format::z24s8)
|
||||
{
|
||||
draw_fbo.depth_stencil = *ds;
|
||||
texel_size = 4;
|
||||
}
|
||||
else
|
||||
draw_fbo.depth = *ds;
|
||||
|
||||
@@ -387,10 +418,9 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk
|
||||
m_depth_surface_info = { depth_address, depth_surface_pitch, true, surface_format, depth_format, clip_horizontal, clip_vertical };
|
||||
|
||||
ds->aa_mode = aa_mode;
|
||||
ds->set_raster_offset(clip_x, clip_y, texel_size);
|
||||
m_gl_texture_cache.notify_surface_changed(depth_address);
|
||||
|
||||
m_gl_texture_cache.tag_framebuffer(depth_address + ds->raster_address_offset);
|
||||
m_gl_texture_cache.tag_framebuffer(depth_address);
|
||||
}
|
||||
else
|
||||
m_depth_surface_info = {};
|
||||
@@ -398,7 +428,7 @@ void GLGSRender::init_buffers(rsx::framebuffer_creation_context context, bool sk
|
||||
framebuffer_status_valid = draw_fbo.check();
|
||||
if (!framebuffer_status_valid) return;
|
||||
|
||||
check_zcull_status(true, false);
|
||||
check_zcull_status(true);
|
||||
set_viewport();
|
||||
|
||||
switch (rsx::method_registers.surface_color_target())
|
||||
|
||||
@@ -654,10 +654,13 @@ namespace gl
|
||||
m_temporary_surfaces.resize(0);
|
||||
}
|
||||
|
||||
u32 create_temporary_subresource_impl(u32 src_id, GLenum sized_internal_fmt, GLenum dst_type, u16 x, u16 y, u16 width, u16 height)
|
||||
u32 create_temporary_subresource_impl(u32 src_id, GLenum sized_internal_fmt, GLenum dst_type, u32 gcm_format, u16 x, u16 y, u16 width, u16 height, bool copy = true)
|
||||
{
|
||||
u32 dst_id = 0;
|
||||
|
||||
if (sized_internal_fmt == GL_NONE)
|
||||
sized_internal_fmt = gl::get_sized_internal_format(gcm_format);
|
||||
|
||||
GLenum ifmt;
|
||||
glBindTexture(GL_TEXTURE_2D, src_id);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, (GLint*)&ifmt);
|
||||
@@ -686,17 +689,27 @@ namespace gl
|
||||
|
||||
m_temporary_surfaces.push_back(dst_id);
|
||||
|
||||
//Empty GL_ERROR
|
||||
glGetError();
|
||||
|
||||
glCopyImageSubData(src_id, GL_TEXTURE_2D, 0, x, y, 0,
|
||||
dst_id, dst_type, 0, 0, 0, 0, width, height, 1);
|
||||
|
||||
//Check for error
|
||||
if (GLenum err = glGetError())
|
||||
if (copy)
|
||||
{
|
||||
LOG_WARNING(RSX, "Failed to copy image subresource with GL error 0x%X", err);
|
||||
return 0;
|
||||
//Empty GL_ERROR
|
||||
glGetError();
|
||||
|
||||
glCopyImageSubData(src_id, GL_TEXTURE_2D, 0, x, y, 0,
|
||||
dst_id, dst_type, 0, 0, 0, 0, width, height, 1);
|
||||
|
||||
//Check for error
|
||||
if (GLenum err = glGetError())
|
||||
{
|
||||
LOG_WARNING(RSX, "Failed to copy image subresource with GL error 0x%X", err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ifmt != sized_internal_fmt)
|
||||
{
|
||||
err_once("GL format mismatch (data cast?). Sized ifmt=0x%X vs Src ifmt=0x%X", sized_internal_fmt, ifmt);
|
||||
//Apply base component map onto the new texture if a data cast has been done
|
||||
apply_component_mapping_flags(dst_type, gcm_format, rsx::texture_create_flags::default_component_order);
|
||||
}
|
||||
|
||||
return dst_id;
|
||||
@@ -761,20 +774,18 @@ 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_impl(*src, ifmt, GL_TEXTURE_2D, x, y, w, h);
|
||||
return create_temporary_subresource_impl(*src, GL_NONE, GL_TEXTURE_2D, gcm_format, 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_impl(src->id(), (GLenum)as_rtt->get_compatible_internal_format(), GL_TEXTURE_2D, x, y, w, h);
|
||||
return create_temporary_subresource_impl(src->id(), (GLenum)as_rtt->get_compatible_internal_format(), GL_TEXTURE_2D, gcm_format, x, y, w, h);
|
||||
}
|
||||
else
|
||||
{
|
||||
const GLenum ifmt = gl::get_sized_internal_format(gcm_format);
|
||||
return create_temporary_subresource_impl(src->id(), ifmt, GL_TEXTURE_2D, x, y, w, h);
|
||||
return create_temporary_subresource_impl(src->id(), GL_NONE, GL_TEXTURE_2D, gcm_format, x, y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,6 +826,25 @@ namespace gl
|
||||
return dst_id;
|
||||
}
|
||||
|
||||
u32 generate_atlas_from_images(void*&, u32 gcm_format, u16 width, u16 height, const std::vector<copy_region_descriptor>& sections_to_copy) override
|
||||
{
|
||||
auto result = create_temporary_subresource_impl(sections_to_copy.front().src, GL_NONE, GL_TEXTURE_2D, gcm_format, 0, 0, width, height, false);
|
||||
|
||||
for (const auto ®ion : sections_to_copy)
|
||||
{
|
||||
glCopyImageSubData(region.src, GL_TEXTURE_2D, 0, region.src_x, region.src_y, 0,
|
||||
result, GL_TEXTURE_2D, 0, region.dst_x, region.dst_y, 0, region.w, region.h, 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void update_image_contents(void*&, u32 dst, u32 src, u16 width, u16 height) override
|
||||
{
|
||||
glCopyImageSubData(src, GL_TEXTURE_2D, 0, 0, 0, 0,
|
||||
dst, GL_TEXTURE_2D, 0, 0, 0, 0, width, height, 1);
|
||||
}
|
||||
|
||||
cached_texture_section* create_new_texture(void*&, u32 rsx_address, u32 rsx_size, u16 width, u16 height, u16 depth, u16 mipmaps, u32 gcm_format,
|
||||
rsx::texture_upload_context context, rsx::texture_dimension_extended type, rsx::texture_create_flags flags,
|
||||
const std::pair<std::array<u8, 4>, std::array<u8, 4>>& /*remap_vector*/) override
|
||||
@@ -947,6 +977,48 @@ namespace gl
|
||||
glTextureBarrierNV();
|
||||
}
|
||||
|
||||
bool render_target_format_is_compatible(gl::texture* tex, u32 gcm_format) override
|
||||
{
|
||||
if (auto as_rtt = dynamic_cast<gl::render_target*>(tex))
|
||||
{
|
||||
auto ifmt = as_rtt->get_compatible_internal_format();
|
||||
switch (gcm_format)
|
||||
{
|
||||
default:
|
||||
//TODO
|
||||
err_once("Format incompatibility detected, reporting failure to force data copy (GL_INTERNAL_FORMAT=0x%X, GCM_FORMAT=0x%X)", (u32)ifmt, gcm_format);
|
||||
return false;
|
||||
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT:
|
||||
return (ifmt == gl::texture::internal_format::rgba16f);
|
||||
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT:
|
||||
return (ifmt == gl::texture::internal_format::rgba32f);
|
||||
case CELL_GCM_TEXTURE_X32_FLOAT:
|
||||
return (ifmt == gl::texture::internal_format::r32f);
|
||||
case CELL_GCM_TEXTURE_R5G6B5:
|
||||
return (ifmt == gl::texture::internal_format::r5g6b5);
|
||||
case CELL_GCM_TEXTURE_A8R8G8B8:
|
||||
return (ifmt == gl::texture::internal_format::rgba8 ||
|
||||
ifmt == gl::texture::internal_format::depth24_stencil8 ||
|
||||
ifmt == gl::texture::internal_format::depth32f_stencil8);
|
||||
case CELL_GCM_TEXTURE_B8:
|
||||
return (ifmt == gl::texture::internal_format::r8);
|
||||
case CELL_GCM_TEXTURE_G8B8:
|
||||
return (ifmt == gl::texture::internal_format::rg8);
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8:
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT:
|
||||
return (ifmt == gl::texture::internal_format::depth24_stencil8 ||
|
||||
ifmt == gl::texture::internal_format::depth32f_stencil8 ||
|
||||
ifmt == gl::texture::internal_format::depth_stencil);
|
||||
case CELL_GCM_TEXTURE_DEPTH16:
|
||||
case CELL_GCM_TEXTURE_DEPTH16_FLOAT:
|
||||
return (ifmt == gl::texture::internal_format::depth16 ||
|
||||
ifmt == gl::texture::internal_format::depth);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::throw_exception("Format comparison for non-rendertargets is not implemented" HERE);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
texture_cache() {}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace
|
||||
gsl::span<gsl::byte> dst{ reinterpret_cast<gsl::byte*>(ptr), ::narrow<u32>(block_sz) };
|
||||
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); });
|
||||
[](auto prim) { return !gl::is_primitive_native(prim); }, rsx::method_registers.vertex_data_base_index());
|
||||
|
||||
return std::make_tuple(min_index, max_index, vertex_draw_count);
|
||||
}
|
||||
@@ -180,7 +180,7 @@ namespace
|
||||
};
|
||||
}
|
||||
|
||||
vertex_upload_info GLGSRender::set_vertex_buffer()
|
||||
gl::vertex_upload_info GLGSRender::set_vertex_buffer()
|
||||
{
|
||||
std::chrono::time_point<steady_clock> then = steady_clock::now();
|
||||
|
||||
@@ -196,7 +196,7 @@ vertex_upload_info GLGSRender::set_vertex_buffer()
|
||||
auto required = calculate_memory_requirements(m_vertex_layout, vertex_count);
|
||||
|
||||
std::pair<void*, u32> persistent_mapping = {}, volatile_mapping = {};
|
||||
vertex_upload_info upload_info = { result.vertex_draw_count, result.allocated_vertex_count, result.vertex_index_base, 0u, 0u, result.index_info };
|
||||
gl::vertex_upload_info upload_info = { result.vertex_draw_count, result.allocated_vertex_count, result.vertex_index_base, 0u, 0u, result.index_info };
|
||||
|
||||
if (required.first > 0)
|
||||
{
|
||||
@@ -233,12 +233,32 @@ vertex_upload_info GLGSRender::set_vertex_buffer()
|
||||
m_vertex_cache->store_range(storage_address, GL_R8UI, required.first, persistent_mapping.second);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_persistent_stream_view.in_range(upload_info.persistent_mapping_offset, required.first, upload_info.persistent_mapping_offset))
|
||||
{
|
||||
const size_t view_size = ((upload_info.persistent_mapping_offset + m_max_texbuffer_size) > m_attrib_ring_buffer->size()) ?
|
||||
(m_attrib_ring_buffer->size() - upload_info.persistent_mapping_offset) : m_max_texbuffer_size;
|
||||
|
||||
m_persistent_stream_view.update(m_attrib_ring_buffer.get(), upload_info.persistent_mapping_offset, (u32)view_size);
|
||||
m_gl_persistent_stream_buffer.copy_from(m_persistent_stream_view);
|
||||
upload_info.persistent_mapping_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (required.second > 0)
|
||||
{
|
||||
volatile_mapping = m_attrib_ring_buffer->alloc_from_heap(required.second, m_min_texbuffer_alignment);
|
||||
upload_info.volatile_mapping_offset = volatile_mapping.second;
|
||||
|
||||
if (!m_volatile_stream_view.in_range(upload_info.volatile_mapping_offset, required.second, upload_info.volatile_mapping_offset))
|
||||
{
|
||||
const size_t view_size = ((upload_info.volatile_mapping_offset + m_max_texbuffer_size) > m_attrib_ring_buffer->size()) ?
|
||||
(m_attrib_ring_buffer->size() - upload_info.volatile_mapping_offset) : m_max_texbuffer_size;
|
||||
|
||||
m_volatile_stream_view.update(m_attrib_ring_buffer.get(), upload_info.volatile_mapping_offset, (u32)view_size);
|
||||
m_gl_volatile_stream_buffer.copy_from(m_volatile_stream_view);
|
||||
upload_info.volatile_mapping_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Write all the data
|
||||
|
||||
+619
-155
@@ -89,6 +89,13 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
// The rsx internally adds the 'data_base_offset' and the 'vert_offset' and masks it
|
||||
// before actually attempting to translate to the internal address. Seen happening heavily in R&C games
|
||||
u32 get_vertex_offset_from_base(u32 vert_data_base_offset, u32 vert_base_offset)
|
||||
{
|
||||
return ((u64)vert_data_base_offset + vert_base_offset) & 0xFFFFFFF;
|
||||
}
|
||||
|
||||
u32 get_vertex_type_size_on_host(vertex_base_type type, u32 size)
|
||||
{
|
||||
switch (type)
|
||||
@@ -330,7 +337,6 @@ namespace rsx
|
||||
|
||||
void thread::end()
|
||||
{
|
||||
rsx::method_registers.transform_constants.clear();
|
||||
in_begin_end = false;
|
||||
|
||||
for (u8 index = 0; index < rsx::limits::vertex_count; ++index)
|
||||
@@ -343,8 +349,8 @@ namespace rsx
|
||||
|
||||
element_push_buffer.resize(0);
|
||||
|
||||
if (zcull_task_queue.active_query && zcull_task_queue.active_query->active)
|
||||
zcull_task_queue.active_query->num_draws++;
|
||||
if (zcull_ctrl->active)
|
||||
zcull_ctrl->on_draw();
|
||||
|
||||
if (capture_current_frame)
|
||||
{
|
||||
@@ -359,6 +365,12 @@ namespace rsx
|
||||
|
||||
reset();
|
||||
|
||||
if (!zcull_ctrl)
|
||||
{
|
||||
//Backend did not provide an implementation, provide NULL object
|
||||
zcull_ctrl = std::make_unique<::rsx::reports::ZCULL_control>();
|
||||
}
|
||||
|
||||
last_flip_time = get_system_time() - 1000000;
|
||||
|
||||
thread_ctrl::spawn(m_vblank_thread, "VBlank Thread", [this]()
|
||||
@@ -497,6 +509,9 @@ namespace rsx
|
||||
//Execute backend-local tasks first
|
||||
do_local_task(ctrl->put.load() == internal_get.load());
|
||||
|
||||
//Update sub-units
|
||||
zcull_ctrl->update(this);
|
||||
|
||||
//Set up restore state if needed
|
||||
if (sync_point_request)
|
||||
{
|
||||
@@ -520,12 +535,29 @@ namespace rsx
|
||||
if (put == internal_get || !Emu.IsRunning())
|
||||
{
|
||||
if (has_deferred_call)
|
||||
{
|
||||
flush_command_queue();
|
||||
}
|
||||
else if (!performance_counters.FIFO_is_idle)
|
||||
{
|
||||
performance_counters.FIFO_idle_timestamp = get_system_time();
|
||||
performance_counters.FIFO_is_idle = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
do_internal_task();
|
||||
}
|
||||
|
||||
do_internal_task();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (performance_counters.FIFO_is_idle)
|
||||
{
|
||||
//Update performance counters with time spent in idle mode
|
||||
performance_counters.FIFO_is_idle = false;
|
||||
performance_counters.idle_time += (get_system_time() - performance_counters.FIFO_idle_timestamp);
|
||||
}
|
||||
|
||||
//Validate put and get registers
|
||||
//TODO: Who should handle graphics exceptions??
|
||||
const u32 get_address = RSXIOMem.RealAddr(internal_get);
|
||||
@@ -729,18 +761,58 @@ namespace rsx
|
||||
flush_commands_flag = false;
|
||||
break;
|
||||
}
|
||||
case NV4097_SET_TEXTURE_OFFSET:
|
||||
case NV4097_SET_TEXTURE_FORMAT:
|
||||
case NV4097_SET_TEXTURE_ADDRESS:
|
||||
case NV4097_SET_TEXTURE_CONTROL0:
|
||||
case NV4097_SET_TEXTURE_CONTROL1:
|
||||
case NV4097_SET_TEXTURE_FILTER:
|
||||
case NV4097_SET_TEXTURE_IMAGE_RECT:
|
||||
case NV4097_SET_TEXTURE_BORDER_COLOR:
|
||||
default:
|
||||
{
|
||||
//Safe to ignore if value has not changed
|
||||
if (method_registers.test(reg, value))
|
||||
flush_commands_flag = false;
|
||||
//TODO: Reorder draw commands between synchronization events to maximize batched sizes
|
||||
static const std::pair<u32, u32> skippable_ranges[] =
|
||||
{
|
||||
//Texture configuration
|
||||
{ NV4097_SET_TEXTURE_OFFSET, 8 * 16 },
|
||||
{ NV4097_SET_TEXTURE_CONTROL2, 16 },
|
||||
{ NV4097_SET_TEXTURE_CONTROL3, 16 },
|
||||
{ NV4097_SET_VERTEX_TEXTURE_OFFSET, 8 * 4 },
|
||||
//Surface configuration
|
||||
{ NV4097_SET_SURFACE_CLIP_HORIZONTAL, 1 },
|
||||
{ NV4097_SET_SURFACE_CLIP_VERTICAL, 1 },
|
||||
{ NV4097_SET_SURFACE_COLOR_AOFFSET, 1 },
|
||||
{ NV4097_SET_SURFACE_COLOR_BOFFSET, 1 },
|
||||
{ NV4097_SET_SURFACE_COLOR_COFFSET, 1 },
|
||||
{ NV4097_SET_SURFACE_COLOR_DOFFSET, 1 },
|
||||
{ NV4097_SET_SURFACE_ZETA_OFFSET, 1 },
|
||||
{ NV4097_SET_CONTEXT_DMA_COLOR_A, 1 },
|
||||
{ NV4097_SET_CONTEXT_DMA_COLOR_B, 1 },
|
||||
{ NV4097_SET_CONTEXT_DMA_COLOR_C, 1 },
|
||||
{ NV4097_SET_CONTEXT_DMA_COLOR_D, 1 },
|
||||
{ NV4097_SET_CONTEXT_DMA_ZETA, 1 },
|
||||
{ NV4097_SET_SURFACE_FORMAT, 1 },
|
||||
{ NV4097_SET_SURFACE_PITCH_A, 1 },
|
||||
{ NV4097_SET_SURFACE_PITCH_B, 1 },
|
||||
{ NV4097_SET_SURFACE_PITCH_C, 1 },
|
||||
{ NV4097_SET_SURFACE_PITCH_D, 1 },
|
||||
{ NV4097_SET_SURFACE_PITCH_Z, 1 }
|
||||
};
|
||||
|
||||
if (has_deferred_call)
|
||||
{
|
||||
//Hopefully this is skippable so the batch can keep growing
|
||||
for (const auto &method : skippable_ranges)
|
||||
{
|
||||
if (reg < method.first)
|
||||
continue;
|
||||
|
||||
if (reg - method.first < method.second)
|
||||
{
|
||||
//Safe to ignore if value has not changed
|
||||
if (method_registers.test(reg, value))
|
||||
{
|
||||
execute_method_call = false;
|
||||
flush_commands_flag = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -876,22 +948,33 @@ namespace rsx
|
||||
*/
|
||||
void thread::fill_vertex_program_constants_data(void *buffer)
|
||||
{
|
||||
//Some games dont initialize some registers that they use in the vertex stage
|
||||
memset(buffer, 0, 512 * 4 * sizeof(float));
|
||||
|
||||
for (const auto &entry : rsx::method_registers.transform_constants)
|
||||
local_transform_constants[entry.first] = entry.second;
|
||||
for (const auto &entry : local_transform_constants)
|
||||
stream_vector_from_memory((char*)buffer + entry.first * 4 * sizeof(float), (void*)entry.second.rgba);
|
||||
memcpy(buffer, rsx::method_registers.transform_constants.data(), 468 * 4 * sizeof(float));
|
||||
}
|
||||
|
||||
void thread::fill_fragment_state_buffer(void *buffer, const RSXFragmentProgram &fragment_program)
|
||||
{
|
||||
const u32 is_alpha_tested = rsx::method_registers.alpha_test_enabled();
|
||||
const f32 alpha_ref = rsx::method_registers.alpha_ref() / 255.f;
|
||||
//TODO: Properly support alpha-to-coverage and alpha-to-one behavior in shaders
|
||||
auto fragment_alpha_func = rsx::method_registers.alpha_func();
|
||||
auto alpha_ref = rsx::method_registers.alpha_ref() / 255.f;
|
||||
auto is_alpha_tested = (u32)rsx::method_registers.alpha_test_enabled();
|
||||
|
||||
if (rsx::method_registers.msaa_alpha_to_coverage_enabled() && !is_alpha_tested)
|
||||
{
|
||||
if (rsx::method_registers.msaa_enabled() &&
|
||||
rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::center_1_sample)
|
||||
{
|
||||
//alpha values generate a coverage mask for order independent blending
|
||||
//requires hardware AA to work properly (or just fragment sample stage in fragment shaders)
|
||||
//simulated using combined alpha blend and alpha test
|
||||
fragment_alpha_func = rsx::comparison_function::greater;
|
||||
alpha_ref = rsx::method_registers.msaa_sample_mask()? 0.25f : 0.f;
|
||||
is_alpha_tested |= (1 << 4);
|
||||
}
|
||||
}
|
||||
|
||||
const f32 fog0 = rsx::method_registers.fog_params_0();
|
||||
const f32 fog1 = rsx::method_registers.fog_params_1();
|
||||
const u32 alpha_func = static_cast<u32>(rsx::method_registers.alpha_func());
|
||||
const u32 alpha_func = static_cast<u32>(fragment_alpha_func);
|
||||
const u32 fog_mode = static_cast<u32>(rsx::method_registers.fog_equation());
|
||||
|
||||
// Generate wpos coeffecients
|
||||
@@ -997,7 +1080,7 @@ namespace rsx
|
||||
gsl::span<const gsl::byte> thread::get_raw_vertex_buffer(const rsx::data_array_format_info& vertex_array_info, u32 base_offset, const std::vector<std::pair<u32, u32>>& vertex_ranges) const
|
||||
{
|
||||
u32 offset = vertex_array_info.offset();
|
||||
u32 address = base_offset + rsx::get_address(offset & 0x7fffffff, offset >> 31);
|
||||
u32 address = rsx::get_address(rsx::get_vertex_offset_from_base(base_offset, offset & 0x7fffffff), offset >> 31);
|
||||
|
||||
u32 element_size = rsx::get_vertex_type_size_on_host(vertex_array_info.type(), vertex_array_info.size());
|
||||
|
||||
@@ -1086,6 +1169,12 @@ namespace rsx
|
||||
|
||||
void thread::do_internal_task()
|
||||
{
|
||||
if (zcull_ctrl->has_pending())
|
||||
{
|
||||
zcull_ctrl->sync(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_internal_tasks.empty())
|
||||
{
|
||||
std::this_thread::yield();
|
||||
@@ -1093,7 +1182,7 @@ namespace rsx
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Disabled" HERE);
|
||||
//std::lock_guard<std::mutex> lock{ m_mtx_task };
|
||||
//std::lock_guard<shared_mutex> lock{ m_mtx_task };
|
||||
|
||||
//internal_task_entry &front = m_internal_tasks.front();
|
||||
|
||||
@@ -1107,7 +1196,7 @@ namespace rsx
|
||||
|
||||
//std::future<void> thread::add_internal_task(std::function<bool()> callback)
|
||||
//{
|
||||
// std::lock_guard<std::mutex> lock{ m_mtx_task };
|
||||
// std::lock_guard<shared_mutex> lock{ m_mtx_task };
|
||||
// m_internal_tasks.emplace_back(callback);
|
||||
|
||||
// return m_internal_tasks.back().promise.get_future();
|
||||
@@ -1257,6 +1346,7 @@ namespace rsx
|
||||
if (state.current_draw_clause.command == rsx::draw_command::inlined_array)
|
||||
{
|
||||
vertex_input_layout result = {};
|
||||
result.interleaved_blocks.reserve(8);
|
||||
|
||||
interleaved_range_info info = {};
|
||||
info.interleaved = true;
|
||||
@@ -1281,6 +1371,8 @@ namespace rsx
|
||||
|
||||
const u32 frequency_divider_mask = rsx::method_registers.frequency_divider_operation_mask();
|
||||
vertex_input_layout result = {};
|
||||
result.interleaved_blocks.reserve(8);
|
||||
result.referenced_registers.reserve(4);
|
||||
|
||||
for (u8 index = 0; index < rsx::limits::vertex_count; ++index)
|
||||
{
|
||||
@@ -1384,7 +1476,7 @@ namespace rsx
|
||||
for (auto &info : result.interleaved_blocks)
|
||||
{
|
||||
//Calculate real data address to be used during upload
|
||||
info.real_offset_address = state.vertex_data_base_offset() + rsx::get_address(info.base_offset, info.memory_location);
|
||||
info.real_offset_address = rsx::get_address(rsx::get_vertex_offset_from_base(state.vertex_data_base_offset(), info.base_offset), info.memory_location);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -2017,9 +2109,21 @@ namespace rsx
|
||||
|
||||
skip_frame = (m_skip_frame_ctr < 0);
|
||||
}
|
||||
|
||||
//Reset zcull ctrl
|
||||
zcull_ctrl->set_active(this, false);
|
||||
zcull_ctrl->clear(this);
|
||||
|
||||
if (zcull_ctrl->has_pending())
|
||||
{
|
||||
LOG_ERROR(RSX, "Dangling reports found, discarding...");
|
||||
zcull_ctrl->sync(this);
|
||||
}
|
||||
|
||||
performance_counters.sampled_frames++;
|
||||
}
|
||||
|
||||
void thread::check_zcull_status(bool framebuffer_swap, bool force_read)
|
||||
void thread::check_zcull_status(bool framebuffer_swap)
|
||||
{
|
||||
if (g_cfg.video.disable_zcull_queries)
|
||||
return;
|
||||
@@ -2049,35 +2153,8 @@ namespace rsx
|
||||
}
|
||||
}
|
||||
|
||||
occlusion_query_info* query = nullptr;
|
||||
|
||||
if (zcull_task_queue.task_stack.size() > 0)
|
||||
query = zcull_task_queue.active_query;
|
||||
|
||||
if (query && query->active)
|
||||
{
|
||||
if (force_read || (!zcull_rendering_enabled || !testing_enabled || !zcull_surface_active))
|
||||
{
|
||||
end_occlusion_query(query);
|
||||
query->active = false;
|
||||
query->pending = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zcull_rendering_enabled && testing_enabled && zcull_surface_active)
|
||||
{
|
||||
//Find query
|
||||
u32 free_index = synchronize_zcull_stats();
|
||||
query = &occlusion_query_data[free_index];
|
||||
zcull_task_queue.add(query);
|
||||
|
||||
begin_occlusion_query(query);
|
||||
query->active = true;
|
||||
query->result = 0;
|
||||
query->num_draws = 0;
|
||||
}
|
||||
}
|
||||
zcull_ctrl->set_enabled(this, zcull_rendering_enabled);
|
||||
zcull_ctrl->set_active(this, zcull_rendering_enabled && testing_enabled && zcull_surface_active);
|
||||
}
|
||||
|
||||
void thread::clear_zcull_stats(u32 type)
|
||||
@@ -2085,113 +2162,54 @@ namespace rsx
|
||||
if (g_cfg.video.disable_zcull_queries)
|
||||
return;
|
||||
|
||||
if (type == CELL_GCM_ZPASS_PIXEL_CNT)
|
||||
{
|
||||
if (zcull_task_queue.active_query &&
|
||||
zcull_task_queue.active_query->active &&
|
||||
zcull_task_queue.active_query->num_draws > 0)
|
||||
{
|
||||
//discard active query results
|
||||
check_zcull_status(false, true);
|
||||
zcull_task_queue.active_query->pending = false;
|
||||
zcull_ctrl->clear(this);
|
||||
}
|
||||
|
||||
//re-enable cull stats if stats are enabled
|
||||
check_zcull_status(false, false);
|
||||
zcull_task_queue.active_query->num_draws = 0;
|
||||
void thread::get_zcull_stats(u32 type, vm::addr_t sink)
|
||||
{
|
||||
u32 value = 0;
|
||||
if (!g_cfg.video.disable_zcull_queries)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CELL_GCM_ZPASS_PIXEL_CNT:
|
||||
case CELL_GCM_ZCULL_STATS:
|
||||
case CELL_GCM_ZCULL_STATS1:
|
||||
case CELL_GCM_ZCULL_STATS2:
|
||||
case CELL_GCM_ZCULL_STATS3:
|
||||
{
|
||||
zcull_ctrl->read_report(this, sink, type);
|
||||
return;
|
||||
}
|
||||
|
||||
current_zcull_stats.clear();
|
||||
}
|
||||
}
|
||||
|
||||
u32 thread::get_zcull_stats(u32 type)
|
||||
{
|
||||
if (g_cfg.video.disable_zcull_queries)
|
||||
return 0u;
|
||||
|
||||
if (zcull_task_queue.active_query &&
|
||||
zcull_task_queue.active_query->active &&
|
||||
current_zcull_stats.zpass_pixel_cnt == 0 &&
|
||||
type == CELL_GCM_ZPASS_PIXEL_CNT)
|
||||
{
|
||||
//The zcull unit is still bound as the read is happening and there are no results ready
|
||||
check_zcull_status(false, true); //close current query
|
||||
check_zcull_status(false, false); //start new query since stat counting is still active
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CELL_GCM_ZPASS_PIXEL_CNT:
|
||||
{
|
||||
if (current_zcull_stats.zpass_pixel_cnt > 0)
|
||||
return UINT16_MAX;
|
||||
|
||||
synchronize_zcull_stats(true);
|
||||
return (current_zcull_stats.zpass_pixel_cnt > 0) ? UINT16_MAX : 0;
|
||||
}
|
||||
case CELL_GCM_ZCULL_STATS:
|
||||
case CELL_GCM_ZCULL_STATS1:
|
||||
case CELL_GCM_ZCULL_STATS2:
|
||||
//TODO
|
||||
return UINT16_MAX;
|
||||
case CELL_GCM_ZCULL_STATS3:
|
||||
{
|
||||
//Some kind of inverse value
|
||||
if (current_zcull_stats.zpass_pixel_cnt > 0)
|
||||
return 0;
|
||||
|
||||
synchronize_zcull_stats(true);
|
||||
return (current_zcull_stats.zpass_pixel_cnt > 0) ? 0 : UINT16_MAX;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(RSX, "Unknown zcull stat type %d", type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 thread::synchronize_zcull_stats(bool hard_sync)
|
||||
{
|
||||
if (!zcull_rendering_enabled || zcull_task_queue.pending == 0)
|
||||
return 0;
|
||||
|
||||
u32 result = UINT16_MAX;
|
||||
|
||||
for (auto &query : zcull_task_queue.task_stack)
|
||||
{
|
||||
if (query == nullptr || query->active)
|
||||
continue;
|
||||
|
||||
bool status = check_occlusion_query_status(query);
|
||||
if (status == false && !hard_sync)
|
||||
continue;
|
||||
|
||||
get_occlusion_query_result(query);
|
||||
current_zcull_stats.zpass_pixel_cnt += query->result;
|
||||
|
||||
query->pending = false;
|
||||
query = nullptr;
|
||||
zcull_task_queue.pending--;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < occlusion_query_count; ++i)
|
||||
{
|
||||
auto &query = occlusion_query_data[i];
|
||||
if (!query.pending && !query.active)
|
||||
{
|
||||
result = i;
|
||||
default:
|
||||
LOG_ERROR(RSX, "Unknown zcull stat type %d", type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == UINT16_MAX && !hard_sync)
|
||||
return synchronize_zcull_stats(true);
|
||||
vm::ptr<CellGcmReportData> result = sink;
|
||||
result->value = value;
|
||||
result->padding = 0;
|
||||
result->timer = timestamp();
|
||||
}
|
||||
|
||||
return result;
|
||||
void thread::sync()
|
||||
{
|
||||
zcull_ctrl->sync(this);
|
||||
|
||||
//TODO: On sync every sub-unit should finish any pending tasks
|
||||
//Might cause zcull lockup due to zombie 'unclaimed reports' which are not forcefully removed currently
|
||||
//verify (HERE), async_tasks_pending.load() == 0;
|
||||
}
|
||||
|
||||
void thread::read_barrier(u32 memory_address, u32 memory_range)
|
||||
{
|
||||
zcull_ctrl->read_barrier(this, memory_address, memory_range);
|
||||
}
|
||||
|
||||
void thread::notify_zcull_info_changed()
|
||||
{
|
||||
check_zcull_status(false, false);
|
||||
check_zcull_status(false);
|
||||
}
|
||||
|
||||
//Pause/cont wrappers for FIFO ctrl. Never call this from rsx thread itself!
|
||||
@@ -2213,6 +2231,28 @@ namespace rsx
|
||||
external_interrupt_lock.store(false);
|
||||
}
|
||||
|
||||
u32 thread::get_load()
|
||||
{
|
||||
//Average load over around 30 frames
|
||||
if (!performance_counters.last_update_timestamp || performance_counters.sampled_frames > 30)
|
||||
{
|
||||
const auto timestamp = get_system_time();
|
||||
const auto idle = performance_counters.idle_time.load();
|
||||
const auto elapsed = timestamp - performance_counters.last_update_timestamp;
|
||||
|
||||
if (elapsed > idle)
|
||||
performance_counters.approximate_load = (elapsed - idle) * 100 / elapsed;
|
||||
else
|
||||
performance_counters.approximate_load = 0;
|
||||
|
||||
performance_counters.idle_time = 0;
|
||||
performance_counters.sampled_frames = 0;
|
||||
performance_counters.last_update_timestamp = timestamp;
|
||||
}
|
||||
|
||||
return performance_counters.approximate_load;
|
||||
}
|
||||
|
||||
//TODO: Move these helpers into a better class dedicated to shell interface handling (use idm?)
|
||||
//They are not dependent on rsx at all
|
||||
rsx::overlays::save_dialog* thread::shell_open_save_dialog()
|
||||
@@ -2275,4 +2315,428 @@ namespace rsx
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace reports
|
||||
{
|
||||
void ZCULL_control::set_enabled(class ::rsx::thread* ptimer, bool state)
|
||||
{
|
||||
if (state != enabled)
|
||||
{
|
||||
enabled = state;
|
||||
|
||||
if (active && !enabled)
|
||||
set_active(ptimer, false);
|
||||
}
|
||||
}
|
||||
|
||||
void ZCULL_control::set_active(class ::rsx::thread* ptimer, bool state)
|
||||
{
|
||||
if (state != active)
|
||||
{
|
||||
active = state;
|
||||
|
||||
if (state)
|
||||
{
|
||||
verify(HERE), enabled && m_current_task == nullptr;
|
||||
allocate_new_query(ptimer);
|
||||
begin_occlusion_query(m_current_task);
|
||||
}
|
||||
else
|
||||
{
|
||||
verify(HERE), m_current_task;
|
||||
if (m_current_task->num_draws)
|
||||
{
|
||||
end_occlusion_query(m_current_task);
|
||||
m_current_task->active = false;
|
||||
m_current_task->pending = true;
|
||||
|
||||
m_pending_writes.push_back({});
|
||||
m_pending_writes.back().query = m_current_task;
|
||||
ptimer->async_tasks_pending++;
|
||||
}
|
||||
else
|
||||
{
|
||||
discard_occlusion_query(m_current_task);
|
||||
m_current_task->active = false;
|
||||
}
|
||||
|
||||
m_current_task = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ZCULL_control::read_report(::rsx::thread* ptimer, vm::addr_t sink, u32 type)
|
||||
{
|
||||
if (m_current_task && type == CELL_GCM_ZPASS_PIXEL_CNT)
|
||||
{
|
||||
m_current_task->owned = true;
|
||||
end_occlusion_query(m_current_task);
|
||||
m_pending_writes.push_back({});
|
||||
|
||||
m_current_task->active = false;
|
||||
m_current_task->pending = true;
|
||||
m_pending_writes.back().query = m_current_task;
|
||||
|
||||
allocate_new_query(ptimer);
|
||||
begin_occlusion_query(m_current_task);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Spam; send null query down the pipeline to copy the last result
|
||||
//Might be used to capture a timestamp (verify)
|
||||
m_pending_writes.push_back({});
|
||||
}
|
||||
|
||||
auto forwarder = &m_pending_writes.back();
|
||||
for (auto It = m_pending_writes.rbegin(); It != m_pending_writes.rend(); It++)
|
||||
{
|
||||
if (!It->sink)
|
||||
{
|
||||
It->counter_tag = m_statistics_tag_id;
|
||||
It->due_tsc = m_tsc + m_cycles_delay;
|
||||
It->sink = sink;
|
||||
It->type = type;
|
||||
|
||||
if (forwarder != &(*It))
|
||||
{
|
||||
//Not the last one in the chain, forward the writing operation to the last writer
|
||||
It->forwarder = forwarder;
|
||||
It->query->owned = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ptimer->async_tasks_pending++;
|
||||
}
|
||||
|
||||
void ZCULL_control::allocate_new_query(::rsx::thread* ptimer)
|
||||
{
|
||||
int retries = 0;
|
||||
while (!Emu.IsStopped())
|
||||
{
|
||||
for (int n = 0; n < occlusion_query_count; ++n)
|
||||
{
|
||||
if (m_occlusion_query_data[n].pending || m_occlusion_query_data[n].active)
|
||||
continue;
|
||||
|
||||
m_current_task = &m_occlusion_query_data[n];
|
||||
m_current_task->num_draws = 0;
|
||||
m_current_task->result = 0;
|
||||
m_current_task->sync_timestamp = 0;
|
||||
m_current_task->active = true;
|
||||
m_current_task->owned = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (retries > 0)
|
||||
{
|
||||
LOG_ERROR(RSX, "ZCULL report queue is overflowing!!");
|
||||
m_statistics_map[m_statistics_tag_id] = 1;
|
||||
|
||||
verify(HERE), m_pending_writes.front().sink == 0;
|
||||
m_pending_writes.resize(0);
|
||||
|
||||
for (auto &query : m_occlusion_query_data)
|
||||
{
|
||||
discard_occlusion_query(&query);
|
||||
query.pending = false;
|
||||
}
|
||||
|
||||
m_current_task = &m_occlusion_query_data[0];
|
||||
m_current_task->num_draws = 0;
|
||||
m_current_task->result = 0;
|
||||
m_current_task->sync_timestamp = 0;
|
||||
m_current_task->active = true;
|
||||
m_current_task->owned = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//All slots are occupied, try to pop the earliest entry
|
||||
m_tsc += max_zcull_cycles_delay;
|
||||
update(ptimer);
|
||||
|
||||
retries++;
|
||||
}
|
||||
}
|
||||
|
||||
void ZCULL_control::clear(class ::rsx::thread* ptimer)
|
||||
{
|
||||
if (!m_pending_writes.empty())
|
||||
{
|
||||
//Remove any dangling/unclaimed queries as the information is lost anyway
|
||||
auto valid_size = m_pending_writes.size();
|
||||
for (auto It = m_pending_writes.rbegin(); It != m_pending_writes.rend(); ++It)
|
||||
{
|
||||
if (!It->sink)
|
||||
{
|
||||
discard_occlusion_query(It->query);
|
||||
It->query->pending = false;
|
||||
valid_size--;
|
||||
ptimer->async_tasks_pending--;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
m_pending_writes.resize(valid_size);
|
||||
}
|
||||
|
||||
m_statistics_tag_id++;
|
||||
m_statistics_map[m_statistics_tag_id] = 0;
|
||||
}
|
||||
|
||||
void ZCULL_control::on_draw()
|
||||
{
|
||||
if (m_current_task)
|
||||
m_current_task->num_draws++;
|
||||
|
||||
m_cycles_delay = max_zcull_cycles_delay;
|
||||
}
|
||||
|
||||
void ZCULL_control::write(vm::addr_t sink, u32 timestamp, u32 type, u32 value)
|
||||
{
|
||||
verify(HERE), sink;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CELL_GCM_ZPASS_PIXEL_CNT:
|
||||
value = value ? UINT16_MAX : 0;
|
||||
break;
|
||||
case CELL_GCM_ZCULL_STATS3:
|
||||
value = value ? 0 : UINT16_MAX;
|
||||
break;
|
||||
case CELL_GCM_ZCULL_STATS2:
|
||||
case CELL_GCM_ZCULL_STATS1:
|
||||
case CELL_GCM_ZCULL_STATS:
|
||||
default:
|
||||
//Not implemented
|
||||
value = UINT32_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
vm::ptr<CellGcmReportData> out = sink;
|
||||
out->value = value;
|
||||
out->timer = timestamp;
|
||||
out->padding = 0;
|
||||
}
|
||||
|
||||
void ZCULL_control::sync(::rsx::thread* ptimer)
|
||||
{
|
||||
if (!m_pending_writes.empty())
|
||||
{
|
||||
u32 processed = 0;
|
||||
const bool has_unclaimed = (m_pending_writes.back().sink == 0);
|
||||
|
||||
//Write all claimed reports unconditionally
|
||||
for (auto &writer : m_pending_writes)
|
||||
{
|
||||
if (!writer.sink)
|
||||
break;
|
||||
|
||||
auto query = writer.query;
|
||||
u32 result = m_statistics_map[writer.counter_tag];
|
||||
|
||||
if (query)
|
||||
{
|
||||
verify(HERE), query->pending;
|
||||
|
||||
if (!result && query->num_draws)
|
||||
{
|
||||
get_occlusion_query_result(query);
|
||||
|
||||
if (query->result)
|
||||
{
|
||||
result += query->result;
|
||||
m_statistics_map[writer.counter_tag] = result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Already have a hit, no need to retest
|
||||
discard_occlusion_query(query);
|
||||
}
|
||||
|
||||
query->pending = false;
|
||||
}
|
||||
|
||||
if (!writer.forwarder)
|
||||
//No other queries in the chain, write result
|
||||
write(writer.sink, ptimer->timestamp(), writer.type, result);
|
||||
|
||||
processed++;
|
||||
}
|
||||
|
||||
if (!has_unclaimed)
|
||||
{
|
||||
verify(HERE), processed == m_pending_writes.size();
|
||||
m_pending_writes.resize(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto remaining = m_pending_writes.size() - processed;
|
||||
verify(HERE), remaining > 0;
|
||||
|
||||
if (remaining == 1)
|
||||
{
|
||||
m_pending_writes.front() = m_pending_writes.back();
|
||||
m_pending_writes.resize(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::move(m_pending_writes.begin() + processed, m_pending_writes.end(), m_pending_writes.begin());
|
||||
m_pending_writes.resize(remaining);
|
||||
}
|
||||
}
|
||||
|
||||
//Delete all statistics caches but leave the current one
|
||||
for (auto It = m_statistics_map.begin(); It != m_statistics_map.end(); )
|
||||
{
|
||||
if (It->first == m_statistics_tag_id)
|
||||
++It;
|
||||
else
|
||||
It = m_statistics_map.erase(It);
|
||||
}
|
||||
|
||||
//Decrement jobs counter
|
||||
ptimer->async_tasks_pending -= processed;
|
||||
}
|
||||
|
||||
//Critical, since its likely a WAIT_FOR_IDLE type has been processed, all results are considered available
|
||||
m_cycles_delay = min_zcull_cycles_delay;
|
||||
}
|
||||
|
||||
void ZCULL_control::update(::rsx::thread* ptimer)
|
||||
{
|
||||
m_tsc++;
|
||||
|
||||
if (m_pending_writes.empty())
|
||||
return;
|
||||
|
||||
u32 stat_tag_to_remove = m_statistics_tag_id;
|
||||
u32 processed = 0;
|
||||
for (auto &writer : m_pending_writes)
|
||||
{
|
||||
if (!writer.sink)
|
||||
break;
|
||||
|
||||
if (writer.counter_tag != stat_tag_to_remove &&
|
||||
stat_tag_to_remove != m_statistics_tag_id)
|
||||
{
|
||||
//If the stat id is different from this stat id and the queue is advancing,
|
||||
//its guaranteed that the previous tag has no remaining writes as the queue is ordered
|
||||
m_statistics_map.erase(stat_tag_to_remove);
|
||||
stat_tag_to_remove = m_statistics_tag_id;
|
||||
}
|
||||
|
||||
auto query = writer.query;
|
||||
u32 result = m_statistics_map[writer.counter_tag];
|
||||
|
||||
if (query)
|
||||
{
|
||||
verify(HERE), query->pending;
|
||||
|
||||
if (UNLIKELY(writer.due_tsc < m_tsc))
|
||||
{
|
||||
if (!result && query->num_draws)
|
||||
{
|
||||
get_occlusion_query_result(query);
|
||||
|
||||
if (query->result)
|
||||
{
|
||||
result += query->result;
|
||||
m_statistics_map[writer.counter_tag] = result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//No need to read this
|
||||
discard_occlusion_query(query);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result || !query->num_draws)
|
||||
{
|
||||
//Not necessary to read the result anymore
|
||||
discard_occlusion_query(query);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Maybe we get lucky and results are ready
|
||||
if (check_occlusion_query_status(query))
|
||||
{
|
||||
get_occlusion_query_result(query);
|
||||
if (query->result)
|
||||
{
|
||||
result += query->result;
|
||||
m_statistics_map[writer.counter_tag] = result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Too early; abort
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query->pending = false;
|
||||
}
|
||||
|
||||
stat_tag_to_remove = writer.counter_tag;
|
||||
|
||||
//only zpass supported right now
|
||||
if (!writer.forwarder)
|
||||
//No other queries in the chain, write result
|
||||
write(writer.sink, ptimer->timestamp(), writer.type, result);
|
||||
|
||||
processed++;
|
||||
}
|
||||
|
||||
if (stat_tag_to_remove != m_statistics_tag_id)
|
||||
m_statistics_map.erase(stat_tag_to_remove);
|
||||
|
||||
if (processed)
|
||||
{
|
||||
auto remaining = m_pending_writes.size() - processed;
|
||||
if (remaining == 1)
|
||||
{
|
||||
m_pending_writes.front() = m_pending_writes.back();
|
||||
m_pending_writes.resize(1);
|
||||
}
|
||||
else if (remaining)
|
||||
{
|
||||
std::move(m_pending_writes.begin() + processed, m_pending_writes.end(), m_pending_writes.begin());
|
||||
m_pending_writes.resize(remaining);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pending_writes.resize(0);
|
||||
}
|
||||
|
||||
ptimer->async_tasks_pending -= processed;
|
||||
}
|
||||
}
|
||||
|
||||
void ZCULL_control::read_barrier(::rsx::thread* ptimer, u32 memory_address, u32 memory_range)
|
||||
{
|
||||
if (m_pending_writes.empty())
|
||||
return;
|
||||
|
||||
const auto memory_end = memory_address + memory_range;
|
||||
for (const auto &writer : m_pending_writes)
|
||||
{
|
||||
if (writer.sink >= memory_address && writer.sink < memory_end)
|
||||
{
|
||||
sync(ptimer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+106
-69
@@ -157,64 +157,93 @@ namespace rsx
|
||||
std::array<attribute_buffer_placement, 16> attribute_placement;
|
||||
};
|
||||
|
||||
struct zcull_statistics
|
||||
namespace reports
|
||||
{
|
||||
u32 zpass_pixel_cnt;
|
||||
u32 zcull_stats;
|
||||
u32 zcull_stats1;
|
||||
u32 zcull_stats2;
|
||||
u32 zcull_stats3;
|
||||
|
||||
void clear()
|
||||
struct occlusion_query_info
|
||||
{
|
||||
zpass_pixel_cnt = zcull_stats = zcull_stats1 = zcull_stats2 = zcull_stats3 = 0;
|
||||
}
|
||||
};
|
||||
u32 driver_handle;
|
||||
u32 result;
|
||||
u32 num_draws;
|
||||
bool pending;
|
||||
bool active;
|
||||
bool owned;
|
||||
|
||||
struct occlusion_query_info
|
||||
{
|
||||
u32 driver_handle;
|
||||
u32 result;
|
||||
u32 num_draws;
|
||||
bool pending;
|
||||
bool active;
|
||||
u64 sync_timestamp;
|
||||
};
|
||||
|
||||
u64 sync_timestamp;
|
||||
u64 external_flags;
|
||||
};
|
||||
|
||||
struct occlusion_task
|
||||
{
|
||||
std::vector<occlusion_query_info*> task_stack;
|
||||
occlusion_query_info* active_query = nullptr;
|
||||
u32 pending = 0;
|
||||
|
||||
//Add one query to the task
|
||||
void add(occlusion_query_info* query)
|
||||
struct queued_report_write
|
||||
{
|
||||
active_query = query;
|
||||
u32 type = CELL_GCM_ZPASS_PIXEL_CNT;
|
||||
u32 counter_tag;
|
||||
occlusion_query_info* query;
|
||||
queued_report_write* forwarder;
|
||||
vm::addr_t sink;
|
||||
|
||||
if (task_stack.size() > 0 && pending == 0)
|
||||
task_stack.resize(0);
|
||||
u32 due_tsc;
|
||||
};
|
||||
|
||||
const auto empty_slots = task_stack.size() - pending;
|
||||
if (empty_slots >= 4)
|
||||
{
|
||||
for (auto &_query : task_stack)
|
||||
{
|
||||
if (_query == nullptr)
|
||||
{
|
||||
_query = query;
|
||||
pending++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
struct ZCULL_control
|
||||
{
|
||||
//Delay in 'cycles' before a report update operation is forced to retire
|
||||
const u32 max_zcull_cycles_delay = 128;
|
||||
const u32 min_zcull_cycles_delay = 16;
|
||||
|
||||
task_stack.push_back(query);
|
||||
pending++;
|
||||
}
|
||||
};
|
||||
//Number of occlusion query slots available. Real hardware actually has far fewer units before choking
|
||||
const u32 occlusion_query_count = 128;
|
||||
|
||||
bool active = false;
|
||||
bool enabled = false;
|
||||
|
||||
std::array<occlusion_query_info, 128> m_occlusion_query_data = {};
|
||||
|
||||
occlusion_query_info* m_current_task = nullptr;
|
||||
u32 m_statistics_tag_id = 0;
|
||||
u32 m_tsc = 0;
|
||||
u32 m_cycles_delay = max_zcull_cycles_delay;
|
||||
|
||||
std::vector<queued_report_write> m_pending_writes;
|
||||
std::unordered_map<u32, u32> m_statistics_map;
|
||||
|
||||
ZCULL_control() {}
|
||||
~ZCULL_control() {}
|
||||
|
||||
void set_enabled(class ::rsx::thread* ptimer, bool enabled);
|
||||
void set_active(class ::rsx::thread* ptimer, bool active);
|
||||
|
||||
void write(vm::addr_t sink, u32 timestamp, u32 type, u32 value);
|
||||
|
||||
//Read current zcull statistics into the address provided
|
||||
void read_report(class ::rsx::thread* ptimer, vm::addr_t sink, u32 type);
|
||||
|
||||
//Sets up a new query slot and sets it to the current task
|
||||
void allocate_new_query(class ::rsx::thread* ptimer);
|
||||
|
||||
//clears current stat block and increments stat_tag_id
|
||||
void clear(class ::rsx::thread* ptimer);
|
||||
|
||||
//forcefully flushes all
|
||||
void sync(class ::rsx::thread* ptimer);
|
||||
|
||||
//conditionally sync any pending writes if range overlaps
|
||||
void read_barrier(class ::rsx::thread* ptimer, u32 memory_address, u32 memory_range);
|
||||
|
||||
//call once every 'tick' to update
|
||||
void update(class ::rsx::thread* ptimer);
|
||||
|
||||
//Draw call notification
|
||||
void on_draw();
|
||||
|
||||
//Check for pending writes
|
||||
bool has_pending() const { return (m_pending_writes.size() != 0); }
|
||||
|
||||
//Backend methods (optional, will return everything as always visible by default)
|
||||
virtual void begin_occlusion_query(occlusion_query_info* /*query*/) {}
|
||||
virtual void end_occlusion_query(occlusion_query_info* /*query*/) {}
|
||||
virtual bool check_occlusion_query_status(occlusion_query_info* /*query*/) { return true; }
|
||||
virtual void get_occlusion_query_result(occlusion_query_info* query) { query->result = UINT32_MAX; }
|
||||
virtual void discard_occlusion_query(occlusion_query_info* /*query*/) {}
|
||||
};
|
||||
}
|
||||
|
||||
struct sampled_image_descriptor_base;
|
||||
|
||||
@@ -236,11 +265,7 @@ namespace rsx
|
||||
|
||||
//occlusion query
|
||||
bool zcull_surface_active = false;
|
||||
zcull_statistics current_zcull_stats;
|
||||
|
||||
const u32 occlusion_query_count = 128;
|
||||
std::array<occlusion_query_info, 128> occlusion_query_data = {};
|
||||
occlusion_task zcull_task_queue = {};
|
||||
std::unique_ptr<reports::ZCULL_control> zcull_ctrl;
|
||||
|
||||
//framebuffer setup
|
||||
rsx::gcm_framebuffer_info m_surface_info[rsx::limits::color_buffers_count];
|
||||
@@ -257,15 +282,24 @@ namespace rsx
|
||||
atomic_t<bool> external_interrupt_lock{ false };
|
||||
atomic_t<bool> external_interrupt_ack{ false };
|
||||
|
||||
//performance approximation counters
|
||||
struct
|
||||
{
|
||||
atomic_t<u64> idle_time{ 0 }; //Time spent idling in microseconds
|
||||
u64 last_update_timestamp = 0; //Timestamp of last load update
|
||||
u64 FIFO_idle_timestamp = 0; //Timestamp of when FIFO queue becomes idle
|
||||
bool FIFO_is_idle = false; //True if FIFO is in idle state
|
||||
u32 approximate_load = 0;
|
||||
u32 sampled_frames = 0;
|
||||
}
|
||||
performance_counters;
|
||||
|
||||
//native UI interrupts
|
||||
atomic_t<bool> native_ui_flip_request{ false };
|
||||
|
||||
GcmTileInfo tiles[limits::tiles_count];
|
||||
GcmZcullInfo zculls[limits::zculls_count];
|
||||
|
||||
// Constant stored for whole frame
|
||||
std::unordered_map<u32, color4f> local_transform_constants;
|
||||
|
||||
bool capture_current_frame = false;
|
||||
void capture_frame(const std::string &name);
|
||||
|
||||
@@ -335,6 +369,8 @@ namespace rsx
|
||||
bool sync_point_request = false;
|
||||
bool in_begin_end = false;
|
||||
|
||||
atomic_t<s32> async_tasks_pending{ 0 };
|
||||
|
||||
bool conditional_render_test_failed = false;
|
||||
bool conditional_render_enabled = false;
|
||||
bool zcull_stats_enabled = false;
|
||||
@@ -373,17 +409,15 @@ namespace rsx
|
||||
virtual void notify_tile_unbound(u32 /*tile*/) {}
|
||||
|
||||
//zcull
|
||||
virtual void notify_zcull_info_changed();
|
||||
virtual void clear_zcull_stats(u32 type);
|
||||
virtual u32 get_zcull_stats(u32 type);
|
||||
virtual void check_zcull_status(bool framebuffer_swap, bool force_read);
|
||||
virtual u32 synchronize_zcull_stats(bool hard_sync = false);
|
||||
|
||||
virtual void begin_occlusion_query(occlusion_query_info* /*query*/) {}
|
||||
virtual void end_occlusion_query(occlusion_query_info* /*query*/) {}
|
||||
virtual bool check_occlusion_query_status(occlusion_query_info* /*query*/) { return true; }
|
||||
virtual void get_occlusion_query_result(occlusion_query_info* query) { query->result = UINT32_MAX; }
|
||||
void notify_zcull_info_changed();
|
||||
void clear_zcull_stats(u32 type);
|
||||
void check_zcull_status(bool framebuffer_swap);
|
||||
void get_zcull_stats(u32 type, vm::addr_t sink);
|
||||
|
||||
//sync
|
||||
void sync();
|
||||
void read_barrier(u32 memory_address, u32 memory_range);
|
||||
|
||||
gsl::span<const gsl::byte> get_raw_index_array(const std::vector<std::pair<u32, u32> >& draw_indexed_clause) const;
|
||||
gsl::span<const gsl::byte> get_raw_vertex_buffer(const rsx::data_array_format_info&, u32 base_offset, const std::vector<std::pair<u32, u32>>& vertex_ranges) const;
|
||||
|
||||
@@ -424,7 +458,7 @@ namespace rsx
|
||||
void write_vertex_data_to_memory(const vertex_input_layout& layout, u32 first_vertex, u32 vertex_count, void *persistent_data, void *volatile_data);
|
||||
|
||||
private:
|
||||
std::mutex m_mtx_task;
|
||||
shared_mutex m_mtx_task;
|
||||
|
||||
struct internal_task_entry
|
||||
{
|
||||
@@ -510,6 +544,9 @@ namespace rsx
|
||||
void pause();
|
||||
void unpause();
|
||||
|
||||
//Get RSX approximate load in %
|
||||
u32 get_load();
|
||||
|
||||
//HLE vsh stuff
|
||||
//TODO: Move into a separate helper
|
||||
virtual rsx::overlays::save_dialog* shell_open_save_dialog();
|
||||
|
||||
@@ -96,7 +96,10 @@ void VKFragmentDecompilerThread::insertOutputs(std::stringstream & OS)
|
||||
for (int i = 0; i < sizeof(table) / sizeof(*table); ++i)
|
||||
{
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", table[i].second))
|
||||
{
|
||||
OS << "layout(location=" << std::to_string(output_index++) << ") " << "out vec4 " << table[i].first << ";\n";
|
||||
vk_prog->output_color_masks[i] = UINT32_MAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,6 +343,26 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
|
||||
}
|
||||
}
|
||||
|
||||
OS << "}\n\n";
|
||||
|
||||
OS << "void main()\n";
|
||||
OS << "{\n";
|
||||
|
||||
std::string parameters = "";
|
||||
for (auto ®_name : output_values)
|
||||
{
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", reg_name))
|
||||
{
|
||||
if (parameters.length())
|
||||
parameters += ", ";
|
||||
|
||||
parameters += reg_name;
|
||||
OS << " vec4 " << reg_name << " = vec4(0.);\n";
|
||||
}
|
||||
}
|
||||
|
||||
OS << "\n" << " fs_main(" + parameters + ");\n\n";
|
||||
|
||||
if (!first_output_name.empty())
|
||||
{
|
||||
auto make_comparison_test = [](rsx::comparison_function compare_func, const std::string &test, const std::string &a, const std::string &b) -> std::string
|
||||
@@ -373,29 +396,9 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
|
||||
}
|
||||
}
|
||||
|
||||
OS << " if (alpha_test != 0 && !comparison_passes(" << first_output_name << ".a, alpha_ref, alpha_func)) discard;\n";
|
||||
OS << " if (alpha_test != 0 && !comparison_passes(" << first_output_name << ".a, alpha_ref, alpha_func)) discard;\n\n";
|
||||
}
|
||||
|
||||
OS << "}\n\n";
|
||||
|
||||
OS << "void main()\n";
|
||||
OS << "{\n";
|
||||
|
||||
std::string parameters = "";
|
||||
for (auto ®_name : output_values)
|
||||
{
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", reg_name))
|
||||
{
|
||||
if (parameters.length())
|
||||
parameters += ", ";
|
||||
|
||||
parameters += reg_name;
|
||||
OS << " vec4 " << reg_name << " = vec4(0.);\n";
|
||||
}
|
||||
}
|
||||
|
||||
OS << "\n" << " fs_main(" + parameters + ");\n\n";
|
||||
|
||||
//Append the color output assignments
|
||||
OS << color_output_block;
|
||||
|
||||
@@ -403,11 +406,8 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
|
||||
{
|
||||
if (m_parr.HasParam(PF_PARAM_NONE, "vec4", "r1"))
|
||||
{
|
||||
/** Note: Naruto Shippuden : Ultimate Ninja Storm 2 sets CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS in a shader
|
||||
* but it writes depth in r1.z and not h2.z.
|
||||
* Maybe there's a different flag for depth ?
|
||||
*/
|
||||
//OS << ((m_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) ? "\tgl_FragDepth = r1.z;\n" : "\tgl_FragDepth = h0.z;\n") << "\n";
|
||||
//Depth writes are always from a fp32 register. See issues section on nvidia's NV_fragment_program spec
|
||||
//https://www.khronos.org/registry/OpenGL/extensions/NV/NV_fragment_program.txt
|
||||
OS << " gl_FragDepth = r1.z;\n";
|
||||
}
|
||||
else
|
||||
|
||||
@@ -52,6 +52,8 @@ public:
|
||||
std::string shader;
|
||||
std::vector<size_t> FragmentConstantOffsetCache;
|
||||
|
||||
std::array<u32, 4> output_color_masks{ {} };
|
||||
|
||||
std::vector<vk::glsl::program_input> uniforms;
|
||||
void SetInputs(std::vector<vk::glsl::program_input>& uniforms);
|
||||
/**
|
||||
|
||||
+219
-141
@@ -566,7 +566,7 @@ VKGSRender::VKGSRender() : GSRender()
|
||||
//Occlusion
|
||||
m_occlusion_query_pool.create((*m_device), DESCRIPTOR_MAX_DRAW_CALLS); //Enough for 4k draw calls per pass
|
||||
for (int n = 0; n < 128; ++n)
|
||||
occlusion_query_data[n].driver_handle = n;
|
||||
m_occlusion_query_data[n].driver_handle = n;
|
||||
|
||||
//Generate frame contexts
|
||||
VkDescriptorPoolSize uniform_buffer_pool = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 3 * DESCRIPTOR_MAX_DRAW_CALLS };
|
||||
@@ -666,6 +666,9 @@ VKGSRender::~VKGSRender()
|
||||
vk::finalize_compiler_context();
|
||||
m_prog_buffer->clear();
|
||||
|
||||
m_persistent_attribute_storage.reset();
|
||||
m_volatile_attribute_storage.reset();
|
||||
|
||||
//Global resources
|
||||
vk::destroy_global_resources();
|
||||
|
||||
@@ -766,7 +769,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing)
|
||||
{
|
||||
vk::texture_cache::thrashed_set result;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_secondary_cb_guard);
|
||||
std::lock_guard<shared_mutex> lock(m_secondary_cb_guard);
|
||||
result = std::move(m_texture_cache.invalidate_address(address, is_writing, false, m_secondary_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue()));
|
||||
}
|
||||
|
||||
@@ -774,7 +777,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing)
|
||||
return false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
std::lock_guard<shared_mutex> lock(m_sampler_mutex);
|
||||
m_samplers_dirty.store(true);
|
||||
}
|
||||
|
||||
@@ -792,7 +795,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing)
|
||||
//Always submit primary cb to ensure state consistency (flush pending changes such as image transitions)
|
||||
vm::temporary_unlock();
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_flush_queue_mutex);
|
||||
std::lock_guard<shared_mutex> lock(m_flush_queue_mutex);
|
||||
|
||||
m_flush_requests.post(sync_timestamp == 0ull);
|
||||
has_queue_ref = true;
|
||||
@@ -843,13 +846,13 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing)
|
||||
|
||||
void VKGSRender::on_notify_memory_unmapped(u32 address_base, u32 size)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_secondary_cb_guard);
|
||||
std::lock_guard<shared_mutex> lock(m_secondary_cb_guard);
|
||||
if (m_texture_cache.invalidate_range(address_base, size, true, true, false,
|
||||
m_secondary_command_buffer, m_memory_type_mapping, m_swapchain->get_graphics_queue()).violation_handled)
|
||||
{
|
||||
m_texture_cache.purge_dirty();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
std::lock_guard<shared_mutex> lock(m_sampler_mutex);
|
||||
m_samplers_dirty.store(true);
|
||||
}
|
||||
}
|
||||
@@ -863,7 +866,7 @@ void VKGSRender::notify_tile_unbound(u32 tile)
|
||||
//m_rtts.invalidate_surface_address(addr, false);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
std::lock_guard<shared_mutex> lock(m_sampler_mutex);
|
||||
m_samplers_dirty.store(true);
|
||||
}
|
||||
}
|
||||
@@ -900,6 +903,7 @@ void VKGSRender::check_heap_status()
|
||||
m_attrib_ring_info.reset_allocation_stats();
|
||||
m_texture_upload_buffer_ring_info.reset_allocation_stats();
|
||||
m_current_frame->reset_heap_ptrs();
|
||||
m_last_heap_sync_time = get_system_time();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1060,7 +1064,7 @@ void VKGSRender::end()
|
||||
std::chrono::time_point<steady_clock> textures_start = vertex_end;
|
||||
//Load textures
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_sampler_mutex);
|
||||
std::lock_guard<shared_mutex> lock(m_sampler_mutex);
|
||||
bool update_framebuffer_sourced = false;
|
||||
|
||||
if (surface_store_tag != m_rtts.cache_tag)
|
||||
@@ -1126,22 +1130,8 @@ void VKGSRender::end()
|
||||
|
||||
if (replace)
|
||||
{
|
||||
for (auto &sampler : m_current_frame->samplers_to_clean)
|
||||
{
|
||||
if (sampler->matches(wrap_s, wrap_t, wrap_r, false, lod_bias, af_level, min_lod, max_lod,
|
||||
min_filter, mag_filter, mip_mode, border_color, compare_enabled, depth_compare_mode))
|
||||
{
|
||||
fs_sampler_handles[i] = std::move(sampler);
|
||||
replace = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (replace)
|
||||
{
|
||||
fs_sampler_handles[i] = std::make_unique<vk::sampler>(*m_device, wrap_s, wrap_t, wrap_r, false, lod_bias, af_level, min_lod, max_lod,
|
||||
min_filter, mag_filter, mip_mode, border_color, compare_enabled, depth_compare_mode);
|
||||
}
|
||||
fs_sampler_handles[i] = std::make_unique<vk::sampler>(*m_device, wrap_s, wrap_t, wrap_r, false, lod_bias, af_level, min_lod, max_lod,
|
||||
min_filter, mag_filter, mip_mode, border_color, compare_enabled, depth_compare_mode);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1185,7 +1175,6 @@ void VKGSRender::end()
|
||||
|
||||
if (replace)
|
||||
{
|
||||
//This is unlikely, there is no need to check the dirty pool
|
||||
vs_sampler_handles[i] = std::make_unique<vk::sampler>(
|
||||
*m_device,
|
||||
VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||
@@ -1209,10 +1198,12 @@ void VKGSRender::end()
|
||||
|
||||
//Load program
|
||||
std::chrono::time_point<steady_clock> program_start = textures_end;
|
||||
load_program(std::get<2>(upload_info), std::get<3>(upload_info));
|
||||
load_program(upload_info);
|
||||
|
||||
m_program->bind_uniform(m_persistent_attribute_storage, "persistent_input_stream", m_current_frame->descriptor_set);
|
||||
m_program->bind_uniform(m_volatile_attribute_storage, "volatile_input_stream", m_current_frame->descriptor_set);
|
||||
VkBufferView persistent_buffer = m_persistent_attribute_storage ? m_persistent_attribute_storage->value : null_buffer_view->value;
|
||||
VkBufferView volatile_buffer = m_volatile_attribute_storage ? m_volatile_attribute_storage->value : null_buffer_view->value;
|
||||
m_program->bind_uniform(persistent_buffer, "persistent_input_stream", m_current_frame->descriptor_set);
|
||||
m_program->bind_uniform(volatile_buffer, "volatile_input_stream", m_current_frame->descriptor_set);
|
||||
|
||||
std::chrono::time_point<steady_clock> program_stop = steady_clock::now();
|
||||
m_setup_time += std::chrono::duration_cast<std::chrono::microseconds>(program_stop - program_start).count();
|
||||
@@ -1366,40 +1357,15 @@ void VKGSRender::end()
|
||||
occlusion_id = m_occlusion_query_pool.find_free_slot();
|
||||
if (occlusion_id == UINT32_MAX)
|
||||
{
|
||||
bool free_slot_found = false;
|
||||
u32 index_to_free = UINT32_MAX;
|
||||
u64 earliest_timestamp = UINT64_MAX;
|
||||
m_tsc += 100;
|
||||
update(this);
|
||||
|
||||
//flush occlusion queries
|
||||
for (auto It : m_occlusion_map)
|
||||
occlusion_id = m_occlusion_query_pool.find_free_slot();
|
||||
if (occlusion_id == UINT32_MAX)
|
||||
{
|
||||
u32 index = It.first;
|
||||
auto query = &occlusion_query_data[index];
|
||||
if (check_occlusion_query_status(query))
|
||||
{
|
||||
free_slot_found = true;
|
||||
get_occlusion_query_result(query);
|
||||
break;
|
||||
}
|
||||
|
||||
if (query->sync_timestamp < earliest_timestamp)
|
||||
{
|
||||
index_to_free = index;
|
||||
earliest_timestamp = query->sync_timestamp;
|
||||
}
|
||||
LOG_ERROR(RSX, "Occlusion pool overflow");
|
||||
if (m_current_task) m_current_task->result = 1;
|
||||
}
|
||||
|
||||
if (free_slot_found)
|
||||
{
|
||||
occlusion_id = m_occlusion_query_pool.find_free_slot();
|
||||
}
|
||||
else
|
||||
{
|
||||
get_occlusion_query_result(&occlusion_query_data[index_to_free]);
|
||||
occlusion_id = m_occlusion_query_pool.find_free_slot();
|
||||
}
|
||||
|
||||
verify(HERE), occlusion_id != UINT32_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1445,15 +1411,13 @@ void VKGSRender::end()
|
||||
vkCmdClearAttachments(*m_current_command_buffer, static_cast<u32>(buffers_to_clear.size()), buffers_to_clear.data(), 1, &clear_rect);
|
||||
}
|
||||
|
||||
std::optional<std::tuple<VkDeviceSize, VkIndexType> > index_info = std::get<4>(upload_info);
|
||||
|
||||
bool primitive_emulated = false;
|
||||
vk::get_appropriate_topology(rsx::method_registers.current_draw_clause.primitive, primitive_emulated);
|
||||
|
||||
const bool is_emulated_restart = (!primitive_emulated && rsx::method_registers.restart_index_enabled() && vk::emulate_primitive_restart() && rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed);
|
||||
const bool single_draw = !supports_multidraw || (!is_emulated_restart && (rsx::method_registers.current_draw_clause.first_count_commands.size() <= 1 || rsx::method_registers.current_draw_clause.is_disjoint_primitive));
|
||||
|
||||
if (m_occlusion_query_active)
|
||||
if (m_occlusion_query_active && (occlusion_id != UINT32_MAX))
|
||||
{
|
||||
//Begin query
|
||||
m_occlusion_query_pool.begin_query(*m_current_command_buffer, occlusion_id);
|
||||
@@ -1461,12 +1425,11 @@ void VKGSRender::end()
|
||||
m_occlusion_map[m_active_query_info->driver_handle].command_buffer_to_wait = m_current_command_buffer;
|
||||
}
|
||||
|
||||
if (!index_info)
|
||||
if (!upload_info.index_info)
|
||||
{
|
||||
if (single_draw)
|
||||
{
|
||||
const auto vertex_count = std::get<1>(upload_info);
|
||||
vkCmdDraw(*m_current_command_buffer, vertex_count, 1, 0, 0);
|
||||
vkCmdDraw(*m_current_command_buffer, upload_info.vertex_draw_count, 1, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1480,10 +1443,10 @@ void VKGSRender::end()
|
||||
else
|
||||
{
|
||||
VkIndexType index_type;
|
||||
u32 index_count = std::get<1>(upload_info);
|
||||
const u32 index_count = upload_info.vertex_draw_count;
|
||||
VkDeviceSize offset;
|
||||
|
||||
std::tie(offset, index_type) = index_info.value();
|
||||
std::tie(offset, index_type) = upload_info.index_info.value();
|
||||
vkCmdBindIndexBuffer(*m_current_command_buffer, m_index_buffer_ring_info.heap->value, offset, index_type);
|
||||
|
||||
if (single_draw)
|
||||
@@ -1513,7 +1476,7 @@ void VKGSRender::end()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_occlusion_query_active)
|
||||
if (m_occlusion_query_active && (occlusion_id != UINT32_MAX))
|
||||
{
|
||||
//End query
|
||||
m_occlusion_query_pool.end_query(*m_current_command_buffer, occlusion_id);
|
||||
@@ -1578,6 +1541,7 @@ void VKGSRender::on_init_thread()
|
||||
|
||||
GSRender::on_init_thread();
|
||||
rsx_thread = std::this_thread::get_id();
|
||||
zcull_ctrl.reset(static_cast<::rsx::reports::ZCULL_control*>(this));
|
||||
|
||||
if (!supports_native_ui)
|
||||
{
|
||||
@@ -1640,6 +1604,7 @@ void VKGSRender::on_init_thread()
|
||||
|
||||
void VKGSRender::on_exit()
|
||||
{
|
||||
zcull_ctrl.release();
|
||||
return GSRender::on_exit();
|
||||
}
|
||||
|
||||
@@ -2015,7 +1980,7 @@ void VKGSRender::do_local_task(bool /*idle*/)
|
||||
{
|
||||
if (m_flush_requests.pending())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_flush_queue_mutex);
|
||||
std::lock_guard<shared_mutex> lock(m_flush_queue_mutex);
|
||||
|
||||
//TODO: Determine if a hard sync is necessary
|
||||
//Pipeline barriers later may do a better job synchronizing than wholly stalling the pipeline
|
||||
@@ -2160,7 +2125,7 @@ bool VKGSRender::check_program_status()
|
||||
return (rsx::method_registers.shader_program_address() != 0);
|
||||
}
|
||||
|
||||
void VKGSRender::load_program(u32 vertex_count, u32 vertex_base)
|
||||
void VKGSRender::load_program(const vk::vertex_upload_info& vertex_info)
|
||||
{
|
||||
get_current_fragment_program(fs_sampler_state);
|
||||
verify(HERE), current_fragment_program.valid;
|
||||
@@ -2195,41 +2160,64 @@ void VKGSRender::load_program(u32 vertex_count, u32 vertex_base)
|
||||
if (rsx::method_registers.color_mask_g()) mask |= VK_COLOR_COMPONENT_G_BIT;
|
||||
if (rsx::method_registers.color_mask_r()) mask |= VK_COLOR_COMPONENT_R_BIT;
|
||||
|
||||
VkColorComponentFlags color_masks[4] = { mask };
|
||||
|
||||
u8 render_targets[] = { 0, 1, 2, 3 };
|
||||
|
||||
for (u8 idx = 0; idx < m_draw_buffers_count; ++idx)
|
||||
{
|
||||
properties.att_state[render_targets[idx]].colorWriteMask = mask;
|
||||
properties.att_state[idx].colorWriteMask = mask;
|
||||
}
|
||||
|
||||
if (rsx::method_registers.blend_enabled())
|
||||
bool mrt_blend_enabled[] =
|
||||
{
|
||||
VkBlendFactor sfactor_rgb = vk::get_blend_factor(rsx::method_registers.blend_func_sfactor_rgb());
|
||||
VkBlendFactor sfactor_a = vk::get_blend_factor(rsx::method_registers.blend_func_sfactor_a());
|
||||
VkBlendFactor dfactor_rgb = vk::get_blend_factor(rsx::method_registers.blend_func_dfactor_rgb());
|
||||
VkBlendFactor dfactor_a = vk::get_blend_factor(rsx::method_registers.blend_func_dfactor_a());
|
||||
rsx::method_registers.blend_enabled(),
|
||||
rsx::method_registers.blend_enabled_surface_1(),
|
||||
rsx::method_registers.blend_enabled_surface_2(),
|
||||
rsx::method_registers.blend_enabled_surface_3()
|
||||
};
|
||||
|
||||
VkBlendOp equation_rgb = vk::get_blend_op(rsx::method_registers.blend_equation_rgb());
|
||||
VkBlendOp equation_a = vk::get_blend_op(rsx::method_registers.blend_equation_a());
|
||||
bool blend_equation_override = false;
|
||||
VkBlendFactor sfactor_rgb, sfactor_a, dfactor_rgb, dfactor_a;
|
||||
VkBlendOp equation_rgb, equation_a;
|
||||
|
||||
for (u8 idx = 0; idx < m_draw_buffers_count; ++idx)
|
||||
if (rsx::method_registers.msaa_alpha_to_coverage_enabled() &&
|
||||
!rsx::method_registers.alpha_test_enabled())
|
||||
{
|
||||
if (rsx::method_registers.msaa_enabled() &&
|
||||
rsx::method_registers.msaa_sample_mask() &&
|
||||
rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::center_1_sample)
|
||||
{
|
||||
properties.att_state[render_targets[idx]].blendEnable = VK_TRUE;
|
||||
properties.att_state[render_targets[idx]].srcColorBlendFactor = sfactor_rgb;
|
||||
properties.att_state[render_targets[idx]].dstColorBlendFactor = dfactor_rgb;
|
||||
properties.att_state[render_targets[idx]].srcAlphaBlendFactor = sfactor_a;
|
||||
properties.att_state[render_targets[idx]].dstAlphaBlendFactor = dfactor_a;
|
||||
properties.att_state[render_targets[idx]].colorBlendOp = equation_rgb;
|
||||
properties.att_state[render_targets[idx]].alphaBlendOp = equation_a;
|
||||
//fake alpha-to-coverage
|
||||
//blend used in conjunction with alpha test to fake order-independent edge transparency
|
||||
mrt_blend_enabled[0] = mrt_blend_enabled[1] = mrt_blend_enabled[2] = mrt_blend_enabled[3] = true;
|
||||
blend_equation_override = true;
|
||||
sfactor_rgb = sfactor_a = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
dfactor_rgb = dfactor_a = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
equation_rgb = equation_a = VK_BLEND_OP_ADD;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (mrt_blend_enabled[0] || mrt_blend_enabled[1] || mrt_blend_enabled[2] || mrt_blend_enabled[3])
|
||||
{
|
||||
if (!blend_equation_override)
|
||||
{
|
||||
sfactor_rgb = vk::get_blend_factor(rsx::method_registers.blend_func_sfactor_rgb());
|
||||
sfactor_a = vk::get_blend_factor(rsx::method_registers.blend_func_sfactor_a());
|
||||
dfactor_rgb = vk::get_blend_factor(rsx::method_registers.blend_func_dfactor_rgb());
|
||||
dfactor_a = vk::get_blend_factor(rsx::method_registers.blend_func_dfactor_a());
|
||||
equation_rgb = vk::get_blend_op(rsx::method_registers.blend_equation_rgb());
|
||||
equation_a = vk::get_blend_op(rsx::method_registers.blend_equation_a());
|
||||
}
|
||||
|
||||
for (u8 idx = 0; idx < m_draw_buffers_count; ++idx)
|
||||
{
|
||||
properties.att_state[render_targets[idx]].blendEnable = VK_FALSE;
|
||||
if (mrt_blend_enabled[idx])
|
||||
{
|
||||
properties.att_state[idx].blendEnable = VK_TRUE;
|
||||
properties.att_state[idx].srcColorBlendFactor = sfactor_rgb;
|
||||
properties.att_state[idx].dstColorBlendFactor = dfactor_rgb;
|
||||
properties.att_state[idx].srcAlphaBlendFactor = sfactor_a;
|
||||
properties.att_state[idx].dstAlphaBlendFactor = dfactor_a;
|
||||
properties.att_state[idx].colorBlendOp = equation_rgb;
|
||||
properties.att_state[idx].alphaBlendOp = equation_a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2343,11 +2331,13 @@ void VKGSRender::load_program(u32 vertex_count, u32 vertex_base)
|
||||
fill_scale_offset_data(buf, false);
|
||||
fill_user_clip_data(buf + 64);
|
||||
*(reinterpret_cast<u32*>(buf + 128)) = rsx::method_registers.transform_branch_bits();
|
||||
*(reinterpret_cast<u32*>(buf + 132)) = vertex_base;
|
||||
*(reinterpret_cast<u32*>(buf + 132)) = vertex_info.vertex_index_base;
|
||||
*(reinterpret_cast<f32*>(buf + 136)) = rsx::method_registers.point_size();
|
||||
*(reinterpret_cast<f32*>(buf + 140)) = rsx::method_registers.clip_min();
|
||||
*(reinterpret_cast<f32*>(buf + 144)) = rsx::method_registers.clip_max();
|
||||
fill_vertex_layout_state(m_vertex_layout, vertex_count, reinterpret_cast<s32*>(buf + 160));
|
||||
|
||||
fill_vertex_layout_state(m_vertex_layout, vertex_info.allocated_vertex_count, reinterpret_cast<s32*>(buf + 160),
|
||||
vertex_info.persistent_window_offset, vertex_info.volatile_window_offset);
|
||||
|
||||
//Vertex constants
|
||||
buf = buf + 512;
|
||||
@@ -2357,7 +2347,10 @@ void VKGSRender::load_program(u32 vertex_count, u32 vertex_base)
|
||||
//Fragment constants
|
||||
buf = buf + 8192;
|
||||
if (fragment_constants_sz)
|
||||
m_prog_buffer->fill_fragment_constants_buffer({ reinterpret_cast<float*>(buf), ::narrow<int>(fragment_constants_sz) }, fragment_program);
|
||||
{
|
||||
m_prog_buffer->fill_fragment_constants_buffer({ reinterpret_cast<float*>(buf), ::narrow<int>(fragment_constants_sz) },
|
||||
fragment_program, vk::sanitize_fp_values());
|
||||
}
|
||||
|
||||
fill_fragment_state_buffer(buf + fragment_constants_sz, fragment_program);
|
||||
|
||||
@@ -2588,6 +2581,43 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context)
|
||||
const auto bpp = get_format_block_size_in_bytes(color_fmt);
|
||||
const u32 aa_factor = (aa_mode == rsx::surface_antialiasing::center_1_sample || aa_mode == rsx::surface_antialiasing::diagonal_centered_2_samples) ? 1 : 2;
|
||||
|
||||
//Window (raster) offsets
|
||||
const auto window_offset_x = rsx::method_registers.window_offset_x();
|
||||
const auto window_offset_y = rsx::method_registers.window_offset_y();
|
||||
const auto window_clip_width = rsx::method_registers.window_clip_horizontal();
|
||||
const auto window_clip_height = rsx::method_registers.window_clip_vertical();
|
||||
|
||||
if (window_offset_x || window_offset_y)
|
||||
{
|
||||
//Window offset is what affects the raster position!
|
||||
//Tested with Turbo: Super stunt squad that only changes the window offset to declare new framebuffers
|
||||
//Sampling behavior clearly indicates the addresses are expected to have changed
|
||||
if (auto clip_type = rsx::method_registers.window_clip_type())
|
||||
LOG_ERROR(RSX, "Unknown window clip type 0x%X" HERE, clip_type);
|
||||
|
||||
for (const auto &index : rsx::utility::get_rtt_indexes(target))
|
||||
{
|
||||
if (surface_addresses[index])
|
||||
{
|
||||
const u32 window_offset_bytes = (std::max<u32>(surface_pitchs[index], clip_width * aa_factor * bpp) * window_offset_y) + ((aa_factor * bpp) * window_offset_x);
|
||||
surface_addresses[index] += window_offset_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
if (zeta_address)
|
||||
{
|
||||
const auto depth_bpp = (depth_fmt == rsx::surface_depth_format::z16 ? 2 : 4);
|
||||
zeta_address += (std::max<u32>(zeta_pitch, clip_width * aa_factor * depth_bpp) * window_offset_y) + ((aa_factor * depth_bpp) * window_offset_x);
|
||||
}
|
||||
}
|
||||
|
||||
if ((window_clip_width && window_clip_width < clip_width) ||
|
||||
(window_clip_height && window_clip_height < clip_height))
|
||||
{
|
||||
LOG_ERROR(RSX, "Unexpected window clip dimensions: window_clip=%dx%d, surface_clip=%dx%d",
|
||||
window_clip_width, window_clip_height, clip_width, clip_height);
|
||||
}
|
||||
|
||||
if (m_draw_fbo)
|
||||
{
|
||||
bool really_changed = false;
|
||||
@@ -2676,10 +2706,9 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context)
|
||||
surface->rsx_pitch = surface_pitchs[index];
|
||||
|
||||
surface->aa_mode = aa_mode;
|
||||
surface->set_raster_offset(clip_x, clip_y, bpp);
|
||||
m_texture_cache.notify_surface_changed(surface_addresses[index]);
|
||||
|
||||
m_texture_cache.tag_framebuffer(surface_addresses[index] + surface->raster_address_offset);
|
||||
m_texture_cache.tag_framebuffer(surface_addresses[index]);
|
||||
m_draw_buffers_count++;
|
||||
}
|
||||
}
|
||||
@@ -2694,10 +2723,9 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context)
|
||||
ds->rsx_pitch = m_depth_surface_info.pitch;
|
||||
|
||||
ds->aa_mode = aa_mode;
|
||||
ds->set_raster_offset(clip_x, clip_y, get_pixel_size(rsx::method_registers.surface_depth_fmt()));
|
||||
m_texture_cache.notify_surface_changed(zeta_address);
|
||||
|
||||
m_texture_cache.tag_framebuffer(zeta_address + ds->raster_address_offset);
|
||||
m_texture_cache.tag_framebuffer(zeta_address);
|
||||
}
|
||||
|
||||
if (g_cfg.video.write_color_buffers)
|
||||
@@ -2791,7 +2819,7 @@ void VKGSRender::prepare_rtts(rsx::framebuffer_creation_context context)
|
||||
m_draw_fbo.reset(new vk::framebuffer_holder(*m_device, current_render_pass, fbo_width, fbo_height, std::move(fbo_images)));
|
||||
}
|
||||
|
||||
check_zcull_status(true, false);
|
||||
check_zcull_status(true);
|
||||
}
|
||||
|
||||
void VKGSRender::reinitialize_swapchain()
|
||||
@@ -3029,6 +3057,11 @@ void VKGSRender::flip(int buffer)
|
||||
//The render might have been done offscreen or in software and a blit used to display
|
||||
image_to_flip = surface->get_raw_texture();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Read from cell
|
||||
image_to_flip = m_texture_cache.upload_image_simple(*m_current_command_buffer, absolute_address, buffer_width, buffer_height);
|
||||
}
|
||||
}
|
||||
|
||||
VkImage target_image = m_swapchain->get_image(m_current_frame->present_image);
|
||||
@@ -3114,22 +3147,24 @@ void VKGSRender::flip(int buffer)
|
||||
|
||||
if (g_cfg.video.overlay)
|
||||
{
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 0, direct_fbo->width(), direct_fbo->height(), "draw calls: " + std::to_string(m_draw_calls));
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 18, direct_fbo->width(), direct_fbo->height(), "draw call setup: " + std::to_string(m_setup_time) + "us");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 36, direct_fbo->width(), direct_fbo->height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 54, direct_fbo->width(), direct_fbo->height(), "texture upload time: " + std::to_string(m_textures_upload_time) + "us");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 72, direct_fbo->width(), direct_fbo->height(), "draw call execution: " + std::to_string(m_draw_time) + "us");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 90, direct_fbo->width(), direct_fbo->height(), "submit and flip: " + std::to_string(m_flip_time) + "us");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 0, direct_fbo->width(), direct_fbo->height(), "RSX Load: " + std::to_string(get_load()) + "%");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 18, direct_fbo->width(), direct_fbo->height(), "draw calls: " + std::to_string(m_draw_calls));
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 36, direct_fbo->width(), direct_fbo->height(), "draw call setup: " + std::to_string(m_setup_time) + "us");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 54, direct_fbo->width(), direct_fbo->height(), "vertex upload time: " + std::to_string(m_vertex_upload_time) + "us");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 72, direct_fbo->width(), direct_fbo->height(), "texture upload time: " + std::to_string(m_textures_upload_time) + "us");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 90, direct_fbo->width(), direct_fbo->height(), "draw call execution: " + std::to_string(m_draw_time) + "us");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 108, direct_fbo->width(), direct_fbo->height(), "submit and flip: " + std::to_string(m_flip_time) + "us");
|
||||
|
||||
auto num_dirty_textures = m_texture_cache.get_unreleased_textures_count();
|
||||
auto texture_memory_size = m_texture_cache.get_texture_memory_in_use() / (1024 * 1024);
|
||||
auto tmp_texture_memory_size = m_texture_cache.get_temporary_memory_in_use() / (1024 * 1024);
|
||||
auto num_flushes = m_texture_cache.get_num_flush_requests();
|
||||
auto cache_miss_ratio = (u32)ceil(m_texture_cache.get_cache_miss_ratio() * 100);
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 126, direct_fbo->width(), direct_fbo->height(), "Unreleased textures: " + std::to_string(num_dirty_textures));
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 144, direct_fbo->width(), direct_fbo->height(), "Texture cache memory: " + std::to_string(texture_memory_size) + "M");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 162, direct_fbo->width(), direct_fbo->height(), "Temporary texture memory: " + std::to_string(tmp_texture_memory_size) + "M");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 180, direct_fbo->width(), direct_fbo->height(), "Flush requests: " + std::to_string(num_flushes) + " (" + std::to_string(cache_miss_ratio) + "% hard faults)");
|
||||
const auto num_dirty_textures = m_texture_cache.get_unreleased_textures_count();
|
||||
const auto texture_memory_size = m_texture_cache.get_texture_memory_in_use() / (1024 * 1024);
|
||||
const auto tmp_texture_memory_size = m_texture_cache.get_temporary_memory_in_use() / (1024 * 1024);
|
||||
const auto num_flushes = m_texture_cache.get_num_flush_requests();
|
||||
const auto num_mispredict = m_texture_cache.get_num_cache_mispredictions();
|
||||
const auto cache_miss_ratio = (u32)ceil(m_texture_cache.get_cache_miss_ratio() * 100);
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 144, direct_fbo->width(), direct_fbo->height(), "Unreleased textures: " + std::to_string(num_dirty_textures));
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 162, direct_fbo->width(), direct_fbo->height(), "Texture cache memory: " + std::to_string(texture_memory_size) + "M");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 180, direct_fbo->width(), direct_fbo->height(), "Temporary texture memory: " + std::to_string(tmp_texture_memory_size) + "M");
|
||||
m_text_writer->print_text(*m_current_command_buffer, *direct_fbo, 0, 198, direct_fbo->width(), direct_fbo->height(), fmt::format("Flush requests: %d (%d%% hard faults, %d mispedictions)", num_flushes, cache_miss_ratio, num_mispredict));
|
||||
}
|
||||
|
||||
vk::change_image_layout(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, present_layout, subres);
|
||||
@@ -3165,7 +3200,7 @@ bool VKGSRender::scaled_image_from_memory(rsx::blit_src_info& src, rsx::blit_dst
|
||||
check_heap_status();
|
||||
|
||||
//Stop all parallel operations until this is finished
|
||||
std::lock_guard<std::mutex> lock(m_secondary_cb_guard);
|
||||
std::lock_guard<shared_mutex> lock(m_secondary_cb_guard);
|
||||
|
||||
auto result = m_texture_cache.blit(src, dst, interpolate, m_rtts, *m_current_command_buffer);
|
||||
m_current_command_buffer->begin();
|
||||
@@ -3211,31 +3246,32 @@ bool VKGSRender::scaled_image_from_memory(rsx::blit_src_info& src, rsx::blit_dst
|
||||
return false;
|
||||
}
|
||||
|
||||
void VKGSRender::clear_zcull_stats(u32 type)
|
||||
{
|
||||
rsx::thread::clear_zcull_stats(type);
|
||||
m_occlusion_map.clear();
|
||||
m_occlusion_query_pool.reset_all(*m_current_command_buffer);
|
||||
}
|
||||
|
||||
void VKGSRender::begin_occlusion_query(rsx::occlusion_query_info* query)
|
||||
void VKGSRender::begin_occlusion_query(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
query->result = 0;
|
||||
query->sync_timestamp = get_system_time();
|
||||
//query->sync_timestamp = get_system_time();
|
||||
m_active_query_info = query;
|
||||
m_occlusion_query_active = true;
|
||||
}
|
||||
|
||||
void VKGSRender::end_occlusion_query(rsx::occlusion_query_info* query)
|
||||
void VKGSRender::end_occlusion_query(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
m_occlusion_query_active = false;
|
||||
m_active_query_info = nullptr;
|
||||
|
||||
flush_command_queue();
|
||||
//Avoid stalling later if this query is already tied to a report
|
||||
if (query->num_draws && query->owned && !m_flush_requests.pending())
|
||||
{
|
||||
m_flush_requests.post(false);
|
||||
m_flush_requests.remove_one();
|
||||
}
|
||||
}
|
||||
|
||||
bool VKGSRender::check_occlusion_query_status(rsx::occlusion_query_info* query)
|
||||
bool VKGSRender::check_occlusion_query_status(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
if (!query->num_draws)
|
||||
return true;
|
||||
|
||||
auto found = m_occlusion_map.find(query->driver_handle);
|
||||
if (found == m_occlusion_map.end())
|
||||
return true;
|
||||
@@ -3245,16 +3281,26 @@ bool VKGSRender::check_occlusion_query_status(rsx::occlusion_query_info* query)
|
||||
return true;
|
||||
|
||||
if (data.command_buffer_to_wait == m_current_command_buffer)
|
||||
{
|
||||
if (!m_flush_requests.pending())
|
||||
{
|
||||
//Likely to be read at some point in the near future, submit now to avoid stalling later
|
||||
m_flush_requests.post(false);
|
||||
m_flush_requests.remove_one();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.command_buffer_to_wait->pending)
|
||||
//Don't bother poking the state, a flush later will likely do it for free
|
||||
return false;
|
||||
|
||||
u32 oldest = data.indices.front();
|
||||
return m_occlusion_query_pool.check_query_status(oldest);
|
||||
}
|
||||
|
||||
void VKGSRender::get_occlusion_query_result(rsx::occlusion_query_info* query)
|
||||
void VKGSRender::get_occlusion_query_result(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
auto found = m_occlusion_map.find(query->driver_handle);
|
||||
if (found == m_occlusion_map.end())
|
||||
@@ -3264,20 +3310,32 @@ void VKGSRender::get_occlusion_query_result(rsx::occlusion_query_info* query)
|
||||
if (data.indices.size() == 0)
|
||||
return;
|
||||
|
||||
if (data.command_buffer_to_wait == m_current_command_buffer)
|
||||
flush_command_queue(); //Should hard sync, but this should almost never ever happen
|
||||
|
||||
if (data.command_buffer_to_wait->pending)
|
||||
data.command_buffer_to_wait->wait();
|
||||
|
||||
//Gather data
|
||||
for (const auto occlusion_id : data.indices)
|
||||
if (query->num_draws)
|
||||
{
|
||||
//We only need one hit
|
||||
if (auto value = m_occlusion_query_pool.get_query_result(occlusion_id))
|
||||
if (data.command_buffer_to_wait == m_current_command_buffer)
|
||||
{
|
||||
query->result = 1;
|
||||
break;
|
||||
flush_command_queue();
|
||||
|
||||
//Clear any deferred flush requests from previous call to get_query_status()
|
||||
if (m_flush_requests.pending())
|
||||
{
|
||||
m_flush_requests.clear_pending_flag();
|
||||
m_flush_requests.consumer_wait();
|
||||
}
|
||||
}
|
||||
|
||||
if (data.command_buffer_to_wait->pending)
|
||||
data.command_buffer_to_wait->wait();
|
||||
|
||||
//Gather data
|
||||
for (const auto occlusion_id : data.indices)
|
||||
{
|
||||
//We only need one hit
|
||||
if (auto value = m_occlusion_query_pool.get_query_result(occlusion_id))
|
||||
{
|
||||
query->result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3285,6 +3343,26 @@ void VKGSRender::get_occlusion_query_result(rsx::occlusion_query_info* query)
|
||||
m_occlusion_map.erase(query->driver_handle);
|
||||
}
|
||||
|
||||
void VKGSRender::discard_occlusion_query(rsx::reports::occlusion_query_info* query)
|
||||
{
|
||||
if (m_active_query_info == query)
|
||||
{
|
||||
m_occlusion_query_active = false;
|
||||
m_active_query_info = nullptr;
|
||||
}
|
||||
|
||||
auto found = m_occlusion_map.find(query->driver_handle);
|
||||
if (found == m_occlusion_map.end())
|
||||
return;
|
||||
|
||||
auto &data = found->second;
|
||||
if (data.indices.size() == 0)
|
||||
return;
|
||||
|
||||
m_occlusion_query_pool.reset_queries(*m_current_command_buffer, data.indices);
|
||||
m_occlusion_map.erase(query->driver_handle);
|
||||
}
|
||||
|
||||
void VKGSRender::shell_do_cleanup()
|
||||
{
|
||||
//TODO: Guard this
|
||||
|
||||
@@ -23,13 +23,24 @@ namespace vk
|
||||
using null_vertex_cache = vertex_cache;
|
||||
|
||||
using shader_cache = rsx::shaders_cache<vk::pipeline_props, VKProgramBuffer>;
|
||||
|
||||
struct vertex_upload_info
|
||||
{
|
||||
VkPrimitiveTopology primitive;
|
||||
u32 vertex_draw_count;
|
||||
u32 allocated_vertex_count;
|
||||
u32 vertex_index_base;
|
||||
u32 persistent_window_offset;
|
||||
u32 volatile_window_offset;
|
||||
std::optional<std::tuple<VkDeviceSize, VkIndexType>> index_info;
|
||||
};
|
||||
}
|
||||
|
||||
//Heap allocation sizes in MB
|
||||
//NOTE: Texture uploads can be huge, upto 16MB for a single texture (4096x4096px)
|
||||
#define VK_ATTRIB_RING_BUFFER_SIZE_M 256
|
||||
#define VK_ATTRIB_RING_BUFFER_SIZE_M 384
|
||||
#define VK_TEXTURE_UPLOAD_RING_BUFFER_SIZE_M 256
|
||||
#define VK_UBO_RING_BUFFER_SIZE_M 64
|
||||
#define VK_UBO_RING_BUFFER_SIZE_M 128
|
||||
#define VK_INDEX_RING_BUFFER_SIZE_M 64
|
||||
|
||||
#define VK_MAX_ASYNC_CB_COUNT 64
|
||||
@@ -44,7 +55,7 @@ struct command_buffer_chunk: public vk::command_buffer
|
||||
|
||||
std::atomic_bool pending = { false };
|
||||
std::atomic<u64> last_sync = { 0 };
|
||||
std::mutex guard_mutex;
|
||||
shared_mutex guard_mutex;
|
||||
|
||||
command_buffer_chunk()
|
||||
{}
|
||||
@@ -86,7 +97,7 @@ struct command_buffer_chunk: public vk::command_buffer
|
||||
{
|
||||
if (vkGetFenceStatus(m_device, submit_fence) == VK_SUCCESS)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(guard_mutex);
|
||||
std::lock_guard<shared_mutex> lock(guard_mutex);
|
||||
|
||||
if (pending)
|
||||
{
|
||||
@@ -100,7 +111,7 @@ struct command_buffer_chunk: public vk::command_buffer
|
||||
|
||||
void wait()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(guard_mutex);
|
||||
std::lock_guard<shared_mutex> lock(guard_mutex);
|
||||
|
||||
if (!pending)
|
||||
return;
|
||||
@@ -233,7 +244,7 @@ struct flush_request_task
|
||||
}
|
||||
};
|
||||
|
||||
class VKGSRender : public GSRender
|
||||
class VKGSRender : public GSRender, public ::rsx::reports::ZCULL_control
|
||||
{
|
||||
private:
|
||||
VKFragmentProgram m_fragment_prog;
|
||||
@@ -254,7 +265,7 @@ private:
|
||||
std::unique_ptr<vk::depth_scaling_pass> m_depth_scaler;
|
||||
std::unique_ptr<vk::ui_overlay_renderer> m_ui_renderer;
|
||||
|
||||
std::mutex m_sampler_mutex;
|
||||
shared_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 = {};
|
||||
@@ -262,8 +273,8 @@ private:
|
||||
std::array<std::unique_ptr<vk::sampler>, rsx::limits::fragment_textures_count> fs_sampler_handles;
|
||||
std::array<std::unique_ptr<vk::sampler>, rsx::limits::vertex_textures_count> vs_sampler_handles;
|
||||
|
||||
VkBufferView m_persistent_attribute_storage;
|
||||
VkBufferView m_volatile_attribute_storage;
|
||||
std::unique_ptr<vk::buffer_view> m_persistent_attribute_storage;
|
||||
std::unique_ptr<vk::buffer_view> m_volatile_attribute_storage;
|
||||
|
||||
public:
|
||||
//vk::fbo draw_fbo;
|
||||
@@ -281,10 +292,10 @@ private:
|
||||
vk::command_pool m_command_buffer_pool;
|
||||
vk::occlusion_query_pool m_occlusion_query_pool;
|
||||
bool m_occlusion_query_active = false;
|
||||
rsx::occlusion_query_info *m_active_query_info = nullptr;
|
||||
rsx::reports::occlusion_query_info *m_active_query_info = nullptr;
|
||||
std::unordered_map<u32, occlusion_data> m_occlusion_map;
|
||||
|
||||
std::mutex m_secondary_cb_guard;
|
||||
shared_mutex m_secondary_cb_guard;
|
||||
vk::command_pool m_secondary_command_buffer_pool;
|
||||
vk::command_buffer m_secondary_command_buffer; //command buffer used for setup operations
|
||||
|
||||
@@ -335,7 +346,7 @@ private:
|
||||
bool m_flush_draw_buffers = false;
|
||||
std::atomic<int> m_last_flushable_cb = {-1 };
|
||||
|
||||
std::mutex m_flush_queue_mutex;
|
||||
shared_mutex m_flush_queue_mutex;
|
||||
flush_request_task m_flush_requests;
|
||||
|
||||
std::thread::id rsx_thread;
|
||||
@@ -379,21 +390,21 @@ private:
|
||||
|
||||
void check_heap_status();
|
||||
|
||||
/// returns primitive topology, index_count, allocated_verts, vertex_base_index, (offset in index buffer, index type)
|
||||
std::tuple<VkPrimitiveTopology, u32, u32, u32, std::optional<std::tuple<VkDeviceSize, VkIndexType> > > upload_vertex_data();
|
||||
vk::vertex_upload_info upload_vertex_data();
|
||||
|
||||
public:
|
||||
bool check_program_status();
|
||||
void load_program(u32 vertex_count, u32 vertex_base);
|
||||
void load_program(const vk::vertex_upload_info& vertex_info);
|
||||
void init_buffers(rsx::framebuffer_creation_context context, bool skip_reading = false);
|
||||
void read_buffers();
|
||||
void write_buffers();
|
||||
void set_viewport();
|
||||
|
||||
void clear_zcull_stats(u32 type) override;
|
||||
void begin_occlusion_query(rsx::occlusion_query_info* query) override;
|
||||
void end_occlusion_query(rsx::occlusion_query_info* query) override;
|
||||
bool check_occlusion_query_status(rsx::occlusion_query_info* query) override;
|
||||
void get_occlusion_query_result(rsx::occlusion_query_info* query) override;
|
||||
void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
void end_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
bool check_occlusion_query_status(rsx::reports::occlusion_query_info* query) override;
|
||||
void get_occlusion_query_result(rsx::reports::occlusion_query_info* query) override;
|
||||
void discard_occlusion_query(rsx::reports::occlusion_query_info* query) override;
|
||||
|
||||
protected:
|
||||
void begin() override;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user