Compare commits
134 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b80c348b09 | ||
|
|
37f1cf8cbd | ||
|
|
84ea9c2b42 | ||
|
|
21c3f48279 | ||
|
|
fcf3425b1b | ||
|
|
a952fbc5b3 | ||
|
|
e210835dd0 | ||
|
|
6536cc9741 | ||
|
|
7b07e521ca | ||
|
|
b5c13ee0eb | ||
|
|
7cacb08cdf | ||
|
|
90bda66028 | ||
|
|
90df4b8e2b | ||
|
|
aa3f9b9606 | ||
|
|
09d766d357 | ||
|
|
1b01c3036d | ||
|
|
ba53543da6 | ||
|
|
7a547b9342 | ||
|
|
3a0c1e79f8 | ||
|
|
77da74e17a | ||
|
|
84e895cdd6 | ||
|
|
3557fa25d0 | ||
|
|
be5fcffb89 | ||
|
|
2245c24e21 | ||
|
|
9751ccc5e0 | ||
|
|
8bb9eef97b | ||
|
|
c81c361e82 | ||
|
|
a0b4be4262 | ||
|
|
0eb36c90f4 | ||
|
|
08c508b1c4 | ||
|
|
7da52673d0 | ||
|
|
bf21aacc74 | ||
|
|
5733287822 | ||
|
|
c31ec00d67 | ||
|
|
2cd51fc9fd | ||
|
|
c7678c3044 | ||
|
|
83f8090273 | ||
|
|
5c61e0ba39 | ||
|
|
fb9c9ddcc9 | ||
|
|
9bb6ab77f4 | ||
|
|
881408445a | ||
|
|
36524465a6 | ||
|
|
4aa9c9632d | ||
|
|
157eb375a5 | ||
|
|
4eed744277 | ||
|
|
223a89a19f | ||
|
|
b5bbe7e752 | ||
|
|
729ca120e3 | ||
|
|
017474c3f8 | ||
|
|
2916c1bc25 | ||
|
|
69a6796de1 | ||
|
|
c18f9898d9 | ||
|
|
6b5b01b29f | ||
|
|
1948fc0858 | ||
|
|
91b0a3f799 | ||
|
|
472319e573 | ||
|
|
2de2bb980e | ||
|
|
16e7b7b83d | ||
|
|
51927bc9dc | ||
|
|
985d0f35e5 | ||
|
|
8a7cdfc3ff | ||
|
|
c593e45dbd | ||
|
|
b69321650e | ||
|
|
d027850f33 | ||
|
|
a7beabb68f | ||
|
|
2db7adc42a | ||
|
|
252415a163 | ||
|
|
c29584a090 | ||
|
|
f92cbc5501 | ||
|
|
8299f1ceef | ||
|
|
788d57d723 | ||
|
|
e651e54b85 | ||
|
|
9f0162e4b5 | ||
|
|
270177f38a | ||
|
|
b35449c85d | ||
|
|
b11aeced18 | ||
|
|
91f79225e7 | ||
|
|
8d6b4e836c | ||
|
|
6e87111f91 | ||
|
|
4bc4fdf5ff | ||
|
|
c457e47297 | ||
|
|
8178fe8960 | ||
|
|
283f3253bc | ||
|
|
bea6327d74 | ||
|
|
abae795986 | ||
|
|
acfb0b4852 | ||
|
|
137a8aa55c | ||
|
|
e3fc3459c8 | ||
|
|
f55f6ff9bb | ||
|
|
d17dfa6104 | ||
|
|
f68bb4f55e | ||
|
|
d95d4ac843 | ||
|
|
bb8eb15d39 | ||
|
|
d26e74f0a3 | ||
|
|
9a2cdf8520 | ||
|
|
531f25a037 | ||
|
|
96638f57c9 | ||
|
|
2a822f3378 | ||
|
|
05df4a8c94 | ||
|
|
2b1d66eda3 | ||
|
|
845a5dbca9 | ||
|
|
dfd998216c | ||
|
|
806f569143 | ||
|
|
3919b7b8a9 | ||
|
|
37b8504faa | ||
|
|
7c530e0666 | ||
|
|
3c34678627 | ||
|
|
2b02f29a2d | ||
|
|
037ea431ce | ||
|
|
f4603d23c5 | ||
|
|
603c861532 | ||
|
|
64496f2456 | ||
|
|
b97608ca64 | ||
|
|
dc5cfa8d28 | ||
|
|
74aa7de5e3 | ||
|
|
1e4b6bef6f | ||
|
|
c921e496eb | ||
|
|
a104b985a8 | ||
|
|
f64adcfc37 | ||
|
|
1690f1adba | ||
|
|
d0e4f1c6f4 | ||
|
|
a31ed02ae4 | ||
|
|
5a7eecc3ad | ||
|
|
4e9331f45d | ||
|
|
999e3f89b9 | ||
|
|
635deb70d4 | ||
|
|
231d9c10f3 | ||
|
|
83be9fc96d | ||
|
|
ae7fd01e38 | ||
|
|
a1667a7b46 | ||
|
|
2fa9a96309 | ||
|
|
90f9c830ca | ||
|
|
351e3fb72e | ||
|
|
4a566b9828 |
@@ -5,7 +5,7 @@ cd /yuzu
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_VULKAN=No
|
||||
|
||||
ninja
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ echo '' >> /bin/cmd
|
||||
chmod +x /bin/cmd
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_VULKAN=No
|
||||
ninja
|
||||
|
||||
# Clean up the dirty hacks
|
||||
|
||||
2
dist/qt_themes/colorful/style.qrc
vendored
2
dist/qt_themes/colorful/style.qrc
vendored
@@ -10,6 +10,6 @@
|
||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="colorful">
|
||||
<file>style.qss</file>
|
||||
<file alias="style.qss">../default/style.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
4
dist/qt_themes/colorful/style.qss
vendored
4
dist/qt_themes/colorful/style.qss
vendored
@@ -1,4 +0,0 @@
|
||||
/*
|
||||
This file is intentionally left blank.
|
||||
We do not want to apply any stylesheet for colorful, only icons.
|
||||
*/
|
||||
13
dist/qt_themes/default/default.qrc
vendored
13
dist/qt_themes/default/default.qrc
vendored
@@ -1,25 +1,18 @@
|
||||
<RCC>
|
||||
<qresource prefix="icons/default">
|
||||
<file alias="index.theme">icons/index.theme</file>
|
||||
|
||||
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
|
||||
|
||||
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
|
||||
|
||||
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
|
||||
|
||||
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
|
||||
|
||||
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
|
||||
|
||||
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
|
||||
|
||||
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
|
||||
|
||||
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
|
||||
|
||||
<file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file>
|
||||
|
||||
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="default">
|
||||
<file>style.qss</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
35
dist/qt_themes/default/style.qss
vendored
Normal file
35
dist/qt_themes/default/style.qss
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
QPushButton#TogglableStatusBarButton {
|
||||
color: #959595;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#TogglableStatusBarButton:checked {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
QPushButton#TogglableStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#RendererStatusBarButton {
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#RendererStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#RendererStatusBarButton:checked {
|
||||
color: #e85c00;
|
||||
}
|
||||
|
||||
QPushButton#RendererStatusBarButton:!checked{
|
||||
color: #0066ff;
|
||||
}
|
||||
260
dist/qt_themes/qdarkstyle/style.qss
vendored
260
dist/qt_themes/qdarkstyle/style.qss
vendored
@@ -2,7 +2,8 @@ QToolTip {
|
||||
border: 1px solid #76797C;
|
||||
background-color: #5A7566;
|
||||
color: white;
|
||||
padding: 0px; /*remove padding, for fix combobox tooltip.*/
|
||||
/*remove padding, for fix combobox tooltip.*/
|
||||
padding: 0;
|
||||
opacity: 200;
|
||||
}
|
||||
|
||||
@@ -13,7 +14,7 @@ QWidget {
|
||||
selection-color: #eff0f1;
|
||||
background-clip: border;
|
||||
border-image: none;
|
||||
border: 0px transparent black;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
@@ -27,10 +28,10 @@ QWidget:item:selected {
|
||||
}
|
||||
|
||||
QCheckBox {
|
||||
spacing: 5px;
|
||||
spacing: 6px;
|
||||
outline: none;
|
||||
color: #eff0f1;
|
||||
margin-bottom: 2px;
|
||||
margin: 0 2px 1px 0;
|
||||
}
|
||||
|
||||
QCheckBox:disabled {
|
||||
@@ -163,7 +164,7 @@ QMenuBar::item:selected {
|
||||
}
|
||||
|
||||
QMenuBar::item:pressed {
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #18465d;
|
||||
background-color: #3daee9;
|
||||
color: #eff0f1;
|
||||
margin-bottom: -1px;
|
||||
@@ -171,9 +172,9 @@ QMenuBar::item:pressed {
|
||||
}
|
||||
|
||||
QMenu {
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #434242;
|
||||
padding: 2px;
|
||||
color: #eff0f1;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
QMenu::icon {
|
||||
@@ -190,11 +191,21 @@ QMenu::item:selected {
|
||||
color: #eff0f1;
|
||||
}
|
||||
|
||||
QMenu::separator {
|
||||
height: 2px;
|
||||
background: #76797C;
|
||||
margin-left: 10px;
|
||||
margin-right: 5px;
|
||||
QMenu::item:disabled {
|
||||
color: #54575B;
|
||||
}
|
||||
|
||||
QMenu::item:disabled:hover,
|
||||
QMenu::item:disabled:selected {
|
||||
background-color: #393e43;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
QMenu::separator,
|
||||
QMenuBar::separator {
|
||||
height: 1px;
|
||||
background-color: #54575B;
|
||||
margin: 2px 4px 2px 40px;
|
||||
}
|
||||
|
||||
QMenu::indicator {
|
||||
@@ -203,10 +214,7 @@ QMenu::indicator {
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
|
||||
/* non-exclusive indicator = check box style indicator
|
||||
(see QActionGroup::setExclusive) */
|
||||
|
||||
/* non-exclusive indicator = check box style indicator (see QActionGroup::setExclusive) */
|
||||
QMenu::indicator:non-exclusive:unchecked {
|
||||
image: url(:/qss_icons/rc/checkbox_unchecked.png);
|
||||
}
|
||||
@@ -223,9 +231,7 @@ QMenu::indicator:non-exclusive:checked:selected {
|
||||
image: url(:/qss_icons/rc/checkbox_checked_disabled.png);
|
||||
}
|
||||
|
||||
|
||||
/* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */
|
||||
|
||||
QMenu::indicator:exclusive:unchecked {
|
||||
image: url(:/qss_icons/rc/radio_unchecked.png);
|
||||
}
|
||||
@@ -243,12 +249,12 @@ QMenu::indicator:exclusive:checked:selected {
|
||||
}
|
||||
|
||||
QMenu::right-arrow {
|
||||
margin: 5px;
|
||||
margin-right: 10px;
|
||||
image: url(:/qss_icons/rc/right_arrow.png)
|
||||
}
|
||||
|
||||
QWidget:disabled {
|
||||
color: #454545;
|
||||
color: #4f515b;
|
||||
background-color: #31363b;
|
||||
}
|
||||
|
||||
@@ -259,23 +265,30 @@ QAbstractItemView {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
QWidget:focus,
|
||||
QMenuBar:focus {
|
||||
QAbstractItemView:disabled,
|
||||
QAbstractItemView:read-only {
|
||||
alternate-background-color: #232629;
|
||||
}
|
||||
|
||||
QWidget:focus {
|
||||
border: 1px solid #3daee9;
|
||||
}
|
||||
|
||||
QTabWidget:focus,
|
||||
QCheckBox:focus,
|
||||
QRadioButton:focus,
|
||||
QSlider:focus {
|
||||
QSlider:focus,
|
||||
QTreeView:focus,
|
||||
QMenu:focus,
|
||||
QMenuBar:focus,
|
||||
QTabBar:focus {
|
||||
border: none;
|
||||
}
|
||||
|
||||
QLineEdit {
|
||||
background-color: #232629;
|
||||
padding: 5px;
|
||||
border-style: solid;
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #54575B;
|
||||
border-radius: 2px;
|
||||
color: #eff0f1;
|
||||
}
|
||||
@@ -285,9 +298,10 @@ QAbstractItemView QLineEdit {
|
||||
}
|
||||
|
||||
QGroupBox {
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #54575B;
|
||||
border-radius: 2px;
|
||||
margin-top: 20px;
|
||||
margin-top: 12px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
QGroupBox::title {
|
||||
@@ -295,12 +309,12 @@ QGroupBox::title {
|
||||
subcontrol-position: top center;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
padding-top: 10px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
QAbstractScrollArea {
|
||||
border-radius: 2px;
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #54575B;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@@ -319,7 +333,7 @@ QScrollBar::handle:horizontal {
|
||||
}
|
||||
|
||||
QScrollBar::add-line:horizontal {
|
||||
margin: 0px 3px 0px 3px;
|
||||
margin: 0 3px;
|
||||
border-image: url(:/qss_icons/rc/right_arrow_disabled.png);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
@@ -328,7 +342,7 @@ QScrollBar::add-line:horizontal {
|
||||
}
|
||||
|
||||
QScrollBar::sub-line:horizontal {
|
||||
margin: 0px 3px 0px 3px;
|
||||
margin: 0 3px;
|
||||
border-image: url(:/qss_icons/rc/left_arrow_disabled.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
@@ -379,7 +393,7 @@ QScrollBar::handle:vertical {
|
||||
}
|
||||
|
||||
QScrollBar::sub-line:vertical {
|
||||
margin: 3px 0px 3px 0px;
|
||||
margin: 3px 0;
|
||||
border-image: url(:/qss_icons/rc/up_arrow_disabled.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
@@ -388,7 +402,7 @@ QScrollBar::sub-line:vertical {
|
||||
}
|
||||
|
||||
QScrollBar::add-line:vertical {
|
||||
margin: 3px 0px 3px 0px;
|
||||
margin: 3px 0;
|
||||
border-image: url(:/qss_icons/rc/down_arrow_disabled.png);
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
@@ -427,15 +441,14 @@ QScrollBar::sub-page:vertical {
|
||||
QTextEdit {
|
||||
background-color: #232629;
|
||||
color: #eff0f1;
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #54575B;
|
||||
}
|
||||
|
||||
QPlainTextEdit {
|
||||
background-color: #232629;
|
||||
;
|
||||
color: #eff0f1;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #54575B;
|
||||
}
|
||||
|
||||
QHeaderView::section {
|
||||
@@ -467,15 +480,6 @@ QMainWindow::separator:hover {
|
||||
spacing: 2px;
|
||||
}
|
||||
|
||||
QMenu::separator {
|
||||
height: 1px;
|
||||
background-color: #76797C;
|
||||
color: white;
|
||||
padding-left: 4px;
|
||||
margin-left: 10px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
QFrame {
|
||||
border-radius: 2px;
|
||||
border: 1px solid #76797C;
|
||||
@@ -518,25 +522,19 @@ QToolButton#qt_toolbar_ext_button {
|
||||
|
||||
QPushButton {
|
||||
color: #eff0f1;
|
||||
background-color: #31363b;
|
||||
border-width: 1px;
|
||||
border-color: #76797C;
|
||||
border-color: #54575B;
|
||||
border-style: solid;
|
||||
padding: 5px;
|
||||
padding: 6px 4px;
|
||||
border-radius: 2px;
|
||||
outline: none;
|
||||
min-width: 100px;
|
||||
background-color: #232629;
|
||||
}
|
||||
|
||||
QPushButton:disabled {
|
||||
background-color: #31363b;
|
||||
border-width: 1px;
|
||||
border-color: #454545;
|
||||
border-style: solid;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
border-radius: 2px;
|
||||
color: #454545;
|
||||
}
|
||||
|
||||
@@ -553,11 +551,11 @@ QPushButton:pressed {
|
||||
|
||||
QComboBox {
|
||||
selection-background-color: #3daee9;
|
||||
border-style: solid;
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #54575B;
|
||||
border-radius: 2px;
|
||||
padding: 5px;
|
||||
padding: 4px 6px;
|
||||
min-width: 75px;
|
||||
background-color: #232629;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
@@ -571,8 +569,7 @@ QAbstractSpinBox:hover,
|
||||
QLineEdit:hover,
|
||||
QTextEdit:hover,
|
||||
QPlainTextEdit:hover,
|
||||
QAbstractView:hover,
|
||||
QTreeView:hover {
|
||||
QAbstractView:hover {
|
||||
border: 1px solid #3daee9;
|
||||
color: #eff0f1;
|
||||
}
|
||||
@@ -591,6 +588,7 @@ QComboBox QAbstractItemView {
|
||||
QComboBox::drop-down {
|
||||
subcontrol-origin: padding;
|
||||
subcontrol-position: top right;
|
||||
left: -6px;
|
||||
width: 15px;
|
||||
border-left-width: 0px;
|
||||
border-left-color: darkgray;
|
||||
@@ -610,8 +608,8 @@ QComboBox::down-arrow:focus {
|
||||
}
|
||||
|
||||
QAbstractSpinBox {
|
||||
padding: 5px;
|
||||
border: 1px solid #76797C;
|
||||
padding: 4px 6px;
|
||||
border: 1px solid #54575B;
|
||||
background-color: #232629;
|
||||
color: #eff0f1;
|
||||
border-radius: 2px;
|
||||
@@ -622,12 +620,14 @@ QAbstractSpinBox:up-button {
|
||||
background-color: transparent;
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: center right;
|
||||
left: -6px;
|
||||
}
|
||||
|
||||
QAbstractSpinBox:down-button {
|
||||
background-color: transparent;
|
||||
subcontrol-origin: border;
|
||||
subcontrol-position: center left;
|
||||
right: -6px;
|
||||
}
|
||||
|
||||
QAbstractSpinBox::up-arrow,
|
||||
@@ -654,22 +654,27 @@ QAbstractSpinBox::down-arrow:hover {
|
||||
image: url(:/qss_icons/rc/down_arrow.png);
|
||||
}
|
||||
|
||||
QLabel {
|
||||
border: 0px solid black;
|
||||
QLabel,
|
||||
QTabWidget {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
QTabWidget {
|
||||
border: 0px transparent black;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
QTabWidget::pane {
|
||||
border: 1px solid #76797C;
|
||||
padding: 5px;
|
||||
margin: 0px;
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
border-top-right-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-bottom-left-radius: 2px;
|
||||
}
|
||||
|
||||
QTabWidget::tab-bar {
|
||||
/* left: 5px; move to the right by 5px */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
QTabBar {
|
||||
@@ -677,10 +682,6 @@ QTabBar {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
QTabBar:focus {
|
||||
border: 0px transparent black;
|
||||
}
|
||||
|
||||
QTabBar::close-button {
|
||||
image: url(:/qss_icons/rc/close.png);
|
||||
background: transparent;
|
||||
@@ -696,36 +697,33 @@ QTabBar::close-button:pressed {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
/* TOP TABS */
|
||||
|
||||
QTabBar::tab:top {
|
||||
color: #eff0f1;
|
||||
border: 1px solid #76797C;
|
||||
border-bottom: 2px transparent;
|
||||
background-color: #31363b;
|
||||
padding: 4px 16px 2px;
|
||||
min-width: 38px;
|
||||
border: 1px solid #54575B;
|
||||
background-color: #2a2f33;
|
||||
padding: 4px 16px 5px;
|
||||
min-width: 36px;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
|
||||
QTabBar::tab:top:selected {
|
||||
color: #eff0f1;
|
||||
background-color: #54575B;
|
||||
border: 1px solid #76797C;
|
||||
border-bottom: 2px solid #3daee9;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
border-color: #76797C;
|
||||
background-color: #31363b;
|
||||
border-bottom-color: #31363b;
|
||||
}
|
||||
|
||||
QTabBar::tab:top:!selected {
|
||||
margin-top: 1px;
|
||||
border-bottom-color: #76797C;
|
||||
}
|
||||
|
||||
QTabBar::tab:top:!selected:hover {
|
||||
background-color: #3daee9;
|
||||
}
|
||||
|
||||
|
||||
/* BOTTOM TABS */
|
||||
|
||||
QTabBar::tab:bottom {
|
||||
color: #eff0f1;
|
||||
border: 1px solid #76797C;
|
||||
@@ -750,9 +748,7 @@ QTabBar::tab:bottom:!selected:hover {
|
||||
background-color: #3daee9;
|
||||
}
|
||||
|
||||
|
||||
/* LEFT TABS */
|
||||
|
||||
QTabBar::tab:left {
|
||||
color: #eff0f1;
|
||||
border: 1px solid #76797C;
|
||||
@@ -777,9 +773,7 @@ QTabBar::tab:left:!selected:hover {
|
||||
background-color: #3daee9;
|
||||
}
|
||||
|
||||
|
||||
/* RIGHT TABS */
|
||||
|
||||
QTabBar::tab:right {
|
||||
color: #eff0f1;
|
||||
border: 1px solid #76797C;
|
||||
@@ -847,7 +841,7 @@ QDockWidget::float-button:pressed {
|
||||
|
||||
QTreeView,
|
||||
QListView {
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #54575B;
|
||||
background-color: #232629;
|
||||
}
|
||||
|
||||
@@ -978,8 +972,8 @@ QSlider::handle:vertical {
|
||||
}
|
||||
|
||||
QToolButton {
|
||||
background-color: transparent;
|
||||
border: 1px transparent #76797C;
|
||||
background-color: #232629;
|
||||
border: 1px solid #54575B;
|
||||
border-radius: 2px;
|
||||
margin: 3px;
|
||||
padding: 5px;
|
||||
@@ -988,7 +982,6 @@ QToolButton {
|
||||
QToolButton[popupMode="1"] {
|
||||
/* only for MenuButtonPopup */
|
||||
padding-right: 20px;
|
||||
/* make way for the popup button */
|
||||
border: 1px #76797C;
|
||||
border-radius: 5px;
|
||||
}
|
||||
@@ -996,7 +989,6 @@ QToolButton[popupMode="1"] {
|
||||
QToolButton[popupMode="2"] {
|
||||
/* only for InstantPopup */
|
||||
padding-right: 10px;
|
||||
/* make way for the popup button */
|
||||
border: 1px #76797C;
|
||||
}
|
||||
|
||||
@@ -1015,19 +1007,14 @@ QToolButton::menu-button:pressed {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
/* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */
|
||||
|
||||
QToolButton::menu-indicator {
|
||||
image: url(:/qss_icons/rc/down_arrow.png);
|
||||
top: -7px;
|
||||
left: -2px;
|
||||
/* shift it a bit */
|
||||
}
|
||||
|
||||
|
||||
/* the subcontrols below are used only in the MenuButtonPopup mode */
|
||||
|
||||
QToolButton::menu-button {
|
||||
border: 1px transparent #76797C;
|
||||
border-top-right-radius: 6px;
|
||||
@@ -1052,14 +1039,22 @@ QPushButton::menu-indicator {
|
||||
}
|
||||
|
||||
QTableView {
|
||||
border: 1px solid #76797C;
|
||||
border: 1px solid #54575B;
|
||||
gridline-color: #31363b;
|
||||
background-color: #232629;
|
||||
}
|
||||
|
||||
QTreeView:disabled {
|
||||
background-color: #1f2225;
|
||||
}
|
||||
|
||||
QTableView,
|
||||
QHeaderView {
|
||||
border-radius: 0px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
QListView:focus {
|
||||
border-color: #54575B;
|
||||
}
|
||||
|
||||
QTableView::item:pressed,
|
||||
@@ -1088,7 +1083,7 @@ QHeaderView::section {
|
||||
background-color: #232629;
|
||||
color: #eff0f1;
|
||||
padding: 0 5px;
|
||||
border: 1px solid #403F3F;
|
||||
border: 1px solid #434242;
|
||||
border-bottom: 0;
|
||||
border-radius: 0px;
|
||||
text-align: center;
|
||||
@@ -1118,9 +1113,7 @@ QHeaderView::section:checked {
|
||||
background-color: #334e5e;
|
||||
}
|
||||
|
||||
|
||||
/* style the sort indicator */
|
||||
|
||||
/* sort indicator */
|
||||
QHeaderView::down-arrow {
|
||||
image: url(:/qss_icons/rc/down_arrow.png);
|
||||
}
|
||||
@@ -1150,14 +1143,13 @@ QToolBox::tab {
|
||||
}
|
||||
|
||||
QToolBox::tab:selected {
|
||||
/* italicize selected tabs */
|
||||
font: italic;
|
||||
background-color: #31363b;
|
||||
border-color: #3daee9;
|
||||
}
|
||||
|
||||
QStatusBar::item {
|
||||
border: 0px transparent dark;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
QFrame[height="3"],
|
||||
@@ -1194,7 +1186,6 @@ QProgressBar::chunk {
|
||||
|
||||
QDateEdit {
|
||||
selection-background-color: #3daee9;
|
||||
border-style: solid;
|
||||
border: 1px solid #3375A3;
|
||||
border-radius: 2px;
|
||||
padding: 1px;
|
||||
@@ -1218,7 +1209,7 @@ QDateEdit::drop-down {
|
||||
subcontrol-origin: padding;
|
||||
subcontrol-position: top right;
|
||||
width: 15px;
|
||||
border-left-width: 0px;
|
||||
border-left-width: 0;
|
||||
border-left-color: darkgray;
|
||||
border-left-style: solid;
|
||||
border-top-right-radius: 3px;
|
||||
@@ -1234,3 +1225,52 @@ QDateEdit::down-arrow:hover,
|
||||
QDateEdit::down-arrow:focus {
|
||||
image: url(:/qss_icons/rc/down_arrow.png);
|
||||
}
|
||||
|
||||
QComboBox:disabled,
|
||||
QPushButton:disabled,
|
||||
QAbstractSpinBox:disabled,
|
||||
QDateEdit:disabled,
|
||||
QLineEdit:disabled,
|
||||
QTextEdit:disabled,
|
||||
QToolButton:disabled,
|
||||
QPlainTextEdit:disabled {
|
||||
background-color: #2b2e31;
|
||||
}
|
||||
|
||||
QPushButton#TogglableStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#TogglableStatusBarButton:checked {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
QPushButton#TogglableStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#RendererStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #656565;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#RendererStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#RendererStatusBarButton:checked {
|
||||
color: #e85c00;
|
||||
}
|
||||
|
||||
QPushButton#RendererStatusBarButton:!checked{
|
||||
color: #00ccdd;
|
||||
}
|
||||
@@ -120,7 +120,7 @@ private:
|
||||
duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
|
||||
entry.log_class = log_class;
|
||||
entry.log_level = log_level;
|
||||
entry.filename = Common::TrimSourcePath(filename);
|
||||
entry.filename = filename;
|
||||
entry.line_num = line_nr;
|
||||
entry.function = function;
|
||||
entry.message = std::move(message);
|
||||
|
||||
@@ -23,7 +23,7 @@ struct Entry {
|
||||
std::chrono::microseconds timestamp;
|
||||
Class log_class;
|
||||
Level log_level;
|
||||
std::string filename;
|
||||
const char* filename;
|
||||
unsigned int line_num;
|
||||
std::string function;
|
||||
std::string message;
|
||||
|
||||
@@ -9,6 +9,15 @@
|
||||
|
||||
namespace Log {
|
||||
|
||||
// trims up to and including the last of ../, ..\, src/, src\ in a string
|
||||
constexpr const char* TrimSourcePath(std::string_view source) {
|
||||
const auto rfind = [source](const std::string_view match) {
|
||||
return source.rfind(match) == source.npos ? 0 : (source.rfind(match) + match.size());
|
||||
};
|
||||
auto idx = std::max({rfind("src/"), rfind("src\\"), rfind("../"), rfind("..\\")});
|
||||
return source.data() + idx;
|
||||
}
|
||||
|
||||
/// Specifies the severity or level of detail of the log message.
|
||||
enum class Level : u8 {
|
||||
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
|
||||
@@ -141,24 +150,24 @@ void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsig
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define LOG_TRACE(log_class, ...) \
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \
|
||||
__func__, __VA_ARGS__)
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, \
|
||||
::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__)
|
||||
#else
|
||||
#define LOG_TRACE(log_class, fmt, ...) (void(0))
|
||||
#endif
|
||||
|
||||
#define LOG_DEBUG(log_class, ...) \
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \
|
||||
__func__, __VA_ARGS__)
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, \
|
||||
::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__)
|
||||
#define LOG_INFO(log_class, ...) \
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \
|
||||
__func__, __VA_ARGS__)
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, \
|
||||
::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__)
|
||||
#define LOG_WARNING(log_class, ...) \
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \
|
||||
__func__, __VA_ARGS__)
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, \
|
||||
::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__)
|
||||
#define LOG_ERROR(log_class, ...) \
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \
|
||||
__func__, __VA_ARGS__)
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, \
|
||||
::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__)
|
||||
#define LOG_CRITICAL(log_class, ...) \
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \
|
||||
__func__, __VA_ARGS__)
|
||||
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, \
|
||||
::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__)
|
||||
|
||||
@@ -223,26 +223,4 @@ std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buff
|
||||
return std::u16string(buffer.begin(), buffer.begin() + len);
|
||||
}
|
||||
|
||||
const char* TrimSourcePath(const char* path, const char* root) {
|
||||
const char* p = path;
|
||||
|
||||
while (*p != '\0') {
|
||||
const char* next_slash = p;
|
||||
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
|
||||
++next_slash;
|
||||
}
|
||||
|
||||
bool is_src = Common::ComparePartialString(p, next_slash, root);
|
||||
p = next_slash;
|
||||
|
||||
if (*p != '\0') {
|
||||
++p;
|
||||
}
|
||||
if (is_src) {
|
||||
path = p;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/memory.h"
|
||||
@@ -87,7 +88,7 @@ public:
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
parent.jit->HaltExecution();
|
||||
parent.SetPC(pc);
|
||||
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
||||
Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread();
|
||||
parent.SaveContext(thread->GetContext());
|
||||
GDBStub::Break();
|
||||
GDBStub::SendTrap(thread, 5);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
|
||||
namespace Core {
|
||||
@@ -177,7 +178,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
|
||||
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
|
||||
}
|
||||
|
||||
Kernel::Thread* thread = Kernel::GetCurrentThread();
|
||||
Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
|
||||
SaveContext(thread->GetContext());
|
||||
if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
|
||||
last_bkpt_hit = false;
|
||||
|
||||
@@ -268,7 +268,9 @@ struct System::Impl {
|
||||
is_powered_on = false;
|
||||
exit_lock = false;
|
||||
|
||||
gpu_core->WaitIdle();
|
||||
if (gpu_core) {
|
||||
gpu_core->WaitIdle();
|
||||
}
|
||||
|
||||
// Shutdown emulation session
|
||||
renderer.reset();
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_manager.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/cpu_manager.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
||||
@@ -75,6 +75,13 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Returns if window is shown (not minimized)
|
||||
virtual bool IsShown() const = 0;
|
||||
|
||||
/// Retrieves Vulkan specific handlers from the window
|
||||
virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const = 0;
|
||||
|
||||
/**
|
||||
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
|
||||
* @param framebuffer_x Framebuffer x-coordinate that was pressed
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
|
||||
namespace Input {
|
||||
|
||||
enum class AnalogDirection : u8 {
|
||||
RIGHT,
|
||||
LEFT,
|
||||
UP,
|
||||
DOWN,
|
||||
};
|
||||
|
||||
/// An abstract class template for an input device (a button, an analog input, etc.).
|
||||
template <typename StatusType>
|
||||
class InputDevice {
|
||||
@@ -23,6 +30,9 @@ public:
|
||||
virtual StatusType GetStatus() const {
|
||||
return {};
|
||||
}
|
||||
virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/// An abstract class template for a factory that can create input devices.
|
||||
|
||||
@@ -284,13 +284,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
|
||||
|
||||
std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
|
||||
std::vector<u8> buffer;
|
||||
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
|
||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||
BufferDescriptorA()[buffer_index].Size()};
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
|
||||
if (is_buffer_a) {
|
||||
ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
|
||||
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
||||
buffer.resize(BufferDescriptorA()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
} else {
|
||||
ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
|
||||
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
||||
buffer.resize(BufferDescriptorX()[buffer_index].Size());
|
||||
memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
|
||||
}
|
||||
@@ -305,7 +310,8 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
|
||||
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
|
||||
BufferDescriptorB()[buffer_index].Size()};
|
||||
const std::size_t buffer_size{GetWriteBufferSize(buffer_index)};
|
||||
if (size > buffer_size) {
|
||||
LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
|
||||
@@ -315,8 +321,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
||||
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
if (is_buffer_b) {
|
||||
ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
|
||||
"BufferDescriptorB invalid buffer_index {}", buffer_index);
|
||||
ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size,
|
||||
"BufferDescriptorB buffer_index {} is not large enough", buffer_index);
|
||||
memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
|
||||
} else {
|
||||
ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
|
||||
"BufferDescriptorC invalid buffer_index {}", buffer_index);
|
||||
ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size,
|
||||
"BufferDescriptorC buffer_index {} is not large enough", buffer_index);
|
||||
memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
|
||||
}
|
||||
|
||||
@@ -324,15 +338,35 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
|
||||
}
|
||||
|
||||
std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const {
|
||||
const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()};
|
||||
return is_buffer_a ? BufferDescriptorA()[buffer_index].Size()
|
||||
: BufferDescriptorX()[buffer_index].Size();
|
||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||
BufferDescriptorA()[buffer_index].Size()};
|
||||
if (is_buffer_a) {
|
||||
ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
|
||||
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
||||
ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0,
|
||||
"BufferDescriptorA buffer_index {} is empty", buffer_index);
|
||||
return BufferDescriptorA()[buffer_index].Size();
|
||||
} else {
|
||||
ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
|
||||
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
||||
ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0,
|
||||
"BufferDescriptorX buffer_index {} is empty", buffer_index);
|
||||
return BufferDescriptorX()[buffer_index].Size();
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const {
|
||||
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()};
|
||||
return is_buffer_b ? BufferDescriptorB()[buffer_index].Size()
|
||||
: BufferDescriptorC()[buffer_index].Size();
|
||||
const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
|
||||
BufferDescriptorB()[buffer_index].Size()};
|
||||
if (is_buffer_b) {
|
||||
ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
|
||||
"BufferDescriptorB invalid buffer_index {}", buffer_index);
|
||||
return BufferDescriptorB()[buffer_index].Size();
|
||||
} else {
|
||||
ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
|
||||
"BufferDescriptorC invalid buffer_index {}", buffer_index);
|
||||
return BufferDescriptorC()[buffer_index].Size();
|
||||
}
|
||||
}
|
||||
|
||||
std::string HLERequestContext::Description() const {
|
||||
|
||||
@@ -101,7 +101,7 @@ struct KernelCore::Impl {
|
||||
void Initialize(KernelCore& kernel) {
|
||||
Shutdown();
|
||||
|
||||
InitializePhysicalCores(kernel);
|
||||
InitializePhysicalCores();
|
||||
InitializeSystemResourceLimit(kernel);
|
||||
InitializeThreads();
|
||||
InitializePreemption();
|
||||
@@ -131,14 +131,14 @@ struct KernelCore::Impl {
|
||||
}
|
||||
cores.clear();
|
||||
|
||||
exclusive_monitor.reset(nullptr);
|
||||
exclusive_monitor.reset();
|
||||
}
|
||||
|
||||
void InitializePhysicalCores(KernelCore& kernel) {
|
||||
void InitializePhysicalCores() {
|
||||
exclusive_monitor =
|
||||
Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount());
|
||||
for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) {
|
||||
cores.emplace_back(system, kernel, i, *exclusive_monitor);
|
||||
cores.emplace_back(system, i, *exclusive_monitor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,24 +10,23 @@
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
PhysicalCore::PhysicalCore(Core::System& system, KernelCore& kernel, std::size_t id,
|
||||
PhysicalCore::PhysicalCore(Core::System& system, std::size_t id,
|
||||
Core::ExclusiveMonitor& exclusive_monitor)
|
||||
: core_index{id}, kernel{kernel} {
|
||||
: core_index{id} {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
arm_interface = std::make_shared<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index);
|
||||
arm_interface = std::make_unique<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index);
|
||||
#else
|
||||
arm_interface = std::make_shared<Core::ARM_Unicorn>(system);
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#endif
|
||||
|
||||
scheduler = std::make_shared<Kernel::Scheduler>(system, *arm_interface, core_index);
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index);
|
||||
}
|
||||
|
||||
PhysicalCore::~PhysicalCore() = default;
|
||||
|
||||
@@ -21,11 +21,15 @@ namespace Kernel {
|
||||
|
||||
class PhysicalCore {
|
||||
public:
|
||||
PhysicalCore(Core::System& system, KernelCore& kernel, std::size_t id,
|
||||
Core::ExclusiveMonitor& exclusive_monitor);
|
||||
|
||||
PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor);
|
||||
~PhysicalCore();
|
||||
|
||||
PhysicalCore(const PhysicalCore&) = delete;
|
||||
PhysicalCore& operator=(const PhysicalCore&) = delete;
|
||||
|
||||
PhysicalCore(PhysicalCore&&) = default;
|
||||
PhysicalCore& operator=(PhysicalCore&&) = default;
|
||||
|
||||
/// Execute current jit state
|
||||
void Run();
|
||||
/// Execute a single instruction in current jit.
|
||||
@@ -66,9 +70,8 @@ public:
|
||||
|
||||
private:
|
||||
std::size_t core_index;
|
||||
KernelCore& kernel;
|
||||
std::shared_ptr<Core::ARM_Interface> arm_interface;
|
||||
std::shared_ptr<Kernel::Scheduler> scheduler;
|
||||
std::unique_ptr<Core::ARM_Interface> arm_interface;
|
||||
std::unique_ptr<Kernel::Scheduler> scheduler;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -1863,10 +1863,14 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd
|
||||
}
|
||||
|
||||
auto& kernel = system.Kernel();
|
||||
auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms);
|
||||
auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms);
|
||||
|
||||
if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
|
||||
return reserve_result;
|
||||
}
|
||||
|
||||
auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
|
||||
const auto result = handle_table.Create(std::move(transfer_mem_handle));
|
||||
const auto result{handle_table.Create(std::move(transfer_mem_handle))};
|
||||
if (result.Failed()) {
|
||||
return result.Code();
|
||||
}
|
||||
|
||||
@@ -474,7 +474,7 @@ void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
|
||||
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
|
||||
return;
|
||||
}
|
||||
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
if (processor_id >= 0) {
|
||||
scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this);
|
||||
}
|
||||
@@ -506,7 +506,7 @@ void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
|
||||
}
|
||||
|
||||
void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) {
|
||||
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
|
||||
auto& scheduler = kernel.GlobalScheduler();
|
||||
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
|
||||
current_priority >= THREADPRIO_COUNT) {
|
||||
return;
|
||||
|
||||
@@ -8,15 +8,23 @@
|
||||
#include "core/hle/kernel/shared_memory.h"
|
||||
#include "core/hle/kernel/transfer_memory.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {}
|
||||
TransferMemory::~TransferMemory() = default;
|
||||
TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory)
|
||||
: Object{kernel}, memory{memory} {}
|
||||
|
||||
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address,
|
||||
u64 size, MemoryPermission permissions) {
|
||||
std::shared_ptr<TransferMemory> transfer_memory{std::make_shared<TransferMemory>(kernel)};
|
||||
TransferMemory::~TransferMemory() {
|
||||
// Release memory region when transfer memory is destroyed
|
||||
Reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory,
|
||||
VAddr base_address, u64 size,
|
||||
MemoryPermission permissions) {
|
||||
std::shared_ptr<TransferMemory> transfer_memory{
|
||||
std::make_shared<TransferMemory>(kernel, memory)};
|
||||
|
||||
transfer_memory->base_address = base_address;
|
||||
transfer_memory->memory_size = size;
|
||||
@@ -27,7 +35,7 @@ std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr
|
||||
}
|
||||
|
||||
const u8* TransferMemory::GetPointer() const {
|
||||
return backing_block.get()->data();
|
||||
return memory.GetPointer(base_address);
|
||||
}
|
||||
|
||||
u64 TransferMemory::GetSize() const {
|
||||
@@ -62,6 +70,52 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultCode TransferMemory::Reserve() {
|
||||
auto& vm_manager{owner_process->VMManager()};
|
||||
const auto check_range_result{vm_manager.CheckRangeState(
|
||||
base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
|
||||
MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All,
|
||||
VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None,
|
||||
MemoryAttribute::IpcAndDeviceMapped)};
|
||||
|
||||
if (check_range_result.Failed()) {
|
||||
return check_range_result.Code();
|
||||
}
|
||||
|
||||
auto [state_, permissions_, attribute] = *check_range_result;
|
||||
|
||||
if (const auto result{vm_manager.ReprotectRange(
|
||||
base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
|
||||
attribute | MemoryAttribute::Locked);
|
||||
}
|
||||
|
||||
ResultCode TransferMemory::Reset() {
|
||||
auto& vm_manager{owner_process->VMManager()};
|
||||
if (const auto result{vm_manager.CheckRangeState(
|
||||
base_address, memory_size,
|
||||
MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated,
|
||||
MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None,
|
||||
VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked,
|
||||
MemoryAttribute::IpcAndDeviceMapped)};
|
||||
result.Failed()) {
|
||||
return result.Code();
|
||||
}
|
||||
|
||||
if (const auto result{
|
||||
vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)};
|
||||
result.IsError()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask,
|
||||
MemoryAttribute::None);
|
||||
}
|
||||
|
||||
ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {
|
||||
if (memory_size != size) {
|
||||
return ERR_INVALID_SIZE;
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
union ResultCode;
|
||||
|
||||
namespace Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class KernelCore;
|
||||
@@ -26,12 +30,13 @@ enum class MemoryPermission : u32;
|
||||
///
|
||||
class TransferMemory final : public Object {
|
||||
public:
|
||||
explicit TransferMemory(KernelCore& kernel);
|
||||
explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory);
|
||||
~TransferMemory() override;
|
||||
|
||||
static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory;
|
||||
|
||||
static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size,
|
||||
static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory,
|
||||
VAddr base_address, u64 size,
|
||||
MemoryPermission permissions);
|
||||
|
||||
TransferMemory(const TransferMemory&) = delete;
|
||||
@@ -80,6 +85,14 @@ public:
|
||||
///
|
||||
ResultCode UnmapMemory(VAddr address, u64 size);
|
||||
|
||||
/// Reserves the region to be used for the transfer memory, called after the transfer memory is
|
||||
/// created.
|
||||
ResultCode Reserve();
|
||||
|
||||
/// Resets the region previously used for the transfer memory, called after the transfer memory
|
||||
/// is closed.
|
||||
ResultCode Reset();
|
||||
|
||||
private:
|
||||
/// Memory block backing this instance.
|
||||
std::shared_ptr<PhysicalMemory> backing_block;
|
||||
@@ -98,6 +111,8 @@ private:
|
||||
|
||||
/// Whether or not this transfer memory instance has mapped memory.
|
||||
bool is_mapped = false;
|
||||
|
||||
Memory::Memory& memory;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -544,7 +544,8 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const {
|
||||
|
||||
ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
|
||||
MemoryAttribute attribute) {
|
||||
constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped;
|
||||
constexpr auto ignore_mask =
|
||||
MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped | MemoryAttribute::Locked;
|
||||
constexpr auto attribute_mask = ~ignore_mask;
|
||||
|
||||
const auto result = CheckRangeState(
|
||||
|
||||
@@ -98,6 +98,8 @@ enum class MemoryAttribute : u32 {
|
||||
DeviceMapped = 4,
|
||||
/// Uncached memory
|
||||
Uncached = 8,
|
||||
|
||||
IpcAndDeviceMapped = LockedForIPC | DeviceMapped,
|
||||
};
|
||||
|
||||
constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) {
|
||||
@@ -654,6 +656,35 @@ public:
|
||||
/// is scheduled.
|
||||
Common::PageTable page_table{Memory::PAGE_BITS};
|
||||
|
||||
using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
|
||||
|
||||
/// Checks if an address range adheres to the specified states provided.
|
||||
///
|
||||
/// @param address The starting address of the address range.
|
||||
/// @param size The size of the address range.
|
||||
/// @param state_mask The memory state mask.
|
||||
/// @param state The state to compare the individual VMA states against,
|
||||
/// which is done in the form of: (vma.state & state_mask) != state.
|
||||
/// @param permission_mask The memory permissions mask.
|
||||
/// @param permissions The permission to compare the individual VMA permissions against,
|
||||
/// which is done in the form of:
|
||||
/// (vma.permission & permission_mask) != permission.
|
||||
/// @param attribute_mask The memory attribute mask.
|
||||
/// @param attribute The memory attributes to compare the individual VMA attributes
|
||||
/// against, which is done in the form of:
|
||||
/// (vma.attributes & attribute_mask) != attribute.
|
||||
/// @param ignore_mask The memory attributes to ignore during the check.
|
||||
///
|
||||
/// @returns If successful, returns a tuple containing the memory attributes
|
||||
/// (with ignored bits specified by ignore_mask unset), memory permissions, and
|
||||
/// memory state across the memory range.
|
||||
/// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
|
||||
///
|
||||
CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
|
||||
VMAPermission permission_mask, VMAPermission permissions,
|
||||
MemoryAttribute attribute_mask, MemoryAttribute attribute,
|
||||
MemoryAttribute ignore_mask) const;
|
||||
|
||||
private:
|
||||
using VMAIter = VMAMap::iterator;
|
||||
|
||||
@@ -707,35 +738,6 @@ private:
|
||||
/// Clears out the page table
|
||||
void ClearPageTable();
|
||||
|
||||
using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
|
||||
|
||||
/// Checks if an address range adheres to the specified states provided.
|
||||
///
|
||||
/// @param address The starting address of the address range.
|
||||
/// @param size The size of the address range.
|
||||
/// @param state_mask The memory state mask.
|
||||
/// @param state The state to compare the individual VMA states against,
|
||||
/// which is done in the form of: (vma.state & state_mask) != state.
|
||||
/// @param permission_mask The memory permissions mask.
|
||||
/// @param permissions The permission to compare the individual VMA permissions against,
|
||||
/// which is done in the form of:
|
||||
/// (vma.permission & permission_mask) != permission.
|
||||
/// @param attribute_mask The memory attribute mask.
|
||||
/// @param attribute The memory attributes to compare the individual VMA attributes
|
||||
/// against, which is done in the form of:
|
||||
/// (vma.attributes & attribute_mask) != attribute.
|
||||
/// @param ignore_mask The memory attributes to ignore during the check.
|
||||
///
|
||||
/// @returns If successful, returns a tuple containing the memory attributes
|
||||
/// (with ignored bits specified by ignore_mask unset), memory permissions, and
|
||||
/// memory state across the memory range.
|
||||
/// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
|
||||
///
|
||||
CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
|
||||
VMAPermission permission_mask, VMAPermission permissions,
|
||||
MemoryAttribute attribute_mask, MemoryAttribute attribute,
|
||||
MemoryAttribute ignore_mask) const;
|
||||
|
||||
/// Gets the amount of memory currently mapped (state != Unmapped) in a range.
|
||||
ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const;
|
||||
|
||||
|
||||
@@ -50,17 +50,8 @@ std::shared_ptr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
|
||||
if (ShouldWait(thread.get()))
|
||||
continue;
|
||||
|
||||
// A thread is ready to run if it's either in ThreadStatus::WaitSynch
|
||||
// and the rest of the objects it is waiting on are ready.
|
||||
bool ready_to_run = true;
|
||||
if (thread_status == ThreadStatus::WaitSynch) {
|
||||
ready_to_run = thread->AllWaitObjectsReady();
|
||||
}
|
||||
|
||||
if (ready_to_run) {
|
||||
candidate = thread.get();
|
||||
candidate_priority = thread->GetPriority();
|
||||
}
|
||||
candidate = thread.get();
|
||||
candidate_priority = thread->GetPriority();
|
||||
}
|
||||
|
||||
return SharedFrom(candidate);
|
||||
|
||||
@@ -709,8 +709,34 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) {
|
||||
apm_sys->SetCpuBoostMode(ctx);
|
||||
}
|
||||
|
||||
IStorage::IStorage(std::vector<u8> buffer)
|
||||
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
|
||||
IStorageImpl::~IStorageImpl() = default;
|
||||
|
||||
class StorageDataImpl final : public IStorageImpl {
|
||||
public:
|
||||
explicit StorageDataImpl(std::vector<u8>&& buffer) : buffer{std::move(buffer)} {}
|
||||
|
||||
std::vector<u8>& GetData() override {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const std::vector<u8>& GetData() const override {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::size_t GetSize() const override {
|
||||
return buffer.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> buffer;
|
||||
};
|
||||
|
||||
IStorage::IStorage(std::vector<u8>&& buffer)
|
||||
: ServiceFramework("IStorage"), impl{std::make_shared<StorageDataImpl>(std::move(buffer))} {
|
||||
Register();
|
||||
}
|
||||
|
||||
void IStorage::Register() {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IStorage::Open, "Open"},
|
||||
@@ -723,8 +749,13 @@ IStorage::IStorage(std::vector<u8> buffer)
|
||||
|
||||
IStorage::~IStorage() = default;
|
||||
|
||||
const std::vector<u8>& IStorage::GetData() const {
|
||||
return buffer;
|
||||
void IStorage::Open(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorageAccessor>(*this);
|
||||
}
|
||||
|
||||
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
|
||||
@@ -816,7 +847,7 @@ private:
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -825,26 +856,25 @@ private:
|
||||
void PopOutData(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
const auto storage = applet->GetBroker().PopNormalDataToGame();
|
||||
if (storage == nullptr) {
|
||||
LOG_ERROR(Service_AM,
|
||||
"storage is a nullptr. There is no data in the current normal channel");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*storage));
|
||||
rb.PushIpcInterface<IStorage>(std::move(storage));
|
||||
}
|
||||
|
||||
void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
IPC::RequestParser rp{ctx};
|
||||
applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
|
||||
applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>());
|
||||
|
||||
ASSERT(applet->IsInitialized());
|
||||
applet->ExecuteInteractive();
|
||||
@@ -857,19 +887,18 @@ private:
|
||||
void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
const auto storage = applet->GetBroker().PopInteractiveDataToGame();
|
||||
if (storage == nullptr) {
|
||||
LOG_ERROR(Service_AM,
|
||||
"storage is a nullptr. There is no data in the current interactive channel");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_NO_DATA_IN_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*storage));
|
||||
rb.PushIpcInterface<IStorage>(std::move(storage));
|
||||
}
|
||||
|
||||
void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
|
||||
@@ -891,15 +920,6 @@ private:
|
||||
std::shared_ptr<Applets::Applet> applet;
|
||||
};
|
||||
|
||||
void IStorage::Open(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<IStorageAccessor>(*this);
|
||||
}
|
||||
|
||||
IStorageAccessor::IStorageAccessor(IStorage& storage)
|
||||
: ServiceFramework("IStorageAccessor"), backing(storage) {
|
||||
// clang-format off
|
||||
@@ -921,7 +941,7 @@ void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(static_cast<u64>(backing.buffer.size()));
|
||||
rb.Push(static_cast<u64>(backing.GetSize()));
|
||||
}
|
||||
|
||||
void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
|
||||
@@ -932,17 +952,17 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
|
||||
|
||||
if (data.size() > backing.buffer.size() - offset) {
|
||||
if (data.size() > backing.GetSize() - offset) {
|
||||
LOG_ERROR(Service_AM,
|
||||
"offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
|
||||
backing.buffer.size(), data.size(), offset);
|
||||
backing.GetSize(), data.size(), offset);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
return;
|
||||
}
|
||||
|
||||
std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
|
||||
std::memcpy(backing.GetData().data() + offset, data.data(), data.size());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -956,16 +976,16 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
|
||||
|
||||
if (size > backing.buffer.size() - offset) {
|
||||
if (size > backing.GetSize() - offset) {
|
||||
LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}",
|
||||
backing.buffer.size(), size, offset);
|
||||
backing.GetSize(), size, offset);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.WriteBuffer(backing.buffer.data() + offset, size);
|
||||
ctx.WriteBuffer(backing.GetData().data() + offset, size);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -1031,7 +1051,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
|
||||
rp.SetCurrentOffset(3);
|
||||
const auto handle{rp.Pop<Kernel::Handle>()};
|
||||
|
||||
const auto transfer_mem =
|
||||
auto transfer_mem =
|
||||
system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
|
||||
|
||||
if (transfer_mem == nullptr) {
|
||||
@@ -1047,7 +1067,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
|
||||
rb.PushIpcInterface<IStorage>(std::move(memory));
|
||||
}
|
||||
|
||||
IApplicationFunctions::IApplicationFunctions(Core::System& system_)
|
||||
@@ -1189,13 +1209,11 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
||||
u64 build_id{};
|
||||
std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
|
||||
|
||||
const auto data =
|
||||
backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});
|
||||
|
||||
auto data = backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});
|
||||
if (data.has_value()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushIpcInterface<AM::IStorage>(*data);
|
||||
rb.PushIpcInterface<IStorage>(std::move(*data));
|
||||
launch_popped_application_specific = true;
|
||||
return;
|
||||
}
|
||||
@@ -1218,7 +1236,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
||||
std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
|
||||
std::memcpy(buffer.data(), ¶ms, buffer.size());
|
||||
|
||||
rb.PushIpcInterface<AM::IStorage>(buffer);
|
||||
rb.PushIpcInterface<IStorage>(std::move(buffer));
|
||||
launch_popped_account_preselect = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
}
|
||||
class TransferMemory;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
class NVFlinger;
|
||||
@@ -188,19 +189,36 @@ private:
|
||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||
};
|
||||
|
||||
class IStorageImpl {
|
||||
public:
|
||||
virtual ~IStorageImpl();
|
||||
virtual std::vector<u8>& GetData() = 0;
|
||||
virtual const std::vector<u8>& GetData() const = 0;
|
||||
virtual std::size_t GetSize() const = 0;
|
||||
};
|
||||
|
||||
class IStorage final : public ServiceFramework<IStorage> {
|
||||
public:
|
||||
explicit IStorage(std::vector<u8> buffer);
|
||||
explicit IStorage(std::vector<u8>&& buffer);
|
||||
~IStorage() override;
|
||||
|
||||
const std::vector<u8>& GetData() const;
|
||||
std::vector<u8>& GetData() {
|
||||
return impl->GetData();
|
||||
}
|
||||
|
||||
const std::vector<u8>& GetData() const {
|
||||
return impl->GetData();
|
||||
}
|
||||
|
||||
std::size_t GetSize() const {
|
||||
return impl->GetSize();
|
||||
}
|
||||
|
||||
private:
|
||||
void Register();
|
||||
void Open(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::vector<u8> buffer;
|
||||
|
||||
friend class IStorageAccessor;
|
||||
std::shared_ptr<IStorageImpl> impl;
|
||||
};
|
||||
|
||||
class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
|
||||
|
||||
@@ -50,16 +50,17 @@ AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() co
|
||||
return {std::move(out_normal), std::move(out_interactive)};
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
|
||||
std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
|
||||
if (out_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_channel.front());
|
||||
out_channel.pop_front();
|
||||
pop_out_data_event.writable->Clear();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
|
||||
std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
|
||||
if (in_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
@@ -68,16 +69,17 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
|
||||
std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
|
||||
if (out_interactive_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
auto out = std::move(out_interactive_channel.front());
|
||||
out_interactive_channel.pop_front();
|
||||
pop_interactive_out_data_event.writable->Clear();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
|
||||
std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
|
||||
if (in_interactive_channel.empty())
|
||||
return nullptr;
|
||||
|
||||
@@ -86,21 +88,21 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
|
||||
return out;
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
|
||||
in_channel.push_back(std::make_unique<IStorage>(storage));
|
||||
void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage) {
|
||||
in_channel.emplace_back(std::move(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
|
||||
out_channel.push_back(std::make_unique<IStorage>(storage));
|
||||
void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) {
|
||||
out_channel.emplace_back(std::move(storage));
|
||||
pop_out_data_event.writable->Signal();
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
|
||||
in_interactive_channel.push_back(std::make_unique<IStorage>(storage));
|
||||
void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) {
|
||||
in_interactive_channel.emplace_back(std::move(storage));
|
||||
}
|
||||
|
||||
void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
|
||||
out_interactive_channel.push_back(std::make_unique<IStorage>(storage));
|
||||
void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) {
|
||||
out_interactive_channel.emplace_back(std::move(storage));
|
||||
pop_interactive_out_data_event.writable->Signal();
|
||||
}
|
||||
|
||||
|
||||
@@ -72,17 +72,17 @@ public:
|
||||
// Retrieves but does not pop the data sent to applet.
|
||||
RawChannelData PeekDataToAppletForDebug() const;
|
||||
|
||||
std::unique_ptr<IStorage> PopNormalDataToGame();
|
||||
std::unique_ptr<IStorage> PopNormalDataToApplet();
|
||||
std::shared_ptr<IStorage> PopNormalDataToGame();
|
||||
std::shared_ptr<IStorage> PopNormalDataToApplet();
|
||||
|
||||
std::unique_ptr<IStorage> PopInteractiveDataToGame();
|
||||
std::unique_ptr<IStorage> PopInteractiveDataToApplet();
|
||||
std::shared_ptr<IStorage> PopInteractiveDataToGame();
|
||||
std::shared_ptr<IStorage> PopInteractiveDataToApplet();
|
||||
|
||||
void PushNormalDataFromGame(IStorage storage);
|
||||
void PushNormalDataFromApplet(IStorage storage);
|
||||
void PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage);
|
||||
void PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage);
|
||||
|
||||
void PushInteractiveDataFromGame(IStorage storage);
|
||||
void PushInteractiveDataFromApplet(IStorage storage);
|
||||
void PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage);
|
||||
void PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage);
|
||||
|
||||
void SignalStateChanged() const;
|
||||
|
||||
@@ -94,16 +94,16 @@ private:
|
||||
// Queues are named from applet's perspective
|
||||
|
||||
// PopNormalDataToApplet and PushNormalDataFromGame
|
||||
std::deque<std::unique_ptr<IStorage>> in_channel;
|
||||
std::deque<std::shared_ptr<IStorage>> in_channel;
|
||||
|
||||
// PopNormalDataToGame and PushNormalDataFromApplet
|
||||
std::deque<std::unique_ptr<IStorage>> out_channel;
|
||||
std::deque<std::shared_ptr<IStorage>> out_channel;
|
||||
|
||||
// PopInteractiveDataToApplet and PushInteractiveDataFromGame
|
||||
std::deque<std::unique_ptr<IStorage>> in_interactive_channel;
|
||||
std::deque<std::shared_ptr<IStorage>> in_interactive_channel;
|
||||
|
||||
// PopInteractiveDataToGame and PushInteractiveDataFromApplet
|
||||
std::deque<std::unique_ptr<IStorage>> out_interactive_channel;
|
||||
std::deque<std::shared_ptr<IStorage>> out_interactive_channel;
|
||||
|
||||
Kernel::EventPair state_changed_event;
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ void Error::Execute() {
|
||||
|
||||
void Error::DisplayCompleted() {
|
||||
complete = true;
|
||||
broker.PushNormalDataFromApplet(IStorage{{}});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{}));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Service::AM::Applets {
|
||||
constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
|
||||
|
||||
static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
|
||||
std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
|
||||
std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet();
|
||||
for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
|
||||
const auto data = storage->GetData();
|
||||
LOG_INFO(Service_AM,
|
||||
@@ -148,7 +148,7 @@ void Auth::AuthFinished(bool successful) {
|
||||
std::vector<u8> out(sizeof(Return));
|
||||
std::memcpy(out.data(), &return_, sizeof(Return));
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{out});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ void PhotoViewer::Execute() {
|
||||
}
|
||||
|
||||
void PhotoViewer::ViewFinished() {
|
||||
broker.PushNormalDataFromApplet(IStorage{{}});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{}));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
@@ -234,8 +234,8 @@ void StubApplet::ExecuteInteractive() {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
LogCurrentStorage(broker, "ExecuteInteractive");
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
|
||||
broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
@@ -243,8 +243,8 @@ void StubApplet::Execute() {
|
||||
LOG_WARNING(Service_AM, "called (STUBBED)");
|
||||
LogCurrentStorage(broker, "Execute");
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
|
||||
broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ void ProfileSelect::ExecuteInteractive() {
|
||||
|
||||
void ProfileSelect::Execute() {
|
||||
if (complete) {
|
||||
broker.PushNormalDataFromApplet(IStorage{final_data});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {
|
||||
|
||||
final_data = std::vector<u8>(sizeof(UserSelectionOutput));
|
||||
std::memcpy(final_data.data(), &output, final_data.size());
|
||||
broker.PushNormalDataFromApplet(IStorage{final_data});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,8 @@ void SoftwareKeyboard::ExecuteInteractive() {
|
||||
|
||||
void SoftwareKeyboard::Execute() {
|
||||
if (complete) {
|
||||
broker.PushNormalDataFromApplet(IStorage{final_data});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data)));
|
||||
broker.SignalStateChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,7 +120,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
|
||||
std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
if (config.utf_8) {
|
||||
const u64 size = text->size() + 8;
|
||||
const u64 size = text->size() + sizeof(u64);
|
||||
const auto new_text = Common::UTF16ToUTF8(*text);
|
||||
|
||||
std::memcpy(output_sub.data(), &size, sizeof(u64));
|
||||
@@ -130,7 +131,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
|
||||
std::memcpy(output_main.data() + 4, new_text.data(),
|
||||
std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
|
||||
} else {
|
||||
const u64 size = text->size() * 2 + 8;
|
||||
const u64 size = text->size() * 2 + sizeof(u64);
|
||||
std::memcpy(output_sub.data(), &size, sizeof(u64));
|
||||
std::memcpy(output_sub.data() + 8, text->data(),
|
||||
std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
|
||||
@@ -144,15 +145,15 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
|
||||
final_data = output_main;
|
||||
|
||||
if (complete) {
|
||||
broker.PushNormalDataFromApplet(IStorage{output_main});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main)));
|
||||
broker.SignalStateChanged();
|
||||
} else {
|
||||
broker.PushInteractiveDataFromApplet(IStorage{output_sub});
|
||||
broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::move(output_sub)));
|
||||
}
|
||||
} else {
|
||||
output_main[0] = 1;
|
||||
complete = true;
|
||||
broker.PushNormalDataFromApplet(IStorage{output_main});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main)));
|
||||
broker.SignalStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ void WebBrowser::Finalize() {
|
||||
std::vector<u8> data(sizeof(WebCommonReturnValue));
|
||||
std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue));
|
||||
|
||||
broker.PushNormalDataFromApplet(IStorage{data});
|
||||
broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data)));
|
||||
broker.SignalStateChanged();
|
||||
|
||||
if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) {
|
||||
|
||||
@@ -420,7 +420,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
IFile file(result.Unwrap());
|
||||
auto file = std::make_shared<IFile>(result.Unwrap());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -445,7 +445,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
IDirectory directory(result.Unwrap());
|
||||
auto directory = std::make_shared<IDirectory>(result.Unwrap());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -794,8 +794,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
|
||||
void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_FS, "called");
|
||||
|
||||
IFileSystem filesystem(fsc.OpenSDMC().Unwrap(),
|
||||
SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
|
||||
auto filesystem = std::make_shared<IFileSystem>(
|
||||
fsc.OpenSDMC().Unwrap(), SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -846,7 +846,8 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
|
||||
id = FileSys::StorageId::NandSystem;
|
||||
}
|
||||
|
||||
IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
|
||||
auto filesystem =
|
||||
std::make_shared<IFileSystem>(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -898,7 +899,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
IStorage storage(std::move(romfs.Unwrap()));
|
||||
auto storage = std::make_shared<IStorage>(std::move(romfs.Unwrap()));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -937,7 +938,8 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
FileSys::PatchManager pm{title_id};
|
||||
|
||||
IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
|
||||
auto storage = std::make_shared<IStorage>(
|
||||
pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data));
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -250,6 +250,10 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
|
||||
auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
|
||||
const auto& button_state = buttons[controller_idx];
|
||||
const auto& analog_state = sticks[controller_idx];
|
||||
const auto [stick_l_x_f, stick_l_y_f] =
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
|
||||
const auto [stick_r_x_f, stick_r_y_f] =
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
|
||||
|
||||
using namespace Settings::NativeButton;
|
||||
pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
|
||||
@@ -270,23 +274,32 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
|
||||
pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.l_stick_right.Assign(
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
|
||||
Input::AnalogDirection::RIGHT));
|
||||
pad_state.l_stick_left.Assign(
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
|
||||
Input::AnalogDirection::LEFT));
|
||||
pad_state.l_stick_up.Assign(
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
|
||||
Input::AnalogDirection::UP));
|
||||
pad_state.l_stick_down.Assign(
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
|
||||
Input::AnalogDirection::DOWN));
|
||||
|
||||
pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
|
||||
->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
|
||||
pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
|
||||
->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
|
||||
pad_state.r_stick_right.Assign(
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
|
||||
->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
|
||||
pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
|
||||
->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
|
||||
|
||||
pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
|
||||
pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
|
||||
|
||||
const auto [stick_l_x_f, stick_l_y_f] =
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
|
||||
const auto [stick_r_x_f, stick_r_y_f] =
|
||||
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
|
||||
lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
|
||||
lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
|
||||
rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
|
||||
|
||||
@@ -50,16 +50,16 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = ctx.ReadBuffer(1);
|
||||
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
|
||||
if (Type == Core::Reporter::PlayReportType::New) {
|
||||
data.emplace_back(ctx.ReadBuffer(1));
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
static_cast<u8>(Type), process_id, data1.size(), data2.size());
|
||||
LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}",
|
||||
static_cast<u8>(Type), process_id, data[0].size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
|
||||
process_id);
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -70,19 +70,19 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = ctx.ReadBuffer(1);
|
||||
std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)};
|
||||
if (Type == Core::Reporter::PlayReportType::New) {
|
||||
data.emplace_back(ctx.ReadBuffer(1));
|
||||
}
|
||||
|
||||
LOG_DEBUG(
|
||||
Service_PREPO,
|
||||
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, "
|
||||
"data2_size={:016X}",
|
||||
static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size());
|
||||
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}",
|
||||
static_cast<u8>(Type), user_id[1], user_id[0], process_id, data[0].size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2},
|
||||
process_id, user_id);
|
||||
reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id,
|
||||
user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
@@ -42,6 +42,26 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::Select(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0); // ret
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::Bind(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0); // ret
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::Connect(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
@@ -52,6 +72,26 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::Listen(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0); // ret
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u32>(0); // ret
|
||||
rb.Push<u32>(0); // bsd errno
|
||||
}
|
||||
|
||||
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service, "(STUBBED) called");
|
||||
|
||||
@@ -80,7 +120,7 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
|
||||
{2, &BSD::Socket, "Socket"},
|
||||
{3, nullptr, "SocketExempt"},
|
||||
{4, nullptr, "Open"},
|
||||
{5, nullptr, "Select"},
|
||||
{5, &BSD::Select, "Select"},
|
||||
{6, nullptr, "Poll"},
|
||||
{7, nullptr, "Sysctl"},
|
||||
{8, nullptr, "Recv"},
|
||||
@@ -88,15 +128,15 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
|
||||
{10, nullptr, "Send"},
|
||||
{11, &BSD::SendTo, "SendTo"},
|
||||
{12, nullptr, "Accept"},
|
||||
{13, nullptr, "Bind"},
|
||||
{13, &BSD::Bind, "Bind"},
|
||||
{14, &BSD::Connect, "Connect"},
|
||||
{15, nullptr, "GetPeerName"},
|
||||
{16, nullptr, "GetSockName"},
|
||||
{17, nullptr, "GetSockOpt"},
|
||||
{18, nullptr, "Listen"},
|
||||
{18, &BSD::Listen, "Listen"},
|
||||
{19, nullptr, "Ioctl"},
|
||||
{20, nullptr, "Fcntl"},
|
||||
{21, nullptr, "SetSockOpt"},
|
||||
{21, &BSD::SetSockOpt, "SetSockOpt"},
|
||||
{22, nullptr, "Shutdown"},
|
||||
{23, nullptr, "ShutdownAllSockets"},
|
||||
{24, nullptr, "Write"},
|
||||
|
||||
@@ -18,7 +18,11 @@ private:
|
||||
void RegisterClient(Kernel::HLERequestContext& ctx);
|
||||
void StartMonitoring(Kernel::HLERequestContext& ctx);
|
||||
void Socket(Kernel::HLERequestContext& ctx);
|
||||
void Select(Kernel::HLERequestContext& ctx);
|
||||
void Bind(Kernel::HLERequestContext& ctx);
|
||||
void Connect(Kernel::HLERequestContext& ctx);
|
||||
void Listen(Kernel::HLERequestContext& ctx);
|
||||
void SetSockOpt(Kernel::HLERequestContext& ctx);
|
||||
void SendTo(Kernel::HLERequestContext& ctx);
|
||||
void Close(Kernel::HLERequestContext& ctx);
|
||||
|
||||
|
||||
@@ -97,7 +97,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
|
||||
if (nso_header.IsSegmentCompressed(i)) {
|
||||
data = DecompressSegment(data, nso_header.segments[i]);
|
||||
}
|
||||
program_image.resize(nso_header.segments[i].location + data.size());
|
||||
program_image.resize(nso_header.segments[i].location +
|
||||
PageAlignSize(static_cast<u32>(data.size())));
|
||||
std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(),
|
||||
data.size());
|
||||
codeset.segments[i].addr = nso_header.segments[i].location;
|
||||
@@ -105,8 +106,12 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
|
||||
codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size()));
|
||||
}
|
||||
|
||||
if (should_pass_arguments && !Settings::values.program_args.empty()) {
|
||||
const auto arg_data = Settings::values.program_args;
|
||||
if (should_pass_arguments) {
|
||||
std::vector<u8> arg_data{Settings::values.program_args.begin(),
|
||||
Settings::values.program_args.end()};
|
||||
if (arg_data.empty()) {
|
||||
arg_data.resize(NSO_ARGUMENT_DEFAULT_SIZE);
|
||||
}
|
||||
codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
|
||||
NSOArgumentHeader args_header{
|
||||
NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
|
||||
|
||||
@@ -56,6 +56,8 @@ static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
|
||||
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
|
||||
|
||||
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
|
||||
// NOTE: Official software default argument state is unverified.
|
||||
constexpr u64 NSO_ARGUMENT_DEFAULT_SIZE = 1;
|
||||
|
||||
struct NSOArgumentHeader {
|
||||
u32_le allocated_size;
|
||||
|
||||
@@ -371,6 +371,11 @@ enum class SDMCSize : u64 {
|
||||
S1TB = 0x10000000000ULL,
|
||||
};
|
||||
|
||||
enum class RendererBackend {
|
||||
OpenGL = 0,
|
||||
Vulkan = 1,
|
||||
};
|
||||
|
||||
struct Values {
|
||||
// System
|
||||
bool use_docked_mode;
|
||||
@@ -419,6 +424,10 @@ struct Values {
|
||||
SDMCSize sdmc_size;
|
||||
|
||||
// Renderer
|
||||
RendererBackend renderer_backend;
|
||||
bool renderer_debug;
|
||||
int vulkan_device;
|
||||
|
||||
float resolution_factor;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
|
||||
@@ -46,6 +46,16 @@ static u64 GenerateTelemetryId() {
|
||||
return telemetry_id;
|
||||
}
|
||||
|
||||
static const char* TranslateRenderer(Settings::RendererBackend backend) {
|
||||
switch (backend) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
return "OpenGL";
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
return "Vulkan";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
u64 GetTelemetryId() {
|
||||
u64 telemetry_id{};
|
||||
const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
|
||||
@@ -169,7 +179,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
|
||||
AddField(field_type, "Audio_SinkId", Settings::values.sink_id);
|
||||
AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
|
||||
AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
AddField(field_type, "Renderer_Backend", "OpenGL");
|
||||
AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend));
|
||||
AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor);
|
||||
AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
|
||||
@@ -41,6 +41,7 @@ void Shutdown() {
|
||||
Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
|
||||
motion_emu.reset();
|
||||
sdl.reset();
|
||||
udp.reset();
|
||||
}
|
||||
|
||||
Keyboard* GetKeyboard() {
|
||||
|
||||
@@ -342,6 +342,22 @@ public:
|
||||
return std::make_tuple<float, float>(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
|
||||
const auto [x, y] = GetStatus();
|
||||
const float directional_deadzone = 0.4f;
|
||||
switch (direction) {
|
||||
case Input::AnalogDirection::RIGHT:
|
||||
return x > directional_deadzone;
|
||||
case Input::AnalogDirection::LEFT:
|
||||
return x < -directional_deadzone;
|
||||
case Input::AnalogDirection::UP:
|
||||
return y > directional_deadzone;
|
||||
case Input::AnalogDirection::DOWN:
|
||||
return y < -directional_deadzone;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<SDLJoystick> joystick;
|
||||
const int axis_x;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "input_common/udp/client.h"
|
||||
#include "input_common/udp/protocol.h"
|
||||
|
||||
using boost::asio::ip::address_v4;
|
||||
using boost::asio::ip::udp;
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
@@ -31,10 +30,10 @@ public:
|
||||
|
||||
explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id,
|
||||
SocketCallback callback)
|
||||
: client_id(client_id), timer(io_service),
|
||||
send_endpoint(udp::endpoint(address_v4::from_string(host), port)),
|
||||
socket(io_service, udp::endpoint(udp::v4(), 0)), pad_index(pad_index),
|
||||
callback(std::move(callback)) {}
|
||||
: callback(std::move(callback)), timer(io_service),
|
||||
socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id),
|
||||
pad_index(pad_index),
|
||||
send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {}
|
||||
|
||||
void Stop() {
|
||||
io_service.stop();
|
||||
@@ -126,7 +125,7 @@ static void SocketLoop(Socket* socket) {
|
||||
|
||||
Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port,
|
||||
u8 pad_index, u32 client_id)
|
||||
: status(status) {
|
||||
: status(std::move(status)) {
|
||||
StartCommunication(host, port, pad_index, client_id);
|
||||
}
|
||||
|
||||
@@ -207,7 +206,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie
|
||||
Common::Event success_event;
|
||||
SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {},
|
||||
[&](Response::PadData data) { success_event.Set(); }};
|
||||
Socket socket{host, port, pad_index, client_id, callback};
|
||||
Socket socket{host, port, pad_index, client_id, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
bool result = success_event.WaitFor(std::chrono::seconds(8));
|
||||
socket.Stop();
|
||||
@@ -267,7 +266,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, pad_index, client_id, callback};
|
||||
Socket socket{host, port, pad_index, client_id, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
socket.Stop();
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/vector_math.h"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/crc.hpp>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/param_package.h"
|
||||
#include "core/frontend/input.h"
|
||||
#include "core/settings.h"
|
||||
@@ -14,7 +16,7 @@ namespace InputCommon::CemuhookUDP {
|
||||
class UDPTouchDevice final : public Input::TouchDevice {
|
||||
public:
|
||||
explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
|
||||
std::tuple<float, float, bool> GetStatus() const {
|
||||
std::tuple<float, float, bool> GetStatus() const override {
|
||||
std::lock_guard guard(status->update_mutex);
|
||||
return status->touch_status;
|
||||
}
|
||||
@@ -26,7 +28,7 @@ private:
|
||||
class UDPMotionDevice final : public Input::MotionDevice {
|
||||
public:
|
||||
explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
|
||||
std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const {
|
||||
std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override {
|
||||
std::lock_guard guard(status->update_mutex);
|
||||
return status->motion_status;
|
||||
}
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/udp/client.h"
|
||||
|
||||
namespace InputCommon::CemuhookUDP {
|
||||
|
||||
class UDPTouchDevice;
|
||||
class UDPMotionDevice;
|
||||
class Client;
|
||||
|
||||
class State {
|
||||
public:
|
||||
|
||||
@@ -29,6 +29,8 @@ add_library(video_core STATIC
|
||||
gpu_synch.h
|
||||
gpu_thread.cpp
|
||||
gpu_thread.h
|
||||
guest_driver.cpp
|
||||
guest_driver.h
|
||||
macro_interpreter.cpp
|
||||
macro_interpreter.h
|
||||
memory_manager.cpp
|
||||
@@ -154,6 +156,7 @@ if (ENABLE_VULKAN)
|
||||
renderer_vulkan/maxwell_to_vk.cpp
|
||||
renderer_vulkan/maxwell_to_vk.h
|
||||
renderer_vulkan/renderer_vulkan.h
|
||||
renderer_vulkan/renderer_vulkan.cpp
|
||||
renderer_vulkan/vk_blit_screen.cpp
|
||||
renderer_vulkan/vk_blit_screen.h
|
||||
renderer_vulkan/vk_buffer_cache.cpp
|
||||
|
||||
@@ -101,7 +101,10 @@ public:
|
||||
void TickFrame() {
|
||||
++epoch;
|
||||
while (!pending_destruction.empty()) {
|
||||
if (pending_destruction.front()->GetEpoch() + 1 > epoch) {
|
||||
// Delay at least 4 frames before destruction.
|
||||
// This is due to triple buffering happening on some drivers.
|
||||
static constexpr u64 epochs_to_destroy = 5;
|
||||
if (pending_destruction.front()->GetEpoch() + epochs_to_destroy > epoch) {
|
||||
break;
|
||||
}
|
||||
pending_destruction.pop_front();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/engines/shader_type.h"
|
||||
#include "video_core/guest_driver.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
@@ -106,6 +107,9 @@ public:
|
||||
virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer,
|
||||
u64 offset) const = 0;
|
||||
virtual u32 GetBoundBuffer() const = 0;
|
||||
|
||||
virtual VideoCore::GuestDriverProfile& AccessGuestDriverProfile() = 0;
|
||||
virtual const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const = 0;
|
||||
};
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -94,6 +94,14 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con
|
||||
return result;
|
||||
}
|
||||
|
||||
VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() {
|
||||
return rasterizer.AccessGuestDriverProfile();
|
||||
}
|
||||
|
||||
const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const {
|
||||
return rasterizer.AccessGuestDriverProfile();
|
||||
}
|
||||
|
||||
void KeplerCompute::ProcessLaunch() {
|
||||
const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address();
|
||||
memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description,
|
||||
|
||||
@@ -218,6 +218,10 @@ public:
|
||||
return regs.tex_cb_index;
|
||||
}
|
||||
|
||||
VideoCore::GuestDriverProfile& AccessGuestDriverProfile() override;
|
||||
|
||||
const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override;
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
@@ -784,4 +784,12 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b
|
||||
return result;
|
||||
}
|
||||
|
||||
VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() {
|
||||
return rasterizer.AccessGuestDriverProfile();
|
||||
}
|
||||
|
||||
const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const {
|
||||
return rasterizer.AccessGuestDriverProfile();
|
||||
}
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
|
||||
@@ -704,8 +704,8 @@ public:
|
||||
INSERT_UNION_PADDING_WORDS(0x15);
|
||||
|
||||
s32 stencil_back_func_ref;
|
||||
u32 stencil_back_func_mask;
|
||||
u32 stencil_back_mask;
|
||||
u32 stencil_back_func_mask;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0xC);
|
||||
|
||||
@@ -862,7 +862,11 @@ public:
|
||||
|
||||
float point_size;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x7);
|
||||
INSERT_UNION_PADDING_WORDS(0x1);
|
||||
|
||||
u32 point_sprite_enable;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x5);
|
||||
|
||||
u32 zeta_enable;
|
||||
|
||||
@@ -1306,6 +1310,10 @@ public:
|
||||
return regs.tex_cb_index;
|
||||
}
|
||||
|
||||
VideoCore::GuestDriverProfile& AccessGuestDriverProfile() override;
|
||||
|
||||
const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override;
|
||||
|
||||
/// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than
|
||||
/// we've seen used.
|
||||
using MacroMemory = std::array<u32, 0x40000>;
|
||||
@@ -1454,8 +1462,8 @@ ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372);
|
||||
ASSERT_REG_POSITION(patch_vertices, 0x373);
|
||||
ASSERT_REG_POSITION(scissor_test, 0x380);
|
||||
ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
|
||||
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D6);
|
||||
ASSERT_REG_POSITION(stencil_back_mask, 0x3D7);
|
||||
ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
|
||||
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
|
||||
ASSERT_REG_POSITION(color_mask_common, 0x3E4);
|
||||
ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
|
||||
ASSERT_REG_POSITION(depth_bounds, 0x3E7);
|
||||
@@ -1490,6 +1498,7 @@ ASSERT_REG_POSITION(vb_element_base, 0x50D);
|
||||
ASSERT_REG_POSITION(vb_base_instance, 0x50E);
|
||||
ASSERT_REG_POSITION(clip_distance_enabled, 0x544);
|
||||
ASSERT_REG_POSITION(point_size, 0x546);
|
||||
ASSERT_REG_POSITION(point_sprite_enable, 0x548);
|
||||
ASSERT_REG_POSITION(zeta_enable, 0x54E);
|
||||
ASSERT_REG_POSITION(multisample_control, 0x54F);
|
||||
ASSERT_REG_POSITION(condition, 0x554);
|
||||
|
||||
@@ -227,6 +227,28 @@ enum class AtomicOp : u64 {
|
||||
Exch = 8,
|
||||
};
|
||||
|
||||
enum class GlobalAtomicOp : u64 {
|
||||
Add = 0,
|
||||
Min = 1,
|
||||
Max = 2,
|
||||
Inc = 3,
|
||||
Dec = 4,
|
||||
And = 5,
|
||||
Or = 6,
|
||||
Xor = 7,
|
||||
Exch = 8,
|
||||
SafeAdd = 10,
|
||||
};
|
||||
|
||||
enum class GlobalAtomicType : u64 {
|
||||
U32 = 0,
|
||||
S32 = 1,
|
||||
U64 = 2,
|
||||
F32_FTZ_RN = 3,
|
||||
F16x2_FTZ_RN = 4,
|
||||
S64 = 5,
|
||||
};
|
||||
|
||||
enum class UniformType : u64 {
|
||||
UnsignedByte = 0,
|
||||
SignedByte = 1,
|
||||
@@ -602,6 +624,19 @@ enum class ShuffleOperation : u64 {
|
||||
Bfly = 3, // shuffleXorNV
|
||||
};
|
||||
|
||||
enum class ShfType : u64 {
|
||||
Bits32 = 0,
|
||||
U64 = 2,
|
||||
S64 = 3,
|
||||
};
|
||||
|
||||
enum class ShfXmode : u64 {
|
||||
None = 0,
|
||||
HI = 1,
|
||||
X = 2,
|
||||
XHI = 3,
|
||||
};
|
||||
|
||||
union Instruction {
|
||||
constexpr Instruction& operator=(const Instruction& instr) {
|
||||
value = instr.value;
|
||||
@@ -753,6 +788,13 @@ union Instruction {
|
||||
BitField<39, 1, u64> wrap;
|
||||
} shr;
|
||||
|
||||
union {
|
||||
BitField<37, 2, ShfType> type;
|
||||
BitField<48, 2, ShfXmode> xmode;
|
||||
BitField<50, 1, u64> wrap;
|
||||
BitField<20, 6, u64> immediate;
|
||||
} shf;
|
||||
|
||||
union {
|
||||
BitField<39, 5, u64> shift_amount;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
@@ -957,6 +999,12 @@ union Instruction {
|
||||
BitField<46, 2, u64> cache_mode;
|
||||
} stg;
|
||||
|
||||
union {
|
||||
BitField<52, 4, GlobalAtomicOp> operation;
|
||||
BitField<49, 3, GlobalAtomicType> type;
|
||||
BitField<28, 20, s64> offset;
|
||||
} atom;
|
||||
|
||||
union {
|
||||
BitField<52, 4, AtomicOp> operation;
|
||||
BitField<28, 2, AtomicType> type;
|
||||
@@ -1095,6 +1143,11 @@ union Instruction {
|
||||
BitField<55, 1, u64> ftz;
|
||||
} fset;
|
||||
|
||||
union {
|
||||
BitField<47, 1, u64> ftz;
|
||||
BitField<48, 4, PredCondition> cond;
|
||||
} fcmp;
|
||||
|
||||
union {
|
||||
BitField<49, 1, u64> bf;
|
||||
BitField<35, 3, PredCondition> cond;
|
||||
@@ -1675,6 +1728,7 @@ public:
|
||||
BFE_C,
|
||||
BFE_R,
|
||||
BFE_IMM,
|
||||
BFI_RC,
|
||||
BFI_IMM_R,
|
||||
BRA,
|
||||
BRX,
|
||||
@@ -1690,6 +1744,7 @@ public:
|
||||
ST_S,
|
||||
ST, // Store in generic memory
|
||||
STG, // Store in global memory
|
||||
ATOM, // Atomic operation on global memory
|
||||
ATOMS, // Atomic operation on shared memory
|
||||
AL2P, // Transforms attribute memory into physical memory
|
||||
TEX,
|
||||
@@ -1771,6 +1826,7 @@ public:
|
||||
ICMP_R,
|
||||
ICMP_CR,
|
||||
ICMP_IMM,
|
||||
FCMP_R,
|
||||
MUFU, // Multi-Function Operator
|
||||
RRO_C, // Range Reduction Operator
|
||||
RRO_R,
|
||||
@@ -1994,6 +2050,7 @@ private:
|
||||
INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
|
||||
INST("101-------------", Id::ST, Type::Memory, "ST"),
|
||||
INST("1110111011011---", Id::STG, Type::Memory, "STG"),
|
||||
INST("11101101--------", Id::ATOM, Type::Memory, "ATOM"),
|
||||
INST("11101100--------", Id::ATOMS, Type::Memory, "ATOMS"),
|
||||
INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"),
|
||||
INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
|
||||
@@ -2074,6 +2131,7 @@ private:
|
||||
INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"),
|
||||
INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"),
|
||||
INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"),
|
||||
INST("010110111010----", Id::FCMP_R, Type::Arithmetic, "FCMP_R"),
|
||||
INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
|
||||
INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
|
||||
INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
|
||||
@@ -2098,6 +2156,7 @@ private:
|
||||
INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"),
|
||||
INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"),
|
||||
INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"),
|
||||
INST("0101001111110---", Id::BFI_RC, Type::Bfi, "BFI_RC"),
|
||||
INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"),
|
||||
INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
|
||||
INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),
|
||||
|
||||
@@ -23,7 +23,7 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
|
||||
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
|
||||
: system{system}, renderer{renderer}, is_async{is_async} {
|
||||
auto& rasterizer{renderer.Rasterizer()};
|
||||
memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
|
||||
memory_manager = std::make_unique<Tegra::MemoryManager>(system);
|
||||
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
|
||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
|
||||
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer);
|
||||
|
||||
@@ -86,7 +86,7 @@ struct CommandDataContainer {
|
||||
struct SynchState final {
|
||||
std::atomic_bool is_running{true};
|
||||
|
||||
using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
|
||||
using CommandQueue = Common::MPSCQueue<CommandDataContainer>;
|
||||
CommandQueue queue;
|
||||
u64 last_fence{};
|
||||
std::atomic<u64> signaled_fence{};
|
||||
|
||||
36
src/video_core/guest_driver.cpp
Normal file
36
src/video_core/guest_driver.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "video_core/guest_driver.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets) {
|
||||
if (texture_handler_size_deduced) {
|
||||
return;
|
||||
}
|
||||
const std::size_t size = bound_offsets.size();
|
||||
if (size < 2) {
|
||||
return;
|
||||
}
|
||||
std::sort(bound_offsets.begin(), bound_offsets.end(), std::less{});
|
||||
u32 min_val = std::numeric_limits<u32>::max();
|
||||
for (std::size_t i = 1; i < size; ++i) {
|
||||
if (bound_offsets[i] == bound_offsets[i - 1]) {
|
||||
continue;
|
||||
}
|
||||
const u32 new_min = bound_offsets[i] - bound_offsets[i - 1];
|
||||
min_val = std::min(min_val, new_min);
|
||||
}
|
||||
if (min_val > 2) {
|
||||
return;
|
||||
}
|
||||
texture_handler_size_deduced = true;
|
||||
texture_handler_size = min_texture_handler_size * min_val;
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
41
src/video_core/guest_driver.h
Normal file
41
src/video_core/guest_driver.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
/**
|
||||
* The GuestDriverProfile class is used to learn about the GPU drivers behavior and collect
|
||||
* information necessary for impossible to avoid HLE methods like shader tracks as they are
|
||||
* Entscheidungsproblems.
|
||||
*/
|
||||
class GuestDriverProfile {
|
||||
public:
|
||||
void DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets);
|
||||
|
||||
u32 GetTextureHandlerSize() const {
|
||||
return texture_handler_size;
|
||||
}
|
||||
|
||||
bool TextureHandlerSizeKnown() const {
|
||||
return texture_handler_size_deduced;
|
||||
}
|
||||
|
||||
private:
|
||||
// Minimum size of texture handler any driver can use.
|
||||
static constexpr u32 min_texture_handler_size = 4;
|
||||
// This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily
|
||||
// use 4 bytes instead. Thus, certain drivers may squish the size.
|
||||
static constexpr u32 default_texture_handler_size = 8;
|
||||
|
||||
u32 texture_handler_size = default_texture_handler_size;
|
||||
bool texture_handler_size_deduced = false;
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
||||
@@ -9,13 +9,12 @@
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
||||
: rasterizer{rasterizer}, system{system} {
|
||||
MemoryManager::MemoryManager(Core::System& system) : system{system} {
|
||||
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
|
||||
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
|
||||
Common::PageType::Unmapped);
|
||||
@@ -84,7 +83,8 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
|
||||
const auto cpu_addr = GpuToCpuAddress(gpu_addr);
|
||||
ASSERT(cpu_addr);
|
||||
|
||||
rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size);
|
||||
system.GPU().FlushAndInvalidateRegion(cache_addr, aligned_size);
|
||||
|
||||
UnmapRange(gpu_addr, aligned_size);
|
||||
ASSERT(system.CurrentProcess()
|
||||
->VMManager()
|
||||
@@ -242,7 +242,7 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Memory: {
|
||||
const u8* src_ptr{page_table.pointers[page_index] + page_offset};
|
||||
rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
@@ -292,7 +292,7 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Memory: {
|
||||
u8* dest_ptr{page_table.pointers[page_index] + page_offset};
|
||||
rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount);
|
||||
system.GPU().InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount);
|
||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||
break;
|
||||
}
|
||||
@@ -340,7 +340,7 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std::
|
||||
switch (page_table.attributes[page_index]) {
|
||||
case Common::PageType::Memory: {
|
||||
const u8* src_ptr{page_table.pointers[page_index] + page_offset};
|
||||
rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount);
|
||||
WriteBlock(dest_addr, src_ptr, copy_amount);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/page_table.h"
|
||||
|
||||
namespace VideoCore {
|
||||
class RasterizerInterface;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
@@ -51,7 +47,7 @@ struct VirtualMemoryArea {
|
||||
|
||||
class MemoryManager final {
|
||||
public:
|
||||
explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer);
|
||||
explicit MemoryManager(Core::System& system);
|
||||
~MemoryManager();
|
||||
|
||||
GPUVAddr AllocateSpace(u64 size, u64 align);
|
||||
@@ -176,7 +172,6 @@ private:
|
||||
|
||||
Common::PageTable page_table{page_bits};
|
||||
VMAMap vma_map;
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/guest_driver.h"
|
||||
|
||||
namespace Tegra {
|
||||
class MemoryManager;
|
||||
@@ -78,5 +79,18 @@ public:
|
||||
/// Initialize disk cached resources for the game being emulated
|
||||
virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
|
||||
const DiskResourceLoadCallback& callback = {}) {}
|
||||
|
||||
/// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
|
||||
GuestDriverProfile& AccessGuestDriverProfile() {
|
||||
return guest_driver_profile;
|
||||
}
|
||||
|
||||
/// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
|
||||
const GuestDriverProfile& AccessGuestDriverProfile() const {
|
||||
return guest_driver_profile;
|
||||
}
|
||||
|
||||
private:
|
||||
GuestDriverProfile guest_driver_profile{};
|
||||
};
|
||||
} // namespace VideoCore
|
||||
|
||||
@@ -55,16 +55,20 @@ namespace {
|
||||
|
||||
template <typename Engine, typename Entry>
|
||||
Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry,
|
||||
Tegra::Engines::ShaderType shader_type) {
|
||||
Tegra::Engines::ShaderType shader_type,
|
||||
std::size_t index = 0) {
|
||||
if (entry.IsBindless()) {
|
||||
const Tegra::Texture::TextureHandle tex_handle =
|
||||
engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset());
|
||||
return engine.GetTextureInfo(tex_handle);
|
||||
}
|
||||
const auto& gpu_profile = engine.AccessGuestDriverProfile();
|
||||
const u32 offset =
|
||||
entry.GetOffset() + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize());
|
||||
if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) {
|
||||
return engine.GetStageTexture(shader_type, entry.GetOffset());
|
||||
return engine.GetStageTexture(shader_type, offset);
|
||||
} else {
|
||||
return engine.GetTexture(entry.GetOffset());
|
||||
return engine.GetTexture(offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,9 +248,6 @@ void RasterizerOpenGL::SetupVertexInstances(GLuint vao) {
|
||||
}
|
||||
|
||||
GLintptr RasterizerOpenGL::SetupIndexBuffer() {
|
||||
if (accelerate_draw != AccelDraw::Indexed) {
|
||||
return 0;
|
||||
}
|
||||
MICROPROFILE_SCOPE(OpenGL_Index);
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
const std::size_t size = CalculateIndexBufferSize();
|
||||
@@ -542,7 +543,8 @@ void RasterizerOpenGL::Clear() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::DrawPrelude() {
|
||||
void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
auto& gpu = system.GPU().Maxwell3D();
|
||||
|
||||
SyncRasterizeEnable(state);
|
||||
@@ -563,9 +565,6 @@ void RasterizerOpenGL::DrawPrelude() {
|
||||
|
||||
buffer_cache.Acquire();
|
||||
|
||||
// Draw the vertex batch
|
||||
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
|
||||
|
||||
std::size_t buffer_size = CalculateVertexArraysSize();
|
||||
|
||||
// Add space for index buffer
|
||||
@@ -592,7 +591,11 @@ void RasterizerOpenGL::DrawPrelude() {
|
||||
// Upload vertex and index data.
|
||||
SetupVertexBuffer(vao);
|
||||
SetupVertexInstances(vao);
|
||||
index_buffer_offset = SetupIndexBuffer();
|
||||
|
||||
GLintptr index_buffer_offset;
|
||||
if (is_indexed) {
|
||||
index_buffer_offset = SetupIndexBuffer();
|
||||
}
|
||||
|
||||
// Prepare packed bindings.
|
||||
bind_ubo_pushbuffer.Setup();
|
||||
@@ -626,6 +629,7 @@ void RasterizerOpenGL::DrawPrelude() {
|
||||
// As all cached buffers are invalidated, we need to recheck their state.
|
||||
gpu.dirty.ResetVertexArrays();
|
||||
}
|
||||
gpu.dirty.memory_general = false;
|
||||
|
||||
shader_program_manager->ApplyTo(state);
|
||||
state.Apply();
|
||||
@@ -633,106 +637,33 @@ void RasterizerOpenGL::DrawPrelude() {
|
||||
if (texture_cache.TextureBarrier()) {
|
||||
glTextureBarrier();
|
||||
}
|
||||
|
||||
const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance);
|
||||
const GLsizei num_instances =
|
||||
static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1);
|
||||
if (is_indexed) {
|
||||
const GLenum index_format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format);
|
||||
const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base);
|
||||
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count);
|
||||
glDrawElementsInstancedBaseVertexBaseInstance(
|
||||
primitive_mode, num_vertices, index_format,
|
||||
reinterpret_cast<const void*>(index_buffer_offset), num_instances, base_vertex,
|
||||
base_instance);
|
||||
} else {
|
||||
const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first);
|
||||
const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count);
|
||||
glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, num_instances,
|
||||
base_instance);
|
||||
}
|
||||
}
|
||||
|
||||
struct DrawParams {
|
||||
bool is_indexed{};
|
||||
bool is_instanced{};
|
||||
GLenum primitive_mode{};
|
||||
GLint count{};
|
||||
GLint base_vertex{};
|
||||
|
||||
// Indexed settings
|
||||
GLenum index_format{};
|
||||
GLintptr index_buffer_offset{};
|
||||
|
||||
// Instanced setting
|
||||
GLint num_instances{};
|
||||
GLint base_instance{};
|
||||
|
||||
void DispatchDraw() {
|
||||
if (is_indexed) {
|
||||
const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
|
||||
if (is_instanced) {
|
||||
glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
|
||||
index_buffer_ptr, num_instances,
|
||||
base_vertex, base_instance);
|
||||
} else {
|
||||
glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
|
||||
base_vertex);
|
||||
}
|
||||
} else {
|
||||
if (is_instanced) {
|
||||
glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances,
|
||||
base_instance);
|
||||
} else {
|
||||
glDrawArrays(primitive_mode, base_vertex, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool RasterizerOpenGL::DrawBatch(bool is_indexed) {
|
||||
accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
|
||||
DrawPrelude();
|
||||
|
||||
auto& maxwell3d = system.GPU().Maxwell3D();
|
||||
const auto& regs = maxwell3d.regs;
|
||||
const auto current_instance = maxwell3d.state.current_instance;
|
||||
DrawParams draw_call{};
|
||||
draw_call.is_indexed = is_indexed;
|
||||
draw_call.num_instances = static_cast<GLint>(1);
|
||||
draw_call.base_instance = static_cast<GLint>(current_instance);
|
||||
draw_call.is_instanced = current_instance > 0;
|
||||
draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
|
||||
if (draw_call.is_indexed) {
|
||||
draw_call.count = static_cast<GLint>(regs.index_array.count);
|
||||
draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
|
||||
draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
|
||||
draw_call.index_buffer_offset = index_buffer_offset;
|
||||
} else {
|
||||
draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
|
||||
draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
|
||||
}
|
||||
draw_call.DispatchDraw();
|
||||
|
||||
maxwell3d.dirty.memory_general = false;
|
||||
accelerate_draw = AccelDraw::Disabled;
|
||||
Draw(is_indexed, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) {
|
||||
accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
|
||||
|
||||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
|
||||
DrawPrelude();
|
||||
|
||||
auto& maxwell3d = system.GPU().Maxwell3D();
|
||||
const auto& regs = maxwell3d.regs;
|
||||
const auto& draw_setup = maxwell3d.mme_draw;
|
||||
DrawParams draw_call{};
|
||||
draw_call.is_indexed = is_indexed;
|
||||
draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count);
|
||||
draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance);
|
||||
draw_call.is_instanced = draw_setup.instance_count > 1;
|
||||
draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
|
||||
if (draw_call.is_indexed) {
|
||||
draw_call.count = static_cast<GLint>(regs.index_array.count);
|
||||
draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
|
||||
draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
|
||||
draw_call.index_buffer_offset = index_buffer_offset;
|
||||
} else {
|
||||
draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
|
||||
draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
|
||||
}
|
||||
draw_call.DispatchDraw();
|
||||
|
||||
maxwell3d.dirty.memory_general = false;
|
||||
accelerate_draw = AccelDraw::Disabled;
|
||||
Draw(is_indexed, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -942,8 +873,15 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader&
|
||||
u32 binding = device.GetBaseBindings(stage_index).sampler;
|
||||
for (const auto& entry : shader->GetShaderEntries().samplers) {
|
||||
const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index);
|
||||
const auto texture = GetTextureInfo(maxwell3d, entry, shader_type);
|
||||
SetupTexture(binding++, texture, entry);
|
||||
if (!entry.IsIndexed()) {
|
||||
const auto texture = GetTextureInfo(maxwell3d, entry, shader_type);
|
||||
SetupTexture(binding++, texture, entry);
|
||||
} else {
|
||||
for (std::size_t i = 0; i < entry.Size(); ++i) {
|
||||
const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i);
|
||||
SetupTexture(binding++, texture, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -952,8 +890,17 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
|
||||
const auto& compute = system.GPU().KeplerCompute();
|
||||
u32 binding = 0;
|
||||
for (const auto& entry : kernel->GetShaderEntries().samplers) {
|
||||
const auto texture = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute);
|
||||
SetupTexture(binding++, texture, entry);
|
||||
if (!entry.IsIndexed()) {
|
||||
const auto texture =
|
||||
GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute);
|
||||
SetupTexture(binding++, texture, entry);
|
||||
} else {
|
||||
for (std::size_t i = 0; i < entry.Size(); ++i) {
|
||||
const auto texture =
|
||||
GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute, i);
|
||||
SetupTexture(binding++, texture, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1273,6 +1220,7 @@ void RasterizerOpenGL::SyncPointState() {
|
||||
// Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
|
||||
// in OpenGL).
|
||||
state.point.program_control = regs.vp_point_size.enable != 0;
|
||||
state.point.sprite = regs.point_sprite_enable != 0;
|
||||
state.point.size = std::max(1.0f, regs.point_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ private:
|
||||
std::size_t size);
|
||||
|
||||
/// Syncs all the state, shaders, render targets and textures setting before a draw call.
|
||||
void DrawPrelude();
|
||||
void Draw(bool is_indexed, bool is_instanced);
|
||||
|
||||
/// Configures the current textures to use for the draw command.
|
||||
void SetupDrawTextures(std::size_t stage_index, const Shader& shader);
|
||||
@@ -220,12 +220,7 @@ private:
|
||||
|
||||
GLintptr SetupIndexBuffer();
|
||||
|
||||
GLintptr index_buffer_offset;
|
||||
|
||||
void SetupShaders(GLenum primitive_mode);
|
||||
|
||||
enum class AccelDraw { Disabled, Arrays, Indexed };
|
||||
AccelDraw accelerate_draw = AccelDraw::Disabled;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -214,6 +214,7 @@ std::unique_ptr<ConstBufferLocker> MakeLocker(Core::System& system, ShaderType s
|
||||
}
|
||||
|
||||
void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) {
|
||||
locker.SetBoundBuffer(usage.bound_buffer);
|
||||
for (const auto& key : usage.keys) {
|
||||
const auto [buffer, offset] = key.first;
|
||||
locker.InsertKey(buffer, offset, key.second);
|
||||
@@ -418,7 +419,8 @@ bool CachedShader::EnsureValidLockerVariant() {
|
||||
|
||||
ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant,
|
||||
const ConstBufferLocker& locker) const {
|
||||
return ShaderDiskCacheUsage{unique_identifier, variant, locker.GetKeys(),
|
||||
return ShaderDiskCacheUsage{unique_identifier, variant,
|
||||
locker.GetBoundBuffer(), locker.GetKeys(),
|
||||
locker.GetBoundSamplers(), locker.GetBindlessSamplers()};
|
||||
}
|
||||
|
||||
|
||||
@@ -391,6 +391,7 @@ public:
|
||||
DeclareVertex();
|
||||
DeclareGeometry();
|
||||
DeclareRegisters();
|
||||
DeclareCustomVariables();
|
||||
DeclarePredicates();
|
||||
DeclareLocalMemory();
|
||||
DeclareInternalFlags();
|
||||
@@ -503,6 +504,16 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareCustomVariables() {
|
||||
const u32 num_custom_variables = ir.GetNumCustomVariables();
|
||||
for (u32 i = 0; i < num_custom_variables; ++i) {
|
||||
code.AddLine("float {} = 0.0f;", GetCustomVariable(i));
|
||||
}
|
||||
if (num_custom_variables > 0) {
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void DeclarePredicates() {
|
||||
const auto& predicates = ir.GetPredicates();
|
||||
for (const auto pred : predicates) {
|
||||
@@ -655,7 +666,8 @@ private:
|
||||
u32 binding = device.GetBaseBindings(stage).sampler;
|
||||
for (const auto& sampler : ir.GetSamplers()) {
|
||||
const std::string name = GetSampler(sampler);
|
||||
const std::string description = fmt::format("layout (binding = {}) uniform", binding++);
|
||||
const std::string description = fmt::format("layout (binding = {}) uniform", binding);
|
||||
binding += sampler.IsIndexed() ? sampler.Size() : 1;
|
||||
|
||||
std::string sampler_type = [&]() {
|
||||
if (sampler.IsBuffer()) {
|
||||
@@ -682,7 +694,11 @@ private:
|
||||
sampler_type += "Shadow";
|
||||
}
|
||||
|
||||
code.AddLine("{} {} {};", description, sampler_type, name);
|
||||
if (!sampler.IsIndexed()) {
|
||||
code.AddLine("{} {} {};", description, sampler_type, name);
|
||||
} else {
|
||||
code.AddLine("{} {} {}[{}];", description, sampler_type, name, sampler.Size());
|
||||
}
|
||||
}
|
||||
if (!ir.GetSamplers().empty()) {
|
||||
code.AddNewLine();
|
||||
@@ -775,6 +791,11 @@ private:
|
||||
return {GetRegister(index), Type::Float};
|
||||
}
|
||||
|
||||
if (const auto cv = std::get_if<CustomVarNode>(&*node)) {
|
||||
const u32 index = cv->GetIndex();
|
||||
return {GetCustomVariable(index), Type::Float};
|
||||
}
|
||||
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
|
||||
const u32 value = immediate->GetValue();
|
||||
if (value < 10) {
|
||||
@@ -1019,7 +1040,6 @@ private:
|
||||
}
|
||||
return {{"gl_ViewportIndex", Type::Int}};
|
||||
case 3:
|
||||
UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader");
|
||||
return {{"gl_PointSize", Type::Float}};
|
||||
}
|
||||
return {};
|
||||
@@ -1099,7 +1119,11 @@ private:
|
||||
} else if (!meta->ptp.empty()) {
|
||||
expr += "Offsets";
|
||||
}
|
||||
expr += '(' + GetSampler(meta->sampler) + ", ";
|
||||
if (!meta->sampler.IsIndexed()) {
|
||||
expr += '(' + GetSampler(meta->sampler) + ", ";
|
||||
} else {
|
||||
expr += '(' + GetSampler(meta->sampler) + '[' + Visit(meta->index).AsUint() + "], ";
|
||||
}
|
||||
expr += coord_constructors.at(count + (has_array ? 1 : 0) +
|
||||
(has_shadow && !separate_dc ? 1 : 0) - 1);
|
||||
expr += '(';
|
||||
@@ -1311,6 +1335,8 @@ private:
|
||||
const std::string final_offset = fmt::format("({} - {}) >> 2", real, base);
|
||||
target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset),
|
||||
Type::Uint};
|
||||
} else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) {
|
||||
target = {GetCustomVariable(cv->GetIndex()), Type::Float};
|
||||
} else {
|
||||
UNREACHABLE_MSG("Assign called without a proper target");
|
||||
}
|
||||
@@ -1858,10 +1884,7 @@ private:
|
||||
|
||||
template <const std::string_view& opname, Type type>
|
||||
Expression Atomic(Operation operation) {
|
||||
ASSERT(stage == ShaderType::Compute);
|
||||
auto& smem = std::get<SmemNode>(*operation[0]);
|
||||
|
||||
return {fmt::format("atomic{}(smem[{} >> 2], {})", opname, Visit(smem.GetAddress()).AsInt(),
|
||||
return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(),
|
||||
Visit(operation[1]).As(type)),
|
||||
type};
|
||||
}
|
||||
@@ -2241,6 +2264,10 @@ private:
|
||||
return GetDeclarationWithSuffix(index, "gpr");
|
||||
}
|
||||
|
||||
std::string GetCustomVariable(u32 index) const {
|
||||
return GetDeclarationWithSuffix(index, "custom_var");
|
||||
}
|
||||
|
||||
std::string GetPredicate(Tegra::Shader::Pred pred) const {
|
||||
return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred");
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ struct BindlessSamplerKey {
|
||||
Tegra::Engines::SamplerDescriptor sampler{};
|
||||
};
|
||||
|
||||
constexpr u32 NativeVersion = 11;
|
||||
constexpr u32 NativeVersion = 12;
|
||||
|
||||
// Making sure sizes doesn't change by accident
|
||||
static_assert(sizeof(ProgramVariant) == 20);
|
||||
@@ -186,7 +186,8 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
|
||||
u32 num_bound_samplers{};
|
||||
u32 num_bindless_samplers{};
|
||||
if (file.ReadArray(&usage.unique_identifier, 1) != 1 ||
|
||||
file.ReadArray(&usage.variant, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 ||
|
||||
file.ReadArray(&usage.variant, 1) != 1 ||
|
||||
file.ReadArray(&usage.bound_buffer, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 ||
|
||||
file.ReadArray(&num_bound_samplers, 1) != 1 ||
|
||||
file.ReadArray(&num_bindless_samplers, 1) != 1) {
|
||||
LOG_ERROR(Render_OpenGL, error_loading);
|
||||
@@ -281,7 +282,9 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) {
|
||||
u32 num_bindless_samplers{};
|
||||
ShaderDiskCacheUsage usage;
|
||||
if (!LoadObjectFromPrecompiled(usage.unique_identifier) ||
|
||||
!LoadObjectFromPrecompiled(usage.variant) || !LoadObjectFromPrecompiled(num_keys) ||
|
||||
!LoadObjectFromPrecompiled(usage.variant) ||
|
||||
!LoadObjectFromPrecompiled(usage.bound_buffer) ||
|
||||
!LoadObjectFromPrecompiled(num_keys) ||
|
||||
!LoadObjectFromPrecompiled(num_bound_samplers) ||
|
||||
!LoadObjectFromPrecompiled(num_bindless_samplers)) {
|
||||
return {};
|
||||
@@ -393,6 +396,7 @@ void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) {
|
||||
|
||||
if (file.WriteObject(TransferableEntryKind::Usage) != 1 ||
|
||||
file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 ||
|
||||
file.WriteObject(usage.bound_buffer) != 1 ||
|
||||
file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 ||
|
||||
file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 ||
|
||||
file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) {
|
||||
@@ -447,7 +451,7 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
|
||||
};
|
||||
|
||||
if (!SaveObjectToPrecompiled(usage.unique_identifier) ||
|
||||
!SaveObjectToPrecompiled(usage.variant) ||
|
||||
!SaveObjectToPrecompiled(usage.variant) || !SaveObjectToPrecompiled(usage.bound_buffer) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) ||
|
||||
!SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) {
|
||||
|
||||
@@ -79,6 +79,7 @@ static_assert(std::is_trivially_copyable_v<ProgramVariant>);
|
||||
struct ShaderDiskCacheUsage {
|
||||
u64 unique_identifier{};
|
||||
ProgramVariant variant;
|
||||
u32 bound_buffer{};
|
||||
VideoCommon::Shader::KeyMap keys;
|
||||
VideoCommon::Shader::BoundSamplerMap bound_samplers;
|
||||
VideoCommon::Shader::BindlessSamplerMap bindless_samplers;
|
||||
|
||||
@@ -128,6 +128,7 @@ void OpenGLState::ApplyClipDistances() {
|
||||
|
||||
void OpenGLState::ApplyPointSize() {
|
||||
Enable(GL_PROGRAM_POINT_SIZE, cur_state.point.program_control, point.program_control);
|
||||
Enable(GL_POINT_SPRITE, cur_state.point.sprite, point.sprite);
|
||||
if (UpdateValue(cur_state.point.size, point.size)) {
|
||||
glPointSize(point.size);
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@ public:
|
||||
|
||||
struct {
|
||||
bool program_control = false; // GL_PROGRAM_POINT_SIZE
|
||||
bool sprite = false; // GL_POINT_SPRITE
|
||||
GLfloat size = 1.0f; // GL_POINT_SIZE
|
||||
} point;
|
||||
|
||||
|
||||
@@ -176,6 +176,19 @@ GLint GetSwizzleSource(SwizzleSource source) {
|
||||
return GL_NONE;
|
||||
}
|
||||
|
||||
GLenum GetComponent(PixelFormat format, bool is_first) {
|
||||
switch (format) {
|
||||
case PixelFormat::Z24S8:
|
||||
case PixelFormat::Z32FS8:
|
||||
return is_first ? GL_DEPTH_COMPONENT : GL_STENCIL_INDEX;
|
||||
case PixelFormat::S8Z24:
|
||||
return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return GL_DEPTH_COMPONENT;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) {
|
||||
if (params.IsBuffer()) {
|
||||
return;
|
||||
@@ -184,7 +197,7 @@ void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) {
|
||||
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, params.num_levels - 1);
|
||||
glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(params.num_levels - 1));
|
||||
if (params.num_levels == 1) {
|
||||
glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f);
|
||||
}
|
||||
@@ -416,11 +429,21 @@ void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_sou
|
||||
if (new_swizzle == swizzle)
|
||||
return;
|
||||
swizzle = new_swizzle;
|
||||
const std::array<GLint, 4> gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source),
|
||||
GetSwizzleSource(z_source),
|
||||
GetSwizzleSource(w_source)};
|
||||
const std::array gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source),
|
||||
GetSwizzleSource(z_source), GetSwizzleSource(w_source)};
|
||||
const GLuint handle = GetTexture();
|
||||
glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
|
||||
const PixelFormat format = surface.GetSurfaceParams().pixel_format;
|
||||
switch (format) {
|
||||
case PixelFormat::Z24S8:
|
||||
case PixelFormat::Z32FS8:
|
||||
case PixelFormat::S8Z24:
|
||||
glTextureParameteri(handle, GL_DEPTH_STENCIL_TEXTURE_MODE,
|
||||
GetComponent(format, x_source == SwizzleSource::R));
|
||||
break;
|
||||
default:
|
||||
glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OGLTextureView CachedSurfaceView::CreateTextureView() const {
|
||||
@@ -529,8 +552,11 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
|
||||
const Common::Rectangle<u32>& dst_rect = copy_config.dst_rect;
|
||||
const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear;
|
||||
|
||||
glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left,
|
||||
dst_rect.top, dst_rect.right, dst_rect.bottom, buffers,
|
||||
glBlitFramebuffer(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.top),
|
||||
static_cast<GLint>(src_rect.right), static_cast<GLint>(src_rect.bottom),
|
||||
static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.top),
|
||||
static_cast<GLint>(dst_rect.right), static_cast<GLint>(dst_rect.bottom),
|
||||
buffers,
|
||||
is_linear && (buffers == GL_COLOR_BUFFER_BIT) ? GL_LINEAR : GL_NEAREST);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
|
||||
UNREACHABLE();
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
|
||||
return {};
|
||||
}
|
||||
case Maxwell::VertexAttribute::Type::SignedInt:
|
||||
@@ -72,8 +71,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return GL_INT_2_10_10_10_REV;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
|
||||
UNREACHABLE();
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
|
||||
return {};
|
||||
}
|
||||
case Maxwell::VertexAttribute::Type::Float:
|
||||
@@ -89,13 +87,19 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) {
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return GL_FLOAT;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
|
||||
UNREACHABLE();
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
|
||||
return {};
|
||||
}
|
||||
case Maxwell::VertexAttribute::Type::UnsignedScaled:
|
||||
switch (attrib.size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
default:
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString());
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
|
||||
UNREACHABLE();
|
||||
LOG_ERROR(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
265
src/video_core/renderer_vulkan/renderer_vulkan.cpp
Normal file
265
src/video_core/renderer_vulkan/renderer_vulkan.cpp
Normal file
@@ -0,0 +1,265 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/telemetry.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/renderer_vulkan/declarations.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#include "video_core/renderer_vulkan/vk_blit_screen.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_memory_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
namespace {
|
||||
|
||||
VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_,
|
||||
VkDebugUtilsMessageTypeFlagsEXT type,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* data,
|
||||
[[maybe_unused]] void* user_data) {
|
||||
const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_};
|
||||
const char* message{data->pMessage};
|
||||
|
||||
if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) {
|
||||
LOG_CRITICAL(Render_Vulkan, "{}", message);
|
||||
} else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) {
|
||||
LOG_WARNING(Render_Vulkan, "{}", message);
|
||||
} else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo) {
|
||||
LOG_INFO(Render_Vulkan, "{}", message);
|
||||
} else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose) {
|
||||
LOG_DEBUG(Render_Vulkan, "{}", message);
|
||||
}
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
std::string GetReadableVersion(u32 version) {
|
||||
return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
|
||||
VK_VERSION_PATCH(version));
|
||||
}
|
||||
|
||||
std::string GetDriverVersion(const VKDevice& device) {
|
||||
// Extracted from
|
||||
// https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314
|
||||
const u32 version = device.GetDriverVersion();
|
||||
|
||||
if (device.GetDriverID() == vk::DriverIdKHR::eNvidiaProprietary) {
|
||||
const u32 major = (version >> 22) & 0x3ff;
|
||||
const u32 minor = (version >> 14) & 0x0ff;
|
||||
const u32 secondary = (version >> 6) & 0x0ff;
|
||||
const u32 tertiary = version & 0x003f;
|
||||
return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary);
|
||||
}
|
||||
if (device.GetDriverID() == vk::DriverIdKHR::eIntelProprietaryWindows) {
|
||||
const u32 major = version >> 14;
|
||||
const u32 minor = version & 0x3fff;
|
||||
return fmt::format("{}.{}", major, minor);
|
||||
}
|
||||
|
||||
return GetReadableVersion(version);
|
||||
}
|
||||
|
||||
std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) {
|
||||
std::sort(std::begin(available_extensions), std::end(available_extensions));
|
||||
|
||||
static constexpr std::size_t AverageExtensionSize = 64;
|
||||
std::string separated_extensions;
|
||||
separated_extensions.reserve(available_extensions.size() * AverageExtensionSize);
|
||||
|
||||
const auto end = std::end(available_extensions);
|
||||
for (auto extension = std::begin(available_extensions); extension != end; ++extension) {
|
||||
if (const bool is_last = extension + 1 == end; is_last) {
|
||||
separated_extensions += *extension;
|
||||
} else {
|
||||
separated_extensions += fmt::format("{},", *extension);
|
||||
}
|
||||
}
|
||||
return separated_extensions;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system)
|
||||
: RendererBase(window), system{system} {}
|
||||
|
||||
RendererVulkan::~RendererVulkan() {
|
||||
ShutDown();
|
||||
}
|
||||
|
||||
void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
const auto& layout = render_window.GetFramebufferLayout();
|
||||
if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
|
||||
const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
|
||||
const bool use_accelerated =
|
||||
rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
|
||||
const bool is_srgb = use_accelerated && screen_info.is_srgb;
|
||||
if (swapchain->HasFramebufferChanged(layout) || swapchain->GetSrgbState() != is_srgb) {
|
||||
swapchain->Create(layout.width, layout.height, is_srgb);
|
||||
blit_screen->Recreate();
|
||||
}
|
||||
|
||||
scheduler->WaitWorker();
|
||||
|
||||
swapchain->AcquireNextImage();
|
||||
const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated);
|
||||
|
||||
scheduler->Flush(false, render_semaphore);
|
||||
|
||||
if (swapchain->Present(render_semaphore, fence)) {
|
||||
blit_screen->Recreate();
|
||||
}
|
||||
|
||||
render_window.SwapBuffers();
|
||||
rasterizer->TickFrame();
|
||||
}
|
||||
|
||||
render_window.PollEvents();
|
||||
}
|
||||
|
||||
bool RendererVulkan::Init() {
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
|
||||
render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
|
||||
const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr);
|
||||
|
||||
std::optional<vk::DebugUtilsMessengerEXT> callback;
|
||||
if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) {
|
||||
callback = CreateDebugCallback(dldi);
|
||||
if (!callback) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PickDevices(dldi)) {
|
||||
if (callback) {
|
||||
instance.destroy(*callback, nullptr, dldi);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
debug_callback = UniqueDebugUtilsMessengerEXT(
|
||||
*callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>(
|
||||
instance, nullptr, device->GetDispatchLoader()));
|
||||
|
||||
Report();
|
||||
|
||||
memory_manager = std::make_unique<VKMemoryManager>(*device);
|
||||
|
||||
resource_manager = std::make_unique<VKResourceManager>(*device);
|
||||
|
||||
const auto& framebuffer = render_window.GetFramebufferLayout();
|
||||
swapchain = std::make_unique<VKSwapchain>(surface, *device);
|
||||
swapchain->Create(framebuffer.width, framebuffer.height, false);
|
||||
|
||||
scheduler = std::make_unique<VKScheduler>(*device, *resource_manager);
|
||||
|
||||
rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device,
|
||||
*resource_manager, *memory_manager, *scheduler);
|
||||
|
||||
blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device,
|
||||
*resource_manager, *memory_manager, *swapchain,
|
||||
*scheduler, screen_info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererVulkan::ShutDown() {
|
||||
if (!device) {
|
||||
return;
|
||||
}
|
||||
const auto dev = device->GetLogical();
|
||||
const auto& dld = device->GetDispatchLoader();
|
||||
if (dev && dld.vkDeviceWaitIdle) {
|
||||
dev.waitIdle(dld);
|
||||
}
|
||||
|
||||
rasterizer.reset();
|
||||
blit_screen.reset();
|
||||
scheduler.reset();
|
||||
swapchain.reset();
|
||||
memory_manager.reset();
|
||||
resource_manager.reset();
|
||||
device.reset();
|
||||
}
|
||||
|
||||
std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback(
|
||||
const vk::DispatchLoaderDynamic& dldi) {
|
||||
const vk::DebugUtilsMessengerCreateInfoEXT callback_ci(
|
||||
{},
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo |
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose,
|
||||
vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
|
||||
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
|
||||
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
|
||||
&DebugCallback, nullptr);
|
||||
vk::DebugUtilsMessengerEXT callback;
|
||||
if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) !=
|
||||
vk::Result::eSuccess) {
|
||||
LOG_ERROR(Render_Vulkan, "Failed to create debug callback");
|
||||
return {};
|
||||
}
|
||||
return callback;
|
||||
}
|
||||
|
||||
bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) {
|
||||
const auto devices = instance.enumeratePhysicalDevices(dldi);
|
||||
|
||||
// TODO(Rodrigo): Choose device from config file
|
||||
const s32 device_index = Settings::values.vulkan_device;
|
||||
if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) {
|
||||
LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
|
||||
return false;
|
||||
}
|
||||
const vk::PhysicalDevice physical_device = devices[device_index];
|
||||
|
||||
if (!VKDevice::IsSuitable(dldi, physical_device, surface)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
device = std::make_unique<VKDevice>(dldi, physical_device, surface);
|
||||
return device->Create(dldi, instance);
|
||||
}
|
||||
|
||||
void RendererVulkan::Report() const {
|
||||
const std::string vendor_name{device->GetVendorName()};
|
||||
const std::string model_name{device->GetModelName()};
|
||||
const std::string driver_version = GetDriverVersion(*device);
|
||||
const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version);
|
||||
|
||||
const std::string api_version = GetReadableVersion(device->GetApiVersion());
|
||||
|
||||
const std::string extensions = BuildCommaSeparatedExtensions(device->GetAvailableExtensions());
|
||||
|
||||
LOG_INFO(Render_Vulkan, "Driver: {}", driver_name);
|
||||
LOG_INFO(Render_Vulkan, "Device: {}", model_name);
|
||||
LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
|
||||
|
||||
auto& telemetry_session = system.TelemetrySession();
|
||||
constexpr auto field = Telemetry::FieldType::UserSystem;
|
||||
telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
|
||||
telemetry_session.AddField(field, "GPU_Model", model_name);
|
||||
telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
|
||||
telemetry_session.AddField(field, "GPU_Vulkan_Version", api_version);
|
||||
telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -400,8 +400,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami
|
||||
VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true);
|
||||
Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME,
|
||||
false);
|
||||
Test(extension, nv_device_diagnostic_checkpoints,
|
||||
VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true);
|
||||
if (Settings::values.renderer_debug) {
|
||||
Test(extension, nv_device_diagnostic_checkpoints,
|
||||
VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (khr_shader_float16_int8) {
|
||||
|
||||
@@ -325,9 +325,6 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
|
||||
specialization.tessellation.primitive = fixed_state.tessellation.primitive;
|
||||
specialization.tessellation.spacing = fixed_state.tessellation.spacing;
|
||||
specialization.tessellation.clockwise = fixed_state.tessellation.clockwise;
|
||||
for (const auto& rt : key.renderpass_params.color_attachments) {
|
||||
specialization.enabled_rendertargets.set(rt.index);
|
||||
}
|
||||
|
||||
SPIRVProgram program;
|
||||
std::vector<vk::DescriptorSetLayoutBinding> bindings;
|
||||
|
||||
@@ -571,7 +571,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
|
||||
color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true);
|
||||
}
|
||||
if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) {
|
||||
texceptions.set(rt);
|
||||
texceptions[rt] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,7 +579,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
|
||||
zeta_attachment = texture_cache.GetDepthBufferSurface(true);
|
||||
}
|
||||
if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) {
|
||||
texceptions.set(ZETA_TEXCEPTION_INDEX);
|
||||
texceptions[ZETA_TEXCEPTION_INDEX] = true;
|
||||
}
|
||||
|
||||
texture_cache.GuardRenderTargets(false);
|
||||
@@ -1122,11 +1122,12 @@ RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions)
|
||||
|
||||
for (std::size_t rt = 0; rt < static_cast<std::size_t>(regs.rt_control.count); ++rt) {
|
||||
const auto& rendertarget = regs.rt[rt];
|
||||
if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE)
|
||||
if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) {
|
||||
continue;
|
||||
}
|
||||
renderpass_params.color_attachments.push_back(RenderPassParams::ColorAttachment{
|
||||
static_cast<u32>(rt), PixelFormatFromRenderTargetFormat(rendertarget.format),
|
||||
texceptions.test(rt)});
|
||||
texceptions[rt]});
|
||||
}
|
||||
|
||||
renderpass_params.has_zeta = regs.zeta_enable;
|
||||
|
||||
@@ -353,6 +353,7 @@ private:
|
||||
DeclareFragment();
|
||||
DeclareCompute();
|
||||
DeclareRegisters();
|
||||
DeclareCustomVariables();
|
||||
DeclarePredicates();
|
||||
DeclareLocalMemory();
|
||||
DeclareSharedMemory();
|
||||
@@ -542,11 +543,10 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 rt = 0; rt < static_cast<u32>(frag_colors.size()); ++rt) {
|
||||
if (!specialization.enabled_rendertargets[rt]) {
|
||||
for (u32 rt = 0; rt < static_cast<u32>(std::size(frag_colors)); ++rt) {
|
||||
if (!IsRenderTargetEnabled(rt)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Id id = AddGlobalVariable(OpVariable(t_out_float4, spv::StorageClass::Output));
|
||||
Name(id, fmt::format("frag_color{}", rt));
|
||||
Decorate(id, spv::Decoration::Location, rt);
|
||||
@@ -587,6 +587,15 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareCustomVariables() {
|
||||
const u32 num_custom_variables = ir.GetNumCustomVariables();
|
||||
for (u32 i = 0; i < num_custom_variables; ++i) {
|
||||
const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero);
|
||||
Name(id, fmt::format("custom_var_{}", i));
|
||||
custom_variables.emplace(i, AddGlobalVariable(id));
|
||||
}
|
||||
}
|
||||
|
||||
void DeclarePredicates() {
|
||||
for (const auto pred : ir.GetPredicates()) {
|
||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||
@@ -852,6 +861,15 @@ private:
|
||||
return binding;
|
||||
}
|
||||
|
||||
bool IsRenderTargetEnabled(u32 rt) const {
|
||||
for (u32 component = 0; component < 4; ++component) {
|
||||
if (header.ps.IsColorComponentOutputEnabled(rt, component)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsInputAttributeArray() const {
|
||||
return stage == ShaderType::TesselationControl || stage == ShaderType::TesselationEval ||
|
||||
stage == ShaderType::Geometry;
|
||||
@@ -974,6 +992,11 @@ private:
|
||||
return {OpLoad(t_float, registers.at(index)), Type::Float};
|
||||
}
|
||||
|
||||
if (const auto cv = std::get_if<CustomVarNode>(&*node)) {
|
||||
const u32 index = cv->GetIndex();
|
||||
return {OpLoad(t_float, custom_variables.at(index)), Type::Float};
|
||||
}
|
||||
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
|
||||
return {Constant(t_uint, immediate->GetValue()), Type::Uint};
|
||||
}
|
||||
@@ -1115,15 +1138,7 @@ private:
|
||||
}
|
||||
|
||||
if (const auto gmem = std::get_if<GmemNode>(&*node)) {
|
||||
const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor());
|
||||
const Id real = AsUint(Visit(gmem->GetRealAddress()));
|
||||
const Id base = AsUint(Visit(gmem->GetBaseAddress()));
|
||||
|
||||
Id offset = OpISub(t_uint, real, base);
|
||||
offset = OpUDiv(t_uint, offset, Constant(t_uint, 4U));
|
||||
return {OpLoad(t_float,
|
||||
OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0U), offset)),
|
||||
Type::Float};
|
||||
return {OpLoad(t_uint, GetGlobalMemoryPointer(*gmem)), Type::Uint};
|
||||
}
|
||||
|
||||
if (const auto lmem = std::get_if<LmemNode>(&*node)) {
|
||||
@@ -1134,10 +1149,7 @@ private:
|
||||
}
|
||||
|
||||
if (const auto smem = std::get_if<SmemNode>(&*node)) {
|
||||
Id address = AsUint(Visit(smem->GetAddress()));
|
||||
address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U));
|
||||
const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address);
|
||||
return {OpLoad(t_uint, pointer), Type::Uint};
|
||||
return {OpLoad(t_uint, GetSharedMemoryPointer(*smem)), Type::Uint};
|
||||
}
|
||||
|
||||
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
|
||||
@@ -1331,20 +1343,13 @@ private:
|
||||
target = {OpAccessChain(t_prv_float, local_memory, address), Type::Float};
|
||||
|
||||
} else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
|
||||
ASSERT(stage == ShaderType::Compute);
|
||||
Id address = AsUint(Visit(smem->GetAddress()));
|
||||
address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U));
|
||||
target = {OpAccessChain(t_smem_uint, shared_memory, address), Type::Uint};
|
||||
target = {GetSharedMemoryPointer(*smem), Type::Uint};
|
||||
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
|
||||
const Id real = AsUint(Visit(gmem->GetRealAddress()));
|
||||
const Id base = AsUint(Visit(gmem->GetBaseAddress()));
|
||||
const Id diff = OpISub(t_uint, real, base);
|
||||
const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2));
|
||||
target = {GetGlobalMemoryPointer(*gmem), Type::Uint};
|
||||
|
||||
const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor());
|
||||
target = {OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0), offset),
|
||||
Type::Float};
|
||||
} else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) {
|
||||
target = {custom_variables.at(cv->GetIndex()), Type::Float};
|
||||
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
@@ -1796,11 +1801,16 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
Expression UAtomicAdd(Operation operation) {
|
||||
const auto& smem = std::get<SmemNode>(*operation[0]);
|
||||
Id address = AsUint(Visit(smem.GetAddress()));
|
||||
address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U));
|
||||
const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address);
|
||||
Expression AtomicAdd(Operation operation) {
|
||||
Id pointer;
|
||||
if (const auto smem = std::get_if<SmemNode>(&*operation[0])) {
|
||||
pointer = GetSharedMemoryPointer(*smem);
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(&*operation[0])) {
|
||||
pointer = GetGlobalMemoryPointer(*gmem);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return {Constant(t_uint, 0), Type::Uint};
|
||||
}
|
||||
|
||||
const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device));
|
||||
const Id semantics = Constant(t_uint, 0U);
|
||||
@@ -1889,19 +1899,14 @@ private:
|
||||
// rendertargets/components are skipped in the register assignment.
|
||||
u32 current_reg = 0;
|
||||
for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) {
|
||||
if (!specialization.enabled_rendertargets[rt]) {
|
||||
// Skip rendertargets that are not enabled
|
||||
continue;
|
||||
}
|
||||
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
|
||||
for (u32 component = 0; component < 4; ++component) {
|
||||
const Id pointer = AccessElement(t_out_float, frag_colors.at(rt), component);
|
||||
if (header.ps.IsColorComponentOutputEnabled(rt, component)) {
|
||||
OpStore(pointer, SafeGetRegister(current_reg));
|
||||
++current_reg;
|
||||
} else {
|
||||
OpStore(pointer, component == 3 ? v_float_one : v_float_zero);
|
||||
if (!header.ps.IsColorComponentOutputEnabled(rt, component)) {
|
||||
continue;
|
||||
}
|
||||
const Id pointer = AccessElement(t_out_float, frag_colors[rt], component);
|
||||
OpStore(pointer, SafeGetRegister(current_reg));
|
||||
++current_reg;
|
||||
}
|
||||
}
|
||||
if (header.ps.omap.depth) {
|
||||
@@ -2240,6 +2245,22 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
Id GetGlobalMemoryPointer(const GmemNode& gmem) {
|
||||
const Id real = AsUint(Visit(gmem.GetRealAddress()));
|
||||
const Id base = AsUint(Visit(gmem.GetBaseAddress()));
|
||||
const Id diff = OpISub(t_uint, real, base);
|
||||
const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2));
|
||||
const Id buffer = global_buffers.at(gmem.GetDescriptor());
|
||||
return OpAccessChain(t_gmem_uint, buffer, Constant(t_uint, 0), offset);
|
||||
}
|
||||
|
||||
Id GetSharedMemoryPointer(const SmemNode& smem) {
|
||||
ASSERT(stage == ShaderType::Compute);
|
||||
Id address = AsUint(Visit(smem.GetAddress()));
|
||||
address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U));
|
||||
return OpAccessChain(t_smem_uint, shared_memory, address);
|
||||
}
|
||||
|
||||
static constexpr std::array operation_decompilers = {
|
||||
&SPIRVDecompiler::Assign,
|
||||
|
||||
@@ -2386,7 +2407,7 @@ private:
|
||||
&SPIRVDecompiler::AtomicImageXor,
|
||||
&SPIRVDecompiler::AtomicImageExchange,
|
||||
|
||||
&SPIRVDecompiler::UAtomicAdd,
|
||||
&SPIRVDecompiler::AtomicAdd,
|
||||
|
||||
&SPIRVDecompiler::Branch,
|
||||
&SPIRVDecompiler::BranchIndirect,
|
||||
@@ -2482,9 +2503,9 @@ private:
|
||||
|
||||
Id t_smem_uint{};
|
||||
|
||||
const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float);
|
||||
const Id t_gmem_uint = TypePointer(spv::StorageClass::StorageBuffer, t_uint);
|
||||
const Id t_gmem_array =
|
||||
Name(Decorate(TypeRuntimeArray(t_float), spv::Decoration::ArrayStride, 4U), "GmemArray");
|
||||
Name(Decorate(TypeRuntimeArray(t_uint), spv::Decoration::ArrayStride, 4U), "GmemArray");
|
||||
const Id t_gmem_struct = MemberDecorate(
|
||||
Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
||||
const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct);
|
||||
@@ -2505,6 +2526,7 @@ private:
|
||||
Id out_vertex{};
|
||||
Id in_vertex{};
|
||||
std::map<u32, Id> registers;
|
||||
std::map<u32, Id> custom_variables;
|
||||
std::map<Tegra::Shader::Pred, Id> predicates;
|
||||
std::map<u32, Id> flow_variables;
|
||||
Id local_memory{};
|
||||
|
||||
@@ -102,9 +102,6 @@ struct Specialization final {
|
||||
Maxwell::TessellationSpacing spacing{};
|
||||
bool clockwise{};
|
||||
} tessellation;
|
||||
|
||||
// Fragment specific
|
||||
std::bitset<8> enabled_rendertargets;
|
||||
};
|
||||
// Old gcc versions don't consider this trivially copyable.
|
||||
// static_assert(std::is_trivially_copyable_v<Specialization>);
|
||||
|
||||
@@ -65,8 +65,8 @@ public:
|
||||
void DetachSegment(ASTNode start, ASTNode end);
|
||||
void Remove(ASTNode node);
|
||||
|
||||
ASTNode first{};
|
||||
ASTNode last{};
|
||||
ASTNode first;
|
||||
ASTNode last;
|
||||
};
|
||||
|
||||
class ASTProgram {
|
||||
@@ -299,9 +299,9 @@ private:
|
||||
friend class ASTZipper;
|
||||
|
||||
ASTData data;
|
||||
ASTNode parent{};
|
||||
ASTNode next{};
|
||||
ASTNode previous{};
|
||||
ASTNode parent;
|
||||
ASTNode next;
|
||||
ASTNode previous;
|
||||
ASTZipper* manager{};
|
||||
};
|
||||
|
||||
|
||||
@@ -66,6 +66,18 @@ std::optional<Tegra::Engines::SamplerDescriptor> ConstBufferLocker::ObtainBindle
|
||||
return value;
|
||||
}
|
||||
|
||||
std::optional<u32> ConstBufferLocker::ObtainBoundBuffer() {
|
||||
if (bound_buffer_saved) {
|
||||
return bound_buffer;
|
||||
}
|
||||
if (!engine) {
|
||||
return std::nullopt;
|
||||
}
|
||||
bound_buffer_saved = true;
|
||||
bound_buffer = engine->GetBoundBuffer();
|
||||
return bound_buffer;
|
||||
}
|
||||
|
||||
void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) {
|
||||
keys.insert_or_assign({buffer, offset}, value);
|
||||
}
|
||||
@@ -78,6 +90,11 @@ void ConstBufferLocker::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDes
|
||||
bindless_samplers.insert_or_assign({buffer, offset}, sampler);
|
||||
}
|
||||
|
||||
void ConstBufferLocker::SetBoundBuffer(u32 buffer) {
|
||||
bound_buffer_saved = true;
|
||||
bound_buffer = buffer;
|
||||
}
|
||||
|
||||
bool ConstBufferLocker::IsConsistent() const {
|
||||
if (!engine) {
|
||||
return false;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/hash.h"
|
||||
#include "video_core/engines/const_buffer_engine_interface.h"
|
||||
#include "video_core/engines/shader_type.h"
|
||||
#include "video_core/guest_driver.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -40,6 +41,8 @@ public:
|
||||
|
||||
std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset);
|
||||
|
||||
std::optional<u32> ObtainBoundBuffer();
|
||||
|
||||
/// Inserts a key.
|
||||
void InsertKey(u32 buffer, u32 offset, u32 value);
|
||||
|
||||
@@ -49,6 +52,9 @@ public:
|
||||
/// Inserts a bindless sampler key.
|
||||
void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler);
|
||||
|
||||
/// Set the bound buffer for this locker.
|
||||
void SetBoundBuffer(u32 buffer);
|
||||
|
||||
/// Checks keys and samplers against engine's current const buffers. Returns true if they are
|
||||
/// the same value, false otherwise;
|
||||
bool IsConsistent() const;
|
||||
@@ -71,12 +77,27 @@ public:
|
||||
return bindless_samplers;
|
||||
}
|
||||
|
||||
/// Gets bound buffer used on this shader
|
||||
u32 GetBoundBuffer() const {
|
||||
return bound_buffer;
|
||||
}
|
||||
|
||||
/// Obtains access to the guest driver's profile.
|
||||
VideoCore::GuestDriverProfile* AccessGuestDriverProfile() const {
|
||||
if (engine) {
|
||||
return &engine->AccessGuestDriverProfile();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
const Tegra::Engines::ShaderType stage;
|
||||
Tegra::Engines::ConstBufferEngineInterface* engine = nullptr;
|
||||
KeyMap keys;
|
||||
BoundSamplerMap bound_samplers;
|
||||
BindlessSamplerMap bindless_samplers;
|
||||
bool bound_buffer_saved{};
|
||||
u32 bound_buffer{};
|
||||
};
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
|
||||
#include <fmt/format.h>
|
||||
@@ -33,6 +34,52 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) {
|
||||
return (absolute_offset % SchedPeriod) == 0;
|
||||
}
|
||||
|
||||
void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile* gpu_driver,
|
||||
const std::list<Sampler>& used_samplers) {
|
||||
if (gpu_driver == nullptr) {
|
||||
LOG_CRITICAL(HW_GPU, "GPU driver profile has not been created yet");
|
||||
return;
|
||||
}
|
||||
if (gpu_driver->TextureHandlerSizeKnown() || used_samplers.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
u32 count{};
|
||||
std::vector<u32> bound_offsets;
|
||||
for (const auto& sampler : used_samplers) {
|
||||
if (sampler.IsBindless()) {
|
||||
continue;
|
||||
}
|
||||
++count;
|
||||
bound_offsets.emplace_back(sampler.GetOffset());
|
||||
}
|
||||
if (count > 1) {
|
||||
gpu_driver->DeduceTextureHandlerSize(std::move(bound_offsets));
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce,
|
||||
VideoCore::GuestDriverProfile* gpu_driver,
|
||||
const std::list<Sampler>& used_samplers) {
|
||||
if (gpu_driver == nullptr) {
|
||||
LOG_CRITICAL(HW_GPU, "GPU Driver profile has not been created yet");
|
||||
return std::nullopt;
|
||||
}
|
||||
const u32 base_offset = sampler_to_deduce.GetOffset();
|
||||
u32 max_offset{std::numeric_limits<u32>::max()};
|
||||
for (const auto& sampler : used_samplers) {
|
||||
if (sampler.IsBindless()) {
|
||||
continue;
|
||||
}
|
||||
if (sampler.GetOffset() > base_offset) {
|
||||
max_offset = std::min(sampler.GetOffset(), max_offset);
|
||||
}
|
||||
}
|
||||
if (max_offset == std::numeric_limits<u32>::max()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return ((max_offset - base_offset) * 4) / gpu_driver->GetTextureHandlerSize();
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
class ASTDecoder {
|
||||
@@ -315,4 +362,25 @@ u32 ShaderIR::DecodeInstr(NodeBlock& bb, u32 pc) {
|
||||
return pc + 1;
|
||||
}
|
||||
|
||||
void ShaderIR::PostDecode() {
|
||||
// Deduce texture handler size if needed
|
||||
auto gpu_driver = locker.AccessGuestDriverProfile();
|
||||
DeduceTextureHandlerSize(gpu_driver, used_samplers);
|
||||
// Deduce Indexed Samplers
|
||||
if (!uses_indexed_samplers) {
|
||||
return;
|
||||
}
|
||||
for (auto& sampler : used_samplers) {
|
||||
if (!sampler.IsIndexed()) {
|
||||
continue;
|
||||
}
|
||||
if (const auto size = TryDeduceSamplerSize(sampler, gpu_driver, used_samplers)) {
|
||||
sampler.SetSize(*size);
|
||||
} else {
|
||||
LOG_CRITICAL(HW_GPU, "Failed to deduce size of indexed sampler");
|
||||
sampler.SetSize(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -21,7 +21,7 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
|
||||
|
||||
Node op_a = GetRegister(instr.gpr8);
|
||||
|
||||
Node op_b = [&]() -> Node {
|
||||
Node op_b = [&] {
|
||||
if (instr.is_b_imm) {
|
||||
return GetImmediate19(instr);
|
||||
} else if (instr.is_b_gpr) {
|
||||
@@ -141,6 +141,15 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
|
||||
SetRegister(bb, instr.gpr0, value);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FCMP_R: {
|
||||
UNIMPLEMENTED_IF(instr.fcmp.ftz == 0);
|
||||
Node op_c = GetRegister(instr.gpr39);
|
||||
Node comp = GetPredicateComparisonFloat(instr.fcmp.cond, std::move(op_c), Immediate(0.0f));
|
||||
SetRegister(
|
||||
bb, instr.gpr0,
|
||||
Operation(OperationCode::Select, std::move(comp), std::move(op_a), std::move(op_b)));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::RRO_C:
|
||||
case OpCode::Id::RRO_R:
|
||||
case OpCode::Id::RRO_IMM: {
|
||||
|
||||
@@ -297,7 +297,7 @@ void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Nod
|
||||
const Node one = Immediate(1);
|
||||
const Node two = Immediate(2);
|
||||
|
||||
Node value{};
|
||||
Node value;
|
||||
for (u32 i = 0; i < lop_iterations; ++i) {
|
||||
const Node shift_amount = Immediate(i);
|
||||
|
||||
|
||||
@@ -17,10 +17,13 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
|
||||
const auto [base, packed_shift] = [&]() -> std::tuple<Node, Node> {
|
||||
const auto [packed_shift, base] = [&]() -> std::pair<Node, Node> {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::BFI_RC:
|
||||
return {GetRegister(instr.gpr39),
|
||||
GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)};
|
||||
case OpCode::Id::BFI_IMM_R:
|
||||
return {GetRegister(instr.gpr39), Immediate(instr.alu.GetSignedImm20_20())};
|
||||
return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {Immediate(0), Immediate(0)};
|
||||
|
||||
@@ -19,9 +19,12 @@ namespace VideoCommon::Shader {
|
||||
using Tegra::Shader::AtomicOp;
|
||||
using Tegra::Shader::AtomicType;
|
||||
using Tegra::Shader::Attribute;
|
||||
using Tegra::Shader::GlobalAtomicOp;
|
||||
using Tegra::Shader::GlobalAtomicType;
|
||||
using Tegra::Shader::Instruction;
|
||||
using Tegra::Shader::OpCode;
|
||||
using Tegra::Shader::Register;
|
||||
using Tegra::Shader::StoreType;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -61,6 +64,27 @@ u32 GetMemorySize(Tegra::Shader::UniformType uniform_type) {
|
||||
}
|
||||
}
|
||||
|
||||
Node ExtractUnaligned(Node value, Node address, u32 mask, u32 size) {
|
||||
Node offset = Operation(OperationCode::UBitwiseAnd, address, Immediate(mask));
|
||||
offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3));
|
||||
return Operation(OperationCode::UBitfieldExtract, std::move(value), std::move(offset),
|
||||
Immediate(size));
|
||||
}
|
||||
|
||||
Node InsertUnaligned(Node dest, Node value, Node address, u32 mask, u32 size) {
|
||||
Node offset = Operation(OperationCode::UBitwiseAnd, std::move(address), Immediate(mask));
|
||||
offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3));
|
||||
return Operation(OperationCode::UBitfieldInsert, std::move(dest), std::move(value),
|
||||
std::move(offset), Immediate(size));
|
||||
}
|
||||
|
||||
Node Sign16Extend(Node value) {
|
||||
Node sign = Operation(OperationCode::UBitwiseAnd, value, Immediate(1U << 15));
|
||||
Node is_sign = Operation(OperationCode::LogicalUEqual, std::move(sign), Immediate(1U << 15));
|
||||
Node extend = Operation(OperationCode::Select, is_sign, Immediate(0xFFFF0000), Immediate(0));
|
||||
return Operation(OperationCode::UBitwiseOr, std::move(value), std::move(extend));
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
@@ -136,26 +160,31 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown));
|
||||
[[fallthrough]];
|
||||
case OpCode::Id::LD_S: {
|
||||
const auto GetMemory = [&](s32 offset) {
|
||||
const auto GetAddress = [&](s32 offset) {
|
||||
ASSERT(offset % 4 == 0);
|
||||
const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset);
|
||||
const Node address = Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8),
|
||||
immediate_offset);
|
||||
return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(address)
|
||||
: GetLocalMemory(address);
|
||||
return Operation(OperationCode::IAdd, GetRegister(instr.gpr8), immediate_offset);
|
||||
};
|
||||
const auto GetMemory = [&](s32 offset) {
|
||||
return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(GetAddress(offset))
|
||||
: GetLocalMemory(GetAddress(offset));
|
||||
};
|
||||
|
||||
switch (instr.ldst_sl.type.Value()) {
|
||||
case Tegra::Shader::StoreType::Bits32:
|
||||
case Tegra::Shader::StoreType::Bits64:
|
||||
case Tegra::Shader::StoreType::Bits128: {
|
||||
const u32 count = [&]() {
|
||||
case StoreType::Signed16:
|
||||
SetRegister(bb, instr.gpr0,
|
||||
Sign16Extend(ExtractUnaligned(GetMemory(0), GetAddress(0), 0b10, 16)));
|
||||
break;
|
||||
case StoreType::Bits32:
|
||||
case StoreType::Bits64:
|
||||
case StoreType::Bits128: {
|
||||
const u32 count = [&] {
|
||||
switch (instr.ldst_sl.type.Value()) {
|
||||
case Tegra::Shader::StoreType::Bits32:
|
||||
case StoreType::Bits32:
|
||||
return 1;
|
||||
case Tegra::Shader::StoreType::Bits64:
|
||||
case StoreType::Bits64:
|
||||
return 2;
|
||||
case Tegra::Shader::StoreType::Bits128:
|
||||
case StoreType::Bits128:
|
||||
return 4;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
@@ -212,12 +241,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
// To handle unaligned loads get the bytes used to dereference global memory and extract
|
||||
// those bytes from the loaded u32.
|
||||
if (IsUnaligned(type)) {
|
||||
Node mask = Immediate(GetUnalignedMask(type));
|
||||
Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask));
|
||||
offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3));
|
||||
|
||||
gmem = Operation(OperationCode::UBitfieldExtract, std::move(gmem),
|
||||
std::move(offset), Immediate(size));
|
||||
gmem = ExtractUnaligned(gmem, real_address, GetUnalignedMask(type), size);
|
||||
}
|
||||
|
||||
SetTemporary(bb, i, gmem);
|
||||
@@ -269,21 +293,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate);
|
||||
};
|
||||
|
||||
const auto set_memory = opcode->get().GetId() == OpCode::Id::ST_L
|
||||
? &ShaderIR::SetLocalMemory
|
||||
: &ShaderIR::SetSharedMemory;
|
||||
const bool is_local = opcode->get().GetId() == OpCode::Id::ST_L;
|
||||
const auto set_memory = is_local ? &ShaderIR::SetLocalMemory : &ShaderIR::SetSharedMemory;
|
||||
const auto get_memory = is_local ? &ShaderIR::GetLocalMemory : &ShaderIR::GetSharedMemory;
|
||||
|
||||
switch (instr.ldst_sl.type.Value()) {
|
||||
case Tegra::Shader::StoreType::Bits128:
|
||||
case StoreType::Bits128:
|
||||
(this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3));
|
||||
(this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2));
|
||||
[[fallthrough]];
|
||||
case Tegra::Shader::StoreType::Bits64:
|
||||
case StoreType::Bits64:
|
||||
(this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1));
|
||||
[[fallthrough]];
|
||||
case Tegra::Shader::StoreType::Bits32:
|
||||
case StoreType::Bits32:
|
||||
(this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0));
|
||||
break;
|
||||
case StoreType::Signed16: {
|
||||
Node address = GetAddress(0);
|
||||
Node memory = (this->*get_memory)(address);
|
||||
(this->*set_memory)(
|
||||
bb, address, InsertUnaligned(memory, GetRegister(instr.gpr0), address, 0b10, 16));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(),
|
||||
static_cast<u32>(instr.ldst_sl.type.Value()));
|
||||
@@ -323,18 +354,32 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
Node value = GetRegister(instr.gpr0.Value() + i);
|
||||
|
||||
if (IsUnaligned(type)) {
|
||||
Node mask = Immediate(GetUnalignedMask(type));
|
||||
Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask));
|
||||
offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3));
|
||||
|
||||
value = Operation(OperationCode::UBitfieldInsert, gmem, std::move(value), offset,
|
||||
Immediate(size));
|
||||
const u32 mask = GetUnalignedMask(type);
|
||||
value = InsertUnaligned(gmem, std::move(value), real_address, mask, size);
|
||||
}
|
||||
|
||||
bb.push_back(Operation(OperationCode::Assign, gmem, value));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::ATOM: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.atom.operation != GlobalAtomicOp::Add, "operation={}",
|
||||
static_cast<int>(instr.atom.operation.Value()));
|
||||
UNIMPLEMENTED_IF_MSG(instr.atom.type != GlobalAtomicType::S32, "type={}",
|
||||
static_cast<int>(instr.atom.type.Value()));
|
||||
|
||||
const auto [real_address, base_address, descriptor] =
|
||||
TrackGlobalMemory(bb, instr, true, true);
|
||||
if (!real_address || !base_address) {
|
||||
// Tracking failed, skip atomic.
|
||||
break;
|
||||
}
|
||||
|
||||
Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
|
||||
Node value = Operation(OperationCode::AtomicAdd, std::move(gmem), GetRegister(instr.gpr20));
|
||||
SetRegister(bb, instr.gpr0, std::move(value));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::ATOMS: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}",
|
||||
static_cast<int>(instr.atoms.operation.Value()));
|
||||
@@ -348,7 +393,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
Node memory = GetSharedMemory(std::move(address));
|
||||
Node data = GetRegister(instr.gpr20);
|
||||
|
||||
Node value = Operation(OperationCode::UAtomicAdd, std::move(memory), std::move(data));
|
||||
Node value = Operation(OperationCode::AtomicAdd, std::move(memory), std::move(data));
|
||||
SetRegister(bb, instr.gpr0, std::move(value));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -69,13 +69,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::MOV_SYS: {
|
||||
const Node value = [this, instr] {
|
||||
switch (instr.sys20) {
|
||||
case SystemVariable::LaneId:
|
||||
LOG_WARNING(HW_GPU, "MOV_SYS instruction with LaneId is incomplete");
|
||||
return Immediate(0U);
|
||||
case SystemVariable::InvocationId:
|
||||
return Operation(OperationCode::InvocationId);
|
||||
case SystemVariable::Ydirection:
|
||||
return Operation(OperationCode::YNegate);
|
||||
case SystemVariable::InvocationInfo:
|
||||
LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
|
||||
return Immediate(0u);
|
||||
return Immediate(0U);
|
||||
case SystemVariable::Tid: {
|
||||
Node value = Immediate(0);
|
||||
value = BitfieldInsert(value, Operation(OperationCode::LocalInvocationIdX), 0, 9);
|
||||
@@ -188,7 +191,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}",
|
||||
static_cast<u32>(cc));
|
||||
|
||||
if (disable_flow_stack) {
|
||||
if (decompiled) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -200,7 +203,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
const Tegra::Shader::ConditionCode cc = instr.flow_condition_code;
|
||||
UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}",
|
||||
static_cast<u32>(cc));
|
||||
if (disable_flow_stack) {
|
||||
if (decompiled) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,80 @@
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
using std::move;
|
||||
using Tegra::Shader::Instruction;
|
||||
using Tegra::Shader::OpCode;
|
||||
using Tegra::Shader::ShfType;
|
||||
using Tegra::Shader::ShfXmode;
|
||||
|
||||
namespace {
|
||||
|
||||
Node IsFull(Node shift) {
|
||||
return Operation(OperationCode::LogicalIEqual, move(shift), Immediate(32));
|
||||
}
|
||||
|
||||
Node Shift(OperationCode opcode, Node value, Node shift) {
|
||||
Node is_full = Operation(OperationCode::LogicalIEqual, shift, Immediate(32));
|
||||
Node shifted = Operation(opcode, move(value), shift);
|
||||
return Operation(OperationCode::Select, IsFull(move(shift)), Immediate(0), move(shifted));
|
||||
}
|
||||
|
||||
Node ClampShift(Node shift, s32 size = 32) {
|
||||
shift = Operation(OperationCode::IMax, move(shift), Immediate(0));
|
||||
return Operation(OperationCode::IMin, move(shift), Immediate(size));
|
||||
}
|
||||
|
||||
Node WrapShift(Node shift, s32 size = 32) {
|
||||
return Operation(OperationCode::UBitwiseAnd, move(shift), Immediate(size - 1));
|
||||
}
|
||||
|
||||
Node ShiftRight(Node low, Node high, Node shift, Node low_shift, ShfType type) {
|
||||
// These values are used when the shift value is less than 32
|
||||
Node less_low = Shift(OperationCode::ILogicalShiftRight, low, shift);
|
||||
Node less_high = Shift(OperationCode::ILogicalShiftLeft, high, low_shift);
|
||||
Node less = Operation(OperationCode::IBitwiseOr, move(less_high), move(less_low));
|
||||
|
||||
if (type == ShfType::Bits32) {
|
||||
// On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits
|
||||
return Operation(OperationCode::Select, IsFull(move(shift)), move(high), move(less));
|
||||
}
|
||||
|
||||
// And these when it's larger than or 32
|
||||
const bool is_signed = type == ShfType::S64;
|
||||
const auto opcode = SignedToUnsignedCode(OperationCode::IArithmeticShiftRight, is_signed);
|
||||
Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32));
|
||||
Node greater = Shift(opcode, high, move(reduced));
|
||||
|
||||
Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32));
|
||||
Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0));
|
||||
|
||||
Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater));
|
||||
return Operation(OperationCode::Select, move(is_zero), move(high), move(value));
|
||||
}
|
||||
|
||||
Node ShiftLeft(Node low, Node high, Node shift, Node low_shift, ShfType type) {
|
||||
// These values are used when the shift value is less than 32
|
||||
Node less_low = Operation(OperationCode::ILogicalShiftRight, low, low_shift);
|
||||
Node less_high = Operation(OperationCode::ILogicalShiftLeft, high, shift);
|
||||
Node less = Operation(OperationCode::IBitwiseOr, move(less_low), move(less_high));
|
||||
|
||||
if (type == ShfType::Bits32) {
|
||||
// On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits
|
||||
return Operation(OperationCode::Select, IsFull(move(shift)), move(low), move(less));
|
||||
}
|
||||
|
||||
// And these when it's larger than or 32
|
||||
Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32));
|
||||
Node greater = Shift(OperationCode::ILogicalShiftLeft, move(low), move(reduced));
|
||||
|
||||
Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32));
|
||||
Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0));
|
||||
|
||||
Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater));
|
||||
return Operation(OperationCode::Select, move(is_zero), move(high), move(value));
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
@@ -28,29 +100,48 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
}();
|
||||
|
||||
switch (opcode->get().GetId()) {
|
||||
switch (const auto opid = opcode->get().GetId(); opid) {
|
||||
case OpCode::Id::SHR_C:
|
||||
case OpCode::Id::SHR_R:
|
||||
case OpCode::Id::SHR_IMM: {
|
||||
if (instr.shr.wrap) {
|
||||
op_b = Operation(OperationCode::UBitwiseAnd, std::move(op_b), Immediate(0x1f));
|
||||
} else {
|
||||
op_b = Operation(OperationCode::IMax, std::move(op_b), Immediate(0));
|
||||
op_b = Operation(OperationCode::IMin, std::move(op_b), Immediate(31));
|
||||
}
|
||||
op_b = instr.shr.wrap ? WrapShift(move(op_b)) : ClampShift(move(op_b));
|
||||
|
||||
Node value = SignedOperation(OperationCode::IArithmeticShiftRight, instr.shift.is_signed,
|
||||
std::move(op_a), std::move(op_b));
|
||||
move(op_a), move(op_b));
|
||||
SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
|
||||
SetRegister(bb, instr.gpr0, std::move(value));
|
||||
SetRegister(bb, instr.gpr0, move(value));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SHL_C:
|
||||
case OpCode::Id::SHL_R:
|
||||
case OpCode::Id::SHL_IMM: {
|
||||
const Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b);
|
||||
Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b);
|
||||
SetInternalFlagsFromInteger(bb, value, instr.generates_cc);
|
||||
SetRegister(bb, instr.gpr0, value);
|
||||
SetRegister(bb, instr.gpr0, move(value));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SHF_RIGHT_R:
|
||||
case OpCode::Id::SHF_RIGHT_IMM:
|
||||
case OpCode::Id::SHF_LEFT_R:
|
||||
case OpCode::Id::SHF_LEFT_IMM: {
|
||||
UNIMPLEMENTED_IF(instr.generates_cc);
|
||||
UNIMPLEMENTED_IF_MSG(instr.shf.xmode != ShfXmode::None, "xmode={}",
|
||||
static_cast<int>(instr.shf.xmode.Value()));
|
||||
|
||||
if (instr.is_b_imm) {
|
||||
op_b = Immediate(static_cast<u32>(instr.shf.immediate));
|
||||
}
|
||||
const s32 size = instr.shf.type == ShfType::Bits32 ? 32 : 64;
|
||||
Node shift = instr.shf.wrap ? WrapShift(move(op_b), size) : ClampShift(move(op_b), size);
|
||||
|
||||
Node negated_shift = Operation(OperationCode::INegate, shift);
|
||||
Node low_shift = Operation(OperationCode::IAdd, move(negated_shift), Immediate(32));
|
||||
|
||||
const bool is_right = opid == OpCode::Id::SHF_RIGHT_R || opid == OpCode::Id::SHF_RIGHT_IMM;
|
||||
Node value = (is_right ? ShiftRight : ShiftLeft)(
|
||||
move(op_a), GetRegister(instr.gpr39), move(shift), move(low_shift), instr.shf.type);
|
||||
|
||||
SetRegister(bb, instr.gpr0, move(value));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -144,7 +144,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto coords_copy = coords;
|
||||
MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {}, {}, {}, component, element};
|
||||
MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {},
|
||||
{}, {}, component, element, {}};
|
||||
values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
|
||||
}
|
||||
|
||||
@@ -161,16 +162,16 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
case OpCode::Id::TXD: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.txd.UsesMiscMode(TextureMiscMode::AOFFI),
|
||||
"AOFFI is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(instr.txd.is_array != 0, "TXD Array is not implemented");
|
||||
|
||||
const bool is_array = instr.txd.is_array != 0;
|
||||
u64 base_reg = instr.gpr8.Value();
|
||||
const auto derivate_reg = instr.gpr20.Value();
|
||||
const auto texture_type = instr.txd.texture_type.Value();
|
||||
const auto coord_count = GetCoordCount(texture_type);
|
||||
|
||||
const Sampler* sampler = is_bindless
|
||||
? GetBindlessSampler(base_reg, {{texture_type, false, false}})
|
||||
: GetSampler(instr.sampler, {{texture_type, false, false}});
|
||||
Node index_var{};
|
||||
const Sampler* sampler =
|
||||
is_bindless ? GetBindlessSampler(base_reg, index_var, {{texture_type, is_array, false}})
|
||||
: GetSampler(instr.sampler, {{texture_type, is_array, false}});
|
||||
Node4 values;
|
||||
if (sampler == nullptr) {
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
@@ -179,6 +180,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
WriteTexInstructionFloat(bb, instr, values);
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_bindless) {
|
||||
base_reg++;
|
||||
}
|
||||
@@ -192,8 +194,15 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
derivates.push_back(GetRegister(derivate_reg + derivate + 1));
|
||||
}
|
||||
|
||||
Node array_node = {};
|
||||
if (is_array) {
|
||||
const Node info_reg = GetRegister(base_reg + coord_count);
|
||||
array_node = BitfieldExtract(info_reg, 0, 16);
|
||||
}
|
||||
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
MetaTexture meta{*sampler, {}, {}, {}, {}, derivates, {}, {}, {}, element};
|
||||
MetaTexture meta{*sampler, array_node, {}, {}, {}, derivates,
|
||||
{}, {}, {}, element, index_var};
|
||||
values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords);
|
||||
}
|
||||
|
||||
@@ -208,8 +217,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
// TODO: The new commits on the texture refactor, change the way samplers work.
|
||||
// Sadly, not all texture instructions specify the type of texture their sampler
|
||||
// uses. This must be fixed at a later instance.
|
||||
Node index_var{};
|
||||
const Sampler* sampler =
|
||||
is_bindless ? GetBindlessSampler(instr.gpr8) : GetSampler(instr.sampler);
|
||||
is_bindless ? GetBindlessSampler(instr.gpr8, index_var) : GetSampler(instr.sampler);
|
||||
|
||||
if (sampler == nullptr) {
|
||||
u32 indexer = 0;
|
||||
@@ -233,7 +243,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
if (!instr.txq.IsComponentEnabled(element)) {
|
||||
continue;
|
||||
}
|
||||
MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element};
|
||||
MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var};
|
||||
const Node value =
|
||||
Operation(OperationCode::TextureQueryDimensions, meta,
|
||||
GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0)));
|
||||
@@ -259,8 +269,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
|
||||
auto texture_type = instr.tmml.texture_type.Value();
|
||||
const bool is_array = instr.tmml.array != 0;
|
||||
Node index_var{};
|
||||
const Sampler* sampler =
|
||||
is_bindless ? GetBindlessSampler(instr.gpr20) : GetSampler(instr.sampler);
|
||||
is_bindless ? GetBindlessSampler(instr.gpr20, index_var) : GetSampler(instr.sampler);
|
||||
|
||||
if (sampler == nullptr) {
|
||||
u32 indexer = 0;
|
||||
@@ -302,7 +313,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
continue;
|
||||
}
|
||||
auto params = coords;
|
||||
MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element};
|
||||
MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var};
|
||||
const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params));
|
||||
SetTemporary(bb, indexer++, value);
|
||||
}
|
||||
@@ -376,37 +387,65 @@ const Sampler* ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
|
||||
// Otherwise create a new mapping for this sampler
|
||||
const auto next_index = static_cast<u32>(used_samplers.size());
|
||||
return &used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow,
|
||||
info.is_buffer);
|
||||
info.is_buffer, false);
|
||||
}
|
||||
|
||||
const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
|
||||
const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var,
|
||||
std::optional<SamplerInfo> sampler_info) {
|
||||
const Node sampler_register = GetRegister(reg);
|
||||
const auto [base_sampler, buffer, offset] =
|
||||
TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
|
||||
ASSERT(base_sampler != nullptr);
|
||||
if (base_sampler == nullptr) {
|
||||
const auto [base_node, tracked_sampler_info] =
|
||||
TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size()));
|
||||
ASSERT(base_node != nullptr);
|
||||
if (base_node == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto info = GetSamplerInfo(sampler_info, offset, buffer);
|
||||
if (const auto bindless_sampler_info =
|
||||
std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) {
|
||||
const u32 buffer = bindless_sampler_info->GetIndex();
|
||||
const u32 offset = bindless_sampler_info->GetOffset();
|
||||
const auto info = GetSamplerInfo(sampler_info, offset, buffer);
|
||||
|
||||
// If this sampler has already been used, return the existing mapping.
|
||||
const auto it =
|
||||
std::find_if(used_samplers.begin(), used_samplers.end(),
|
||||
[buffer = buffer, offset = offset](const Sampler& entry) {
|
||||
return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
|
||||
});
|
||||
if (it != used_samplers.end()) {
|
||||
ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
|
||||
it->IsShadow() == info.is_shadow);
|
||||
return &*it;
|
||||
// If this sampler has already been used, return the existing mapping.
|
||||
const auto it =
|
||||
std::find_if(used_samplers.begin(), used_samplers.end(),
|
||||
[buffer = buffer, offset = offset](const Sampler& entry) {
|
||||
return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
|
||||
});
|
||||
if (it != used_samplers.end()) {
|
||||
ASSERT(it->IsBindless() && it->GetType() == info.type &&
|
||||
it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow);
|
||||
return &*it;
|
||||
}
|
||||
|
||||
// Otherwise create a new mapping for this sampler
|
||||
const auto next_index = static_cast<u32>(used_samplers.size());
|
||||
return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array,
|
||||
info.is_shadow, info.is_buffer, false);
|
||||
} else if (const auto array_sampler_info =
|
||||
std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) {
|
||||
const u32 base_offset = array_sampler_info->GetBaseOffset() / 4;
|
||||
index_var = GetCustomVariable(array_sampler_info->GetIndexVar());
|
||||
const auto info = GetSamplerInfo(sampler_info, base_offset);
|
||||
|
||||
// If this sampler has already been used, return the existing mapping.
|
||||
const auto it = std::find_if(
|
||||
used_samplers.begin(), used_samplers.end(),
|
||||
[base_offset](const Sampler& entry) { return entry.GetOffset() == base_offset; });
|
||||
if (it != used_samplers.end()) {
|
||||
ASSERT(!it->IsBindless() && it->GetType() == info.type &&
|
||||
it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow &&
|
||||
it->IsBuffer() == info.is_buffer && it->IsIndexed());
|
||||
return &*it;
|
||||
}
|
||||
|
||||
uses_indexed_samplers = true;
|
||||
// Otherwise create a new mapping for this sampler
|
||||
const auto next_index = static_cast<u32>(used_samplers.size());
|
||||
return &used_samplers.emplace_back(next_index, base_offset, info.type, info.is_array,
|
||||
info.is_shadow, info.is_buffer, true);
|
||||
}
|
||||
|
||||
// Otherwise create a new mapping for this sampler
|
||||
const auto next_index = static_cast<u32>(used_samplers.size());
|
||||
return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array,
|
||||
info.is_shadow, info.is_buffer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) {
|
||||
@@ -492,8 +531,9 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
|
||||
"This method is not supported.");
|
||||
|
||||
const SamplerInfo info{texture_type, is_array, is_shadow, false};
|
||||
const Sampler* sampler =
|
||||
is_bindless ? GetBindlessSampler(*bindless_reg, info) : GetSampler(instr.sampler, info);
|
||||
Node index_var{};
|
||||
const Sampler* sampler = is_bindless ? GetBindlessSampler(*bindless_reg, index_var, info)
|
||||
: GetSampler(instr.sampler, info);
|
||||
Node4 values;
|
||||
if (sampler == nullptr) {
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
@@ -541,7 +581,8 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
|
||||
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto copy_coords = coords;
|
||||
MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, lod, {}, element};
|
||||
MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias,
|
||||
lod, {}, element, index_var};
|
||||
values[element] = Operation(read_method, meta, std::move(copy_coords));
|
||||
}
|
||||
|
||||
@@ -589,7 +630,7 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
|
||||
aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false);
|
||||
}
|
||||
|
||||
Node dc{};
|
||||
Node dc;
|
||||
if (depth_compare) {
|
||||
// Depth is always stored in the register signaled by gpr20 or in the next register if lod
|
||||
// or bias are used
|
||||
@@ -625,7 +666,7 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
|
||||
|
||||
const Node array = is_array ? GetRegister(array_register) : nullptr;
|
||||
|
||||
Node dc{};
|
||||
Node dc;
|
||||
if (depth_compare) {
|
||||
// Depth is always stored in the register signaled by gpr20 or in the next register if lod
|
||||
// or bias are used
|
||||
@@ -656,7 +697,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
|
||||
u64 parameter_register = instr.gpr20.Value();
|
||||
|
||||
const SamplerInfo info{texture_type, is_array, depth_compare, false};
|
||||
const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, info)
|
||||
Node index_var{};
|
||||
const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, index_var, info)
|
||||
: GetSampler(instr.sampler, info);
|
||||
Node4 values;
|
||||
if (sampler == nullptr) {
|
||||
@@ -685,7 +727,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto coords_copy = coords;
|
||||
MetaTexture meta{
|
||||
*sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element};
|
||||
*sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element,
|
||||
index_var};
|
||||
values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy));
|
||||
}
|
||||
|
||||
@@ -718,7 +761,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto coords_copy = coords;
|
||||
MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element};
|
||||
MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element, {}};
|
||||
values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
|
||||
}
|
||||
|
||||
@@ -768,7 +811,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
|
||||
Node4 values;
|
||||
for (u32 element = 0; element < values.size(); ++element) {
|
||||
auto coords_copy = coords;
|
||||
MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element};
|
||||
MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element, {}};
|
||||
values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
|
||||
}
|
||||
return values;
|
||||
|
||||
@@ -162,7 +162,7 @@ enum class OperationCode {
|
||||
AtomicImageXor, /// (MetaImage, int[N] coords) -> void
|
||||
AtomicImageExchange, /// (MetaImage, int[N] coords) -> void
|
||||
|
||||
UAtomicAdd, /// (smem, uint) -> uint
|
||||
AtomicAdd, /// (memory, {u}int) -> {u}int
|
||||
|
||||
Branch, /// (uint branch_target) -> void
|
||||
BranchIndirect, /// (uint branch_target) -> void
|
||||
@@ -212,6 +212,7 @@ enum class MetaStackClass {
|
||||
class OperationNode;
|
||||
class ConditionalNode;
|
||||
class GprNode;
|
||||
class CustomVarNode;
|
||||
class ImmediateNode;
|
||||
class InternalFlagNode;
|
||||
class PredicateNode;
|
||||
@@ -223,26 +224,32 @@ class SmemNode;
|
||||
class GmemNode;
|
||||
class CommentNode;
|
||||
|
||||
using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode,
|
||||
using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, CustomVarNode, ImmediateNode,
|
||||
InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode,
|
||||
LmemNode, SmemNode, GmemNode, CommentNode>;
|
||||
using Node = std::shared_ptr<NodeData>;
|
||||
using Node4 = std::array<Node, 4>;
|
||||
using NodeBlock = std::vector<Node>;
|
||||
|
||||
class BindlessSamplerNode;
|
||||
class ArraySamplerNode;
|
||||
|
||||
using TrackSamplerData = std::variant<BindlessSamplerNode, ArraySamplerNode>;
|
||||
using TrackSampler = std::shared_ptr<TrackSamplerData>;
|
||||
|
||||
class Sampler {
|
||||
public:
|
||||
/// This constructor is for bound samplers
|
||||
constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow, bool is_buffer)
|
||||
bool is_array, bool is_shadow, bool is_buffer, bool is_indexed)
|
||||
: index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow},
|
||||
is_buffer{is_buffer} {}
|
||||
is_buffer{is_buffer}, is_indexed{is_indexed} {}
|
||||
|
||||
/// This constructor is for bindless samplers
|
||||
constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type,
|
||||
bool is_array, bool is_shadow, bool is_buffer)
|
||||
bool is_array, bool is_shadow, bool is_buffer, bool is_indexed)
|
||||
: index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array},
|
||||
is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true} {}
|
||||
is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true}, is_indexed{is_indexed} {}
|
||||
|
||||
constexpr u32 GetIndex() const {
|
||||
return index;
|
||||
@@ -276,16 +283,72 @@ public:
|
||||
return is_bindless;
|
||||
}
|
||||
|
||||
constexpr bool IsIndexed() const {
|
||||
return is_indexed;
|
||||
}
|
||||
|
||||
constexpr u32 Size() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
constexpr void SetSize(u32 new_size) {
|
||||
size = new_size;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 index{}; ///< Emulated index given for the this sampler.
|
||||
u32 offset{}; ///< Offset in the const buffer from where the sampler is being read.
|
||||
u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers).
|
||||
u32 size{}; ///< Size of the sampler if indexed.
|
||||
|
||||
Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc)
|
||||
bool is_array{}; ///< Whether the texture is being sampled as an array texture or not.
|
||||
bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not.
|
||||
bool is_buffer{}; ///< Whether the texture is a texture buffer without sampler.
|
||||
bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
|
||||
bool is_indexed{}; ///< Whether this sampler is an indexed array of textures.
|
||||
};
|
||||
|
||||
/// Represents a tracked bindless sampler into a direct const buffer
|
||||
class ArraySamplerNode final {
|
||||
public:
|
||||
explicit ArraySamplerNode(u32 index, u32 base_offset, u32 bindless_var)
|
||||
: index{index}, base_offset{base_offset}, bindless_var{bindless_var} {}
|
||||
|
||||
constexpr u32 GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
constexpr u32 GetBaseOffset() const {
|
||||
return base_offset;
|
||||
}
|
||||
|
||||
constexpr u32 GetIndexVar() const {
|
||||
return bindless_var;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 index;
|
||||
u32 base_offset;
|
||||
u32 bindless_var;
|
||||
};
|
||||
|
||||
/// Represents a tracked bindless sampler into a direct const buffer
|
||||
class BindlessSamplerNode final {
|
||||
public:
|
||||
explicit BindlessSamplerNode(u32 index, u32 offset) : index{index}, offset{offset} {}
|
||||
|
||||
constexpr u32 GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
constexpr u32 GetOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 index;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
class Image final {
|
||||
@@ -380,8 +443,9 @@ struct MetaTexture {
|
||||
std::vector<Node> derivates;
|
||||
Node bias;
|
||||
Node lod;
|
||||
Node component{};
|
||||
Node component;
|
||||
u32 element{};
|
||||
Node index;
|
||||
};
|
||||
|
||||
struct MetaImage {
|
||||
@@ -488,6 +552,19 @@ private:
|
||||
Tegra::Shader::Register index{};
|
||||
};
|
||||
|
||||
/// A custom variable
|
||||
class CustomVarNode final {
|
||||
public:
|
||||
explicit constexpr CustomVarNode(u32 index) : index{index} {}
|
||||
|
||||
constexpr u32 GetIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 index{};
|
||||
};
|
||||
|
||||
/// A 32-bits value that represents an immediate value
|
||||
class ImmediateNode final {
|
||||
public:
|
||||
|
||||
@@ -45,6 +45,12 @@ Node MakeNode(Args&&... args) {
|
||||
return std::make_shared<NodeData>(T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
TrackSampler MakeTrackSampler(Args&&... args) {
|
||||
static_assert(std::is_convertible_v<T, TrackSamplerData>);
|
||||
return std::make_shared<TrackSamplerData>(T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Node Operation(OperationCode code, Args&&... args) {
|
||||
if constexpr (sizeof...(args) == 0) {
|
||||
|
||||
@@ -27,6 +27,7 @@ ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSet
|
||||
ConstBufferLocker& locker)
|
||||
: program_code{program_code}, main_offset{main_offset}, settings{settings}, locker{locker} {
|
||||
Decode();
|
||||
PostDecode();
|
||||
}
|
||||
|
||||
ShaderIR::~ShaderIR() = default;
|
||||
@@ -38,6 +39,10 @@ Node ShaderIR::GetRegister(Register reg) {
|
||||
return MakeNode<GprNode>(reg);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetCustomVariable(u32 id) {
|
||||
return MakeNode<CustomVarNode>(id);
|
||||
}
|
||||
|
||||
Node ShaderIR::GetImmediate19(Instruction instr) {
|
||||
return Immediate(instr.alu.GetImm20_19());
|
||||
}
|
||||
@@ -452,4 +457,8 @@ std::size_t ShaderIR::DeclareAmend(Node new_amend) {
|
||||
return id;
|
||||
}
|
||||
|
||||
u32 ShaderIR::NewCustomVariable() {
|
||||
return num_custom_variables++;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon::Shader
|
||||
|
||||
@@ -180,6 +180,10 @@ public:
|
||||
return amend_code[index];
|
||||
}
|
||||
|
||||
u32 GetNumCustomVariables() const {
|
||||
return num_custom_variables;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ASTDecoder;
|
||||
|
||||
@@ -191,6 +195,7 @@ private:
|
||||
};
|
||||
|
||||
void Decode();
|
||||
void PostDecode();
|
||||
|
||||
NodeBlock DecodeRange(u32 begin, u32 end);
|
||||
void DecodeRangeInner(NodeBlock& bb, u32 begin, u32 end);
|
||||
@@ -235,6 +240,8 @@ private:
|
||||
|
||||
/// Generates a node for a passed register.
|
||||
Node GetRegister(Tegra::Shader::Register reg);
|
||||
/// Generates a node for a custom variable
|
||||
Node GetCustomVariable(u32 id);
|
||||
/// Generates a node representing a 19-bit immediate value
|
||||
Node GetImmediate19(Tegra::Shader::Instruction instr);
|
||||
/// Generates a node representing a 32-bit immediate value
|
||||
@@ -321,7 +328,7 @@ private:
|
||||
std::optional<SamplerInfo> sampler_info = std::nullopt);
|
||||
|
||||
/// Accesses a texture sampler for a bindless texture.
|
||||
const Sampler* GetBindlessSampler(Tegra::Shader::Register reg,
|
||||
const Sampler* GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var,
|
||||
std::optional<SamplerInfo> sampler_info = std::nullopt);
|
||||
|
||||
/// Accesses an image.
|
||||
@@ -387,6 +394,9 @@ private:
|
||||
|
||||
std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const;
|
||||
|
||||
std::tuple<Node, TrackSampler> TrackBindlessSampler(Node tracked, const NodeBlock& code,
|
||||
s64 cursor);
|
||||
|
||||
std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const;
|
||||
|
||||
std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code,
|
||||
@@ -399,6 +409,8 @@ private:
|
||||
/// Register new amending code and obtain the reference id.
|
||||
std::size_t DeclareAmend(Node new_amend);
|
||||
|
||||
u32 NewCustomVariable();
|
||||
|
||||
const ProgramCode& program_code;
|
||||
const u32 main_offset;
|
||||
const CompilerSettings settings;
|
||||
@@ -414,6 +426,7 @@ private:
|
||||
NodeBlock global_code;
|
||||
ASTManager program_manager{true, true};
|
||||
std::vector<Node> amend_code;
|
||||
u32 num_custom_variables{};
|
||||
|
||||
std::set<u32> used_registers;
|
||||
std::set<Tegra::Shader::Pred> used_predicates;
|
||||
@@ -431,6 +444,7 @@ private:
|
||||
bool uses_instance_id{};
|
||||
bool uses_vertex_id{};
|
||||
bool uses_warps{};
|
||||
bool uses_indexed_samplers{};
|
||||
|
||||
Tegra::Shader::Header header;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/shader/node.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
@@ -35,8 +36,113 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor,
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::pair<Node, Node>> DecoupleIndirectRead(const OperationNode& operation) {
|
||||
if (operation.GetCode() != OperationCode::UAdd) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Node gpr;
|
||||
Node offset;
|
||||
ASSERT(operation.GetOperandsCount() == 2);
|
||||
for (std::size_t i = 0; i < operation.GetOperandsCount(); i++) {
|
||||
Node operand = operation[i];
|
||||
if (std::holds_alternative<ImmediateNode>(*operand)) {
|
||||
offset = operation[i];
|
||||
} else if (std::holds_alternative<GprNode>(*operand)) {
|
||||
gpr = operation[i];
|
||||
}
|
||||
}
|
||||
if (offset && gpr) {
|
||||
return std::make_pair(gpr, offset);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool AmendNodeCv(std::size_t amend_index, Node node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
||||
operation->SetAmendIndex(amend_index);
|
||||
return true;
|
||||
} else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
|
||||
conditional->SetAmendIndex(amend_index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code,
|
||||
s64 cursor) {
|
||||
if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
|
||||
// Constant buffer found, test if it's an immediate
|
||||
const auto offset = cbuf->GetOffset();
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
|
||||
auto track =
|
||||
MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue());
|
||||
return {tracked, track};
|
||||
} else if (const auto operation = std::get_if<OperationNode>(&*offset)) {
|
||||
auto bound_buffer = locker.ObtainBoundBuffer();
|
||||
if (!bound_buffer) {
|
||||
return {};
|
||||
}
|
||||
if (*bound_buffer != cbuf->GetIndex()) {
|
||||
return {};
|
||||
}
|
||||
auto pair = DecoupleIndirectRead(*operation);
|
||||
if (!pair) {
|
||||
return {};
|
||||
}
|
||||
auto [gpr, base_offset] = *pair;
|
||||
const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset);
|
||||
auto gpu_driver = locker.AccessGuestDriverProfile();
|
||||
if (gpu_driver == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const u32 bindless_cv = NewCustomVariable();
|
||||
const Node op = Operation(OperationCode::UDiv, NO_PRECISE, gpr,
|
||||
Immediate(gpu_driver->GetTextureHandlerSize()));
|
||||
|
||||
const Node cv_node = GetCustomVariable(bindless_cv);
|
||||
Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op));
|
||||
const std::size_t amend_index = DeclareAmend(amend_op);
|
||||
AmendNodeCv(amend_index, code[cursor]);
|
||||
// TODO Implement Bindless Index custom variable
|
||||
auto track = MakeTrackSampler<ArraySamplerNode>(cbuf->GetIndex(),
|
||||
offset_inm->GetValue(), bindless_cv);
|
||||
return {tracked, track};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
if (const auto gpr = std::get_if<GprNode>(&*tracked)) {
|
||||
if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) {
|
||||
return {};
|
||||
}
|
||||
// Reduce the cursor in one to avoid infinite loops when the instruction sets the same
|
||||
// register that it uses as operand
|
||||
const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1);
|
||||
if (!source) {
|
||||
return {};
|
||||
}
|
||||
return TrackBindlessSampler(source, code, new_cursor);
|
||||
}
|
||||
if (const auto operation = std::get_if<OperationNode>(&*tracked)) {
|
||||
for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) {
|
||||
if (auto found = TrackBindlessSampler((*operation)[i - 1], code, cursor);
|
||||
std::get<0>(found)) {
|
||||
// Cbuf found in operand.
|
||||
return found;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) {
|
||||
const auto& conditional_code = conditional->GetCode();
|
||||
return TrackBindlessSampler(tracked, conditional_code,
|
||||
static_cast<s64>(conditional_code.size()));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code,
|
||||
s64 cursor) const {
|
||||
if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user