Compare commits

...

88 Commits

Author SHA1 Message Date
Zangetsu38 a33e5e909e Test 2018-03-13 21:43:38 +01:00
Jake 5cd8a00c8c quick primitive base implementation 2018-03-13 21:43:38 +01:00
Jake 488c4d198f trophyfix 2018-03-13 21:43:38 +01:00
Zangetsu38 081d7c5de7 Update LLVM 6.0. 2018-03-13 21:43:37 +01:00
Zion Nimchuk 09c77ff46c Implement LLVM 6 Polly 2018-03-13 21:43:37 +01:00
Zangetsu38 2102b5d4aa Update Project to Visual Studio 2017.
Fix VS2017 compile by clienthax (PPUAnalyser.cpp).
2018-03-13 21:43:36 +01:00
eladash 57f0ddd13c spu: allow branching to self on asmjit 2018-03-13 21:43:35 +01:00
kd-11 5f047034ae rsx: Disable async count verification to avoid lockup due to zombie reports in ZCULL 2018-03-13 18:55:03 +03:00
kd-11 ffe6c9ba5a fix linux builds 2018-03-13 18:55:03 +03:00
kd-11 910fc54ee2 vk: Implement reading from cell if swap image isn't found 2018-03-13 18:55:03 +03:00
kd-11 f00d9a7c7f rssx" Halfplement alpha-to-coverage AA transparency 2018-03-13 18:55:03 +03:00
kd-11 2dce55d036 rsx: ZCULL synchronization fixes
- Track asynchronous operations in RSX core
- Add read barriers to force pending writes to finish.
  Fixes zcull delay flicker in all UE3 titles without forcing hard stall
- Increase zcull latency as all writes should be synchronized now
2018-03-13 18:55:03 +03:00
kd-11 315798b1f4 rsx: ZCULL rewrite and other improvements
- ZCULL unit emulation rewritten
- ZCULL reports are now deferred avoiding pipeline stalls
- Minor optimizations; replaced std::mutex with shared_mutex where contention is rare
- Silence unnecessary error message
- Small improvement to out of memory handling for vulkan and slightly bump vertex buffer heap
2018-03-13 18:55:03 +03:00
kd-11 dece1e01f4 rsx: Improve transform constants management
- Removes the duplicate local_transform_constants
- Resets the transform constants on every context reset
- Simplifies the code abit which should make it faster
- NOTE: Transform constants are persistent across context re-init events (VF5)
2018-03-13 18:55:03 +03:00
kd-11 a19ffba8e8 rsx: Simplify MRT blend setup; Enable separable MRT blend on vulkan and fix corner cases for GL 2018-03-13 18:55:03 +03:00
kd-11 e230867492 rsx: Properly implement raster window offsets 2018-03-13 18:55:03 +03:00
kd-11 0c8e4c0887 rsx: Improve FIFO commandlist flattening
- TODO: Alot of work is still needed to execute draw commands out of order
  Thats the only solution to games sending many draw calls with high frequency of state changes
2018-03-13 18:55:03 +03:00
kd-11 84b8a08d26 rsx: Basic performance counters 2018-03-13 18:55:03 +03:00
kd-11 2855869530 vulkan: Support APPLE
- Adds support for compilation on MAC with moltenVK. Note that vulkan does
  not work on MacOS yet. There are two main blockers:-
  1) Texture component swizzles are not supported except for
  RGBA8_UNORM->BGRA8_UNORM.
  2) There is a bug in their SPIR-V -> MSL generator.
  GLSL.std.450.xxxx functions are not implemented which breaks rpcs3
  functionality. Trying to compile a vertex shader will throw because
  unpackHalf2x16 is missing.
2018-03-13 18:55:03 +03:00
kd-11 d41b49d8b4 rsx/fp: Color output registers are always present and zero initialized
- According to NV_fragment_program spec, registers are zero initialized always
- A program even without writing to these registers will have black (0, 0, 0, 0) output
  Confirmed behaviour with MotorStorm games. Their engine uses this quirk to clear color buffers when doing depth replace
  Might be an unfixed game bug
