Compare commits

...

33 Commits

Author SHA1 Message Date
ReinUsesLisp
d027850f33 ci: Disable Vulkan for Windows MinGW builds 2020-01-29 19:44:00 -03:00
ReinUsesLisp
a7beabb68f yuzu/bootmanager: Define Vulkan widget only when enabled 2020-01-29 19:20:12 -03:00
ReinUsesLisp
252415a163 ci: Disable Vulkan for Linux builds 2020-01-29 18:06:16 -03:00
ReinUsesLisp
c29584a090 yuzu_cmd: Fix memcpy on Vulkan handlers 2020-01-29 17:53:11 -03:00
ReinUsesLisp
f92cbc5501 yuzu: Implement Vulkan frontend
Adds a Qt and SDL2 frontend for Vulkan. It also finishes the missing
bits on Vulkan initialization.
2020-01-29 17:53:11 -03:00
ReinUsesLisp
8299f1ceef web_service/telemetry_json: Report USER_CONFIG 2020-01-29 17:53:11 -03:00
ReinUsesLisp
788d57d723 settings: Add settings for graphics backend 2020-01-29 17:53:11 -03:00
ReinUsesLisp
e651e54b85 core: Only wait for idle on gpu_core when it was initialized
This fixes crashes when a Vulkan device fails to initialize.
2020-01-29 17:53:11 -03:00
ReinUsesLisp
9f0162e4b5 shader/other: Fix skips for SYNC and BRK 2020-01-29 17:53:11 -03:00
ReinUsesLisp
270177f38a shader/other: Stub S2R LaneId 2020-01-29 17:53:11 -03:00
ReinUsesLisp
b35449c85d buffer_cache: Delay buffer destructions
Delay buffer destruction some extra frames to avoid destroying buffers
that are still being used from older frames. This happens on Nvidia's
driver with mailbox.
2020-01-29 17:53:11 -03:00
bunnei
b11aeced18 Merge pull request #3355 from ReinUsesLisp/break-down
texture_cache/surface_base: Fix layered break down
2020-01-29 12:29:56 -05:00
bunnei
91f79225e7 Merge pull request #3358 from ReinUsesLisp/implicit-texture-cache
gl_texture_cache: Silence implicit sign cast warnings
2020-01-29 11:23:50 -05:00
bunnei
c457e47297 Merge pull request #3359 from ReinUsesLisp/assert-point-size
gl_shader_decompiler: Remove UNIMPLEMENTED for gl_PointSize
2020-01-28 15:19:51 -05:00
ReinUsesLisp
8178fe8960 gl_shader_decompiler: Remove UNIMPLEMENTED for gl_PointSize
This was implemented by a previous commit and it's no longer required.
2020-01-28 16:32:30 -03:00
bunnei
283f3253bc Merge pull request #3352 from Simek/dark-theme-refinements
GUI: dark themes refinements and QSS cleanup
2020-01-28 14:05:36 -05:00
bunnei
bea6327d74 Merge pull request #3354 from ReinUsesLisp/depth-stencil
gl_texture_cache: Properly implement depth/stencil sampling
2020-01-28 12:06:11 -05:00
ReinUsesLisp
abae795986 gl_texture_cache: Silence implicit sign cast warnings 2020-01-27 20:59:11 -03:00
bunnei
acfb0b4852 Merge pull request #3346 from bunnei/bsd-stub
bsd: Stub several more functions.
2020-01-27 13:06:05 -05:00
ReinUsesLisp
f55f6ff9bb texture_cache/surface_base: Fix layered break down
Layered break downs was passing "layer" as a "depth" parameter. This
commit addresses that.
2020-01-26 21:48:07 -03:00
ReinUsesLisp
d17dfa6104 gl_texture_cache: Properly implement depth/stencil sampling
This addresses the long standing issue of compatibility vs. core
profiles on OpenGL, properly implementing depth vs. stencil sampling
depending on the texture swizzle.
2020-01-26 21:44:08 -03:00
Bartosz Kaszubowski
f68bb4f55e dark themes refinements and cleanup 2020-01-26 11:50:01 +01:00
bunnei
2a822f3378 bsd: Stub several more functions.
- Required for Little Town Hero to boot further.
2020-01-25 00:47:15 -05:00
bunnei
05df4a8c94 Merge pull request #3343 from FearlessTobi/ui-tab
yuzu/configuration: create UI tab and move gamelist settings there
2020-01-25 00:40:13 -05:00
bunnei
2b1d66eda3 Merge pull request #3326 from FearlessTobi/port-5039
Port citra-emu/citra#5039: "common/logging: don't use regex for path trimming"
2020-01-24 20:59:57 -05:00
bunnei
dfd998216c Merge pull request #3344 from ReinUsesLisp/vk-botw
vk_shader_decompiler: Disable default values on unwritten render targets
2020-01-24 17:31:55 -05:00
bunnei
a104b985a8 Merge pull request #3273 from FernandoS27/txd-array
Shader_IR: Implement TXD Array.
2020-01-24 14:02:40 -05:00
bunnei
f64adcfc37 Merge pull request #3340 from SciresM/pmdx
loader: provide default arguments (zero byte) to NSOs
2020-01-24 10:31:43 -05:00
ReinUsesLisp
1690f1adba vk_shader_decompiler: Disable default values on unwritten render targets
Some games like The Legend of Zelda: Breath of the Wild assign
render targets without writing them from the fragment shader. This
generates Vulkan validation errors, so silence these I previously
introduced a commit to set "vec4(0, 0, 0, 1)" for these attachments. The
problem is that this is not what games expect. This commit reverts that
change.
2020-01-24 01:16:21 -03:00
FearlessTobi
d0e4f1c6f4 yuzu/configuration: create UI tab and move gamelist settings there 2020-01-24 00:15:51 +01:00
BreadFish64
a31ed02ae4 common/logging: don't use regex for path trimming 2020-01-23 23:08:05 +01:00
Michael Scire
5a7eecc3ad loader: provide default arguments (zero byte) to NSOs
Certain newer unity games (Terraria, Pokemon Mystery Dungeon) require
that the argument region be populated. Failure to do so results in
an integer underflow in argument count, and eventually an unmapped
read at 0x800000000. Providing this default fixes this.

