Compare commits
33 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d027850f33 | ||
|
|
a7beabb68f | ||
|
|
252415a163 | ||
|
|
c29584a090 | ||
|
|
f92cbc5501 | ||
|
|
8299f1ceef | ||
|
|
788d57d723 | ||
|
|
e651e54b85 | ||
|
|
9f0162e4b5 | ||
|
|
270177f38a | ||
|
|
b35449c85d | ||
|
|
b11aeced18 | ||
|
|
91f79225e7 | ||
|
|
c457e47297 | ||
|
|
8178fe8960 | ||
|
|
283f3253bc | ||
|
|
bea6327d74 | ||
|
|
abae795986 | ||
|
|
acfb0b4852 | ||
|
|
f55f6ff9bb | ||
|
|
d17dfa6104 | ||
|
|
f68bb4f55e | ||
|
|
2a822f3378 | ||
|
|
05df4a8c94 | ||
|
|
2b1d66eda3 | ||
|
|
dfd998216c | ||
|
|
a104b985a8 | ||
|
|
f64adcfc37 | ||
|
|
1690f1adba | ||
|
|
d0e4f1c6f4 | ||
|
|
a31ed02ae4 | ||
|
|
5a7eecc3ad | ||
|
|
a1667a7b46 |
@@ -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
|
||||
|
||||
222
dist/qt_themes/qdarkstyle/style.qss
vendored
222
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,14 @@ 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -259,7 +259,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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -154,6 +154,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();
|
||||
|
||||
@@ -1019,7 +1019,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 {};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
@@ -542,11 +542,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);
|
||||
@@ -852,6 +851,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;
|
||||
@@ -1889,19 +1897,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) {
|
||||
|
||||
@@ -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>);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -161,16 +161,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}});
|
||||
const Sampler* sampler =
|
||||
is_bindless ? GetBindlessSampler(base_reg, {{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 +179,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
|
||||
WriteTexInstructionFloat(bb, instr, values);
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_bindless) {
|
||||
base_reg++;
|
||||
}
|
||||
@@ -192,8 +193,14 @@ 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};
|
||||
values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords);
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ std::vector<CopyParams> SurfaceBaseImpl::BreakDownLayered(const SurfaceParams& i
|
||||
for (u32 level = 0; level < mipmaps; level++) {
|
||||
const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level);
|
||||
const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level);
|
||||
result.emplace_back(width, height, layer, level);
|
||||
result.emplace_back(0, 0, layer, 0, 0, layer, level, level, width, height, 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -3,19 +3,32 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <memory>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "video_core/gpu_asynch.h"
|
||||
#include "video_core/gpu_synch.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
#ifdef HAS_VULKAN
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#endif
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
||||
Core::System& system) {
|
||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
|
||||
switch (Settings::values.renderer_backend) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
|
||||
#ifdef HAS_VULKAN
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
|
||||
#endif
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
|
||||
|
||||
@@ -117,6 +117,7 @@ bool TelemetryJson::SubmitTestcase() {
|
||||
impl->SerializeSection(Telemetry::FieldType::Session, "Session");
|
||||
impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
|
||||
impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
|
||||
impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
|
||||
|
||||
auto content = impl->TopSection().dump();
|
||||
Client client(impl->host, impl->username, impl->token);
|
||||
|
||||
@@ -36,9 +36,6 @@ add_executable(yuzu
|
||||
configuration/configure_filesystem.cpp
|
||||
configuration/configure_filesystem.h
|
||||
configuration/configure_filesystem.ui
|
||||
configuration/configure_gamelist.cpp
|
||||
configuration/configure_gamelist.h
|
||||
configuration/configure_gamelist.ui
|
||||
configuration/configure_general.cpp
|
||||
configuration/configure_general.h
|
||||
configuration/configure_general.ui
|
||||
@@ -75,6 +72,9 @@ add_executable(yuzu
|
||||
configuration/configure_touchscreen_advanced.cpp
|
||||
configuration/configure_touchscreen_advanced.h
|
||||
configuration/configure_touchscreen_advanced.ui
|
||||
configuration/configure_ui.cpp
|
||||
configuration/configure_ui.h
|
||||
configuration/configure_ui.ui
|
||||
configuration/configure_web.cpp
|
||||
configuration/configure_web.h
|
||||
configuration/configure_web.ui
|
||||
@@ -200,3 +200,8 @@ if (MSVC)
|
||||
copy_yuzu_SDL_deps(yuzu)
|
||||
copy_yuzu_unicorn_deps(yuzu)
|
||||
endif()
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
|
||||
target_compile_definitions(yuzu PRIVATE HAS_VULKAN)
|
||||
endif()
|
||||
|
||||
@@ -2,19 +2,30 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QHBoxLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLWindow>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
#include <QStringList>
|
||||
#include <QWindow>
|
||||
#ifdef HAS_VULKAN
|
||||
#include <QVulkanWindow>
|
||||
#endif
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "core/frontend/scope_acquire_window_context.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
@@ -114,19 +125,10 @@ private:
|
||||
QOpenGLContext context;
|
||||
};
|
||||
|
||||
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
|
||||
// context.
|
||||
// The corresponding functionality is handled in EmuThread instead
|
||||
class GGLWidgetInternal : public QOpenGLWindow {
|
||||
class GWidgetInternal : public QWindow {
|
||||
public:
|
||||
GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
|
||||
: QOpenGLWindow(shared_context), parent(parent) {}
|
||||
|
||||
void paintEvent(QPaintEvent* ev) override {
|
||||
if (do_painting) {
|
||||
QPainter painter(this);
|
||||
}
|
||||
}
|
||||
GWidgetInternal(GRenderWindow* parent) : parent(parent) {}
|
||||
virtual ~GWidgetInternal() = default;
|
||||
|
||||
void resizeEvent(QResizeEvent* ev) override {
|
||||
parent->OnClientAreaResized(ev->size().width(), ev->size().height());
|
||||
@@ -182,11 +184,47 @@ public:
|
||||
do_painting = true;
|
||||
}
|
||||
|
||||
std::pair<unsigned, unsigned> GetSize() const {
|
||||
return std::make_pair(width(), height());
|
||||
}
|
||||
|
||||
protected:
|
||||
bool IsPaintingEnabled() const {
|
||||
return do_painting;
|
||||
}
|
||||
|
||||
private:
|
||||
GRenderWindow* parent;
|
||||
bool do_painting;
|
||||
bool do_painting = false;
|
||||
};
|
||||
|
||||
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
|
||||
// context.
|
||||
// The corresponding functionality is handled in EmuThread instead
|
||||
class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow {
|
||||
public:
|
||||
GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
|
||||
: GWidgetInternal(parent), QOpenGLWindow(shared_context) {}
|
||||
~GGLWidgetInternal() override = default;
|
||||
|
||||
void paintEvent(QPaintEvent* ev) override {
|
||||
if (IsPaintingEnabled()) {
|
||||
QPainter painter(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef HAS_VULKAN
|
||||
class GVKWidgetInternal final : public GWidgetInternal {
|
||||
public:
|
||||
GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) {
|
||||
setSurfaceType(QSurface::SurfaceType::VulkanSurface);
|
||||
setVulkanInstance(instance);
|
||||
}
|
||||
~GVKWidgetInternal() override = default;
|
||||
};
|
||||
#endif
|
||||
|
||||
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
|
||||
: QWidget(parent), emu_thread(emu_thread) {
|
||||
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
||||
@@ -201,9 +239,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
|
||||
|
||||
GRenderWindow::~GRenderWindow() {
|
||||
InputCommon::Shutdown();
|
||||
|
||||
// Avoid an unordered destruction that generates a segfault
|
||||
delete child;
|
||||
}
|
||||
|
||||
void GRenderWindow::moveContext() {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
DoneCurrent();
|
||||
|
||||
// If the thread started running, move the GL Context to the new thread. Otherwise, move it
|
||||
@@ -215,8 +259,9 @@ void GRenderWindow::moveContext() {
|
||||
}
|
||||
|
||||
void GRenderWindow::SwapBuffers() {
|
||||
context->swapBuffers(child);
|
||||
|
||||
if (context) {
|
||||
context->swapBuffers(child);
|
||||
}
|
||||
if (!first_frame) {
|
||||
first_frame = true;
|
||||
emit FirstFrameDisplayed();
|
||||
@@ -224,15 +269,38 @@ void GRenderWindow::SwapBuffers() {
|
||||
}
|
||||
|
||||
void GRenderWindow::MakeCurrent() {
|
||||
context->makeCurrent(child);
|
||||
if (context) {
|
||||
context->makeCurrent(child);
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::DoneCurrent() {
|
||||
context->doneCurrent();
|
||||
if (context) {
|
||||
context->doneCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::PollEvents() {}
|
||||
|
||||
bool GRenderWindow::IsShown() const {
|
||||
return !isMinimized();
|
||||
}
|
||||
|
||||
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const {
|
||||
#ifdef HAS_VULKAN
|
||||
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
|
||||
const VkInstance instance_copy = vk_instance->vkInstance();
|
||||
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child);
|
||||
|
||||
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
|
||||
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
|
||||
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
|
||||
#else
|
||||
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
|
||||
#endif
|
||||
}
|
||||
|
||||
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
|
||||
//
|
||||
// Older versions get the window size (density independent pixels),
|
||||
@@ -241,10 +309,9 @@ void GRenderWindow::PollEvents() {}
|
||||
void GRenderWindow::OnFramebufferSizeChanged() {
|
||||
// Screen changes potentially incur a change in screen DPI, hence we should update the
|
||||
// framebuffer size
|
||||
const qreal pixel_ratio = GetWindowPixelRatio();
|
||||
const u32 width = child->QPaintDevice::width() * pixel_ratio;
|
||||
const u32 height = child->QPaintDevice::height() * pixel_ratio;
|
||||
UpdateCurrentFramebufferLayout(width, height);
|
||||
const qreal pixelRatio{GetWindowPixelRatio()};
|
||||
const auto size{child->GetSize()};
|
||||
UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio);
|
||||
}
|
||||
|
||||
void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
|
||||
@@ -290,7 +357,7 @@ qreal GRenderWindow::GetWindowPixelRatio() const {
|
||||
}
|
||||
|
||||
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
||||
const qreal pixel_ratio = GetWindowPixelRatio();
|
||||
const qreal pixel_ratio{GetWindowPixelRatio()};
|
||||
return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||
static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||
}
|
||||
@@ -356,50 +423,46 @@ std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedCont
|
||||
return std::make_unique<GGLContext>(context.get());
|
||||
}
|
||||
|
||||
void GRenderWindow::InitRenderTarget() {
|
||||
bool GRenderWindow::InitRenderTarget() {
|
||||
shared_context.reset();
|
||||
context.reset();
|
||||
|
||||
delete child;
|
||||
child = nullptr;
|
||||
|
||||
delete container;
|
||||
container = nullptr;
|
||||
|
||||
delete layout();
|
||||
if (child) {
|
||||
delete child;
|
||||
}
|
||||
if (container) {
|
||||
delete container;
|
||||
}
|
||||
if (layout()) {
|
||||
delete layout();
|
||||
}
|
||||
|
||||
first_frame = false;
|
||||
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setVersion(4, 3);
|
||||
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
|
||||
fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
shared_context = std::make_unique<QOpenGLContext>();
|
||||
shared_context->setFormat(fmt);
|
||||
shared_context->create();
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setShareContext(shared_context.get());
|
||||
context->setFormat(fmt);
|
||||
context->create();
|
||||
fmt.setSwapInterval(0);
|
||||
switch (Settings::values.renderer_backend) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
if (!InitializeOpenGL()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
if (!InitializeVulkan()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
child = new GGLWidgetInternal(this, shared_context.get());
|
||||
container = QWidget::createWindowContainer(child, this);
|
||||
|
||||
QBoxLayout* layout = new QHBoxLayout(this);
|
||||
|
||||
layout->addWidget(container);
|
||||
layout->setMargin(0);
|
||||
setLayout(layout);
|
||||
|
||||
// Reset minimum size to avoid unwanted resizes when this function is called for a second time.
|
||||
// Reset minimum required size to avoid resizing issues on the main window after restarting.
|
||||
setMinimumSize(1, 1);
|
||||
|
||||
// Show causes the window to actually be created and the OpenGL context as well, but we don't
|
||||
// want the widget to be shown yet, so immediately hide it.
|
||||
// Show causes the window to actually be created and the gl context as well, but we don't want
|
||||
// the widget to be shown yet, so immediately hide it.
|
||||
show();
|
||||
hide();
|
||||
|
||||
@@ -410,9 +473,17 @@ void GRenderWindow::InitRenderTarget() {
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
|
||||
OnFramebufferSizeChanged();
|
||||
NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height()));
|
||||
NotifyClientAreaSizeChanged(child->GetSize());
|
||||
|
||||
BackupGeometry();
|
||||
|
||||
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
|
||||
if (!LoadOpenGL()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
|
||||
@@ -441,6 +512,113 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
|
||||
setMinimumSize(minimal_size.first, minimal_size.second);
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitializeOpenGL() {
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setVersion(4, 3);
|
||||
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
|
||||
fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
shared_context = std::make_unique<QOpenGLContext>();
|
||||
shared_context->setFormat(fmt);
|
||||
shared_context->create();
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setShareContext(shared_context.get());
|
||||
context->setFormat(fmt);
|
||||
context->create();
|
||||
fmt.setSwapInterval(false);
|
||||
|
||||
child = new GGLWidgetInternal(this, shared_context.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitializeVulkan() {
|
||||
#ifdef HAS_VULKAN
|
||||
vk_instance = std::make_unique<QVulkanInstance>();
|
||||
vk_instance->setApiVersion(QVersionNumber(1, 1, 0));
|
||||
vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
|
||||
if (Settings::values.renderer_debug) {
|
||||
const auto supported_layers{vk_instance->supportedLayers()};
|
||||
const bool found =
|
||||
std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) {
|
||||
constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation";
|
||||
return layer.name == searched_layer;
|
||||
});
|
||||
if (found) {
|
||||
vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
|
||||
vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
if (!vk_instance->create()) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Error while initializing Vulkan 1.1!"),
|
||||
tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the "
|
||||
"latest graphics drivers."));
|
||||
return false;
|
||||
}
|
||||
|
||||
child = new GVKWidgetInternal(this, vk_instance.get());
|
||||
return true;
|
||||
#else
|
||||
QMessageBox::critical(this, tr("Vulkan not available!"),
|
||||
tr("yuzu has not been compiled with Vulkan support."));
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GRenderWindow::LoadOpenGL() {
|
||||
Core::Frontend::ScopeAcquireWindowContext acquire_context{*this};
|
||||
if (!gladLoadGL()) {
|
||||
QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
|
||||
tr("Your GPU may not support OpenGL 4.3, or you do not have the "
|
||||
"latest graphics driver."));
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
|
||||
if (!unsupported_gl_extensions.empty()) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Error while initializing OpenGL!"),
|
||||
tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
|
||||
"have the latest graphics driver.<br><br>Unsupported extensions:<br>") +
|
||||
unsupported_gl_extensions.join(QStringLiteral("<br>")));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
|
||||
QStringList unsupported_ext;
|
||||
|
||||
if (!GLAD_GL_ARB_buffer_storage)
|
||||
unsupported_ext.append(QStringLiteral("ARB_buffer_storage"));
|
||||
if (!GLAD_GL_ARB_direct_state_access)
|
||||
unsupported_ext.append(QStringLiteral("ARB_direct_state_access"));
|
||||
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
|
||||
unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev"));
|
||||
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
|
||||
unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge"));
|
||||
if (!GLAD_GL_ARB_multi_bind)
|
||||
unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
|
||||
if (!GLAD_GL_ARB_clip_control)
|
||||
unsupported_ext.append(QStringLiteral("ARB_clip_control"));
|
||||
|
||||
// Extensions required to support some texture formats.
|
||||
if (!GLAD_GL_EXT_texture_compression_s3tc)
|
||||
unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
|
||||
if (!GLAD_GL_ARB_texture_compression_rgtc)
|
||||
unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc"));
|
||||
if (!GLAD_GL_ARB_depth_buffer_float)
|
||||
unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
|
||||
|
||||
for (const QString& ext : unsupported_ext)
|
||||
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
|
||||
|
||||
return unsupported_ext;
|
||||
}
|
||||
|
||||
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
|
||||
this->emu_thread = emu_thread;
|
||||
child->DisablePainting();
|
||||
|
||||
@@ -7,17 +7,28 @@
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include <QImage>
|
||||
#include <QThread>
|
||||
#include <QWidget>
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
|
||||
class QKeyEvent;
|
||||
class QScreen;
|
||||
class QTouchEvent;
|
||||
class QStringList;
|
||||
class QSurface;
|
||||
class QOpenGLContext;
|
||||
#ifdef HAS_VULKAN
|
||||
class QVulkanInstance;
|
||||
#endif
|
||||
|
||||
class GWidgetInternal;
|
||||
class GGLWidgetInternal;
|
||||
class GVKWidgetInternal;
|
||||
class GMainWindow;
|
||||
class GRenderWindow;
|
||||
class QSurface;
|
||||
@@ -123,6 +134,9 @@ public:
|
||||
void MakeCurrent() override;
|
||||
void DoneCurrent() override;
|
||||
void PollEvents() override;
|
||||
bool IsShown() const override;
|
||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const override;
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
void ForwardKeyPressEvent(QKeyEvent* event);
|
||||
@@ -142,7 +156,7 @@ public:
|
||||
|
||||
void OnClientAreaResized(u32 width, u32 height);
|
||||
|
||||
void InitRenderTarget();
|
||||
bool InitRenderTarget();
|
||||
|
||||
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
|
||||
|
||||
@@ -165,10 +179,13 @@ private:
|
||||
|
||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||
|
||||
QWidget* container = nullptr;
|
||||
GGLWidgetInternal* child = nullptr;
|
||||
bool InitializeOpenGL();
|
||||
bool InitializeVulkan();
|
||||
bool LoadOpenGL();
|
||||
QStringList GetUnsupportedGLExtensions() const;
|
||||
|
||||
QByteArray geometry;
|
||||
QWidget* container = nullptr;
|
||||
GWidgetInternal* child = nullptr;
|
||||
|
||||
EmuThread* emu_thread;
|
||||
// Context that backs the GGLWidgetInternal (and will be used by core to render)
|
||||
@@ -177,9 +194,14 @@ private:
|
||||
// current
|
||||
std::unique_ptr<QOpenGLContext> shared_context;
|
||||
|
||||
#ifdef HAS_VULKAN
|
||||
std::unique_ptr<QVulkanInstance> vk_instance;
|
||||
#endif
|
||||
|
||||
/// Temporary storage of the screenshot taken
|
||||
QImage screenshot_image;
|
||||
|
||||
QByteArray geometry;
|
||||
bool first_frame = false;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -624,6 +624,10 @@ void Config::ReadPathValues() {
|
||||
void Config::ReadRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
Settings::values.renderer_backend =
|
||||
static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt());
|
||||
Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool();
|
||||
Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt();
|
||||
Settings::values.resolution_factor =
|
||||
ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat();
|
||||
Settings::values.use_frame_limit =
|
||||
@@ -1056,6 +1060,9 @@ void Config::SavePathValues() {
|
||||
void Config::SaveRendererValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Renderer"));
|
||||
|
||||
WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0);
|
||||
WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false);
|
||||
WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0);
|
||||
WriteSetting(QStringLiteral("resolution_factor"),
|
||||
static_cast<double>(Settings::values.resolution_factor), 1.0);
|
||||
WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true);
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<string>General</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="ConfigureGameList" name="gameListTab">
|
||||
<widget class="ConfigureUi" name="uiTab">
|
||||
<attribute name="title">
|
||||
<string>Game List</string>
|
||||
</attribute>
|
||||
@@ -166,9 +166,9 @@
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ConfigureGameList</class>
|
||||
<class>ConfigureUi</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>configuration/configure_gamelist.h</header>
|
||||
<header>configuration/configure_ui.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
|
||||
@@ -36,6 +36,8 @@ void ConfigureDebug::SetConfiguration() {
|
||||
ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
|
||||
ui->reporting_services->setChecked(Settings::values.reporting_services);
|
||||
ui->quest_flag->setChecked(Settings::values.quest_flag);
|
||||
ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
|
||||
}
|
||||
|
||||
void ConfigureDebug::ApplyConfiguration() {
|
||||
@@ -46,6 +48,7 @@ void ConfigureDebug::ApplyConfiguration() {
|
||||
Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
|
||||
Settings::values.reporting_services = ui->reporting_services->isChecked();
|
||||
Settings::values.quest_flag = ui->quest_flag->isChecked();
|
||||
Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
|
||||
Debugger::ToggleConsole();
|
||||
Log::Filter filter;
|
||||
filter.ParseFilterString(Settings::values.log_filter);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>474</height>
|
||||
<height>467</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -103,44 +103,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="reporting_services">
|
||||
<property name="text">
|
||||
<string>Enable Verbose Reporting Services</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>This will be reset automatically when yuzu closes.</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
<string>Advanced</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="quest_flag">
|
||||
<property name="text">
|
||||
<string>Kiosk (Quest) Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -167,6 +129,95 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>Graphics</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enable_graphics_debugging">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>When checked, the graphics API enters in a slower debugging mode</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enable Graphics Debugging</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
<string>Dump</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="dump_decompressed_nso">
|
||||
<property name="whatsThis">
|
||||
<string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump Decompressed NSOs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="dump_exefs">
|
||||
<property name="whatsThis">
|
||||
<string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Dump ExeFS</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="reporting_services">
|
||||
<property name="text">
|
||||
<string>Enable Verbose Reporting Services</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<italic>true</italic>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>This will be reset automatically when yuzu closes.</string>
|
||||
</property>
|
||||
<property name="indent">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_6">
|
||||
<property name="title">
|
||||
<string>Advanced</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="quest_flag">
|
||||
<property name="text">
|
||||
<string>Kiosk (Quest) Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -185,6 +236,19 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>toggle_gdbstub</tabstop>
|
||||
<tabstop>gdbport_spinbox</tabstop>
|
||||
<tabstop>log_filter_edit</tabstop>
|
||||
<tabstop>toggle_console</tabstop>
|
||||
<tabstop>open_log_button</tabstop>
|
||||
<tabstop>homebrew_args_edit</tabstop>
|
||||
<tabstop>enable_graphics_debugging</tabstop>
|
||||
<tabstop>dump_decompressed_nso</tabstop>
|
||||
<tabstop>dump_exefs</tabstop>
|
||||
<tabstop>reporting_services</tabstop>
|
||||
<tabstop>quest_flag</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
||||
@@ -34,7 +34,7 @@ void ConfigureDialog::SetConfiguration() {}
|
||||
|
||||
void ConfigureDialog::ApplyConfiguration() {
|
||||
ui->generalTab->ApplyConfiguration();
|
||||
ui->gameListTab->ApplyConfiguration();
|
||||
ui->uiTab->ApplyConfiguration();
|
||||
ui->systemTab->ApplyConfiguration();
|
||||
ui->profileManagerTab->ApplyConfiguration();
|
||||
ui->filesystemTab->applyConfiguration();
|
||||
@@ -74,7 +74,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
|
||||
|
||||
void ConfigureDialog::PopulateSelectionList() {
|
||||
const std::array<std::pair<QString, QList<QWidget*>>, 5> items{
|
||||
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}},
|
||||
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
|
||||
{tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
|
||||
{tr("Graphics"), {ui->graphicsTab}},
|
||||
{tr("Audio"), {ui->audioTab}},
|
||||
@@ -108,7 +108,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
|
||||
{ui->audioTab, tr("Audio")},
|
||||
{ui->debugTab, tr("Debug")},
|
||||
{ui->webTab, tr("Web")},
|
||||
{ui->gameListTab, tr("Game List")},
|
||||
{ui->uiTab, tr("UI")},
|
||||
{ui->filesystemTab, tr("Filesystem")},
|
||||
{ui->serviceTab, tr("Services")},
|
||||
};
|
||||
|
||||
@@ -15,11 +15,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
for (const auto& theme : UISettings::themes) {
|
||||
ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
|
||||
QString::fromUtf8(theme.second));
|
||||
}
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled);
|
||||
@@ -30,7 +25,6 @@ ConfigureGeneral::~ConfigureGeneral() = default;
|
||||
void ConfigureGeneral::SetConfiguration() {
|
||||
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
|
||||
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
|
||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||
ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
|
||||
|
||||
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
|
||||
@@ -41,8 +35,6 @@ void ConfigureGeneral::SetConfiguration() {
|
||||
void ConfigureGeneral::ApplyConfiguration() {
|
||||
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
|
||||
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
|
||||
UISettings::values.theme =
|
||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
|
||||
|
||||
Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
|
||||
|
||||
@@ -65,39 +65,12 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_background_pause">
|
||||
<property name="text">
|
||||
<string>Pause emulation when in background</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="theme_group_box">
|
||||
<property name="title">
|
||||
<string>Theme</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="theme_qhbox_layout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="theme_qvbox_layout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="theme_qhbox_layout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="theme_label">
|
||||
<property name="text">
|
||||
<string>Theme:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="theme_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QCheckBox" name="toggle_background_pause">
|
||||
<property name="text">
|
||||
<string>Pause emulation when in background</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QColorDialog>
|
||||
#include <QComboBox>
|
||||
#ifdef HAS_VULKAN
|
||||
#include <QVulkanInstance>
|
||||
#endif
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_graphics.h"
|
||||
@@ -51,10 +58,18 @@ Resolution FromResolutionFactor(float factor) {
|
||||
|
||||
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
: QWidget(parent), ui(new Ui::ConfigureGraphics) {
|
||||
vulkan_device = Settings::values.vulkan_device;
|
||||
RetrieveVulkanDevices();
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
|
||||
[this] { UpdateDeviceComboBox(); });
|
||||
connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
|
||||
[this](int device) { UpdateDeviceSelection(device); });
|
||||
|
||||
connect(ui->bg_button, &QPushButton::clicked, this, [this] {
|
||||
const QColor new_bg_color = QColorDialog::getColor(bg_color);
|
||||
if (!new_bg_color.isValid()) {
|
||||
@@ -64,11 +79,22 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigureGraphics::UpdateDeviceSelection(int device) {
|
||||
if (device == -1) {
|
||||
return;
|
||||
}
|
||||
if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
|
||||
vulkan_device = device;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigureGraphics::~ConfigureGraphics() = default;
|
||||
|
||||
void ConfigureGraphics::SetConfiguration() {
|
||||
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
|
||||
|
||||
ui->api->setEnabled(runtime_lock);
|
||||
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
|
||||
ui->resolution_factor_combobox->setCurrentIndex(
|
||||
static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
|
||||
ui->use_disk_shader_cache->setEnabled(runtime_lock);
|
||||
@@ -80,9 +106,12 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
|
||||
UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
|
||||
Settings::values.bg_blue));
|
||||
UpdateDeviceComboBox();
|
||||
}
|
||||
|
||||
void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.renderer_backend = GetCurrentGraphicsBackend();
|
||||
Settings::values.vulkan_device = vulkan_device;
|
||||
Settings::values.resolution_factor =
|
||||
ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
|
||||
Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
|
||||
@@ -116,3 +145,68 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) {
|
||||
const QIcon color_icon(pixmap);
|
||||
ui->bg_button->setIcon(color_icon);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::UpdateDeviceComboBox() {
|
||||
ui->device->clear();
|
||||
|
||||
bool enabled = false;
|
||||
switch (GetCurrentGraphicsBackend()) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
ui->device->addItem(tr("OpenGL Graphics Device"));
|
||||
enabled = false;
|
||||
break;
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
for (const auto device : vulkan_devices) {
|
||||
ui->device->addItem(device);
|
||||
}
|
||||
ui->device->setCurrentIndex(vulkan_device);
|
||||
enabled = !vulkan_devices.empty();
|
||||
break;
|
||||
}
|
||||
ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
|
||||
}
|
||||
|
||||
void ConfigureGraphics::RetrieveVulkanDevices() {
|
||||
#ifdef HAS_VULKAN
|
||||
QVulkanInstance instance;
|
||||
instance.setApiVersion(QVersionNumber(1, 1, 0));
|
||||
if (!instance.create()) {
|
||||
LOG_INFO(Frontend, "Vulkan 1.1 not available");
|
||||
return;
|
||||
}
|
||||
const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(
|
||||
instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))};
|
||||
if (vkEnumeratePhysicalDevices == nullptr) {
|
||||
LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices");
|
||||
return;
|
||||
}
|
||||
u32 physical_device_count;
|
||||
if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) !=
|
||||
VK_SUCCESS) {
|
||||
LOG_INFO(Frontend, "Failed to get physical devices count");
|
||||
return;
|
||||
}
|
||||
std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
|
||||
if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count,
|
||||
physical_devices.data()) != VK_SUCCESS) {
|
||||
LOG_INFO(Frontend, "Failed to get physical devices");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
|
||||
instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))};
|
||||
if (vkGetPhysicalDeviceProperties == nullptr) {
|
||||
LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties");
|
||||
return;
|
||||
}
|
||||
for (const auto physical_device : physical_devices) {
|
||||
VkPhysicalDeviceProperties properties;
|
||||
vkGetPhysicalDeviceProperties(physical_device, &properties);
|
||||
vulkan_devices.push_back(QString::fromUtf8(properties.deviceName));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
|
||||
return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureGraphics;
|
||||
@@ -27,7 +30,16 @@ private:
|
||||
void SetConfiguration();
|
||||
|
||||
void UpdateBackgroundColorButton(QColor color);
|
||||
void UpdateDeviceComboBox();
|
||||
void UpdateDeviceSelection(int device);
|
||||
|
||||
void RetrieveVulkanDevices();
|
||||
|
||||
Settings::RendererBackend GetCurrentGraphicsBackend() const;
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGraphics> ui;
|
||||
QColor bg_color;
|
||||
|
||||
std::vector<QString> vulkan_devices;
|
||||
u32 vulkan_device{};
|
||||
};
|
||||
|
||||
@@ -7,21 +7,69 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
<height>321</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_1">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>API Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>API:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="api">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">OpenGL</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string notr="true">Vulkan</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Device:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="device"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Graphics</string>
|
||||
<string>Graphics Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_disk_shader_cache">
|
||||
<property name="text">
|
||||
@@ -29,13 +77,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_accurate_gpu_emulation">
|
||||
<property name="text">
|
||||
<string>Use accurate GPU emulation (slow)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_asynchronous_gpu_emulation">
|
||||
<property name="text">
|
||||
@@ -43,6 +84,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_accurate_gpu_emulation">
|
||||
<property name="text">
|
||||
<string>Use accurate GPU emulation (slow)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="force_30fps_mode">
|
||||
<property name="text">
|
||||
@@ -51,11 +99,11 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Internal Resolution</string>
|
||||
<string>Internal Resolution:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -91,7 +139,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="bg_label">
|
||||
<property name="text">
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/settings.h"
|
||||
#include "ui_configure_gamelist.h"
|
||||
#include "yuzu/configuration/configure_gamelist.h"
|
||||
#include "ui_configure_ui.h"
|
||||
#include "yuzu/configuration/configure_ui.h"
|
||||
#include "yuzu/uisettings.h"
|
||||
|
||||
namespace {
|
||||
@@ -26,35 +26,40 @@ constexpr std::array row_text_names{
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
ConfigureGameList::ConfigureGameList(QWidget* parent)
|
||||
: QWidget(parent), ui(new Ui::ConfigureGameList) {
|
||||
ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) {
|
||||
ui->setupUi(this);
|
||||
|
||||
for (const auto& theme : UISettings::themes) {
|
||||
ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
|
||||
QString::fromUtf8(theme.second));
|
||||
}
|
||||
|
||||
InitializeIconSizeComboBox();
|
||||
InitializeRowComboBoxes();
|
||||
|
||||
SetConfiguration();
|
||||
|
||||
// Force game list reload if any of the relevant settings are changed.
|
||||
connect(ui->show_unknown, &QCheckBox::stateChanged, this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
connect(ui->show_unknown, &QCheckBox::stateChanged, this, &ConfigureUi::RequestGameListUpdate);
|
||||
connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
&ConfigureUi::RequestGameListUpdate);
|
||||
connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
&ConfigureUi::RequestGameListUpdate);
|
||||
connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&ConfigureGameList::RequestGameListUpdate);
|
||||
&ConfigureUi::RequestGameListUpdate);
|
||||
|
||||
// Update text ComboBoxes after user interaction.
|
||||
connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::activated),
|
||||
[=]() { ConfigureGameList::UpdateSecondRowComboBox(); });
|
||||
[=]() { ConfigureUi::UpdateSecondRowComboBox(); });
|
||||
connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::activated),
|
||||
[=]() { ConfigureGameList::UpdateFirstRowComboBox(); });
|
||||
[=]() { ConfigureUi::UpdateFirstRowComboBox(); });
|
||||
}
|
||||
|
||||
ConfigureGameList::~ConfigureGameList() = default;
|
||||
ConfigureUi::~ConfigureUi() = default;
|
||||
|
||||
void ConfigureGameList::ApplyConfiguration() {
|
||||
void ConfigureUi::ApplyConfiguration() {
|
||||
UISettings::values.theme =
|
||||
ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
|
||||
UISettings::values.show_unknown = ui->show_unknown->isChecked();
|
||||
UISettings::values.show_add_ons = ui->show_add_ons->isChecked();
|
||||
UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt();
|
||||
@@ -63,18 +68,19 @@ void ConfigureGameList::ApplyConfiguration() {
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void ConfigureGameList::RequestGameListUpdate() {
|
||||
void ConfigureUi::RequestGameListUpdate() {
|
||||
UISettings::values.is_game_list_reload_pending.exchange(true);
|
||||
}
|
||||
|
||||
void ConfigureGameList::SetConfiguration() {
|
||||
void ConfigureUi::SetConfiguration() {
|
||||
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
|
||||
ui->show_unknown->setChecked(UISettings::values.show_unknown);
|
||||
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
|
||||
ui->icon_size_combobox->setCurrentIndex(
|
||||
ui->icon_size_combobox->findData(UISettings::values.icon_size));
|
||||
}
|
||||
|
||||
void ConfigureGameList::changeEvent(QEvent* event) {
|
||||
void ConfigureUi::changeEvent(QEvent* event) {
|
||||
if (event->type() == QEvent::LanguageChange) {
|
||||
RetranslateUI();
|
||||
}
|
||||
@@ -82,7 +88,7 @@ void ConfigureGameList::changeEvent(QEvent* event) {
|
||||
QWidget::changeEvent(event);
|
||||
}
|
||||
|
||||
void ConfigureGameList::RetranslateUI() {
|
||||
void ConfigureUi::RetranslateUI() {
|
||||
ui->retranslateUi(this);
|
||||
|
||||
for (int i = 0; i < ui->icon_size_combobox->count(); i++) {
|
||||
@@ -97,18 +103,18 @@ void ConfigureGameList::RetranslateUI() {
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGameList::InitializeIconSizeComboBox() {
|
||||
void ConfigureUi::InitializeIconSizeComboBox() {
|
||||
for (const auto& size : default_icon_sizes) {
|
||||
ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureGameList::InitializeRowComboBoxes() {
|
||||
void ConfigureUi::InitializeRowComboBoxes() {
|
||||
UpdateFirstRowComboBox(true);
|
||||
UpdateSecondRowComboBox(true);
|
||||
}
|
||||
|
||||
void ConfigureGameList::UpdateFirstRowComboBox(bool init) {
|
||||
void ConfigureUi::UpdateFirstRowComboBox(bool init) {
|
||||
const int currentIndex =
|
||||
init ? UISettings::values.row_1_text_id
|
||||
: ui->row_1_text_combobox->findData(ui->row_1_text_combobox->currentData());
|
||||
@@ -127,7 +133,7 @@ void ConfigureGameList::UpdateFirstRowComboBox(bool init) {
|
||||
ui->row_1_text_combobox->findData(ui->row_2_text_combobox->currentData()));
|
||||
}
|
||||
|
||||
void ConfigureGameList::UpdateSecondRowComboBox(bool init) {
|
||||
void ConfigureUi::UpdateSecondRowComboBox(bool init) {
|
||||
const int currentIndex =
|
||||
init ? UISettings::values.row_2_text_id
|
||||
: ui->row_2_text_combobox->findData(ui->row_2_text_combobox->currentData());
|
||||
@@ -8,15 +8,15 @@
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class ConfigureGameList;
|
||||
class ConfigureUi;
|
||||
}
|
||||
|
||||
class ConfigureGameList : public QWidget {
|
||||
class ConfigureUi : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConfigureGameList(QWidget* parent = nullptr);
|
||||
~ConfigureGameList() override;
|
||||
explicit ConfigureUi(QWidget* parent = nullptr);
|
||||
~ConfigureUi() override;
|
||||
|
||||
void ApplyConfiguration();
|
||||
|
||||
@@ -34,5 +34,5 @@ private:
|
||||
void UpdateFirstRowComboBox(bool init = false);
|
||||
void UpdateSecondRowComboBox(bool init = false);
|
||||
|
||||
std::unique_ptr<Ui::ConfigureGameList> ui;
|
||||
std::unique_ptr<Ui::ConfigureUi> ui;
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConfigureGameList</class>
|
||||
<widget class="QWidget" name="ConfigureGameList">
|
||||
<class>ConfigureUi</class>
|
||||
<widget class="QWidget" name="ConfigureUi">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
@@ -21,7 +21,34 @@
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="GeneralHorizontalLayout">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="theme_label">
|
||||
<property name="text">
|
||||
<string>Theme:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="theme_combobox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="GameListGroupBox">
|
||||
<property name="title">
|
||||
<string>Game List</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="GameListHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="GeneralVerticalLayout">
|
||||
<item>
|
||||
@@ -38,19 +65,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="IconSizeGroupBox">
|
||||
<property name="title">
|
||||
<string>Icon Size</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="icon_size_qvbox_layout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
|
||||
<item>
|
||||
@@ -65,19 +79,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="RowGroupBox">
|
||||
<property name="title">
|
||||
<string>Row Text</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="RowHorizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="RowVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="row_1_qhbox_layout">
|
||||
<item>
|
||||
@@ -806,70 +806,12 @@ void GMainWindow::AllowOSSleep() {
|
||||
#endif
|
||||
}
|
||||
|
||||
QStringList GMainWindow::GetUnsupportedGLExtensions() {
|
||||
QStringList unsupported_ext;
|
||||
|
||||
if (!GLAD_GL_ARB_buffer_storage) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_buffer_storage"));
|
||||
}
|
||||
if (!GLAD_GL_ARB_direct_state_access) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_direct_state_access"));
|
||||
}
|
||||
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev"));
|
||||
}
|
||||
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge"));
|
||||
}
|
||||
if (!GLAD_GL_ARB_multi_bind) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
|
||||
}
|
||||
if (!GLAD_GL_ARB_clip_control) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_clip_control"));
|
||||
}
|
||||
|
||||
// Extensions required to support some texture formats.
|
||||
if (!GLAD_GL_EXT_texture_compression_s3tc) {
|
||||
unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
|
||||
}
|
||||
if (!GLAD_GL_ARB_texture_compression_rgtc) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc"));
|
||||
}
|
||||
if (!GLAD_GL_ARB_depth_buffer_float) {
|
||||
unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
|
||||
}
|
||||
|
||||
for (const QString& ext : unsupported_ext) {
|
||||
LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
|
||||
}
|
||||
|
||||
return unsupported_ext;
|
||||
}
|
||||
|
||||
bool GMainWindow::LoadROM(const QString& filename) {
|
||||
// Shutdown previous session if the emu thread is still active...
|
||||
if (emu_thread != nullptr)
|
||||
ShutdownGame();
|
||||
|
||||
render_window->InitRenderTarget();
|
||||
|
||||
{
|
||||
Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window};
|
||||
if (!gladLoadGL()) {
|
||||
QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"),
|
||||
tr("Your GPU may not support OpenGL 4.3, or you do not "
|
||||
"have the latest graphics driver."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
|
||||
if (!unsupported_gl_extensions.empty()) {
|
||||
QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"),
|
||||
tr("Your GPU may not support one or more required OpenGL"
|
||||
"extensions. Please ensure you have the latest graphics "
|
||||
"driver.<br><br>Unsupported extensions:<br>") +
|
||||
unsupported_gl_extensions.join(QStringLiteral("<br>")));
|
||||
if (!render_window->InitRenderTarget()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -980,7 +922,9 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
// Create and start the emulation thread
|
||||
emu_thread = std::make_unique<EmuThread>(render_window);
|
||||
emit EmulationStarting(emu_thread.get());
|
||||
render_window->moveContext();
|
||||
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
|
||||
render_window->moveContext();
|
||||
}
|
||||
emu_thread->start();
|
||||
|
||||
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
|
||||
@@ -2195,6 +2139,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
|
||||
QWidget::closeEvent(event);
|
||||
}
|
||||
|
||||
void GMainWindow::keyPressEvent(QKeyEvent* event) {
|
||||
if (render_window) {
|
||||
render_window->ForwardKeyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||
if (render_window) {
|
||||
render_window->ForwardKeyReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsSingleFileDropEvent(QDropEvent* event) {
|
||||
const QMimeData* mimeData = event->mimeData();
|
||||
return mimeData->hasUrls() && mimeData->urls().length() == 1;
|
||||
@@ -2227,18 +2183,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
void GMainWindow::keyPressEvent(QKeyEvent* event) {
|
||||
if (render_window) {
|
||||
render_window->ForwardKeyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||
if (render_window) {
|
||||
render_window->ForwardKeyReleaseEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
bool GMainWindow::ConfirmChangeGame() {
|
||||
if (emu_thread == nullptr)
|
||||
return true;
|
||||
|
||||
@@ -130,7 +130,6 @@ private:
|
||||
void PreventOSSleep();
|
||||
void AllowOSSleep();
|
||||
|
||||
QStringList GetUnsupportedGLExtensions();
|
||||
bool LoadROM(const QString& filename);
|
||||
void BootGame(const QString& filename);
|
||||
void ShutdownGame();
|
||||
|
||||
@@ -8,11 +8,22 @@ add_executable(yuzu-cmd
|
||||
emu_window/emu_window_sdl2_gl.h
|
||||
emu_window/emu_window_sdl2.cpp
|
||||
emu_window/emu_window_sdl2.h
|
||||
emu_window/emu_window_sdl2_gl.cpp
|
||||
emu_window/emu_window_sdl2_gl.h
|
||||
resource.h
|
||||
yuzu.cpp
|
||||
yuzu.rc
|
||||
)
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
target_sources(yuzu-cmd PRIVATE
|
||||
emu_window/emu_window_sdl2_vk.cpp
|
||||
emu_window/emu_window_sdl2_vk.h)
|
||||
|
||||
target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include)
|
||||
target_compile_definitions(yuzu-cmd PRIVATE HAS_VULKAN)
|
||||
endif()
|
||||
|
||||
create_target_directory_groups(yuzu-cmd)
|
||||
|
||||
target_link_libraries(yuzu-cmd PRIVATE common core input_common)
|
||||
|
||||
@@ -371,6 +371,12 @@ void Config::ReadValues() {
|
||||
Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
|
||||
|
||||
// Renderer
|
||||
const int renderer_backend = sdl2_config->GetInteger(
|
||||
"Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL));
|
||||
Settings::values.renderer_backend = static_cast<Settings::RendererBackend>(renderer_backend);
|
||||
Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false);
|
||||
Settings::values.vulkan_device = sdl2_config->GetInteger("Renderer", "vulkan_device", 0);
|
||||
|
||||
Settings::values.resolution_factor =
|
||||
static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0));
|
||||
Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true);
|
||||
|
||||
@@ -98,6 +98,17 @@ udp_pad_index=
|
||||
use_multi_core=
|
||||
|
||||
[Renderer]
|
||||
# Which backend API to use.
|
||||
# 0 (default): OpenGL, 1: Vulkan
|
||||
backend =
|
||||
|
||||
# Enable graphics API debugging mode.
|
||||
# 0 (default): Disabled, 1: Enabled
|
||||
debug =
|
||||
|
||||
# Which Vulkan physical device to use (defaults to 0)
|
||||
vulkan_device =
|
||||
|
||||
# Whether to use software or hardware rendering.
|
||||
# 0: Software, 1 (default): Hardware
|
||||
use_hw_renderer =
|
||||
|
||||
@@ -89,6 +89,10 @@ bool EmuWindow_SDL2::IsOpen() const {
|
||||
return is_open;
|
||||
}
|
||||
|
||||
bool EmuWindow_SDL2::IsShown() const {
|
||||
return is_shown;
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2::OnResize() {
|
||||
int width, height;
|
||||
SDL_GetWindowSize(render_window, &width, &height);
|
||||
|
||||
@@ -21,6 +21,9 @@ public:
|
||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
||||
bool IsOpen() const;
|
||||
|
||||
/// Returns if window is shown (not minimized)
|
||||
bool IsShown() const override;
|
||||
|
||||
protected:
|
||||
/// Called by PollEvents when a key is pressed or released.
|
||||
void OnKeyEvent(int key, u8 state);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <SDL.h>
|
||||
#include <fmt/format.h>
|
||||
#include <glad/glad.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/string_util.h"
|
||||
@@ -151,6 +152,12 @@ void EmuWindow_SDL2_GL::DoneCurrent() {
|
||||
SDL_GL_MakeCurrent(render_window, nullptr);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const {
|
||||
// Should not have been called from OpenGL
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
|
||||
return std::make_unique<SDLGLContext>();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ public:
|
||||
/// Releases the GL context from the caller thread
|
||||
void DoneCurrent() override;
|
||||
|
||||
/// Ignored in OpenGL
|
||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const override;
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
private:
|
||||
|
||||
162
src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
Normal file
162
src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <SDL.h>
|
||||
#include <SDL_vulkan.h>
|
||||
#include <fmt/format.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/settings.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
|
||||
|
||||
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) {
|
||||
if (SDL_Vulkan_LoadLibrary(nullptr) != 0) {
|
||||
LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
vkGetInstanceProcAddr =
|
||||
reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr());
|
||||
if (vkGetInstanceProcAddr == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
render_window =
|
||||
SDL_CreateWindow(window_title.c_str(),
|
||||
SDL_WINDOWPOS_UNDEFINED, // x position
|
||||
SDL_WINDOWPOS_UNDEFINED, // y position
|
||||
Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
|
||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN);
|
||||
|
||||
const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr);
|
||||
|
||||
u32 extra_ext_count{};
|
||||
if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) {
|
||||
LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}",
|
||||
SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count);
|
||||
if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) {
|
||||
LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
std::vector<const char*> enabled_extensions;
|
||||
enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(),
|
||||
extra_ext_names.get() + extra_ext_count);
|
||||
|
||||
std::vector<const char*> enabled_layers;
|
||||
if (use_standard_layers) {
|
||||
enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation");
|
||||
}
|
||||
|
||||
VkApplicationInfo app_info{};
|
||||
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
app_info.apiVersion = VK_API_VERSION_1_1;
|
||||
app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
|
||||
app_info.pApplicationName = "yuzu-emu";
|
||||
app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
|
||||
app_info.pEngineName = "yuzu-emu";
|
||||
|
||||
VkInstanceCreateInfo instance_ci{};
|
||||
instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
instance_ci.pApplicationInfo = &app_info;
|
||||
instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size());
|
||||
instance_ci.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
if (Settings::values.renderer_debug) {
|
||||
instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size());
|
||||
instance_ci.ppEnabledLayerNames = enabled_layers.data();
|
||||
}
|
||||
|
||||
const auto vkCreateInstance =
|
||||
reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance"));
|
||||
if (vkCreateInstance == nullptr ||
|
||||
vkCreateInstance(&instance_ci, nullptr, &vk_instance) != VK_SUCCESS) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
|
||||
vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance"));
|
||||
if (vkDestroyInstance == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!SDL_Vulkan_CreateSurface(render_window, vk_instance, &vk_surface)) {
|
||||
LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
OnResize();
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
SDL_PumpEvents();
|
||||
LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
}
|
||||
|
||||
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
|
||||
vkDestroyInstance(vk_instance, nullptr);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_VK::SwapBuffers() {}
|
||||
|
||||
void EmuWindow_SDL2_VK::MakeCurrent() {
|
||||
// Unused on Vulkan
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_VK::DoneCurrent() {
|
||||
// Unused on Vulkan
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const {
|
||||
const auto instance_proc_addr = vkGetInstanceProcAddr;
|
||||
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
|
||||
std::memcpy(instance, &vk_instance, sizeof(vk_instance));
|
||||
std::memcpy(surface, &vk_surface, sizeof(vk_surface));
|
||||
}
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const {
|
||||
if (!Settings::values.renderer_debug) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto vkEnumerateInstanceLayerProperties =
|
||||
reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
|
||||
vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties"));
|
||||
if (vkEnumerateInstanceLayerProperties == nullptr) {
|
||||
LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 available_layers_count{};
|
||||
if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) {
|
||||
LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
|
||||
return false;
|
||||
}
|
||||
std::vector<VkLayerProperties> layers(available_layers_count);
|
||||
if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) {
|
||||
LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) {
|
||||
return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation");
|
||||
}) != layers.end();
|
||||
}
|
||||
39
src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
Normal file
39
src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
|
||||
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
|
||||
public:
|
||||
explicit EmuWindow_SDL2_VK(bool fullscreen);
|
||||
~EmuWindow_SDL2_VK();
|
||||
|
||||
/// Swap buffers to display the next frame
|
||||
void SwapBuffers() override;
|
||||
|
||||
/// Makes the graphics context current for the caller thread
|
||||
void MakeCurrent() override;
|
||||
|
||||
/// Releases the GL context from the caller thread
|
||||
void DoneCurrent() override;
|
||||
|
||||
/// Retrieves Vulkan specific handlers from the window
|
||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const override;
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
private:
|
||||
bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const;
|
||||
|
||||
VkInstance vk_instance{};
|
||||
VkSurfaceKHR vk_surface{};
|
||||
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
|
||||
PFN_vkDestroyInstance vkDestroyInstance{};
|
||||
};
|
||||
@@ -32,6 +32,9 @@
|
||||
#include "yuzu_cmd/config.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
|
||||
#ifdef HAS_VULKAN
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
|
||||
#endif
|
||||
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
|
||||
@@ -174,7 +177,20 @@ int main(int argc, char** argv) {
|
||||
Settings::values.use_gdbstub = use_gdbstub;
|
||||
Settings::Apply();
|
||||
|
||||
std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)};
|
||||
std::unique_ptr<EmuWindow_SDL2> emu_window;
|
||||
switch (Settings::values.renderer_backend) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen);
|
||||
break;
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
#ifdef HAS_VULKAN
|
||||
emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen);
|
||||
break;
|
||||
#else
|
||||
LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!Settings::values.use_multi_core) {
|
||||
// Single core mode must acquire OpenGL context for entire emulation session
|
||||
|
||||
@@ -5,10 +5,15 @@
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "core/settings.h"
|
||||
@@ -120,3 +125,11 @@ void EmuWindow_SDL2_Hide::MakeCurrent() {
|
||||
void EmuWindow_SDL2_Hide::DoneCurrent() {
|
||||
SDL_GL_MakeCurrent(render_window, nullptr);
|
||||
}
|
||||
|
||||
bool EmuWindow_SDL2_Hide::IsShown() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,13 @@ public:
|
||||
/// Releases the GL context from the caller thread
|
||||
void DoneCurrent() override;
|
||||
|
||||
/// Whether the screen is being shown or not.
|
||||
bool IsShown() const override;
|
||||
|
||||
/// Retrieves Vulkan specific handlers from the window
|
||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const override;
|
||||
|
||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
||||
bool IsOpen() const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user