2018-03-13 18:55:03 +03:00
kd-11 4804efc17d rsx: Clear up confusion on depth writes.
According to the NV_fragment_program spec, its not feasible to have 16-bit depth wries
 NOTE: NV_fragement_program precedes NV_fragment_program2 which is very
 close to what RSX consumes. It is hardware from that era afterall
2018-03-13 18:55:03 +03:00
kd-11 053ab585f4 gl/vk: Clean up some format casts
- TODO: Byte ordering considerations on data casts
2018-03-13 18:55:03 +03:00
kd-11 20d4c09a1c rsx/vk/gl: Enforce format matching for render target resources. Fall back to raw data copy if match fails
- Forces Bitcast of texture data if input format cannot possibly be the
  same as the existing texture format

- rsx: Other minor improvements to texture cache :-
  - remove obsolete blit engine incompatibility warning. The texture will be re-uploaded if it is indeed incompatible
  - Implement warn_once and err_once to avoid spamming the log with systemic errors
  - Track mispredicted flushes
  - Reswizzle bitcasted texture data to native layout
    TODO: Also needs reshuffle according to input remap vector
2018-03-13 18:55:03 +03:00
kd-11 68b3229756 rsx/fp: Improve rgister component gather detection
- Also avoids clobbering register data by keeping gathered bits in a temp var
2018-03-13 18:55:03 +03:00
kd-11 87741141f1 rsx/vulkan: Add post-compilation key validation and dynamically determine attachment write maks based on decompiled shader
- A new step is added between decompilation and pipeline object creation allowing for properties to be updated based on shader contents
- Allos masking off attachment writes that are unmodified in the shader
2018-03-13 18:55:03 +03:00
kd-11 705820c430 rsx: Nvidia driver compatibility workarounds
- Sanitize NaN values before they reach the driver. On nvidia (X * NaN = X)
2018-03-13 18:55:03 +03:00
kd-11 6b23e733d0 rsx/gl/vk: Improvements
- gl: Do not call makeCurrent every flip - it is already called in set_current()
- gl: Improve ring buffer behaviour; use sliding window to view buffers larger than maximum viewable hardware range
  NV hardware can only view 128M at a time
- gl/vk: Bump transform constant heap size When lots of draw calls are issued, the heap is exhaused very fast (8k per draw)
- gl: Remove CLIENT_STORAGE_BIT from ring buffers. Performance is marginally better without this flag (at least on windows)
2018-03-13 18:55:03 +03:00
kd-11 07cbf3da48 rsx/gl: Minor fixes
- Identify depth textures reaching the gpu via shader_read upload path
- Use correct timestamp counter for opengl
- inline draw_state::test_property because msvc doesnt do it for us
2018-03-13 18:55:03 +03:00
kd-11 af1b13550b rsx/vk: More optimizations
- Do not bother rechecking the dirty sampler pool for hits. Its faster to create new sampler than to search the pool
- Reserve some memory on vertex layout struct to reduce reallocation penalty
2018-03-13 18:55:03 +03:00
kd-11 8ccaabb502 vulkan: Optimize vertex data upload
- Reuse buffer views as much as possible, vkCreateBufferView is slow on NV
  Implemented as a large sliding window, reuseable until it is filled