Note that the behavior of official software is as yet unverified,
arguments-wise.
2020-01-22 20:14:06 -08:00
Fernando Sahmkow
a1667a7b46 Shader_IR: Implement TXD Array.
This commit extends the compilation of TXD to support array samplers on
TXD.
2020-01-04 13:28:02 -04:00
59 changed files with 1503 additions and 487 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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__)

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -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"},

View File

@@ -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);

View File

@@ -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()), {}};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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();

View File

@@ -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 {};

View File

@@ -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);
}

View 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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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>);

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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()

View File

@@ -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();

View File

@@ -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:

View File

@@ -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);

View File

@@ -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>

View File

@@ -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);

View File

@@ -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>

View File

@@ -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")},
};

View File

@@ -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();

View File

@@ -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>

View File

@@ -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());
}

View File

@@ -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{};
};

View File

@@ -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">

View File

@@ -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());

View File

@@ -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;
};

View File

@@ -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>

View File

@@ -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;

View File

@@ -130,7 +130,6 @@ private:
void PreventOSSleep();
void AllowOSSleep();
QStringList GetUnsupportedGLExtensions();
bool LoadROM(const QString& filename);
void BootGame(const QString& filename);
void ShutdownGame();

View File

@@ -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)

View File

@@ -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);

View File

@@ -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 =

View File

@@ -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);

View File

@@ -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);

View File

@@ -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>();
}

View File

@@ -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:

View 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();
}

View 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{};
};

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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;