2018-03-13 18:55:03 +03:00
kd-11 01349b8cee rsx: Texture cache fixes - Optionally attempt to merge framebuffers into an atlas if partial resources are missing - Support for data update requests to the temporary subresource handler This is useful for framebuffer feedback loops where a new copy is needed after every draw call (resource is always dirty) 2018-03-13 18:55:03 +03:00
kd-11 4487cc8e7a Remove an ugly hack pertaining to partial framebuffer-resident texture data - Its better to fill in the missing information with a wrap or clamp than to fake the texture reads in valid regions - Texture coordinate scaling is used to fill in for the cropped dimension available 2018-03-13 18:55:03 +03:00
TGEnigma cb9e6e75db Debugger improvements (#4026)
* Improve debugger
 * Added 'Step Over' functionality
 * Added special SPU pause functionality that pauses the SPU thread when the tag mask is at 0x80000000 by holding ctrl while pausing
  * Go to address dialog now evaluates expressions, including defined variables such as pc, r1, r2, etc
    * Requires QtScript to be linked with the project
  * Made the option to center shown addresses (Go to addr/pc) optional by making it an entry in the GUI ini config
  * Shown addresses now appear 'selected'
  * New keyboard shortcuts!
    - Ctrl+G -> Go to address
    - F10 -> Step Over
    - F11 -> Step (Into)
2018-03-13 16:23:12 +04:00
Jan Beich e487480ca9 Thread: unbreak build on BSDs after ac82ecf387
Utilities/Thread.cpp:1644:2: error: use of undeclared identifier 'pthread_setname_np'; did you mean 'pthread_set_name_np'?
        pthread_setname_np(pthread_self(), m_name.substr(0, 15).c_str());
        ^~~~~~~~~~~~~~~~~~
        pthread_set_name_np
/usr/include/pthread_np.h:58:6: note: 'pthread_set_name_np' declared here
void pthread_set_name_np(pthread_t, const char *);
     ^
2018-03-13 12:51:22 +04:00
Danila Malyutin bc9594ed57 Use latest Qt patch level on appveyor. 2018-03-13 04:56:47 +04:00
Nekotekina 817dfe8651 cellGameContentPermit workaround 2018-03-12 19:16:52 +03:00
Nekotekina ba54f7ae39 Improve cellGameCreateGameData error checks 2018-03-12 18:53:57 +03:00
Nekotekina f328cdbaef Fix cellGame regression 2018-03-12 11:40:12 +03:00
Roberto Anić Banić f2e5b6c350 Travis Fixup Fixup (#4273) 2018-03-12 03:07:37 +04:00
Nekotekina ac82ecf387 Use pthread_setname_np (Linux, GDB) 2018-03-11 19:33:02 +03:00
Nekotekina 4b1c052030 Fix crash on exit after access violation 2018-03-11 19:33:02 +03:00
Nekotekina c0a0cb98c5 Fix argv for DG category 2018-03-11 19:33:01 +03:00
Nekotekina 4fd69a09c7 Fix cellGameDataCheckCreate2 2018-03-11 19:33:01 +03:00
Nekotekina eea2c20420 Improve vfs::mount
Mount /dev_bdvd/PS3_GAME if necessary
2018-03-11 19:33:01 +03:00
Nekotekina f4d2fccdfe Improve boot dir access
Add Emu.GetDir() method
2018-03-11 19:33:01 +03:00
Nekotekina c8a6bc6e24 Relax /dev_bdvd mounting requirements
Allow some PSN games with DG category to boot
2018-03-11 19:33:01 +03:00
Nekotekina 7f542a5c99 Fix safe_writer_lock (typo) 2018-03-11 19:33:01 +03:00
Roberto Anić Banić c1f32aa570 Fix Travis by using a PPA to install QT. (#4269) 2018-03-11 20:13:56 +04:00
Jake 7bbadfd08b ppu/sys_interrupt: add stack_reset command to fix sys_interrupt stack overflowing 2018-03-09 20:30:59 +04:00
Jake 3a9428a88e rsx: Fix alpha test on VK/GL
Moving alpha test outside of fp_main. This enforces alpha test is still done even if shader asm has early return
2018-03-09 18:06:20 +03:00
Jake 7233640cf0 rsx: add vertex data base to offset and mask before translating address 2018-03-07 16:57:20 +03:00
Zion Nimchuk effdb65ae4 Update Vulkan-LoadAndValidationLayers to make CI work again 2018-03-06 22:22:15 +04:00
Ani 91d0511154 gui/themes: YoRHa by Ani - Bugfixes
- Fixed Library List
-- Removed uppercasing
-- Formatted checkboxes
-- Fixed selected text being white (unreadable)

- Fixed Log readability issues
-- Uses dark flat color background for improved readibility
-- Uses default font family and size for improved readibiluty
-- Adjusts success, always, stack and TTY colors

- Fixed Debugger issues
-- Removed uppercasing
-- Restored default font family
-- Restored default font size

- Styled QSliders
- Added border to SpinBox
2018-03-05 19:20:57 +00:00
Talkashie d4e88aa11c gui: Fix typos and clean up Engrish (#4250) 2018-03-05 18:28:10 +00:00
Jake ec0cd9f006 cellGame: Fix cellDiscGameGetBootDiscInfo return values (#4241) 2018-03-03 03:11:25 +04:00
Megamouse 8db7ef4d5a Qt: enable background images for floating dock widgets 2018-03-02 21:50:48 +04:00
Nick Renieris 504e3112dd [HLE] First steps to Playstation Move (#4083)
* [sysutil] Add Magnetometer system param

*  [ui] Add UI for Move handler

 Current options are "Null" and "Fake".

* cellGem: Improvements

* cellCamera: Improvements
2018-03-02 20:51:21 +04:00
Nekotekina f96e9b6ed7 Fix boot game crash 2018-03-01 16:28:08 +03:00
Nekotekina f056b2f4ab Improve TTY output
Use atomic variable to sync TTY size
Implement console_putc (liblv2)
Write plaintext instead of HTML
Slightly improve performance
Fix random line breaks in TTY
2018-03-01 16:28:08 +03:00
Nekotekina 445b7c0758 Optimize SPU interpreter
Made SPU decoder similar to PPU decoder
2018-03-01 16:13:35 +03:00
Nekotekina 53f8b03acc CPUTranslator: implement ICmp ops
Added operators ==, !=, >, <, >=, <=
2018-03-01 16:13:35 +03:00
Megamouse 900329a1de Qt: fix dockwidget background 2018-03-01 17:10:26 +04:00
Ani 7c6c33eef8 gui/themes: YoRHa by Ani
I hope you enjoy the theme as much as I enjoyed making it.
Based on NieR Automata's YoRHa UI style.
2018-03-01 01:34:11 +00:00
scribam dacc9af38f Add 2-channels mode for cellAudioOutGetDeviceInfo 2018-03-01 04:20:54 +04:00
scribam 8d7620d95f Change return of function "sceNpMatching2Init" to CELL_OK 2018-03-01 01:41:42 +04:00
Megamouse 3043c73745 Fix Boot regression introduced by #4158 2018-02-28 23:44:06 +04:00
Megamouse f786c078c2 Qt: enable custom fonts for stylesheets and add random object names 2018-02-28 23:44:06 +04:00
scribam 31cee7e4c8 Make cellNetCtlGetInfo returns the correct value in disconnected mode 2018-02-28 22:25:51 +04:00
elad 6e8ccbcf17 spu/interp: optimize floating point exception checking
read once the MXCSR register and use his value to check for all exceptions
2018-02-28 21:30:33 +04:00
Megamouse 72e54e8b60 Qt: ask for cache and config removal on app remove (#4016) 2018-02-28 20:06:44 +04:00
Andrey e0f53ace19 Improve GDB debug server (#4027)
* Made GDB debugger working with IDA

* Added async interrupts support

* Report proper thread after pausing

* Support attaching debugger before running app
2018-02-28 19:31:39 +04:00
Megamouse 2444385763 Qt: simplify Boot options a bit (#4158) 2018-02-28 18:53:39 +04:00
Megamouse 6141bc5598 Fix firmware installation for vfs 2018-02-28 17:27:06 +04:00
Megamouse 9138d537c8 Qt: remove gamelist toolbar 2018-02-28 16:52:19 +04:00
scribam 05354698f0 Add callbacks for cellStorage 2018-02-28 15:58:50 +04:00
scribam 14e15b2feb Add callbacks for cellUserInfo 2018-02-28 15:58:50 +04:00
scribam 324d21d849 Add callbacks for cellPhotoImport 2018-02-28 15:58:50 +04:00
scribam aa30e865c2 Add callbacks for cellSysconf 2018-02-28 15:58:50 +04:00
scribam 0772b9c879 Add callbacks for cellCrossController 2018-02-28 15:58:50 +04:00
scribam 296052b1ff Add callbacks for cellPrint 2018-02-28 15:58:50 +04:00
scribam be834a67d1 Add callbacks for cellPhotoDecode 2018-02-28 15:58:50 +04:00
scribam b63461f599 Add callbacks for cellMusicExport 2018-02-28 15:58:50 +04:00
scribam 1fb7cc4002 Add callbacks for cellMusicDecode 2018-02-28 14:47:35 +04:00
Zion Nimchuk a766478e15 Refactor .travis.yml and qt installation 2018-02-28 00:21:54 +00:00
elad b61a69c877 fix spu interpreter single stepping 2018-02-25 20:30:44 +04:00
scribam 7753ac60b0 Add CELL_GAMEUPDATE_RESULT_STATUS_* enum 2018-02-25 13:17:38 +00:00
scribam aabf3c6695 Checks for content_id in sceNpDrmVerifyUpgradeLicense and sceNpDrmVerifyUpgradeLicense2 2018-02-25 13:17:38 +00:00
zarroboogs d78b0467d8 RPCS3 0.0.5: Versioning Changes (Hotfix) (#4208)
* travis hotfix

* expose env vars for tag, hash and commit number

* bump version

* also update av version string

* remove hash from av version for master builds

* change hash encoding back to ascii
2018-02-24 19:54:58 +00:00
170 changed files with 6307 additions and 2775 deletions
+1
View File
@@ -60,6 +60,7 @@ rpcs3/git-version.h
# Visual Studio Files
.vs/*
.vscode/*
*.ipch
*.vspx
*.psess
*.VC.*
+7 -3
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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
+62 -26
View File
@@ -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
+14 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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">
+8 -8
View File
@@ -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>
+8 -8
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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 {
-3
View File
@@ -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 {
+440
View File
@@ -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 -1
View File
@@ -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
+8 -8
View File
@@ -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>
+3
View File
@@ -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
+1 -2
View File
@@ -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);
}
+2 -2
View File
@@ -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>
+5 -2
View File
@@ -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
View File
@@ -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)
+1 -1
View File
@@ -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">
+1 -1
View File
@@ -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
+112
View File
@@ -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:
+5 -1
View File
@@ -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;
}
+475 -80
View File
@@ -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;
}
}
+79 -4
View File
@@ -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;
}
+2 -2
View File
@@ -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()
+44 -16
View File
@@ -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")));
+7
View File
@@ -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
{
+616 -46
View File
@@ -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;
}
+15 -6
View File
@@ -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;
+222 -36
View File
@@ -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;
}
+59 -10
View File
@@ -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;
}
+25 -21
View File
@@ -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;
+12
View File
@@ -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
+41 -15
View File
@@ -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;
}
+70 -19
View File
@@ -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;
}
+73 -26
View File
@@ -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;
}
+4 -4
View File
@@ -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
+22 -1
View File
@@ -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;
}
+8
View File
@@ -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;
}
+25 -4
View File
@@ -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);
});
+2
View File
@@ -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&)>&&);
+17
View File
@@ -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;
}
+10
View File
@@ -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
+1 -1
View File
@@ -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)
+19 -11
View File
@@ -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;
}
+7 -12
View File
@@ -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;
}
+4 -8
View File
@@ -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;
}
+4 -4
View File
@@ -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)
+20 -1
View File
@@ -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());
+1
View File
@@ -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
+1 -1
View File
@@ -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;
+3 -3
View File
@@ -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 -13
View File
@@ -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)));
File diff suppressed because it is too large Load Diff
+239 -239
View File
@@ -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);
};
+62 -11
View File
@@ -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;
}
}
}
+1
View File
@@ -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 }
+2 -2
View File
@@ -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);
+4 -2
View File
@@ -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)
+8 -7
View File
@@ -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);
}
+1 -1
View File
@@ -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 &reg = 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;
}
};
+3 -3
View File
@@ -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:
+11 -2
View File
@@ -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;
}
};
+79 -31
View File
@@ -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;
}
};
}
+148 -52
View File
@@ -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 &section : 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
+4 -4
View File
@@ -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);
+1 -1
View File
@@ -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
+5
View File
@@ -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,
+23 -26
View File
@@ -348,6 +348,26 @@ void GLFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
}
}
OS << "}\n\n";
OS << "void main()\n";
OS << "{\n";
std::string parameters = "";
for (auto &reg_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 &reg_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
View File
@@ -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()
+23 -21
View File
@@ -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;
+90 -25
View File
@@ -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);
}
};
}
+5
View File
@@ -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&)
{
+44 -14
View File
@@ -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())
+88 -16
View File
@@ -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 &region : 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() {}
+23 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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();
+26 -26
View File
@@ -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 &reg_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 &reg_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
+2
View File
@@ -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
View File
@@ -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
+31 -20
View File
@@ -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