diff options
219 files changed, 8099 insertions, 2844 deletions
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh index 5559a527c..f11878128 100644 --- a/.ci/scripts/linux/docker.sh +++ b/.ci/scripts/linux/docker.sh | |||
| @@ -5,7 +5,7 @@ cd /yuzu | |||
| 5 | ccache -s | 5 | ccache -s |
| 6 | 6 | ||
| 7 | mkdir build || true && cd build | 7 | mkdir build || true && cd build |
| 8 | 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 | 8 | 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 |
| 9 | 9 | ||
| 10 | ninja | 10 | ninja |
| 11 | 11 | ||
diff --git a/.ci/scripts/windows/docker.sh b/.ci/scripts/windows/docker.sh index e8f26933a..beb554b65 100644 --- a/.ci/scripts/windows/docker.sh +++ b/.ci/scripts/windows/docker.sh | |||
| @@ -13,7 +13,7 @@ echo '' >> /bin/cmd | |||
| 13 | chmod +x /bin/cmd | 13 | chmod +x /bin/cmd |
| 14 | 14 | ||
| 15 | mkdir build || true && cd build | 15 | mkdir build || true && cd build |
| 16 | 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 | 16 | 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 |
| 17 | ninja | 17 | ninja |
| 18 | 18 | ||
| 19 | # Clean up the dirty hacks | 19 | # Clean up the dirty hacks |
diff --git a/CMakeLists.txt b/CMakeLists.txt index 118572c03..467d769a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -151,15 +151,22 @@ if (ENABLE_SDL2) | |||
| 151 | set(SDL2_INCLUDE_DIR "${SDL2_PREFIX}/include" CACHE PATH "Path to SDL2 headers") | 151 | set(SDL2_INCLUDE_DIR "${SDL2_PREFIX}/include" CACHE PATH "Path to SDL2 headers") |
| 152 | set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library") | 152 | set(SDL2_LIBRARY "${SDL2_PREFIX}/lib/x64/SDL2.lib" CACHE PATH "Path to SDL2 library") |
| 153 | set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll") | 153 | set(SDL2_DLL_DIR "${SDL2_PREFIX}/lib/x64/" CACHE PATH "Path to SDL2.dll") |
| 154 | else() | ||
| 155 | find_package(SDL2 REQUIRED) | ||
| 156 | endif() | ||
| 157 | 154 | ||
| 158 | if (SDL2_FOUND) | ||
| 159 | # TODO(yuriks): Make FindSDL2.cmake export an IMPORTED library instead | ||
| 160 | add_library(SDL2 INTERFACE) | 155 | add_library(SDL2 INTERFACE) |
| 161 | target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}") | 156 | target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARY}") |
| 162 | target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") | 157 | target_include_directories(SDL2 INTERFACE "${SDL2_INCLUDE_DIR}") |
| 158 | else() | ||
| 159 | find_package(SDL2 REQUIRED) | ||
| 160 | |||
| 161 | # Some installations don't set SDL2_LIBRARIES | ||
| 162 | if("${SDL2_LIBRARIES}" STREQUAL "") | ||
| 163 | message(WARNING "SDL2_LIBRARIES wasn't set, manually setting to SDL2::SDL2") | ||
| 164 | set(SDL2_LIBRARIES "SDL2::SDL2") | ||
| 165 | endif() | ||
| 166 | |||
| 167 | include_directories(${SDL2_INCLUDE_DIRS}) | ||
| 168 | add_library(SDL2 INTERFACE) | ||
| 169 | target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}") | ||
| 163 | endif() | 170 | endif() |
| 164 | else() | 171 | else() |
| 165 | set(SDL2_FOUND NO) | 172 | set(SDL2_FOUND NO) |
| @@ -350,6 +357,13 @@ function(create_target_directory_groups target_name) | |||
| 350 | endforeach() | 357 | endforeach() |
| 351 | endfunction() | 358 | endfunction() |
| 352 | 359 | ||
| 360 | # Prevent boost from linking against libs when building | ||
| 361 | add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY | ||
| 362 | -DBOOST_SYSTEM_NO_LIB | ||
| 363 | -DBOOST_DATE_TIME_NO_LIB | ||
| 364 | -DBOOST_REGEX_NO_LIB | ||
| 365 | ) | ||
| 366 | |||
| 353 | enable_testing() | 367 | enable_testing() |
| 354 | add_subdirectory(externals) | 368 | add_subdirectory(externals) |
| 355 | add_subdirectory(src) | 369 | add_subdirectory(src) |
diff --git a/dist/qt_themes/colorful/style.qrc b/dist/qt_themes/colorful/style.qrc index af2f3fd56..36735519a 100644 --- a/dist/qt_themes/colorful/style.qrc +++ b/dist/qt_themes/colorful/style.qrc | |||
| @@ -10,6 +10,6 @@ | |||
| 10 | <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> | 10 | <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> |
| 11 | </qresource> | 11 | </qresource> |
| 12 | <qresource prefix="colorful"> | 12 | <qresource prefix="colorful"> |
| 13 | <file>style.qss</file> | 13 | <file alias="style.qss">../default/style.qss</file> |
| 14 | </qresource> | 14 | </qresource> |
| 15 | </RCC> | 15 | </RCC> |
diff --git a/dist/qt_themes/colorful/style.qss b/dist/qt_themes/colorful/style.qss deleted file mode 100644 index 413fc81da..000000000 --- a/dist/qt_themes/colorful/style.qss +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | /* | ||
| 2 | This file is intentionally left blank. | ||
| 3 | We do not want to apply any stylesheet for colorful, only icons. | ||
| 4 | */ | ||
diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc index d1a0ee1be..c51fdb26c 100644 --- a/dist/qt_themes/default/default.qrc +++ b/dist/qt_themes/default/default.qrc | |||
| @@ -1,25 +1,18 @@ | |||
| 1 | <RCC> | 1 | <RCC> |
| 2 | <qresource prefix="icons/default"> | 2 | <qresource prefix="icons/default"> |
| 3 | <file alias="index.theme">icons/index.theme</file> | 3 | <file alias="index.theme">icons/index.theme</file> |
| 4 | |||
| 5 | <file alias="16x16/checked.png">icons/16x16/checked.png</file> | 4 | <file alias="16x16/checked.png">icons/16x16/checked.png</file> |
| 6 | |||
| 7 | <file alias="16x16/failed.png">icons/16x16/failed.png</file> | 5 | <file alias="16x16/failed.png">icons/16x16/failed.png</file> |
| 8 | |||
| 9 | <file alias="16x16/lock.png">icons/16x16/lock.png</file> | 6 | <file alias="16x16/lock.png">icons/16x16/lock.png</file> |
| 10 | |||
| 11 | <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> | 7 | <file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file> |
| 12 | |||
| 13 | <file alias="48x48/chip.png">icons/48x48/chip.png</file> | 8 | <file alias="48x48/chip.png">icons/48x48/chip.png</file> |
| 14 | |||
| 15 | <file alias="48x48/folder.png">icons/48x48/folder.png</file> | 9 | <file alias="48x48/folder.png">icons/48x48/folder.png</file> |
| 16 | |||
| 17 | <file alias="48x48/plus.png">icons/48x48/plus.png</file> | 10 | <file alias="48x48/plus.png">icons/48x48/plus.png</file> |
| 18 | |||
| 19 | <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> | 11 | <file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file> |
| 20 | |||
| 21 | <file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file> | 12 | <file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file> |
| 22 | |||
| 23 | <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> | 13 | <file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file> |
| 24 | </qresource> | 14 | </qresource> |
| 15 | <qresource prefix="default"> | ||
| 16 | <file>style.qss</file> | ||
| 17 | </qresource> | ||
| 25 | </RCC> | 18 | </RCC> |
diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss new file mode 100644 index 000000000..6b5953e38 --- /dev/null +++ b/dist/qt_themes/default/style.qss | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | QPushButton#TogglableStatusBarButton { | ||
| 2 | color: #959595; | ||
| 3 | border: 1px solid transparent; | ||
| 4 | background-color: transparent; | ||
| 5 | padding: 0px 3px 0px 3px; | ||
| 6 | text-align: center; | ||
| 7 | } | ||
| 8 | |||
| 9 | QPushButton#TogglableStatusBarButton:checked { | ||
| 10 | color: #000000; | ||
| 11 | } | ||
| 12 | |||
| 13 | QPushButton#TogglableStatusBarButton:hover { | ||
| 14 | border: 1px solid #76797C; | ||
| 15 | } | ||
| 16 | |||
| 17 | QPushButton#RendererStatusBarButton { | ||
| 18 | color: #656565; | ||
| 19 | border: 1px solid transparent; | ||
| 20 | background-color: transparent; | ||
| 21 | padding: 0px 3px 0px 3px; | ||
| 22 | text-align: center; | ||
| 23 | } | ||
| 24 | |||
| 25 | QPushButton#RendererStatusBarButton:hover { | ||
| 26 | border: 1px solid #76797C; | ||
| 27 | } | ||
| 28 | |||
| 29 | QPushButton#RendererStatusBarButton:checked { | ||
| 30 | color: #e85c00; | ||
| 31 | } | ||
| 32 | |||
| 33 | QPushButton#RendererStatusBarButton:!checked{ | ||
| 34 | color: #0066ff; | ||
| 35 | } | ||
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index a3983b27e..7d088a719 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss | |||
| @@ -2,7 +2,8 @@ QToolTip { | |||
| 2 | border: 1px solid #76797C; | 2 | border: 1px solid #76797C; |
| 3 | background-color: #5A7566; | 3 | background-color: #5A7566; |
| 4 | color: white; | 4 | color: white; |
| 5 | padding: 0px; /*remove padding, for fix combobox tooltip.*/ | 5 | /*remove padding, for fix combobox tooltip.*/ |
| 6 | padding: 0; | ||
| 6 | opacity: 200; | 7 | opacity: 200; |
| 7 | } | 8 | } |
| 8 | 9 | ||
| @@ -13,7 +14,7 @@ QWidget { | |||
| 13 | selection-color: #eff0f1; | 14 | selection-color: #eff0f1; |
| 14 | background-clip: border; | 15 | background-clip: border; |
| 15 | border-image: none; | 16 | border-image: none; |
| 16 | border: 0px transparent black; | 17 | border: 0; |
| 17 | outline: 0; | 18 | outline: 0; |
| 18 | } | 19 | } |
| 19 | 20 | ||
| @@ -27,10 +28,10 @@ QWidget:item:selected { | |||
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | QCheckBox { | 30 | QCheckBox { |
| 30 | spacing: 5px; | 31 | spacing: 6px; |
| 31 | outline: none; | 32 | outline: none; |
| 32 | color: #eff0f1; | 33 | color: #eff0f1; |
| 33 | margin-bottom: 2px; | 34 | margin: 0 2px 1px 0; |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | QCheckBox:disabled { | 37 | QCheckBox:disabled { |
| @@ -163,7 +164,7 @@ QMenuBar::item:selected { | |||
| 163 | } | 164 | } |
| 164 | 165 | ||
| 165 | QMenuBar::item:pressed { | 166 | QMenuBar::item:pressed { |
| 166 | border: 1px solid #76797C; | 167 | border: 1px solid #18465d; |
| 167 | background-color: #3daee9; | 168 | background-color: #3daee9; |
| 168 | color: #eff0f1; | 169 | color: #eff0f1; |
| 169 | margin-bottom: -1px; | 170 | margin-bottom: -1px; |
| @@ -171,9 +172,9 @@ QMenuBar::item:pressed { | |||
| 171 | } | 172 | } |
| 172 | 173 | ||
| 173 | QMenu { | 174 | QMenu { |
| 174 | border: 1px solid #76797C; | 175 | border: 1px solid #434242; |
| 176 | padding: 2px; | ||
| 175 | color: #eff0f1; | 177 | color: #eff0f1; |
| 176 | margin: 2px; | ||
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | QMenu::icon { | 180 | QMenu::icon { |
| @@ -190,11 +191,21 @@ QMenu::item:selected { | |||
| 190 | color: #eff0f1; | 191 | color: #eff0f1; |
| 191 | } | 192 | } |
| 192 | 193 | ||
| 193 | QMenu::separator { | 194 | QMenu::item:disabled { |
| 194 | height: 2px; | 195 | color: #54575B; |
| 195 | background: #76797C; | 196 | } |
| 196 | margin-left: 10px; | 197 | |
| 197 | margin-right: 5px; | 198 | QMenu::item:disabled:hover, |
| 199 | QMenu::item:disabled:selected { | ||
| 200 | background-color: #393e43; | ||
| 201 | color: #666; | ||
| 202 | } | ||
| 203 | |||
| 204 | QMenu::separator, | ||
| 205 | QMenuBar::separator { | ||
| 206 | height: 1px; | ||
| 207 | background-color: #54575B; | ||
| 208 | margin: 2px 4px 2px 40px; | ||
| 198 | } | 209 | } |
| 199 | 210 | ||
| 200 | QMenu::indicator { | 211 | QMenu::indicator { |
| @@ -203,10 +214,7 @@ QMenu::indicator { | |||
| 203 | height: 18px; | 214 | height: 18px; |
| 204 | } | 215 | } |
| 205 | 216 | ||
| 206 | 217 | /* non-exclusive indicator = check box style indicator (see QActionGroup::setExclusive) */ | |
| 207 | /* non-exclusive indicator = check box style indicator | ||
| 208 | (see QActionGroup::setExclusive) */ | ||
| 209 | |||
| 210 | QMenu::indicator:non-exclusive:unchecked { | 218 | QMenu::indicator:non-exclusive:unchecked { |
| 211 | image: url(:/qss_icons/rc/checkbox_unchecked.png); | 219 | image: url(:/qss_icons/rc/checkbox_unchecked.png); |
| 212 | } | 220 | } |
| @@ -223,9 +231,7 @@ QMenu::indicator:non-exclusive:checked:selected { | |||
| 223 | image: url(:/qss_icons/rc/checkbox_checked_disabled.png); | 231 | image: url(:/qss_icons/rc/checkbox_checked_disabled.png); |
| 224 | } | 232 | } |
| 225 | 233 | ||
| 226 | |||
| 227 | /* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ | 234 | /* exclusive indicator = radio button style indicator (see QActionGroup::setExclusive) */ |
| 228 | |||
| 229 | QMenu::indicator:exclusive:unchecked { | 235 | QMenu::indicator:exclusive:unchecked { |
| 230 | image: url(:/qss_icons/rc/radio_unchecked.png); | 236 | image: url(:/qss_icons/rc/radio_unchecked.png); |
| 231 | } | 237 | } |
| @@ -243,12 +249,12 @@ QMenu::indicator:exclusive:checked:selected { | |||
| 243 | } | 249 | } |
| 244 | 250 | ||
| 245 | QMenu::right-arrow { | 251 | QMenu::right-arrow { |
| 246 | margin: 5px; | 252 | margin-right: 10px; |
| 247 | image: url(:/qss_icons/rc/right_arrow.png) | 253 | image: url(:/qss_icons/rc/right_arrow.png) |
| 248 | } | 254 | } |
| 249 | 255 | ||
| 250 | QWidget:disabled { | 256 | QWidget:disabled { |
| 251 | color: #454545; | 257 | color: #4f515b; |
| 252 | background-color: #31363b; | 258 | background-color: #31363b; |
| 253 | } | 259 | } |
| 254 | 260 | ||
| @@ -259,23 +265,30 @@ QAbstractItemView { | |||
| 259 | border-radius: 2px; | 265 | border-radius: 2px; |
| 260 | } | 266 | } |
| 261 | 267 | ||
| 262 | QWidget:focus, | 268 | QAbstractItemView:disabled, |
| 263 | QMenuBar:focus { | 269 | QAbstractItemView:read-only { |
| 270 | alternate-background-color: #232629; | ||
| 271 | } | ||
| 272 | |||
| 273 | QWidget:focus { | ||
| 264 | border: 1px solid #3daee9; | 274 | border: 1px solid #3daee9; |
| 265 | } | 275 | } |
| 266 | 276 | ||
| 267 | QTabWidget:focus, | 277 | QTabWidget:focus, |
| 268 | QCheckBox:focus, | 278 | QCheckBox:focus, |
| 269 | QRadioButton:focus, | 279 | QRadioButton:focus, |
| 270 | QSlider:focus { | 280 | QSlider:focus, |
| 281 | QTreeView:focus, | ||
| 282 | QMenu:focus, | ||
| 283 | QMenuBar:focus, | ||
| 284 | QTabBar:focus { | ||
| 271 | border: none; | 285 | border: none; |
| 272 | } | 286 | } |
| 273 | 287 | ||
| 274 | QLineEdit { | 288 | QLineEdit { |
| 275 | background-color: #232629; | 289 | background-color: #232629; |
| 276 | padding: 5px; | 290 | padding: 5px; |
| 277 | border-style: solid; | 291 | border: 1px solid #54575B; |
| 278 | border: 1px solid #76797C; | ||
| 279 | border-radius: 2px; | 292 | border-radius: 2px; |
| 280 | color: #eff0f1; | 293 | color: #eff0f1; |
| 281 | } | 294 | } |
| @@ -285,9 +298,10 @@ QAbstractItemView QLineEdit { | |||
| 285 | } | 298 | } |
| 286 | 299 | ||
| 287 | QGroupBox { | 300 | QGroupBox { |
| 288 | border: 1px solid #76797C; | 301 | border: 1px solid #54575B; |
| 289 | border-radius: 2px; | 302 | border-radius: 2px; |
| 290 | margin-top: 20px; | 303 | margin-top: 12px; |
| 304 | padding-top: 2px; | ||
| 291 | } | 305 | } |
| 292 | 306 | ||
| 293 | QGroupBox::title { | 307 | QGroupBox::title { |
| @@ -295,12 +309,12 @@ QGroupBox::title { | |||
| 295 | subcontrol-position: top center; | 309 | subcontrol-position: top center; |
| 296 | padding-left: 10px; | 310 | padding-left: 10px; |
| 297 | padding-right: 10px; | 311 | padding-right: 10px; |
| 298 | padding-top: 10px; | 312 | padding-top: 2px; |
| 299 | } | 313 | } |
| 300 | 314 | ||
| 301 | QAbstractScrollArea { | 315 | QAbstractScrollArea { |
| 302 | border-radius: 2px; | 316 | border-radius: 2px; |
| 303 | border: 1px solid #76797C; | 317 | border: 1px solid #54575B; |
| 304 | background-color: transparent; | 318 | background-color: transparent; |
| 305 | } | 319 | } |
| 306 | 320 | ||
| @@ -319,7 +333,7 @@ QScrollBar::handle:horizontal { | |||
| 319 | } | 333 | } |
| 320 | 334 | ||
| 321 | QScrollBar::add-line:horizontal { | 335 | QScrollBar::add-line:horizontal { |
| 322 | margin: 0px 3px 0px 3px; | 336 | margin: 0 3px; |
| 323 | border-image: url(:/qss_icons/rc/right_arrow_disabled.png); | 337 | border-image: url(:/qss_icons/rc/right_arrow_disabled.png); |
| 324 | width: 10px; | 338 | width: 10px; |
| 325 | height: 10px; | 339 | height: 10px; |
| @@ -328,7 +342,7 @@ QScrollBar::add-line:horizontal { | |||
| 328 | } | 342 | } |
| 329 | 343 | ||
| 330 | QScrollBar::sub-line:horizontal { | 344 | QScrollBar::sub-line:horizontal { |
| 331 | margin: 0px 3px 0px 3px; | 345 | margin: 0 3px; |
| 332 | border-image: url(:/qss_icons/rc/left_arrow_disabled.png); | 346 | border-image: url(:/qss_icons/rc/left_arrow_disabled.png); |
| 333 | height: 10px; | 347 | height: 10px; |
| 334 | width: 10px; | 348 | width: 10px; |
| @@ -379,7 +393,7 @@ QScrollBar::handle:vertical { | |||
| 379 | } | 393 | } |
| 380 | 394 | ||
| 381 | QScrollBar::sub-line:vertical { | 395 | QScrollBar::sub-line:vertical { |
| 382 | margin: 3px 0px 3px 0px; | 396 | margin: 3px 0; |
| 383 | border-image: url(:/qss_icons/rc/up_arrow_disabled.png); | 397 | border-image: url(:/qss_icons/rc/up_arrow_disabled.png); |
| 384 | height: 10px; | 398 | height: 10px; |
| 385 | width: 10px; | 399 | width: 10px; |
| @@ -388,7 +402,7 @@ QScrollBar::sub-line:vertical { | |||
| 388 | } | 402 | } |
| 389 | 403 | ||
| 390 | QScrollBar::add-line:vertical { | 404 | QScrollBar::add-line:vertical { |
| 391 | margin: 3px 0px 3px 0px; | 405 | margin: 3px 0; |
| 392 | border-image: url(:/qss_icons/rc/down_arrow_disabled.png); | 406 | border-image: url(:/qss_icons/rc/down_arrow_disabled.png); |
| 393 | height: 10px; | 407 | height: 10px; |
| 394 | width: 10px; | 408 | width: 10px; |
| @@ -427,15 +441,14 @@ QScrollBar::sub-page:vertical { | |||
| 427 | QTextEdit { | 441 | QTextEdit { |
| 428 | background-color: #232629; | 442 | background-color: #232629; |
| 429 | color: #eff0f1; | 443 | color: #eff0f1; |
| 430 | border: 1px solid #76797C; | 444 | border: 1px solid #54575B; |
| 431 | } | 445 | } |
| 432 | 446 | ||
| 433 | QPlainTextEdit { | 447 | QPlainTextEdit { |
| 434 | background-color: #232629; | 448 | background-color: #232629; |
| 435 | ; | ||
| 436 | color: #eff0f1; | 449 | color: #eff0f1; |
| 437 | border-radius: 2px; | 450 | border-radius: 2px; |
| 438 | border: 1px solid #76797C; | 451 | border: 1px solid #54575B; |
| 439 | } | 452 | } |
| 440 | 453 | ||
| 441 | QHeaderView::section { | 454 | QHeaderView::section { |
| @@ -467,15 +480,6 @@ QMainWindow::separator:hover { | |||
| 467 | spacing: 2px; | 480 | spacing: 2px; |
| 468 | } | 481 | } |
| 469 | 482 | ||
| 470 | QMenu::separator { | ||
| 471 | height: 1px; | ||
| 472 | background-color: #76797C; | ||
| 473 | color: white; | ||
| 474 | padding-left: 4px; | ||
| 475 | margin-left: 10px; | ||
| 476 | margin-right: 5px; | ||
| 477 | } | ||
| 478 | |||
| 479 | QFrame { | 483 | QFrame { |
| 480 | border-radius: 2px; | 484 | border-radius: 2px; |
| 481 | border: 1px solid #76797C; | 485 | border: 1px solid #76797C; |
| @@ -518,25 +522,19 @@ QToolButton#qt_toolbar_ext_button { | |||
| 518 | 522 | ||
| 519 | QPushButton { | 523 | QPushButton { |
| 520 | color: #eff0f1; | 524 | color: #eff0f1; |
| 521 | background-color: #31363b; | ||
| 522 | border-width: 1px; | 525 | border-width: 1px; |
| 523 | border-color: #76797C; | 526 | border-color: #54575B; |
| 524 | border-style: solid; | 527 | border-style: solid; |
| 525 | padding: 5px; | 528 | padding: 6px 4px; |
| 526 | border-radius: 2px; | 529 | border-radius: 2px; |
| 527 | outline: none; | 530 | outline: none; |
| 531 | min-width: 100px; | ||
| 532 | background-color: #232629; | ||
| 528 | } | 533 | } |
| 529 | 534 | ||
| 530 | QPushButton:disabled { | 535 | QPushButton:disabled { |
| 531 | background-color: #31363b; | 536 | background-color: #31363b; |
| 532 | border-width: 1px; | ||
| 533 | border-color: #454545; | 537 | border-color: #454545; |
| 534 | border-style: solid; | ||
| 535 | padding-top: 5px; | ||
| 536 | padding-bottom: 5px; | ||
| 537 | padding-left: 10px; | ||
| 538 | padding-right: 10px; | ||
| 539 | border-radius: 2px; | ||
| 540 | color: #454545; | 538 | color: #454545; |
| 541 | } | 539 | } |
| 542 | 540 | ||
| @@ -553,11 +551,11 @@ QPushButton:pressed { | |||
| 553 | 551 | ||
| 554 | QComboBox { | 552 | QComboBox { |
| 555 | selection-background-color: #3daee9; | 553 | selection-background-color: #3daee9; |
| 556 | border-style: solid; | 554 | border: 1px solid #54575B; |
| 557 | border: 1px solid #76797C; | ||
| 558 | border-radius: 2px; | 555 | border-radius: 2px; |
| 559 | padding: 5px; | 556 | padding: 4px 6px; |
| 560 | min-width: 75px; | 557 | min-width: 75px; |
| 558 | background-color: #232629; | ||
| 561 | } | 559 | } |
| 562 | 560 | ||
| 563 | QPushButton:checked { | 561 | QPushButton:checked { |
| @@ -571,8 +569,7 @@ QAbstractSpinBox:hover, | |||
| 571 | QLineEdit:hover, | 569 | QLineEdit:hover, |
| 572 | QTextEdit:hover, | 570 | QTextEdit:hover, |
| 573 | QPlainTextEdit:hover, | 571 | QPlainTextEdit:hover, |
| 574 | QAbstractView:hover, | 572 | QAbstractView:hover { |
| 575 | QTreeView:hover { | ||
| 576 | border: 1px solid #3daee9; | 573 | border: 1px solid #3daee9; |
| 577 | color: #eff0f1; | 574 | color: #eff0f1; |
| 578 | } | 575 | } |
| @@ -591,6 +588,7 @@ QComboBox QAbstractItemView { | |||
| 591 | QComboBox::drop-down { | 588 | QComboBox::drop-down { |
| 592 | subcontrol-origin: padding; | 589 | subcontrol-origin: padding; |
| 593 | subcontrol-position: top right; | 590 | subcontrol-position: top right; |
| 591 | left: -6px; | ||
| 594 | width: 15px; | 592 | width: 15px; |
| 595 | border-left-width: 0px; | 593 | border-left-width: 0px; |
| 596 | border-left-color: darkgray; | 594 | border-left-color: darkgray; |
| @@ -610,8 +608,8 @@ QComboBox::down-arrow:focus { | |||
| 610 | } | 608 | } |
| 611 | 609 | ||
| 612 | QAbstractSpinBox { | 610 | QAbstractSpinBox { |
| 613 | padding: 5px; | 611 | padding: 4px 6px; |
| 614 | border: 1px solid #76797C; | 612 | border: 1px solid #54575B; |
| 615 | background-color: #232629; | 613 | background-color: #232629; |
| 616 | color: #eff0f1; | 614 | color: #eff0f1; |
| 617 | border-radius: 2px; | 615 | border-radius: 2px; |
| @@ -622,12 +620,14 @@ QAbstractSpinBox:up-button { | |||
| 622 | background-color: transparent; | 620 | background-color: transparent; |
| 623 | subcontrol-origin: border; | 621 | subcontrol-origin: border; |
| 624 | subcontrol-position: center right; | 622 | subcontrol-position: center right; |
| 623 | left: -6px; | ||
| 625 | } | 624 | } |
| 626 | 625 | ||
| 627 | QAbstractSpinBox:down-button { | 626 | QAbstractSpinBox:down-button { |
| 628 | background-color: transparent; | 627 | background-color: transparent; |
| 629 | subcontrol-origin: border; | 628 | subcontrol-origin: border; |
| 630 | subcontrol-position: center left; | 629 | subcontrol-position: center left; |
| 630 | right: -6px; | ||
| 631 | } | 631 | } |
| 632 | 632 | ||
| 633 | QAbstractSpinBox::up-arrow, | 633 | QAbstractSpinBox::up-arrow, |
| @@ -654,22 +654,27 @@ QAbstractSpinBox::down-arrow:hover { | |||
| 654 | image: url(:/qss_icons/rc/down_arrow.png); | 654 | image: url(:/qss_icons/rc/down_arrow.png); |
| 655 | } | 655 | } |
| 656 | 656 | ||
| 657 | QLabel { | 657 | QLabel, |
| 658 | border: 0px solid black; | 658 | QTabWidget { |
| 659 | border: 0; | ||
| 659 | } | 660 | } |
| 660 | 661 | ||
| 661 | QTabWidget { | 662 | QTabWidget { |
| 662 | border: 0px transparent black; | 663 | padding-top: 1px; |
| 663 | } | 664 | } |
| 664 | 665 | ||
| 665 | QTabWidget::pane { | 666 | QTabWidget::pane { |
| 666 | border: 1px solid #76797C; | 667 | border: 1px solid #76797C; |
| 667 | padding: 5px; | 668 | padding: 5px; |
| 668 | margin: 0px; | 669 | position: absolute; |
| 670 | top: -1px; | ||
| 671 | border-top-right-radius: 2px; | ||
| 672 | border-bottom-right-radius: 2px; | ||
| 673 | border-bottom-left-radius: 2px; | ||
| 669 | } | 674 | } |
| 670 | 675 | ||
| 671 | QTabWidget::tab-bar { | 676 | QTabWidget::tab-bar { |
| 672 | /* left: 5px; move to the right by 5px */ | 677 | overflow: visible; |
| 673 | } | 678 | } |
| 674 | 679 | ||
| 675 | QTabBar { | 680 | QTabBar { |
| @@ -677,10 +682,6 @@ QTabBar { | |||
| 677 | border-radius: 3px; | 682 | border-radius: 3px; |
| 678 | } | 683 | } |
| 679 | 684 | ||
| 680 | QTabBar:focus { | ||
| 681 | border: 0px transparent black; | ||
| 682 | } | ||
| 683 | |||
| 684 | QTabBar::close-button { | 685 | QTabBar::close-button { |
| 685 | image: url(:/qss_icons/rc/close.png); | 686 | image: url(:/qss_icons/rc/close.png); |
| 686 | background: transparent; | 687 | background: transparent; |
| @@ -696,36 +697,33 @@ QTabBar::close-button:pressed { | |||
| 696 | background: transparent; | 697 | background: transparent; |
| 697 | } | 698 | } |
| 698 | 699 | ||
| 699 | |||
| 700 | /* TOP TABS */ | 700 | /* TOP TABS */ |
| 701 | |||
| 702 | QTabBar::tab:top { | 701 | QTabBar::tab:top { |
| 703 | color: #eff0f1; | 702 | color: #eff0f1; |
| 704 | border: 1px solid #76797C; | 703 | border: 1px solid #54575B; |
| 705 | border-bottom: 2px transparent; | 704 | background-color: #2a2f33; |
| 706 | background-color: #31363b; | 705 | padding: 4px 16px 5px; |
| 707 | padding: 4px 16px 2px; | 706 | min-width: 36px; |
| 708 | min-width: 38px; | ||
| 709 | border-top-left-radius: 2px; | 707 | border-top-left-radius: 2px; |
| 710 | border-top-right-radius: 2px; | 708 | border-top-right-radius: 2px; |
| 711 | } | 709 | } |
| 712 | 710 | ||
| 713 | QTabBar::tab:top:selected { | 711 | QTabBar::tab:top:selected { |
| 714 | color: #eff0f1; | 712 | border-color: #76797C; |
| 715 | background-color: #54575B; | 713 | background-color: #31363b; |
| 716 | border: 1px solid #76797C; | 714 | border-bottom-color: #31363b; |
| 717 | border-bottom: 2px solid #3daee9; | 715 | } |
| 718 | border-top-left-radius: 2px; | 716 | |
| 719 | border-top-right-radius: 2px; | 717 | QTabBar::tab:top:!selected { |
| 718 | margin-top: 1px; | ||
| 719 | border-bottom-color: #76797C; | ||
| 720 | } | 720 | } |
| 721 | 721 | ||
| 722 | QTabBar::tab:top:!selected:hover { | 722 | QTabBar::tab:top:!selected:hover { |
| 723 | background-color: #3daee9; | 723 | background-color: #3daee9; |
| 724 | } | 724 | } |
| 725 | 725 | ||
| 726 | |||
| 727 | /* BOTTOM TABS */ | 726 | /* BOTTOM TABS */ |
| 728 | |||
| 729 | QTabBar::tab:bottom { | 727 | QTabBar::tab:bottom { |
| 730 | color: #eff0f1; | 728 | color: #eff0f1; |
| 731 | border: 1px solid #76797C; | 729 | border: 1px solid #76797C; |
| @@ -750,9 +748,7 @@ QTabBar::tab:bottom:!selected:hover { | |||
| 750 | background-color: #3daee9; | 748 | background-color: #3daee9; |
| 751 | } | 749 | } |
| 752 | 750 | ||
| 753 | |||
| 754 | /* LEFT TABS */ | 751 | /* LEFT TABS */ |
| 755 | |||
| 756 | QTabBar::tab:left { | 752 | QTabBar::tab:left { |
| 757 | color: #eff0f1; | 753 | color: #eff0f1; |
| 758 | border: 1px solid #76797C; | 754 | border: 1px solid #76797C; |
| @@ -777,9 +773,7 @@ QTabBar::tab:left:!selected:hover { | |||
| 777 | background-color: #3daee9; | 773 | background-color: #3daee9; |
| 778 | } | 774 | } |
| 779 | 775 | ||
| 780 | |||
| 781 | /* RIGHT TABS */ | 776 | /* RIGHT TABS */ |
| 782 | |||
| 783 | QTabBar::tab:right { | 777 | QTabBar::tab:right { |
| 784 | color: #eff0f1; | 778 | color: #eff0f1; |
| 785 | border: 1px solid #76797C; | 779 | border: 1px solid #76797C; |
| @@ -847,7 +841,7 @@ QDockWidget::float-button:pressed { | |||
| 847 | 841 | ||
| 848 | QTreeView, | 842 | QTreeView, |
| 849 | QListView { | 843 | QListView { |
| 850 | border: 1px solid #76797C; | 844 | border: 1px solid #54575B; |
| 851 | background-color: #232629; | 845 | background-color: #232629; |
| 852 | } | 846 | } |
| 853 | 847 | ||
| @@ -978,8 +972,8 @@ QSlider::handle:vertical { | |||
| 978 | } | 972 | } |
| 979 | 973 | ||
| 980 | QToolButton { | 974 | QToolButton { |
| 981 | background-color: transparent; | 975 | background-color: #232629; |
| 982 | border: 1px transparent #76797C; | 976 | border: 1px solid #54575B; |
| 983 | border-radius: 2px; | 977 | border-radius: 2px; |
| 984 | margin: 3px; | 978 | margin: 3px; |
| 985 | padding: 5px; | 979 | padding: 5px; |
| @@ -988,7 +982,6 @@ QToolButton { | |||
| 988 | QToolButton[popupMode="1"] { | 982 | QToolButton[popupMode="1"] { |
| 989 | /* only for MenuButtonPopup */ | 983 | /* only for MenuButtonPopup */ |
| 990 | padding-right: 20px; | 984 | padding-right: 20px; |
| 991 | /* make way for the popup button */ | ||
| 992 | border: 1px #76797C; | 985 | border: 1px #76797C; |
| 993 | border-radius: 5px; | 986 | border-radius: 5px; |
| 994 | } | 987 | } |
| @@ -996,7 +989,6 @@ QToolButton[popupMode="1"] { | |||
| 996 | QToolButton[popupMode="2"] { | 989 | QToolButton[popupMode="2"] { |
| 997 | /* only for InstantPopup */ | 990 | /* only for InstantPopup */ |
| 998 | padding-right: 10px; | 991 | padding-right: 10px; |
| 999 | /* make way for the popup button */ | ||
| 1000 | border: 1px #76797C; | 992 | border: 1px #76797C; |
| 1001 | } | 993 | } |
| 1002 | 994 | ||
| @@ -1015,19 +1007,14 @@ QToolButton::menu-button:pressed { | |||
| 1015 | padding: 5px; | 1007 | padding: 5px; |
| 1016 | } | 1008 | } |
| 1017 | 1009 | ||
| 1018 | |||
| 1019 | /* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */ | 1010 | /* the subcontrol below is used only in the InstantPopup or DelayedPopup mode */ |
| 1020 | |||
| 1021 | QToolButton::menu-indicator { | 1011 | QToolButton::menu-indicator { |
| 1022 | image: url(:/qss_icons/rc/down_arrow.png); | 1012 | image: url(:/qss_icons/rc/down_arrow.png); |
| 1023 | top: -7px; | 1013 | top: -7px; |
| 1024 | left: -2px; | 1014 | left: -2px; |
| 1025 | /* shift it a bit */ | ||
| 1026 | } | 1015 | } |
| 1027 | 1016 | ||
| 1028 | |||
| 1029 | /* the subcontrols below are used only in the MenuButtonPopup mode */ | 1017 | /* the subcontrols below are used only in the MenuButtonPopup mode */ |
| 1030 | |||
| 1031 | QToolButton::menu-button { | 1018 | QToolButton::menu-button { |
| 1032 | border: 1px transparent #76797C; | 1019 | border: 1px transparent #76797C; |
| 1033 | border-top-right-radius: 6px; | 1020 | border-top-right-radius: 6px; |
| @@ -1052,14 +1039,22 @@ QPushButton::menu-indicator { | |||
| 1052 | } | 1039 | } |
| 1053 | 1040 | ||
| 1054 | QTableView { | 1041 | QTableView { |
| 1055 | border: 1px solid #76797C; | 1042 | border: 1px solid #54575B; |
| 1056 | gridline-color: #31363b; | 1043 | gridline-color: #31363b; |
| 1057 | background-color: #232629; | 1044 | background-color: #232629; |
| 1058 | } | 1045 | } |
| 1059 | 1046 | ||
| 1047 | QTreeView:disabled { | ||
| 1048 | background-color: #1f2225; | ||
| 1049 | } | ||
| 1050 | |||
| 1060 | QTableView, | 1051 | QTableView, |
| 1061 | QHeaderView { | 1052 | QHeaderView { |
| 1062 | border-radius: 0px; | 1053 | border-radius: 0; |
| 1054 | } | ||
| 1055 | |||
| 1056 | QListView:focus { | ||
| 1057 | border-color: #54575B; | ||
| 1063 | } | 1058 | } |
| 1064 | 1059 | ||
| 1065 | QTableView::item:pressed, | 1060 | QTableView::item:pressed, |
| @@ -1088,7 +1083,7 @@ QHeaderView::section { | |||
| 1088 | background-color: #232629; | 1083 | background-color: #232629; |
| 1089 | color: #eff0f1; | 1084 | color: #eff0f1; |
| 1090 | padding: 0 5px; | 1085 | padding: 0 5px; |
| 1091 | border: 1px solid #403F3F; | 1086 | border: 1px solid #434242; |
| 1092 | border-bottom: 0; | 1087 | border-bottom: 0; |
| 1093 | border-radius: 0px; | 1088 | border-radius: 0px; |
| 1094 | text-align: center; | 1089 | text-align: center; |
| @@ -1118,9 +1113,7 @@ QHeaderView::section:checked { | |||
| 1118 | background-color: #334e5e; | 1113 | background-color: #334e5e; |
| 1119 | } | 1114 | } |
| 1120 | 1115 | ||
| 1121 | 1116 | /* sort indicator */ | |
| 1122 | /* style the sort indicator */ | ||
| 1123 | |||
| 1124 | QHeaderView::down-arrow { | 1117 | QHeaderView::down-arrow { |
| 1125 | image: url(:/qss_icons/rc/down_arrow.png); | 1118 | image: url(:/qss_icons/rc/down_arrow.png); |
| 1126 | } | 1119 | } |
| @@ -1150,14 +1143,13 @@ QToolBox::tab { | |||
| 1150 | } | 1143 | } |
| 1151 | 1144 | ||
| 1152 | QToolBox::tab:selected { | 1145 | QToolBox::tab:selected { |
| 1153 | /* italicize selected tabs */ | ||
| 1154 | font: italic; | 1146 | font: italic; |
| 1155 | background-color: #31363b; | 1147 | background-color: #31363b; |
| 1156 | border-color: #3daee9; | 1148 | border-color: #3daee9; |
| 1157 | } | 1149 | } |
| 1158 | 1150 | ||
| 1159 | QStatusBar::item { | 1151 | QStatusBar::item { |
| 1160 | border: 0px transparent dark; | 1152 | border: 0; |
| 1161 | } | 1153 | } |
| 1162 | 1154 | ||
| 1163 | QFrame[height="3"], | 1155 | QFrame[height="3"], |
| @@ -1194,7 +1186,6 @@ QProgressBar::chunk { | |||
| 1194 | 1186 | ||
| 1195 | QDateEdit { | 1187 | QDateEdit { |
| 1196 | selection-background-color: #3daee9; | 1188 | selection-background-color: #3daee9; |
| 1197 | border-style: solid; | ||
| 1198 | border: 1px solid #3375A3; | 1189 | border: 1px solid #3375A3; |
| 1199 | border-radius: 2px; | 1190 | border-radius: 2px; |
| 1200 | padding: 1px; | 1191 | padding: 1px; |
| @@ -1218,7 +1209,7 @@ QDateEdit::drop-down { | |||
| 1218 | subcontrol-origin: padding; | 1209 | subcontrol-origin: padding; |
| 1219 | subcontrol-position: top right; | 1210 | subcontrol-position: top right; |
| 1220 | width: 15px; | 1211 | width: 15px; |
| 1221 | border-left-width: 0px; | 1212 | border-left-width: 0; |
| 1222 | border-left-color: darkgray; | 1213 | border-left-color: darkgray; |
| 1223 | border-left-style: solid; | 1214 | border-left-style: solid; |
| 1224 | border-top-right-radius: 3px; | 1215 | border-top-right-radius: 3px; |
| @@ -1234,3 +1225,52 @@ QDateEdit::down-arrow:hover, | |||
| 1234 | QDateEdit::down-arrow:focus { | 1225 | QDateEdit::down-arrow:focus { |
| 1235 | image: url(:/qss_icons/rc/down_arrow.png); | 1226 | image: url(:/qss_icons/rc/down_arrow.png); |
| 1236 | } | 1227 | } |
| 1228 | |||
| 1229 | QComboBox:disabled, | ||
| 1230 | QPushButton:disabled, | ||
| 1231 | QAbstractSpinBox:disabled, | ||
| 1232 | QDateEdit:disabled, | ||
| 1233 | QLineEdit:disabled, | ||
| 1234 | QTextEdit:disabled, | ||
| 1235 | QToolButton:disabled, | ||
| 1236 | QPlainTextEdit:disabled { | ||
| 1237 | background-color: #2b2e31; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | QPushButton#TogglableStatusBarButton { | ||
| 1241 | min-width: 0px; | ||
| 1242 | color: #656565; | ||
| 1243 | border: 1px solid transparent; | ||
| 1244 | background-color: transparent; | ||
| 1245 | padding: 0px 3px 0px 3px; | ||
| 1246 | text-align: center; | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | QPushButton#TogglableStatusBarButton:checked { | ||
| 1250 | color: #ffffff; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | QPushButton#TogglableStatusBarButton:hover { | ||
| 1254 | border: 1px solid #76797C; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | QPushButton#RendererStatusBarButton { | ||
| 1258 | min-width: 0px; | ||
| 1259 | color: #656565; | ||
| 1260 | border: 1px solid transparent; | ||
| 1261 | background-color: transparent; | ||
| 1262 | padding: 0px 3px 0px 3px; | ||
| 1263 | text-align: center; | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | QPushButton#RendererStatusBarButton:hover { | ||
| 1267 | border: 1px solid #76797C; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | QPushButton#RendererStatusBarButton:checked { | ||
| 1271 | color: #e85c00; | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | QPushButton#RendererStatusBarButton:!checked{ | ||
| 1275 | color: #00ccdd; | ||
| 1276 | } \ No newline at end of file | ||
diff --git a/externals/cmake-modules/FindSDL2.cmake b/externals/cmake-modules/FindSDL2.cmake deleted file mode 100644 index 22ce752c5..000000000 --- a/externals/cmake-modules/FindSDL2.cmake +++ /dev/null | |||
| @@ -1,239 +0,0 @@ | |||
| 1 | |||
| 2 | # This module defines | ||
| 3 | # SDL2_LIBRARY, the name of the library to link against | ||
| 4 | # SDL2_FOUND, if false, do not try to link to SDL2 | ||
| 5 | # SDL2_INCLUDE_DIR, where to find SDL.h | ||
| 6 | # SDL2_DLL_DIR, where to find SDL2.dll if it exists | ||
| 7 | # | ||
| 8 | # This module responds to the the flag: | ||
| 9 | # SDL2_BUILDING_LIBRARY | ||
| 10 | # If this is defined, then no SDL2main will be linked in because | ||
| 11 | # only applications need main(). | ||
| 12 | # Otherwise, it is assumed you are building an application and this | ||
| 13 | # module will attempt to locate and set the the proper link flags | ||
| 14 | # as part of the returned SDL2_LIBRARY variable. | ||
| 15 | # | ||
| 16 | # Don't forget to include SDLmain.h and SDLmain.m your project for the | ||
| 17 | # OS X framework based version. (Other versions link to -lSDL2main which | ||
| 18 | # this module will try to find on your behalf.) Also for OS X, this | ||
| 19 | # module will automatically add the -framework Cocoa on your behalf. | ||
| 20 | # | ||
| 21 | # | ||
| 22 | # Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration | ||
| 23 | # and no SDL2_LIBRARY, it means CMake did not find your SDL2 library | ||
| 24 | # (SDL2.dll, libsdl2.so, SDL2.framework, etc). | ||
| 25 | # Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. | ||
| 26 | # Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value | ||
| 27 | # as appropriate. These values are used to generate the final SDL2_LIBRARY | ||
| 28 | # variable, but when these values are unset, SDL2_LIBRARY does not get created. | ||
| 29 | # | ||
| 30 | # | ||
| 31 | # $SDL2DIR is an environment variable that would | ||
| 32 | # correspond to the ./configure --prefix=$SDL2DIR | ||
| 33 | # used in building SDL2. | ||
| 34 | # l.e.galup 9-20-02 | ||
| 35 | # | ||
| 36 | # Modified by Eric Wing. | ||
| 37 | # Added code to assist with automated building by using environmental variables | ||
| 38 | # and providing a more controlled/consistent search behavior. | ||
| 39 | # Added new modifications to recognize OS X frameworks and | ||
| 40 | # additional Unix paths (FreeBSD, etc). | ||
| 41 | # Also corrected the header search path to follow "proper" SDL guidelines. | ||
| 42 | # Added a search for SDL2main which is needed by some platforms. | ||
| 43 | # Added a search for threads which is needed by some platforms. | ||
| 44 | # Added needed compile switches for MinGW. | ||
| 45 | # | ||
| 46 | # On OSX, this will prefer the Framework version (if found) over others. | ||
| 47 | # People will have to manually change the cache values of | ||
| 48 | # SDL2_LIBRARY to override this selection or set the CMake environment | ||
| 49 | # CMAKE_INCLUDE_PATH to modify the search paths. | ||
| 50 | # | ||
| 51 | # Note that the header path has changed from SDL2/SDL.h to just SDL.h | ||
| 52 | # This needed to change because "proper" SDL convention | ||
| 53 | # is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability | ||
| 54 | # reasons because not all systems place things in SDL2/ (see FreeBSD). | ||
| 55 | |||
| 56 | #============================================================================= | ||
| 57 | # Copyright 2003-2009 Kitware, Inc. | ||
| 58 | # | ||
| 59 | # Distributed under the OSI-approved BSD License (the "License"). | ||
| 60 | # | ||
| 61 | # This software is distributed WITHOUT ANY WARRANTY; without even the | ||
| 62 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
| 63 | # See the License for more information. | ||
| 64 | #============================================================================= | ||
| 65 | # CMake - Cross Platform Makefile Generator | ||
| 66 | # Copyright 2000-2016 Kitware, Inc. | ||
| 67 | # Copyright 2000-2011 Insight Software Consortium | ||
| 68 | # All rights reserved. | ||
| 69 | # | ||
| 70 | # Redistribution and use in source and binary forms, with or without | ||
| 71 | # modification, are permitted provided that the following conditions | ||
| 72 | # are met: | ||
| 73 | # | ||
| 74 | # * Redistributions of source code must retain the above copyright | ||
| 75 | # notice, this list of conditions and the following disclaimer. | ||
| 76 | # | ||
| 77 | # * Redistributions in binary form must reproduce the above copyright | ||
| 78 | # notice, this list of conditions and the following disclaimer in the | ||
| 79 | # documentation and/or other materials provided with the distribution. | ||
| 80 | # | ||
| 81 | # * Neither the names of Kitware, Inc., the Insight Software Consortium, | ||
| 82 | # nor the names of their contributors may be used to endorse or promote | ||
| 83 | # products derived from this software without specific prior written | ||
| 84 | # permission. | ||
| 85 | # | ||
| 86 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 87 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 88 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 89 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 90 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 91 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 92 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 93 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 94 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 95 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 96 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 97 | # | ||
| 98 | # ------------------------------------------------------------------------------ | ||
| 99 | # | ||
| 100 | # The above copyright and license notice applies to distributions of | ||
| 101 | # CMake in source and binary form. Some source files contain additional | ||
| 102 | # notices of original copyright by their contributors; see each source | ||
| 103 | # for details. Third-party software packages supplied with CMake under | ||
| 104 | # compatible licenses provide their own copyright notices documented in | ||
| 105 | # corresponding subdirectories. | ||
| 106 | # | ||
| 107 | # ------------------------------------------------------------------------------ | ||
| 108 | # | ||
| 109 | # CMake was initially developed by Kitware with the following sponsorship: | ||
| 110 | # | ||
| 111 | # * National Library of Medicine at the National Institutes of Health | ||
| 112 | # as part of the Insight Segmentation and Registration Toolkit (ITK). | ||
| 113 | # | ||
| 114 | # * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel | ||
| 115 | # Visualization Initiative. | ||
| 116 | # | ||
| 117 | # * National Alliance for Medical Image Computing (NAMIC) is funded by the | ||
| 118 | # National Institutes of Health through the NIH Roadmap for Medical Research, | ||
| 119 | # Grant U54 EB005149. | ||
| 120 | # | ||
| 121 | # * Kitware, Inc. | ||
| 122 | # | ||
| 123 | |||
| 124 | message("<FindSDL2.cmake>") | ||
| 125 | |||
| 126 | SET(SDL2_SEARCH_PATHS | ||
| 127 | ~/Library/Frameworks | ||
| 128 | /Library/Frameworks | ||
| 129 | /usr/local | ||
| 130 | /usr | ||
| 131 | /sw # Fink | ||
| 132 | /opt/local # DarwinPorts | ||
| 133 | /opt/csw # Blastwave | ||
| 134 | /opt | ||
| 135 | ${SDL2_PATH} | ||
| 136 | ) | ||
| 137 | |||
| 138 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) | ||
| 139 | set(VC_LIB_PATH_SUFFIX lib/x64) | ||
| 140 | else() | ||
| 141 | set(VC_LIB_PATH_SUFFIX lib/x86) | ||
| 142 | endif() | ||
| 143 | |||
| 144 | FIND_LIBRARY(SDL2_LIBRARY_TEMP | ||
| 145 | NAMES SDL2 | ||
| 146 | HINTS | ||
| 147 | $ENV{SDL2DIR} | ||
| 148 | PATH_SUFFIXES lib64 lib ${VC_LIB_PATH_SUFFIX} | ||
| 149 | PATHS ${SDL2_SEARCH_PATHS} | ||
| 150 | ) | ||
| 151 | |||
| 152 | IF(SDL2_LIBRARY_TEMP) | ||
| 153 | if(MSVC) | ||
| 154 | get_filename_component(SDL2_DLL_DIR_TEMP ${SDL2_LIBRARY_TEMP} DIRECTORY) | ||
| 155 | if(EXISTS ${SDL2_DLL_DIR_TEMP}/SDL2.dll) | ||
| 156 | set(SDL2_DLL_DIR ${SDL2_DLL_DIR_TEMP}) | ||
| 157 | unset(SDL2_DLL_DIR_TEMP) | ||
| 158 | endif() | ||
| 159 | endif() | ||
| 160 | |||
| 161 | FIND_PATH(SDL2_INCLUDE_DIR SDL.h | ||
| 162 | HINTS | ||
| 163 | $ENV{SDL2DIR} | ||
| 164 | PATH_SUFFIXES include/SDL2 include | ||
| 165 | PATHS ${SDL2_SEARCH_PATHS} | ||
| 166 | ) | ||
| 167 | |||
| 168 | IF(NOT SDL2_BUILDING_LIBRARY) | ||
| 169 | IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") | ||
| 170 | # Non-OS X framework versions expect you to also dynamically link to | ||
| 171 | # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms | ||
| 172 | # seem to provide SDL2main for compatibility even though they don't | ||
| 173 | # necessarily need it. | ||
| 174 | FIND_LIBRARY(SDL2MAIN_LIBRARY | ||
| 175 | NAMES SDL2main | ||
| 176 | HINTS | ||
| 177 | $ENV{SDL2DIR} | ||
| 178 | PATH_SUFFIXES lib64 lib | ||
| 179 | PATHS ${SDL2_SEARCH_PATHS} | ||
| 180 | ) | ||
| 181 | ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") | ||
| 182 | ENDIF(NOT SDL2_BUILDING_LIBRARY) | ||
| 183 | |||
| 184 | # SDL2 may require threads on your system. | ||
| 185 | # The Apple build may not need an explicit flag because one of the | ||
| 186 | # frameworks may already provide it. | ||
| 187 | # But for non-OSX systems, I will use the CMake Threads package. | ||
| 188 | IF(NOT APPLE) | ||
| 189 | FIND_PACKAGE(Threads) | ||
| 190 | ENDIF(NOT APPLE) | ||
| 191 | |||
| 192 | # MinGW needs an additional library, mwindows | ||
| 193 | # It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows | ||
| 194 | # (Actually on second look, I think it only needs one of the m* libraries.) | ||
| 195 | IF(MINGW) | ||
| 196 | SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") | ||
| 197 | ENDIF(MINGW) | ||
| 198 | |||
| 199 | # For SDL2main | ||
| 200 | IF(NOT SDL2_BUILDING_LIBRARY) | ||
| 201 | IF(SDL2MAIN_LIBRARY) | ||
| 202 | SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) | ||
| 203 | ENDIF(SDL2MAIN_LIBRARY) | ||
| 204 | ENDIF(NOT SDL2_BUILDING_LIBRARY) | ||
| 205 | |||
| 206 | # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. | ||
| 207 | # CMake doesn't display the -framework Cocoa string in the UI even | ||
| 208 | # though it actually is there if I modify a pre-used variable. | ||
| 209 | # I think it has something to do with the CACHE STRING. | ||
| 210 | # So I use a temporary variable until the end so I can set the | ||
| 211 | # "real" variable in one-shot. | ||
| 212 | IF(APPLE) | ||
| 213 | SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") | ||
| 214 | ENDIF(APPLE) | ||
| 215 | |||
| 216 | # For threads, as mentioned Apple doesn't need this. | ||
| 217 | # In fact, there seems to be a problem if I used the Threads package | ||
| 218 | # and try using this line, so I'm just skipping it entirely for OS X. | ||
| 219 | IF(NOT APPLE) | ||
| 220 | SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) | ||
| 221 | ENDIF(NOT APPLE) | ||
| 222 | |||
| 223 | # For MinGW library | ||
| 224 | IF(MINGW) | ||
| 225 | SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) | ||
| 226 | ENDIF(MINGW) | ||
| 227 | |||
| 228 | # Set the final string here so the GUI reflects the final state. | ||
| 229 | SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") | ||
| 230 | |||
| 231 | # Unset the temp variable to INTERNAL so it is not seen in the CMake GUI | ||
| 232 | UNSET(SDL2_LIBRARY_TEMP) | ||
| 233 | ENDIF(SDL2_LIBRARY_TEMP) | ||
| 234 | |||
| 235 | message("</FindSDL2.cmake>") | ||
| 236 | |||
| 237 | INCLUDE(FindPackageHandleStandardArgs) | ||
| 238 | |||
| 239 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) | ||
diff --git a/externals/httplib/README.md b/externals/httplib/README.md index 0e26522b5..73037d297 100644 --- a/externals/httplib/README.md +++ b/externals/httplib/README.md | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | From https://github.com/yhirose/cpp-httplib/commit/d9479bc0b12e8a1e8bce2d34da4feeef488581f3 | 1 | From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53 |
| 2 | 2 | ||
| 3 | MIT License | 3 | MIT License |
| 4 | 4 | ||
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h index fa2edcc94..e03842e6d 100644 --- a/externals/httplib/httplib.h +++ b/externals/httplib/httplib.h | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | // | 1 | // |
| 2 | // httplib.h | 2 | // httplib.h |
| 3 | // | 3 | // |
| 4 | // Copyright (c) 2019 Yuji Hirose. All rights reserved. | 4 | // Copyright (c) 2020 Yuji Hirose. All rights reserved. |
| 5 | // MIT License | 5 | // MIT License |
| 6 | // | 6 | // |
| 7 | 7 | ||
| @@ -11,6 +11,7 @@ | |||
| 11 | /* | 11 | /* |
| 12 | * Configuration | 12 | * Configuration |
| 13 | */ | 13 | */ |
| 14 | |||
| 14 | #ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND | 15 | #ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND |
| 15 | #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 | 16 | #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 |
| 16 | #endif | 17 | #endif |
| @@ -40,7 +41,7 @@ | |||
| 40 | #endif | 41 | #endif |
| 41 | 42 | ||
| 42 | #ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH | 43 | #ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH |
| 43 | #define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max)() | 44 | #define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max()) |
| 44 | #endif | 45 | #endif |
| 45 | 46 | ||
| 46 | #ifndef CPPHTTPLIB_RECV_BUFSIZ | 47 | #ifndef CPPHTTPLIB_RECV_BUFSIZ |
| @@ -48,9 +49,14 @@ | |||
| 48 | #endif | 49 | #endif |
| 49 | 50 | ||
| 50 | #ifndef CPPHTTPLIB_THREAD_POOL_COUNT | 51 | #ifndef CPPHTTPLIB_THREAD_POOL_COUNT |
| 51 | #define CPPHTTPLIB_THREAD_POOL_COUNT 8 | 52 | #define CPPHTTPLIB_THREAD_POOL_COUNT \ |
| 53 | (std::max(1u, std::thread::hardware_concurrency() - 1)) | ||
| 52 | #endif | 54 | #endif |
| 53 | 55 | ||
| 56 | /* | ||
| 57 | * Headers | ||
| 58 | */ | ||
| 59 | |||
| 54 | #ifdef _WIN32 | 60 | #ifdef _WIN32 |
| 55 | #ifndef _CRT_SECURE_NO_WARNINGS | 61 | #ifndef _CRT_SECURE_NO_WARNINGS |
| 56 | #define _CRT_SECURE_NO_WARNINGS | 62 | #define _CRT_SECURE_NO_WARNINGS |
| @@ -62,9 +68,9 @@ | |||
| 62 | 68 | ||
| 63 | #if defined(_MSC_VER) | 69 | #if defined(_MSC_VER) |
| 64 | #ifdef _WIN64 | 70 | #ifdef _WIN64 |
| 65 | typedef __int64 ssize_t; | 71 | using ssize_t = __int64; |
| 66 | #else | 72 | #else |
| 67 | typedef int ssize_t; | 73 | using ssize_t = int; |
| 68 | #endif | 74 | #endif |
| 69 | 75 | ||
| 70 | #if _MSC_VER < 1900 | 76 | #if _MSC_VER < 1900 |
| @@ -100,7 +106,7 @@ typedef int ssize_t; | |||
| 100 | #define strcasecmp _stricmp | 106 | #define strcasecmp _stricmp |
| 101 | #endif // strcasecmp | 107 | #endif // strcasecmp |
| 102 | 108 | ||
| 103 | typedef SOCKET socket_t; | 109 | using socket_t = SOCKET; |
| 104 | #ifdef CPPHTTPLIB_USE_POLL | 110 | #ifdef CPPHTTPLIB_USE_POLL |
| 105 | #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) | 111 | #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout) |
| 106 | #endif | 112 | #endif |
| @@ -109,23 +115,25 @@ typedef SOCKET socket_t; | |||
| 109 | 115 | ||
| 110 | #include <arpa/inet.h> | 116 | #include <arpa/inet.h> |
| 111 | #include <cstring> | 117 | #include <cstring> |
| 118 | #include <ifaddrs.h> | ||
| 112 | #include <netdb.h> | 119 | #include <netdb.h> |
| 113 | #include <netinet/in.h> | 120 | #include <netinet/in.h> |
| 114 | #ifdef CPPHTTPLIB_USE_POLL | 121 | #ifdef CPPHTTPLIB_USE_POLL |
| 115 | #include <poll.h> | 122 | #include <poll.h> |
| 116 | #endif | 123 | #endif |
| 124 | #include <csignal> | ||
| 117 | #include <pthread.h> | 125 | #include <pthread.h> |
| 118 | #include <signal.h> | ||
| 119 | #include <sys/select.h> | 126 | #include <sys/select.h> |
| 120 | #include <sys/socket.h> | 127 | #include <sys/socket.h> |
| 121 | #include <unistd.h> | 128 | #include <unistd.h> |
| 122 | 129 | ||
| 123 | typedef int socket_t; | 130 | using socket_t = int; |
| 124 | #define INVALID_SOCKET (-1) | 131 | #define INVALID_SOCKET (-1) |
| 125 | #endif //_WIN32 | 132 | #endif //_WIN32 |
| 126 | 133 | ||
| 127 | #include <assert.h> | 134 | #include <array> |
| 128 | #include <atomic> | 135 | #include <atomic> |
| 136 | #include <cassert> | ||
| 129 | #include <condition_variable> | 137 | #include <condition_variable> |
| 130 | #include <errno.h> | 138 | #include <errno.h> |
| 131 | #include <fcntl.h> | 139 | #include <fcntl.h> |
| @@ -143,9 +151,13 @@ typedef int socket_t; | |||
| 143 | 151 | ||
| 144 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | 152 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT |
| 145 | #include <openssl/err.h> | 153 | #include <openssl/err.h> |
| 154 | #include <openssl/md5.h> | ||
| 146 | #include <openssl/ssl.h> | 155 | #include <openssl/ssl.h> |
| 147 | #include <openssl/x509v3.h> | 156 | #include <openssl/x509v3.h> |
| 148 | 157 | ||
| 158 | #include <iomanip> | ||
| 159 | #include <sstream> | ||
| 160 | |||
| 149 | // #if OPENSSL_VERSION_NUMBER < 0x1010100fL | 161 | // #if OPENSSL_VERSION_NUMBER < 0x1010100fL |
| 150 | // #error Sorry, OpenSSL versions prior to 1.1.1 are not supported | 162 | // #error Sorry, OpenSSL versions prior to 1.1.1 are not supported |
| 151 | // #endif | 163 | // #endif |
| @@ -162,6 +174,9 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) { | |||
| 162 | #include <zlib.h> | 174 | #include <zlib.h> |
| 163 | #endif | 175 | #endif |
| 164 | 176 | ||
| 177 | /* | ||
| 178 | * Declaration | ||
| 179 | */ | ||
| 165 | namespace httplib { | 180 | namespace httplib { |
| 166 | 181 | ||
| 167 | namespace detail { | 182 | namespace detail { |
| @@ -176,37 +191,15 @@ struct ci { | |||
| 176 | 191 | ||
| 177 | } // namespace detail | 192 | } // namespace detail |
| 178 | 193 | ||
| 179 | enum class HttpVersion { v1_0 = 0, v1_1 }; | 194 | using Headers = std::multimap<std::string, std::string, detail::ci>; |
| 180 | |||
| 181 | typedef std::multimap<std::string, std::string, detail::ci> Headers; | ||
| 182 | |||
| 183 | typedef std::multimap<std::string, std::string> Params; | ||
| 184 | typedef std::smatch Match; | ||
| 185 | |||
| 186 | typedef std::function<void(const char *data, size_t data_len)> DataSink; | ||
| 187 | |||
| 188 | typedef std::function<void()> Done; | ||
| 189 | |||
| 190 | typedef std::function<void(size_t offset, size_t length, DataSink sink, | ||
| 191 | Done done)> | ||
| 192 | ContentProvider; | ||
| 193 | 195 | ||
| 194 | typedef std::function<bool(const char *data, size_t data_length, size_t offset, | 196 | using Params = std::multimap<std::string, std::string>; |
| 195 | uint64_t content_length)> | 197 | using Match = std::smatch; |
| 196 | ContentReceiver; | ||
| 197 | 198 | ||
| 198 | typedef std::function<bool(uint64_t current, uint64_t total)> Progress; | 199 | using Progress = std::function<bool(uint64_t current, uint64_t total)>; |
| 199 | 200 | ||
| 200 | struct Response; | 201 | struct Response; |
| 201 | typedef std::function<bool(const Response &response)> ResponseHandler; | 202 | using ResponseHandler = std::function<bool(const Response &response)>; |
| 202 | |||
| 203 | struct MultipartFile { | ||
| 204 | std::string filename; | ||
| 205 | std::string content_type; | ||
| 206 | size_t offset = 0; | ||
| 207 | size_t length = 0; | ||
| 208 | }; | ||
| 209 | typedef std::multimap<std::string, MultipartFile> MultipartFiles; | ||
| 210 | 203 | ||
| 211 | struct MultipartFormData { | 204 | struct MultipartFormData { |
| 212 | std::string name; | 205 | std::string name; |
| @@ -214,10 +207,53 @@ struct MultipartFormData { | |||
| 214 | std::string filename; | 207 | std::string filename; |
| 215 | std::string content_type; | 208 | std::string content_type; |
| 216 | }; | 209 | }; |
| 217 | typedef std::vector<MultipartFormData> MultipartFormDataItems; | 210 | using MultipartFormDataItems = std::vector<MultipartFormData>; |
| 211 | using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>; | ||
| 212 | |||
| 213 | class DataSink { | ||
| 214 | public: | ||
| 215 | DataSink() = default; | ||
| 216 | DataSink(const DataSink &) = delete; | ||
| 217 | DataSink &operator=(const DataSink &) = delete; | ||
| 218 | DataSink(DataSink &&) = delete; | ||
| 219 | DataSink &operator=(DataSink &&) = delete; | ||
| 220 | |||
| 221 | std::function<void(const char *data, size_t data_len)> write; | ||
| 222 | std::function<void()> done; | ||
| 223 | std::function<bool()> is_writable; | ||
| 224 | }; | ||
| 225 | |||
| 226 | using ContentProvider = | ||
| 227 | std::function<void(size_t offset, size_t length, DataSink &sink)>; | ||
| 228 | |||
| 229 | using ContentReceiver = | ||
| 230 | std::function<bool(const char *data, size_t data_length)>; | ||
| 231 | |||
| 232 | using MultipartContentHeader = | ||
| 233 | std::function<bool(const MultipartFormData &file)>; | ||
| 234 | |||
| 235 | class ContentReader { | ||
| 236 | public: | ||
| 237 | using Reader = std::function<bool(ContentReceiver receiver)>; | ||
| 238 | using MultipartReader = std::function<bool(MultipartContentHeader header, | ||
| 239 | ContentReceiver receiver)>; | ||
| 240 | |||
| 241 | ContentReader(Reader reader, MultipartReader muitlpart_reader) | ||
| 242 | : reader_(reader), muitlpart_reader_(muitlpart_reader) {} | ||
| 243 | |||
| 244 | bool operator()(MultipartContentHeader header, | ||
| 245 | ContentReceiver receiver) const { | ||
| 246 | return muitlpart_reader_(header, receiver); | ||
| 247 | } | ||
| 248 | |||
| 249 | bool operator()(ContentReceiver receiver) const { return reader_(receiver); } | ||
| 250 | |||
| 251 | Reader reader_; | ||
| 252 | MultipartReader muitlpart_reader_; | ||
| 253 | }; | ||
| 218 | 254 | ||
| 219 | typedef std::pair<ssize_t, ssize_t> Range; | 255 | using Range = std::pair<ssize_t, ssize_t>; |
| 220 | typedef std::vector<Range> Ranges; | 256 | using Ranges = std::vector<Range>; |
| 221 | 257 | ||
| 222 | struct Request { | 258 | struct Request { |
| 223 | std::string method; | 259 | std::string method; |
| @@ -229,7 +265,7 @@ struct Request { | |||
| 229 | std::string version; | 265 | std::string version; |
| 230 | std::string target; | 266 | std::string target; |
| 231 | Params params; | 267 | Params params; |
| 232 | MultipartFiles files; | 268 | MultipartFormDataMap files; |
| 233 | Ranges ranges; | 269 | Ranges ranges; |
| 234 | Match matches; | 270 | Match matches; |
| 235 | 271 | ||
| @@ -253,13 +289,19 @@ struct Request { | |||
| 253 | std::string get_param_value(const char *key, size_t id = 0) const; | 289 | std::string get_param_value(const char *key, size_t id = 0) const; |
| 254 | size_t get_param_value_count(const char *key) const; | 290 | size_t get_param_value_count(const char *key) const; |
| 255 | 291 | ||
| 292 | bool is_multipart_form_data() const; | ||
| 293 | |||
| 256 | bool has_file(const char *key) const; | 294 | bool has_file(const char *key) const; |
| 257 | MultipartFile get_file_value(const char *key) const; | 295 | MultipartFormData get_file_value(const char *key) const; |
| 296 | |||
| 297 | // private members... | ||
| 298 | size_t content_length; | ||
| 299 | ContentProvider content_provider; | ||
| 258 | }; | 300 | }; |
| 259 | 301 | ||
| 260 | struct Response { | 302 | struct Response { |
| 261 | std::string version; | 303 | std::string version; |
| 262 | int status; | 304 | int status = -1; |
| 263 | Headers headers; | 305 | Headers headers; |
| 264 | std::string body; | 306 | std::string body; |
| 265 | 307 | ||
| @@ -269,106 +311,81 @@ struct Response { | |||
| 269 | void set_header(const char *key, const char *val); | 311 | void set_header(const char *key, const char *val); |
| 270 | void set_header(const char *key, const std::string &val); | 312 | void set_header(const char *key, const std::string &val); |
| 271 | 313 | ||
| 272 | void set_redirect(const char *uri); | 314 | void set_redirect(const char *url); |
| 273 | void set_content(const char *s, size_t n, const char *content_type); | 315 | void set_content(const char *s, size_t n, const char *content_type); |
| 274 | void set_content(const std::string &s, const char *content_type); | 316 | void set_content(const std::string &s, const char *content_type); |
| 275 | 317 | ||
| 276 | void set_content_provider( | 318 | void set_content_provider( |
| 277 | size_t length, | 319 | size_t length, |
| 278 | std::function<void(size_t offset, size_t length, DataSink sink)> provider, | 320 | std::function<void(size_t offset, size_t length, DataSink &sink)> |
| 321 | provider, | ||
| 279 | std::function<void()> resource_releaser = [] {}); | 322 | std::function<void()> resource_releaser = [] {}); |
| 280 | 323 | ||
| 281 | void set_chunked_content_provider( | 324 | void set_chunked_content_provider( |
| 282 | std::function<void(size_t offset, DataSink sink, Done done)> provider, | 325 | std::function<void(size_t offset, DataSink &sink)> provider, |
| 283 | std::function<void()> resource_releaser = [] {}); | 326 | std::function<void()> resource_releaser = [] {}); |
| 284 | 327 | ||
| 285 | Response() : status(-1), content_provider_resource_length(0) {} | 328 | Response() = default; |
| 286 | 329 | Response(const Response &) = default; | |
| 330 | Response &operator=(const Response &) = default; | ||
| 331 | Response(Response &&) = default; | ||
| 332 | Response &operator=(Response &&) = default; | ||
| 287 | ~Response() { | 333 | ~Response() { |
| 288 | if (content_provider_resource_releaser) { | 334 | if (content_provider_resource_releaser) { |
| 289 | content_provider_resource_releaser(); | 335 | content_provider_resource_releaser(); |
| 290 | } | 336 | } |
| 291 | } | 337 | } |
| 292 | 338 | ||
| 293 | size_t content_provider_resource_length; | 339 | // private members... |
| 340 | size_t content_length = 0; | ||
| 294 | ContentProvider content_provider; | 341 | ContentProvider content_provider; |
| 295 | std::function<void()> content_provider_resource_releaser; | 342 | std::function<void()> content_provider_resource_releaser; |
| 296 | }; | 343 | }; |
| 297 | 344 | ||
| 298 | class Stream { | 345 | class Stream { |
| 299 | public: | 346 | public: |
| 300 | virtual ~Stream() {} | 347 | virtual ~Stream() = default; |
| 348 | |||
| 349 | virtual bool is_readable() const = 0; | ||
| 350 | virtual bool is_writable() const = 0; | ||
| 351 | |||
| 301 | virtual int read(char *ptr, size_t size) = 0; | 352 | virtual int read(char *ptr, size_t size) = 0; |
| 302 | virtual int write(const char *ptr, size_t size1) = 0; | 353 | virtual int write(const char *ptr, size_t size) = 0; |
| 303 | virtual int write(const char *ptr) = 0; | ||
| 304 | virtual int write(const std::string &s) = 0; | ||
| 305 | virtual std::string get_remote_addr() const = 0; | 354 | virtual std::string get_remote_addr() const = 0; |
| 306 | 355 | ||
| 307 | template <typename... Args> | 356 | template <typename... Args> |
| 308 | int write_format(const char *fmt, const Args &... args); | 357 | int write_format(const char *fmt, const Args &... args); |
| 309 | }; | 358 | int write(const char *ptr); |
| 310 | 359 | int write(const std::string &s); | |
| 311 | class SocketStream : public Stream { | ||
| 312 | public: | ||
| 313 | SocketStream(socket_t sock); | ||
| 314 | virtual ~SocketStream(); | ||
| 315 | |||
| 316 | virtual int read(char *ptr, size_t size); | ||
| 317 | virtual int write(const char *ptr, size_t size); | ||
| 318 | virtual int write(const char *ptr); | ||
| 319 | virtual int write(const std::string &s); | ||
| 320 | virtual std::string get_remote_addr() const; | ||
| 321 | |||
| 322 | private: | ||
| 323 | socket_t sock_; | ||
| 324 | }; | ||
| 325 | |||
| 326 | class BufferStream : public Stream { | ||
| 327 | public: | ||
| 328 | BufferStream() {} | ||
| 329 | virtual ~BufferStream() {} | ||
| 330 | |||
| 331 | virtual int read(char *ptr, size_t size); | ||
| 332 | virtual int write(const char *ptr, size_t size); | ||
| 333 | virtual int write(const char *ptr); | ||
| 334 | virtual int write(const std::string &s); | ||
| 335 | virtual std::string get_remote_addr() const; | ||
| 336 | |||
| 337 | const std::string &get_buffer() const; | ||
| 338 | |||
| 339 | private: | ||
| 340 | std::string buffer; | ||
| 341 | }; | 360 | }; |
| 342 | 361 | ||
| 343 | class TaskQueue { | 362 | class TaskQueue { |
| 344 | public: | 363 | public: |
| 345 | TaskQueue() {} | 364 | TaskQueue() = default; |
| 346 | virtual ~TaskQueue() {} | 365 | virtual ~TaskQueue() = default; |
| 347 | virtual void enqueue(std::function<void()> fn) = 0; | 366 | virtual void enqueue(std::function<void()> fn) = 0; |
| 348 | virtual void shutdown() = 0; | 367 | virtual void shutdown() = 0; |
| 349 | }; | 368 | }; |
| 350 | 369 | ||
| 351 | #if CPPHTTPLIB_THREAD_POOL_COUNT > 0 | ||
| 352 | class ThreadPool : public TaskQueue { | 370 | class ThreadPool : public TaskQueue { |
| 353 | public: | 371 | public: |
| 354 | ThreadPool(size_t n) : shutdown_(false) { | 372 | explicit ThreadPool(size_t n) : shutdown_(false) { |
| 355 | while (n) { | 373 | while (n) { |
| 356 | auto t = std::make_shared<std::thread>(worker(*this)); | 374 | threads_.emplace_back(worker(*this)); |
| 357 | threads_.push_back(t); | ||
| 358 | n--; | 375 | n--; |
| 359 | } | 376 | } |
| 360 | } | 377 | } |
| 361 | 378 | ||
| 362 | ThreadPool(const ThreadPool &) = delete; | 379 | ThreadPool(const ThreadPool &) = delete; |
| 363 | virtual ~ThreadPool() {} | 380 | ~ThreadPool() override = default; |
| 364 | 381 | ||
| 365 | virtual void enqueue(std::function<void()> fn) override { | 382 | void enqueue(std::function<void()> fn) override { |
| 366 | std::unique_lock<std::mutex> lock(mutex_); | 383 | std::unique_lock<std::mutex> lock(mutex_); |
| 367 | jobs_.push_back(fn); | 384 | jobs_.push_back(fn); |
| 368 | cond_.notify_one(); | 385 | cond_.notify_one(); |
| 369 | } | 386 | } |
| 370 | 387 | ||
| 371 | virtual void shutdown() override { | 388 | void shutdown() override { |
| 372 | // Stop all worker threads... | 389 | // Stop all worker threads... |
| 373 | { | 390 | { |
| 374 | std::unique_lock<std::mutex> lock(mutex_); | 391 | std::unique_lock<std::mutex> lock(mutex_); |
| @@ -378,14 +395,14 @@ public: | |||
| 378 | cond_.notify_all(); | 395 | cond_.notify_all(); |
| 379 | 396 | ||
| 380 | // Join... | 397 | // Join... |
| 381 | for (auto t : threads_) { | 398 | for (auto &t : threads_) { |
| 382 | t->join(); | 399 | t.join(); |
| 383 | } | 400 | } |
| 384 | } | 401 | } |
| 385 | 402 | ||
| 386 | private: | 403 | private: |
| 387 | struct worker { | 404 | struct worker { |
| 388 | worker(ThreadPool &pool) : pool_(pool) {} | 405 | explicit worker(ThreadPool &pool) : pool_(pool) {} |
| 389 | 406 | ||
| 390 | void operator()() { | 407 | void operator()() { |
| 391 | for (;;) { | 408 | for (;;) { |
| @@ -411,7 +428,7 @@ private: | |||
| 411 | }; | 428 | }; |
| 412 | friend struct worker; | 429 | friend struct worker; |
| 413 | 430 | ||
| 414 | std::vector<std::shared_ptr<std::thread>> threads_; | 431 | std::vector<std::thread> threads_; |
| 415 | std::list<std::function<void()>> jobs_; | 432 | std::list<std::function<void()>> jobs_; |
| 416 | 433 | ||
| 417 | bool shutdown_; | 434 | bool shutdown_; |
| @@ -419,46 +436,16 @@ private: | |||
| 419 | std::condition_variable cond_; | 436 | std::condition_variable cond_; |
| 420 | std::mutex mutex_; | 437 | std::mutex mutex_; |
| 421 | }; | 438 | }; |
| 422 | #else | ||
| 423 | class Threads : public TaskQueue { | ||
| 424 | public: | ||
| 425 | Threads() : running_threads_(0) {} | ||
| 426 | virtual ~Threads() {} | ||
| 427 | |||
| 428 | virtual void enqueue(std::function<void()> fn) override { | ||
| 429 | std::thread([=]() { | ||
| 430 | { | ||
| 431 | std::lock_guard<std::mutex> guard(running_threads_mutex_); | ||
| 432 | running_threads_++; | ||
| 433 | } | ||
| 434 | |||
| 435 | fn(); | ||
| 436 | |||
| 437 | { | ||
| 438 | std::lock_guard<std::mutex> guard(running_threads_mutex_); | ||
| 439 | running_threads_--; | ||
| 440 | } | ||
| 441 | }).detach(); | ||
| 442 | } | ||
| 443 | |||
| 444 | virtual void shutdown() override { | ||
| 445 | for (;;) { | ||
| 446 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||
| 447 | std::lock_guard<std::mutex> guard(running_threads_mutex_); | ||
| 448 | if (!running_threads_) { break; } | ||
| 449 | } | ||
| 450 | } | ||
| 451 | 439 | ||
| 452 | private: | 440 | using Logger = std::function<void(const Request &, const Response &)>; |
| 453 | std::mutex running_threads_mutex_; | ||
| 454 | int running_threads_; | ||
| 455 | }; | ||
| 456 | #endif | ||
| 457 | 441 | ||
| 458 | class Server { | 442 | class Server { |
| 459 | public: | 443 | public: |
| 460 | typedef std::function<void(const Request &, Response &)> Handler; | 444 | using Handler = std::function<void(const Request &, Response &)>; |
| 461 | typedef std::function<void(const Request &, const Response &)> Logger; | 445 | using HandlerWithContentReader = std::function<void( |
| 446 | const Request &, Response &, const ContentReader &content_reader)>; | ||
| 447 | using Expect100ContinueHandler = | ||
| 448 | std::function<int(const Request &, Response &)>; | ||
| 462 | 449 | ||
| 463 | Server(); | 450 | Server(); |
| 464 | 451 | ||
| @@ -468,21 +455,32 @@ public: | |||
| 468 | 455 | ||
| 469 | Server &Get(const char *pattern, Handler handler); | 456 | Server &Get(const char *pattern, Handler handler); |
| 470 | Server &Post(const char *pattern, Handler handler); | 457 | Server &Post(const char *pattern, Handler handler); |
| 471 | 458 | Server &Post(const char *pattern, HandlerWithContentReader handler); | |
| 472 | Server &Put(const char *pattern, Handler handler); | 459 | Server &Put(const char *pattern, Handler handler); |
| 460 | Server &Put(const char *pattern, HandlerWithContentReader handler); | ||
| 473 | Server &Patch(const char *pattern, Handler handler); | 461 | Server &Patch(const char *pattern, Handler handler); |
| 462 | Server &Patch(const char *pattern, HandlerWithContentReader handler); | ||
| 474 | Server &Delete(const char *pattern, Handler handler); | 463 | Server &Delete(const char *pattern, Handler handler); |
| 475 | Server &Options(const char *pattern, Handler handler); | 464 | Server &Options(const char *pattern, Handler handler); |
| 476 | 465 | ||
| 477 | bool set_base_dir(const char *path); | 466 | [[deprecated]] bool set_base_dir(const char *dir, |
| 467 | const char *mount_point = nullptr); | ||
| 468 | bool set_mount_point(const char *mount_point, const char *dir); | ||
| 469 | bool remove_mount_point(const char *mount_point); | ||
| 470 | void set_file_extension_and_mimetype_mapping(const char *ext, | ||
| 471 | const char *mime); | ||
| 478 | void set_file_request_handler(Handler handler); | 472 | void set_file_request_handler(Handler handler); |
| 479 | 473 | ||
| 480 | void set_error_handler(Handler handler); | 474 | void set_error_handler(Handler handler); |
| 481 | void set_logger(Logger logger); | 475 | void set_logger(Logger logger); |
| 482 | 476 | ||
| 477 | void set_expect_100_continue_handler(Expect100ContinueHandler handler); | ||
| 478 | |||
| 483 | void set_keep_alive_max_count(size_t count); | 479 | void set_keep_alive_max_count(size_t count); |
| 480 | void set_read_timeout(time_t sec, time_t usec); | ||
| 484 | void set_payload_max_length(size_t length); | 481 | void set_payload_max_length(size_t length); |
| 485 | 482 | ||
| 483 | bool bind_to_port(const char *host, int port, int socket_flags = 0); | ||
| 486 | int bind_to_any_port(const char *host, int socket_flags = 0); | 484 | int bind_to_any_port(const char *host, int socket_flags = 0); |
| 487 | bool listen_after_bind(); | 485 | bool listen_after_bind(); |
| 488 | 486 | ||
| @@ -496,22 +494,29 @@ public: | |||
| 496 | protected: | 494 | protected: |
| 497 | bool process_request(Stream &strm, bool last_connection, | 495 | bool process_request(Stream &strm, bool last_connection, |
| 498 | bool &connection_close, | 496 | bool &connection_close, |
| 499 | std::function<void(Request &)> setup_request); | 497 | const std::function<void(Request &)> &setup_request); |
| 500 | 498 | ||
| 501 | size_t keep_alive_max_count_; | 499 | size_t keep_alive_max_count_; |
| 500 | time_t read_timeout_sec_; | ||
| 501 | time_t read_timeout_usec_; | ||
| 502 | size_t payload_max_length_; | 502 | size_t payload_max_length_; |
| 503 | 503 | ||
| 504 | private: | 504 | private: |
| 505 | typedef std::vector<std::pair<std::regex, Handler>> Handlers; | 505 | using Handlers = std::vector<std::pair<std::regex, Handler>>; |
| 506 | using HandlersForContentReader = | ||
| 507 | std::vector<std::pair<std::regex, HandlerWithContentReader>>; | ||
| 506 | 508 | ||
| 507 | socket_t create_server_socket(const char *host, int port, | 509 | socket_t create_server_socket(const char *host, int port, |
| 508 | int socket_flags) const; | 510 | int socket_flags) const; |
| 509 | int bind_internal(const char *host, int port, int socket_flags); | 511 | int bind_internal(const char *host, int port, int socket_flags); |
| 510 | bool listen_internal(); | 512 | bool listen_internal(); |
| 511 | 513 | ||
| 512 | bool routing(Request &req, Response &res); | 514 | bool routing(Request &req, Response &res, Stream &strm, bool last_connection); |
| 513 | bool handle_file_request(Request &req, Response &res); | 515 | bool handle_file_request(Request &req, Response &res, bool head = false); |
| 514 | bool dispatch_request(Request &req, Response &res, Handlers &handlers); | 516 | bool dispatch_request(Request &req, Response &res, Handlers &handlers); |
| 517 | bool dispatch_request_for_content_reader(Request &req, Response &res, | ||
| 518 | ContentReader content_reader, | ||
| 519 | HandlersForContentReader &handlers); | ||
| 515 | 520 | ||
| 516 | bool parse_request_line(const char *s, Request &req); | 521 | bool parse_request_line(const char *s, Request &req); |
| 517 | bool write_response(Stream &strm, bool last_connection, const Request &req, | 522 | bool write_response(Stream &strm, bool last_connection, const Request &req, |
| @@ -519,26 +524,43 @@ private: | |||
| 519 | bool write_content_with_provider(Stream &strm, const Request &req, | 524 | bool write_content_with_provider(Stream &strm, const Request &req, |
| 520 | Response &res, const std::string &boundary, | 525 | Response &res, const std::string &boundary, |
| 521 | const std::string &content_type); | 526 | const std::string &content_type); |
| 527 | bool read_content(Stream &strm, bool last_connection, Request &req, | ||
| 528 | Response &res); | ||
| 529 | bool read_content_with_content_receiver( | ||
| 530 | Stream &strm, bool last_connection, Request &req, Response &res, | ||
| 531 | ContentReceiver receiver, MultipartContentHeader multipart_header, | ||
| 532 | ContentReceiver multipart_receiver); | ||
| 533 | bool read_content_core(Stream &strm, bool last_connection, Request &req, | ||
| 534 | Response &res, ContentReceiver receiver, | ||
| 535 | MultipartContentHeader mulitpart_header, | ||
| 536 | ContentReceiver multipart_receiver); | ||
| 522 | 537 | ||
| 523 | virtual bool process_and_close_socket(socket_t sock); | 538 | virtual bool process_and_close_socket(socket_t sock); |
| 524 | 539 | ||
| 525 | std::atomic<bool> is_running_; | 540 | std::atomic<bool> is_running_; |
| 526 | std::atomic<socket_t> svr_sock_; | 541 | std::atomic<socket_t> svr_sock_; |
| 527 | std::string base_dir_; | 542 | std::vector<std::pair<std::string, std::string>> base_dirs_; |
| 543 | std::map<std::string, std::string> file_extension_and_mimetype_map_; | ||
| 528 | Handler file_request_handler_; | 544 | Handler file_request_handler_; |
| 529 | Handlers get_handlers_; | 545 | Handlers get_handlers_; |
| 530 | Handlers post_handlers_; | 546 | Handlers post_handlers_; |
| 547 | HandlersForContentReader post_handlers_for_content_reader_; | ||
| 531 | Handlers put_handlers_; | 548 | Handlers put_handlers_; |
| 549 | HandlersForContentReader put_handlers_for_content_reader_; | ||
| 532 | Handlers patch_handlers_; | 550 | Handlers patch_handlers_; |
| 551 | HandlersForContentReader patch_handlers_for_content_reader_; | ||
| 533 | Handlers delete_handlers_; | 552 | Handlers delete_handlers_; |
| 534 | Handlers options_handlers_; | 553 | Handlers options_handlers_; |
| 535 | Handler error_handler_; | 554 | Handler error_handler_; |
| 536 | Logger logger_; | 555 | Logger logger_; |
| 556 | Expect100ContinueHandler expect_100_continue_handler_; | ||
| 537 | }; | 557 | }; |
| 538 | 558 | ||
| 539 | class Client { | 559 | class Client { |
| 540 | public: | 560 | public: |
| 541 | Client(const char *host, int port = 80, time_t timeout_sec = 300); | 561 | explicit Client(const std::string &host, int port = 80, |
| 562 | const std::string &client_cert_path = std::string(), | ||
| 563 | const std::string &client_key_path = std::string()); | ||
| 542 | 564 | ||
| 543 | virtual ~Client(); | 565 | virtual ~Client(); |
| 544 | 566 | ||
| @@ -586,6 +608,15 @@ public: | |||
| 586 | const std::string &body, | 608 | const std::string &body, |
| 587 | const char *content_type); | 609 | const char *content_type); |
| 588 | 610 | ||
| 611 | std::shared_ptr<Response> Post(const char *path, size_t content_length, | ||
| 612 | ContentProvider content_provider, | ||
| 613 | const char *content_type); | ||
| 614 | |||
| 615 | std::shared_ptr<Response> Post(const char *path, const Headers &headers, | ||
| 616 | size_t content_length, | ||
| 617 | ContentProvider content_provider, | ||
| 618 | const char *content_type); | ||
| 619 | |||
| 589 | std::shared_ptr<Response> Post(const char *path, const Params ¶ms); | 620 | std::shared_ptr<Response> Post(const char *path, const Params ¶ms); |
| 590 | 621 | ||
| 591 | std::shared_ptr<Response> Post(const char *path, const Headers &headers, | 622 | std::shared_ptr<Response> Post(const char *path, const Headers &headers, |
| @@ -604,6 +635,20 @@ public: | |||
| 604 | const std::string &body, | 635 | const std::string &body, |
| 605 | const char *content_type); | 636 | const char *content_type); |
| 606 | 637 | ||
| 638 | std::shared_ptr<Response> Put(const char *path, size_t content_length, | ||
| 639 | ContentProvider content_provider, | ||
| 640 | const char *content_type); | ||
| 641 | |||
| 642 | std::shared_ptr<Response> Put(const char *path, const Headers &headers, | ||
| 643 | size_t content_length, | ||
| 644 | ContentProvider content_provider, | ||
| 645 | const char *content_type); | ||
| 646 | |||
| 647 | std::shared_ptr<Response> Put(const char *path, const Params ¶ms); | ||
| 648 | |||
| 649 | std::shared_ptr<Response> Put(const char *path, const Headers &headers, | ||
| 650 | const Params ¶ms); | ||
| 651 | |||
| 607 | std::shared_ptr<Response> Patch(const char *path, const std::string &body, | 652 | std::shared_ptr<Response> Patch(const char *path, const std::string &body, |
| 608 | const char *content_type); | 653 | const char *content_type); |
| 609 | 654 | ||
| @@ -611,6 +656,15 @@ public: | |||
| 611 | const std::string &body, | 656 | const std::string &body, |
| 612 | const char *content_type); | 657 | const char *content_type); |
| 613 | 658 | ||
| 659 | std::shared_ptr<Response> Patch(const char *path, size_t content_length, | ||
| 660 | ContentProvider content_provider, | ||
| 661 | const char *content_type); | ||
| 662 | |||
| 663 | std::shared_ptr<Response> Patch(const char *path, const Headers &headers, | ||
| 664 | size_t content_length, | ||
| 665 | ContentProvider content_provider, | ||
| 666 | const char *content_type); | ||
| 667 | |||
| 614 | std::shared_ptr<Response> Delete(const char *path); | 668 | std::shared_ptr<Response> Delete(const char *path); |
| 615 | 669 | ||
| 616 | std::shared_ptr<Response> Delete(const char *path, const std::string &body, | 670 | std::shared_ptr<Response> Delete(const char *path, const std::string &body, |
| @@ -631,9 +685,33 @@ public: | |||
| 631 | bool send(const std::vector<Request> &requests, | 685 | bool send(const std::vector<Request> &requests, |
| 632 | std::vector<Response> &responses); | 686 | std::vector<Response> &responses); |
| 633 | 687 | ||
| 688 | void set_timeout_sec(time_t timeout_sec); | ||
| 689 | |||
| 690 | void set_read_timeout(time_t sec, time_t usec); | ||
| 691 | |||
| 634 | void set_keep_alive_max_count(size_t count); | 692 | void set_keep_alive_max_count(size_t count); |
| 635 | 693 | ||
| 636 | void follow_location(bool on); | 694 | void set_basic_auth(const char *username, const char *password); |
| 695 | |||
| 696 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 697 | void set_digest_auth(const char *username, const char *password); | ||
| 698 | #endif | ||
| 699 | |||
| 700 | void set_follow_location(bool on); | ||
| 701 | |||
| 702 | void set_compress(bool on); | ||
| 703 | |||
| 704 | void set_interface(const char *intf); | ||
| 705 | |||
| 706 | void set_proxy(const char *host, int port); | ||
| 707 | |||
| 708 | void set_proxy_basic_auth(const char *username, const char *password); | ||
| 709 | |||
| 710 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 711 | void set_proxy_digest_auth(const char *username, const char *password); | ||
| 712 | #endif | ||
| 713 | |||
| 714 | void set_logger(Logger logger); | ||
| 637 | 715 | ||
| 638 | protected: | 716 | protected: |
| 639 | bool process_request(Stream &strm, const Request &req, Response &res, | 717 | bool process_request(Stream &strm, const Request &req, Response &res, |
| @@ -641,16 +719,85 @@ protected: | |||
| 641 | 719 | ||
| 642 | const std::string host_; | 720 | const std::string host_; |
| 643 | const int port_; | 721 | const int port_; |
| 644 | time_t timeout_sec_; | ||
| 645 | const std::string host_and_port_; | 722 | const std::string host_and_port_; |
| 646 | size_t keep_alive_max_count_; | 723 | |
| 647 | size_t follow_location_; | 724 | // Settings |
| 725 | std::string client_cert_path_; | ||
| 726 | std::string client_key_path_; | ||
| 727 | |||
| 728 | time_t timeout_sec_ = 300; | ||
| 729 | time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND; | ||
| 730 | time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND; | ||
| 731 | |||
| 732 | size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT; | ||
| 733 | |||
| 734 | std::string basic_auth_username_; | ||
| 735 | std::string basic_auth_password_; | ||
| 736 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 737 | std::string digest_auth_username_; | ||
| 738 | std::string digest_auth_password_; | ||
| 739 | #endif | ||
| 740 | |||
| 741 | bool follow_location_ = false; | ||
| 742 | |||
| 743 | bool compress_ = false; | ||
| 744 | |||
| 745 | std::string interface_; | ||
| 746 | |||
| 747 | std::string proxy_host_; | ||
| 748 | int proxy_port_; | ||
| 749 | |||
| 750 | std::string proxy_basic_auth_username_; | ||
| 751 | std::string proxy_basic_auth_password_; | ||
| 752 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 753 | std::string proxy_digest_auth_username_; | ||
| 754 | std::string proxy_digest_auth_password_; | ||
| 755 | #endif | ||
| 756 | |||
| 757 | Logger logger_; | ||
| 758 | |||
| 759 | void copy_settings(const Client &rhs) { | ||
| 760 | client_cert_path_ = rhs.client_cert_path_; | ||
| 761 | client_key_path_ = rhs.client_key_path_; | ||
| 762 | timeout_sec_ = rhs.timeout_sec_; | ||
| 763 | read_timeout_sec_ = rhs.read_timeout_sec_; | ||
| 764 | read_timeout_usec_ = rhs.read_timeout_usec_; | ||
| 765 | keep_alive_max_count_ = rhs.keep_alive_max_count_; | ||
| 766 | basic_auth_username_ = rhs.basic_auth_username_; | ||
| 767 | basic_auth_password_ = rhs.basic_auth_password_; | ||
| 768 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 769 | digest_auth_username_ = rhs.digest_auth_username_; | ||
| 770 | digest_auth_password_ = rhs.digest_auth_password_; | ||
| 771 | #endif | ||
| 772 | follow_location_ = rhs.follow_location_; | ||
| 773 | compress_ = rhs.compress_; | ||
| 774 | interface_ = rhs.interface_; | ||
| 775 | proxy_host_ = rhs.proxy_host_; | ||
| 776 | proxy_port_ = rhs.proxy_port_; | ||
| 777 | proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_; | ||
| 778 | proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_; | ||
| 779 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 780 | proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_; | ||
| 781 | proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_; | ||
| 782 | #endif | ||
| 783 | logger_ = rhs.logger_; | ||
| 784 | } | ||
| 648 | 785 | ||
| 649 | private: | 786 | private: |
| 650 | socket_t create_client_socket() const; | 787 | socket_t create_client_socket() const; |
| 651 | bool read_response_line(Stream &strm, Response &res); | 788 | bool read_response_line(Stream &strm, Response &res); |
| 652 | void write_request(Stream &strm, const Request &req, bool last_connection); | 789 | bool write_request(Stream &strm, const Request &req, bool last_connection); |
| 653 | bool redirect(const Request &req, Response &res); | 790 | bool redirect(const Request &req, Response &res); |
| 791 | bool handle_request(Stream &strm, const Request &req, Response &res, | ||
| 792 | bool last_connection, bool &connection_close); | ||
| 793 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 794 | bool connect(socket_t sock, Response &res, bool &error); | ||
| 795 | #endif | ||
| 796 | |||
| 797 | std::shared_ptr<Response> send_with_content_provider( | ||
| 798 | const char *method, const char *path, const Headers &headers, | ||
| 799 | const std::string &body, size_t content_length, | ||
| 800 | ContentProvider content_provider, const char *content_type); | ||
| 654 | 801 | ||
| 655 | virtual bool process_and_close_socket( | 802 | virtual bool process_and_close_socket( |
| 656 | socket_t sock, size_t request_count, | 803 | socket_t sock, size_t request_count, |
| @@ -692,22 +839,6 @@ inline void Post(std::vector<Request> &requests, const char *path, | |||
| 692 | } | 839 | } |
| 693 | 840 | ||
| 694 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | 841 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT |
| 695 | class SSLSocketStream : public Stream { | ||
| 696 | public: | ||
| 697 | SSLSocketStream(socket_t sock, SSL *ssl); | ||
| 698 | virtual ~SSLSocketStream(); | ||
| 699 | |||
| 700 | virtual int read(char *ptr, size_t size); | ||
| 701 | virtual int write(const char *ptr, size_t size); | ||
| 702 | virtual int write(const char *ptr); | ||
| 703 | virtual int write(const std::string &s); | ||
| 704 | virtual std::string get_remote_addr() const; | ||
| 705 | |||
| 706 | private: | ||
| 707 | socket_t sock_; | ||
| 708 | SSL *ssl_; | ||
| 709 | }; | ||
| 710 | |||
| 711 | class SSLServer : public Server { | 842 | class SSLServer : public Server { |
| 712 | public: | 843 | public: |
| 713 | SSLServer(const char *cert_path, const char *private_key_path, | 844 | SSLServer(const char *cert_path, const char *private_key_path, |
| @@ -727,9 +858,9 @@ private: | |||
| 727 | 858 | ||
| 728 | class SSLClient : public Client { | 859 | class SSLClient : public Client { |
| 729 | public: | 860 | public: |
| 730 | SSLClient(const char *host, int port = 443, time_t timeout_sec = 300, | 861 | SSLClient(const std::string &host, int port = 443, |
| 731 | const char *client_cert_path = nullptr, | 862 | const std::string &client_cert_path = std::string(), |
| 732 | const char *client_key_path = nullptr); | 863 | const std::string &client_key_path = std::string()); |
| 733 | 864 | ||
| 734 | virtual ~SSLClient(); | 865 | virtual ~SSLClient(); |
| 735 | 866 | ||
| @@ -737,11 +868,12 @@ public: | |||
| 737 | 868 | ||
| 738 | void set_ca_cert_path(const char *ca_ceert_file_path, | 869 | void set_ca_cert_path(const char *ca_ceert_file_path, |
| 739 | const char *ca_cert_dir_path = nullptr); | 870 | const char *ca_cert_dir_path = nullptr); |
| 871 | |||
| 740 | void enable_server_certificate_verification(bool enabled); | 872 | void enable_server_certificate_verification(bool enabled); |
| 741 | 873 | ||
| 742 | long get_openssl_verify_result() const; | 874 | long get_openssl_verify_result() const; |
| 743 | 875 | ||
| 744 | SSL_CTX* ssl_context() const noexcept; | 876 | SSL_CTX *ssl_context() const noexcept; |
| 745 | 877 | ||
| 746 | private: | 878 | private: |
| 747 | virtual bool process_and_close_socket( | 879 | virtual bool process_and_close_socket( |
| @@ -759,6 +891,7 @@ private: | |||
| 759 | SSL_CTX *ctx_; | 891 | SSL_CTX *ctx_; |
| 760 | std::mutex ctx_mutex_; | 892 | std::mutex ctx_mutex_; |
| 761 | std::vector<std::string> host_components_; | 893 | std::vector<std::string> host_components_; |
| 894 | |||
| 762 | std::string ca_cert_file_path_; | 895 | std::string ca_cert_file_path_; |
| 763 | std::string ca_cert_dir_path_; | 896 | std::string ca_cert_dir_path_; |
| 764 | bool server_certificate_verification_ = false; | 897 | bool server_certificate_verification_ = false; |
| @@ -766,9 +899,12 @@ private: | |||
| 766 | }; | 899 | }; |
| 767 | #endif | 900 | #endif |
| 768 | 901 | ||
| 902 | // ---------------------------------------------------------------------------- | ||
| 903 | |||
| 769 | /* | 904 | /* |
| 770 | * Implementation | 905 | * Implementation |
| 771 | */ | 906 | */ |
| 907 | |||
| 772 | namespace detail { | 908 | namespace detail { |
| 773 | 909 | ||
| 774 | inline bool is_hex(char c, int &v) { | 910 | inline bool is_hex(char c, int &v) { |
| @@ -932,8 +1068,8 @@ inline void read_file(const std::string &path, std::string &out) { | |||
| 932 | 1068 | ||
| 933 | inline std::string file_extension(const std::string &path) { | 1069 | inline std::string file_extension(const std::string &path) { |
| 934 | std::smatch m; | 1070 | std::smatch m; |
| 935 | auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); | 1071 | static auto re = std::regex("\\.([a-zA-Z0-9]+)$"); |
| 936 | if (std::regex_search(path, m, pat)) { return m[1].str(); } | 1072 | if (std::regex_search(path, m, re)) { return m[1].str(); } |
| 937 | return std::string(); | 1073 | return std::string(); |
| 938 | } | 1074 | } |
| 939 | 1075 | ||
| @@ -976,6 +1112,11 @@ public: | |||
| 976 | } | 1112 | } |
| 977 | } | 1113 | } |
| 978 | 1114 | ||
| 1115 | bool end_with_crlf() const { | ||
| 1116 | auto end = ptr() + size(); | ||
| 1117 | return size() >= 2 && end[-2] == '\r' && end[-1] == '\n'; | ||
| 1118 | } | ||
| 1119 | |||
| 979 | bool getline() { | 1120 | bool getline() { |
| 980 | fixed_buffer_used_size_ = 0; | 1121 | fixed_buffer_used_size_ = 0; |
| 981 | glowable_buffer_.clear(); | 1122 | glowable_buffer_.clear(); |
| @@ -1019,7 +1160,7 @@ private: | |||
| 1019 | Stream &strm_; | 1160 | Stream &strm_; |
| 1020 | char *fixed_buffer_; | 1161 | char *fixed_buffer_; |
| 1021 | const size_t fixed_buffer_size_; | 1162 | const size_t fixed_buffer_size_; |
| 1022 | size_t fixed_buffer_used_size_; | 1163 | size_t fixed_buffer_used_size_ = 0; |
| 1023 | std::string glowable_buffer_; | 1164 | std::string glowable_buffer_; |
| 1024 | }; | 1165 | }; |
| 1025 | 1166 | ||
| @@ -1053,6 +1194,28 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) { | |||
| 1053 | #endif | 1194 | #endif |
| 1054 | } | 1195 | } |
| 1055 | 1196 | ||
| 1197 | inline int select_write(socket_t sock, time_t sec, time_t usec) { | ||
| 1198 | #ifdef CPPHTTPLIB_USE_POLL | ||
| 1199 | struct pollfd pfd_read; | ||
| 1200 | pfd_read.fd = sock; | ||
| 1201 | pfd_read.events = POLLOUT; | ||
| 1202 | |||
| 1203 | auto timeout = static_cast<int>(sec * 1000 + usec / 1000); | ||
| 1204 | |||
| 1205 | return poll(&pfd_read, 1, timeout); | ||
| 1206 | #else | ||
| 1207 | fd_set fds; | ||
| 1208 | FD_ZERO(&fds); | ||
| 1209 | FD_SET(sock, &fds); | ||
| 1210 | |||
| 1211 | timeval tv; | ||
| 1212 | tv.tv_sec = static_cast<long>(sec); | ||
| 1213 | tv.tv_usec = static_cast<long>(usec); | ||
| 1214 | |||
| 1215 | return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv); | ||
| 1216 | #endif | ||
| 1217 | } | ||
| 1218 | |||
| 1056 | inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { | 1219 | inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { |
| 1057 | #ifdef CPPHTTPLIB_USE_POLL | 1220 | #ifdef CPPHTTPLIB_USE_POLL |
| 1058 | struct pollfd pfd_read; | 1221 | struct pollfd pfd_read; |
| @@ -1065,7 +1228,8 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { | |||
| 1065 | pfd_read.revents & (POLLIN | POLLOUT)) { | 1228 | pfd_read.revents & (POLLIN | POLLOUT)) { |
| 1066 | int error = 0; | 1229 | int error = 0; |
| 1067 | socklen_t len = sizeof(error); | 1230 | socklen_t len = sizeof(error); |
| 1068 | return getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) >= 0 && | 1231 | return getsockopt(sock, SOL_SOCKET, SO_ERROR, |
| 1232 | reinterpret_cast<char *>(&error), &len) >= 0 && | ||
| 1069 | !error; | 1233 | !error; |
| 1070 | } | 1234 | } |
| 1071 | return false; | 1235 | return false; |
| @@ -1085,27 +1249,86 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) { | |||
| 1085 | (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { | 1249 | (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) { |
| 1086 | int error = 0; | 1250 | int error = 0; |
| 1087 | socklen_t len = sizeof(error); | 1251 | socklen_t len = sizeof(error); |
| 1088 | return getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &len) >= 0 && | 1252 | return getsockopt(sock, SOL_SOCKET, SO_ERROR, |
| 1253 | reinterpret_cast<char *>(&error), &len) >= 0 && | ||
| 1089 | !error; | 1254 | !error; |
| 1090 | } | 1255 | } |
| 1091 | return false; | 1256 | return false; |
| 1092 | #endif | 1257 | #endif |
| 1093 | } | 1258 | } |
| 1094 | 1259 | ||
| 1260 | class SocketStream : public Stream { | ||
| 1261 | public: | ||
| 1262 | SocketStream(socket_t sock, time_t read_timeout_sec, | ||
| 1263 | time_t read_timeout_usec); | ||
| 1264 | ~SocketStream() override; | ||
| 1265 | |||
| 1266 | bool is_readable() const override; | ||
| 1267 | bool is_writable() const override; | ||
| 1268 | int read(char *ptr, size_t size) override; | ||
| 1269 | int write(const char *ptr, size_t size) override; | ||
| 1270 | std::string get_remote_addr() const override; | ||
| 1271 | |||
| 1272 | private: | ||
| 1273 | socket_t sock_; | ||
| 1274 | time_t read_timeout_sec_; | ||
| 1275 | time_t read_timeout_usec_; | ||
| 1276 | }; | ||
| 1277 | |||
| 1278 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 1279 | class SSLSocketStream : public Stream { | ||
| 1280 | public: | ||
| 1281 | SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec, | ||
| 1282 | time_t read_timeout_usec); | ||
| 1283 | virtual ~SSLSocketStream(); | ||
| 1284 | |||
| 1285 | bool is_readable() const override; | ||
| 1286 | bool is_writable() const override; | ||
| 1287 | int read(char *ptr, size_t size) override; | ||
| 1288 | int write(const char *ptr, size_t size) override; | ||
| 1289 | std::string get_remote_addr() const override; | ||
| 1290 | |||
| 1291 | private: | ||
| 1292 | socket_t sock_; | ||
| 1293 | SSL *ssl_; | ||
| 1294 | time_t read_timeout_sec_; | ||
| 1295 | time_t read_timeout_usec_; | ||
| 1296 | }; | ||
| 1297 | #endif | ||
| 1298 | |||
| 1299 | class BufferStream : public Stream { | ||
| 1300 | public: | ||
| 1301 | BufferStream() = default; | ||
| 1302 | ~BufferStream() override = default; | ||
| 1303 | |||
| 1304 | bool is_readable() const override; | ||
| 1305 | bool is_writable() const override; | ||
| 1306 | int read(char *ptr, size_t size) override; | ||
| 1307 | int write(const char *ptr, size_t size) override; | ||
| 1308 | std::string get_remote_addr() const override; | ||
| 1309 | |||
| 1310 | const std::string &get_buffer() const; | ||
| 1311 | |||
| 1312 | private: | ||
| 1313 | std::string buffer; | ||
| 1314 | int position = 0; | ||
| 1315 | }; | ||
| 1316 | |||
| 1095 | template <typename T> | 1317 | template <typename T> |
| 1096 | inline bool process_and_close_socket(bool is_client_request, socket_t sock, | 1318 | inline bool process_socket(bool is_client_request, socket_t sock, |
| 1097 | size_t keep_alive_max_count, T callback) { | 1319 | size_t keep_alive_max_count, time_t read_timeout_sec, |
| 1320 | time_t read_timeout_usec, T callback) { | ||
| 1098 | assert(keep_alive_max_count > 0); | 1321 | assert(keep_alive_max_count > 0); |
| 1099 | 1322 | ||
| 1100 | bool ret = false; | 1323 | auto ret = false; |
| 1101 | 1324 | ||
| 1102 | if (keep_alive_max_count > 1) { | 1325 | if (keep_alive_max_count > 1) { |
| 1103 | auto count = keep_alive_max_count; | 1326 | auto count = keep_alive_max_count; |
| 1104 | while (count > 0 && | 1327 | while (count > 0 && |
| 1105 | (is_client_request || | 1328 | (is_client_request || |
| 1106 | detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, | 1329 | select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, |
| 1107 | CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) { | 1330 | CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) { |
| 1108 | SocketStream strm(sock); | 1331 | SocketStream strm(sock, read_timeout_sec, read_timeout_usec); |
| 1109 | auto last_connection = count == 1; | 1332 | auto last_connection = count == 1; |
| 1110 | auto connection_close = false; | 1333 | auto connection_close = false; |
| 1111 | 1334 | ||
| @@ -1114,12 +1337,22 @@ inline bool process_and_close_socket(bool is_client_request, socket_t sock, | |||
| 1114 | 1337 | ||
| 1115 | count--; | 1338 | count--; |
| 1116 | } | 1339 | } |
| 1117 | } else { | 1340 | } else { // keep_alive_max_count is 0 or 1 |
| 1118 | SocketStream strm(sock); | 1341 | SocketStream strm(sock, read_timeout_sec, read_timeout_usec); |
| 1119 | auto dummy_connection_close = false; | 1342 | auto dummy_connection_close = false; |
| 1120 | ret = callback(strm, true, dummy_connection_close); | 1343 | ret = callback(strm, true, dummy_connection_close); |
| 1121 | } | 1344 | } |
| 1122 | 1345 | ||
| 1346 | return ret; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | template <typename T> | ||
| 1350 | inline bool process_and_close_socket(bool is_client_request, socket_t sock, | ||
| 1351 | size_t keep_alive_max_count, | ||
| 1352 | time_t read_timeout_sec, | ||
| 1353 | time_t read_timeout_usec, T callback) { | ||
| 1354 | auto ret = process_socket(is_client_request, sock, keep_alive_max_count, | ||
| 1355 | read_timeout_sec, read_timeout_usec, callback); | ||
| 1123 | close_socket(sock); | 1356 | close_socket(sock); |
| 1124 | return ret; | 1357 | return ret; |
| 1125 | } | 1358 | } |
| @@ -1165,6 +1398,23 @@ socket_t create_socket(const char *host, int port, Fn fn, | |||
| 1165 | #ifdef _WIN32 | 1398 | #ifdef _WIN32 |
| 1166 | auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, | 1399 | auto sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, |
| 1167 | nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); | 1400 | nullptr, 0, WSA_FLAG_NO_HANDLE_INHERIT); |
| 1401 | /** | ||
| 1402 | * Since the WSA_FLAG_NO_HANDLE_INHERIT is only supported on Windows 7 SP1 | ||
| 1403 | * and above the socket creation fails on older Windows Systems. | ||
| 1404 | * | ||
| 1405 | * Let's try to create a socket the old way in this case. | ||
| 1406 | * | ||
| 1407 | * Reference: | ||
| 1408 | * https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketa | ||
| 1409 | * | ||
| 1410 | * WSA_FLAG_NO_HANDLE_INHERIT: | ||
| 1411 | * This flag is supported on Windows 7 with SP1, Windows Server 2008 R2 with | ||
| 1412 | * SP1, and later | ||
| 1413 | * | ||
| 1414 | */ | ||
| 1415 | if (sock == INVALID_SOCKET) { | ||
| 1416 | sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); | ||
| 1417 | } | ||
| 1168 | #else | 1418 | #else |
| 1169 | auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); | 1419 | auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); |
| 1170 | #endif | 1420 | #endif |
| @@ -1176,9 +1426,11 @@ socket_t create_socket(const char *host, int port, Fn fn, | |||
| 1176 | 1426 | ||
| 1177 | // Make 'reuse address' option available | 1427 | // Make 'reuse address' option available |
| 1178 | int yes = 1; | 1428 | int yes = 1; |
| 1179 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&yes), sizeof(yes)); | 1429 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes), |
| 1430 | sizeof(yes)); | ||
| 1180 | #ifdef SO_REUSEPORT | 1431 | #ifdef SO_REUSEPORT |
| 1181 | setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char*>(&yes), sizeof(yes)); | 1432 | setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char *>(&yes), |
| 1433 | sizeof(yes)); | ||
| 1182 | #endif | 1434 | #endif |
| 1183 | 1435 | ||
| 1184 | // bind or connect | 1436 | // bind or connect |
| @@ -1213,27 +1465,105 @@ inline bool is_connection_error() { | |||
| 1213 | #endif | 1465 | #endif |
| 1214 | } | 1466 | } |
| 1215 | 1467 | ||
| 1468 | inline bool bind_ip_address(socket_t sock, const char *host) { | ||
| 1469 | struct addrinfo hints; | ||
| 1470 | struct addrinfo *result; | ||
| 1471 | |||
| 1472 | memset(&hints, 0, sizeof(struct addrinfo)); | ||
| 1473 | hints.ai_family = AF_UNSPEC; | ||
| 1474 | hints.ai_socktype = SOCK_STREAM; | ||
| 1475 | hints.ai_protocol = 0; | ||
| 1476 | |||
| 1477 | if (getaddrinfo(host, "0", &hints, &result)) { return false; } | ||
| 1478 | |||
| 1479 | auto ret = false; | ||
| 1480 | for (auto rp = result; rp; rp = rp->ai_next) { | ||
| 1481 | const auto &ai = *rp; | ||
| 1482 | if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) { | ||
| 1483 | ret = true; | ||
| 1484 | break; | ||
| 1485 | } | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | freeaddrinfo(result); | ||
| 1489 | return ret; | ||
| 1490 | } | ||
| 1491 | |||
| 1492 | inline std::string if2ip(const std::string &ifn) { | ||
| 1493 | #ifndef _WIN32 | ||
| 1494 | struct ifaddrs *ifap; | ||
| 1495 | getifaddrs(&ifap); | ||
| 1496 | for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { | ||
| 1497 | if (ifa->ifa_addr && ifn == ifa->ifa_name) { | ||
| 1498 | if (ifa->ifa_addr->sa_family == AF_INET) { | ||
| 1499 | auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr); | ||
| 1500 | char buf[INET_ADDRSTRLEN]; | ||
| 1501 | if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) { | ||
| 1502 | freeifaddrs(ifap); | ||
| 1503 | return std::string(buf, INET_ADDRSTRLEN); | ||
| 1504 | } | ||
| 1505 | } | ||
| 1506 | } | ||
| 1507 | } | ||
| 1508 | freeifaddrs(ifap); | ||
| 1509 | #endif | ||
| 1510 | return std::string(); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | inline socket_t create_client_socket(const char *host, int port, | ||
| 1514 | time_t timeout_sec, | ||
| 1515 | const std::string &intf) { | ||
| 1516 | return create_socket( | ||
| 1517 | host, port, [&](socket_t sock, struct addrinfo &ai) -> bool { | ||
| 1518 | if (!intf.empty()) { | ||
| 1519 | auto ip = if2ip(intf); | ||
| 1520 | if (ip.empty()) { ip = intf; } | ||
| 1521 | if (!bind_ip_address(sock, ip.c_str())) { return false; } | ||
| 1522 | } | ||
| 1523 | |||
| 1524 | set_nonblocking(sock, true); | ||
| 1525 | |||
| 1526 | auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen)); | ||
| 1527 | if (ret < 0) { | ||
| 1528 | if (is_connection_error() || | ||
| 1529 | !wait_until_socket_is_ready(sock, timeout_sec, 0)) { | ||
| 1530 | close_socket(sock); | ||
| 1531 | return false; | ||
| 1532 | } | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | set_nonblocking(sock, false); | ||
| 1536 | return true; | ||
| 1537 | }); | ||
| 1538 | } | ||
| 1539 | |||
| 1216 | inline std::string get_remote_addr(socket_t sock) { | 1540 | inline std::string get_remote_addr(socket_t sock) { |
| 1217 | struct sockaddr_storage addr; | 1541 | struct sockaddr_storage addr; |
| 1218 | socklen_t len = sizeof(addr); | 1542 | socklen_t len = sizeof(addr); |
| 1219 | 1543 | ||
| 1220 | if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) { | 1544 | if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) { |
| 1221 | char ipstr[NI_MAXHOST]; | 1545 | std::array<char, NI_MAXHOST> ipstr{}; |
| 1222 | 1546 | ||
| 1223 | if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len, ipstr, sizeof(ipstr), | 1547 | if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len, |
| 1224 | nullptr, 0, NI_NUMERICHOST)) { | 1548 | ipstr.data(), static_cast<unsigned int>(ipstr.size()), nullptr, 0, NI_NUMERICHOST)) { |
| 1225 | return ipstr; | 1549 | return ipstr.data(); |
| 1226 | } | 1550 | } |
| 1227 | } | 1551 | } |
| 1228 | 1552 | ||
| 1229 | return std::string(); | 1553 | return std::string(); |
| 1230 | } | 1554 | } |
| 1231 | 1555 | ||
| 1232 | inline const char *find_content_type(const std::string &path) { | 1556 | inline const char * |
| 1557 | find_content_type(const std::string &path, | ||
| 1558 | const std::map<std::string, std::string> &user_data) { | ||
| 1233 | auto ext = file_extension(path); | 1559 | auto ext = file_extension(path); |
| 1560 | |||
| 1561 | auto it = user_data.find(ext); | ||
| 1562 | if (it != user_data.end()) { return it->second.c_str(); } | ||
| 1563 | |||
| 1234 | if (ext == "txt") { | 1564 | if (ext == "txt") { |
| 1235 | return "text/plain"; | 1565 | return "text/plain"; |
| 1236 | } else if (ext == "html") { | 1566 | } else if (ext == "html" || ext == "htm") { |
| 1237 | return "text/html"; | 1567 | return "text/html"; |
| 1238 | } else if (ext == "css") { | 1568 | } else if (ext == "css") { |
| 1239 | return "text/css"; | 1569 | return "text/css"; |
| @@ -1253,6 +1583,8 @@ inline const char *find_content_type(const std::string &path) { | |||
| 1253 | return "application/pdf"; | 1583 | return "application/pdf"; |
| 1254 | } else if (ext == "js") { | 1584 | } else if (ext == "js") { |
| 1255 | return "application/javascript"; | 1585 | return "application/javascript"; |
| 1586 | } else if (ext == "wasm") { | ||
| 1587 | return "application/wasm"; | ||
| 1256 | } else if (ext == "xml") { | 1588 | } else if (ext == "xml") { |
| 1257 | return "application/xml"; | 1589 | return "application/xml"; |
| 1258 | } else if (ext == "xhtml") { | 1590 | } else if (ext == "xhtml") { |
| @@ -1263,19 +1595,25 @@ inline const char *find_content_type(const std::string &path) { | |||
| 1263 | 1595 | ||
| 1264 | inline const char *status_message(int status) { | 1596 | inline const char *status_message(int status) { |
| 1265 | switch (status) { | 1597 | switch (status) { |
| 1598 | case 100: return "Continue"; | ||
| 1266 | case 200: return "OK"; | 1599 | case 200: return "OK"; |
| 1600 | case 202: return "Accepted"; | ||
| 1601 | case 204: return "No Content"; | ||
| 1267 | case 206: return "Partial Content"; | 1602 | case 206: return "Partial Content"; |
| 1268 | case 301: return "Moved Permanently"; | 1603 | case 301: return "Moved Permanently"; |
| 1269 | case 302: return "Found"; | 1604 | case 302: return "Found"; |
| 1270 | case 303: return "See Other"; | 1605 | case 303: return "See Other"; |
| 1271 | case 304: return "Not Modified"; | 1606 | case 304: return "Not Modified"; |
| 1272 | case 400: return "Bad Request"; | 1607 | case 400: return "Bad Request"; |
| 1608 | case 401: return "Unauthorized"; | ||
| 1273 | case 403: return "Forbidden"; | 1609 | case 403: return "Forbidden"; |
| 1274 | case 404: return "Not Found"; | 1610 | case 404: return "Not Found"; |
| 1275 | case 413: return "Payload Too Large"; | 1611 | case 413: return "Payload Too Large"; |
| 1276 | case 414: return "Request-URI Too Long"; | 1612 | case 414: return "Request-URI Too Long"; |
| 1277 | case 415: return "Unsupported Media Type"; | 1613 | case 415: return "Unsupported Media Type"; |
| 1278 | case 416: return "Range Not Satisfiable"; | 1614 | case 416: return "Range Not Satisfiable"; |
| 1615 | case 417: return "Expectation Failed"; | ||
| 1616 | case 503: return "Service Unavailable"; | ||
| 1279 | 1617 | ||
| 1280 | default: | 1618 | default: |
| 1281 | case 500: return "Internal Server Error"; | 1619 | case 500: return "Internal Server Error"; |
| @@ -1302,18 +1640,18 @@ inline bool compress(std::string &content) { | |||
| 1302 | if (ret != Z_OK) { return false; } | 1640 | if (ret != Z_OK) { return false; } |
| 1303 | 1641 | ||
| 1304 | strm.avail_in = content.size(); | 1642 | strm.avail_in = content.size(); |
| 1305 | strm.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(content.data())); | 1643 | strm.next_in = |
| 1644 | const_cast<Bytef *>(reinterpret_cast<const Bytef *>(content.data())); | ||
| 1306 | 1645 | ||
| 1307 | std::string compressed; | 1646 | std::string compressed; |
| 1308 | 1647 | ||
| 1309 | const auto bufsiz = 16384; | 1648 | std::array<char, 16384> buff{}; |
| 1310 | char buff[bufsiz]; | ||
| 1311 | do { | 1649 | do { |
| 1312 | strm.avail_out = bufsiz; | 1650 | strm.avail_out = buff.size(); |
| 1313 | strm.next_out = reinterpret_cast<Bytef*>(buff); | 1651 | strm.next_out = reinterpret_cast<Bytef *>(buff.data()); |
| 1314 | ret = deflate(&strm, Z_FINISH); | 1652 | ret = deflate(&strm, Z_FINISH); |
| 1315 | assert(ret != Z_STREAM_ERROR); | 1653 | assert(ret != Z_STREAM_ERROR); |
| 1316 | compressed.append(buff, bufsiz - strm.avail_out); | 1654 | compressed.append(buff.data(), buff.size() - strm.avail_out); |
| 1317 | } while (strm.avail_out == 0); | 1655 | } while (strm.avail_out == 0); |
| 1318 | 1656 | ||
| 1319 | assert(ret == Z_STREAM_END); | 1657 | assert(ret == Z_STREAM_END); |
| @@ -1347,13 +1685,12 @@ public: | |||
| 1347 | int ret = Z_OK; | 1685 | int ret = Z_OK; |
| 1348 | 1686 | ||
| 1349 | strm.avail_in = data_length; | 1687 | strm.avail_in = data_length; |
| 1350 | strm.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef *>(data)); | 1688 | strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data)); |
| 1351 | 1689 | ||
| 1352 | const auto bufsiz = 16384; | 1690 | std::array<char, 16384> buff{}; |
| 1353 | char buff[bufsiz]; | ||
| 1354 | do { | 1691 | do { |
| 1355 | strm.avail_out = bufsiz; | 1692 | strm.avail_out = buff.size(); |
| 1356 | strm.next_out = reinterpret_cast<Bytef*>(buff); | 1693 | strm.next_out = reinterpret_cast<Bytef *>(buff.data()); |
| 1357 | 1694 | ||
| 1358 | ret = inflate(&strm, Z_NO_FLUSH); | 1695 | ret = inflate(&strm, Z_NO_FLUSH); |
| 1359 | assert(ret != Z_STREAM_ERROR); | 1696 | assert(ret != Z_STREAM_ERROR); |
| @@ -1363,10 +1700,12 @@ public: | |||
| 1363 | case Z_MEM_ERROR: inflateEnd(&strm); return false; | 1700 | case Z_MEM_ERROR: inflateEnd(&strm); return false; |
| 1364 | } | 1701 | } |
| 1365 | 1702 | ||
| 1366 | if (!callback(buff, bufsiz - strm.avail_out)) { return false; } | 1703 | if (!callback(buff.data(), buff.size() - strm.avail_out)) { |
| 1704 | return false; | ||
| 1705 | } | ||
| 1367 | } while (strm.avail_out == 0); | 1706 | } while (strm.avail_out == 0); |
| 1368 | 1707 | ||
| 1369 | return ret == Z_STREAM_END; | 1708 | return ret == Z_OK || ret == Z_STREAM_END; |
| 1370 | } | 1709 | } |
| 1371 | 1710 | ||
| 1372 | private: | 1711 | private: |
| @@ -1397,18 +1736,35 @@ inline uint64_t get_header_value_uint64(const Headers &headers, const char *key, | |||
| 1397 | } | 1736 | } |
| 1398 | 1737 | ||
| 1399 | inline bool read_headers(Stream &strm, Headers &headers) { | 1738 | inline bool read_headers(Stream &strm, Headers &headers) { |
| 1400 | static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)"); | ||
| 1401 | |||
| 1402 | const auto bufsiz = 2048; | 1739 | const auto bufsiz = 2048; |
| 1403 | char buf[bufsiz]; | 1740 | char buf[bufsiz]; |
| 1404 | 1741 | stream_line_reader line_reader(strm, buf, bufsiz); | |
| 1405 | stream_line_reader reader(strm, buf, bufsiz); | ||
| 1406 | 1742 | ||
| 1407 | for (;;) { | 1743 | for (;;) { |
| 1408 | if (!reader.getline()) { return false; } | 1744 | if (!line_reader.getline()) { return false; } |
| 1409 | if (!strcmp(reader.ptr(), "\r\n")) { break; } | 1745 | |
| 1746 | // Check if the line ends with CRLF. | ||
| 1747 | if (line_reader.end_with_crlf()) { | ||
| 1748 | // Blank line indicates end of headers. | ||
| 1749 | if (line_reader.size() == 2) { break; } | ||
| 1750 | } else { | ||
| 1751 | continue; // Skip invalid line. | ||
| 1752 | } | ||
| 1753 | |||
| 1754 | // Skip trailing spaces and tabs. | ||
| 1755 | auto end = line_reader.ptr() + line_reader.size() - 2; | ||
| 1756 | while (line_reader.ptr() < end && (end[-1] == ' ' || end[-1] == '\t')) { | ||
| 1757 | end--; | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | // Horizontal tab and ' ' are considered whitespace and are ignored when on | ||
| 1761 | // the left or right side of the header value: | ||
| 1762 | // - https://stackoverflow.com/questions/50179659/ | ||
| 1763 | // - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html | ||
| 1764 | static const std::regex re(R"((.+?):[\t ]*(.+))"); | ||
| 1765 | |||
| 1410 | std::cmatch m; | 1766 | std::cmatch m; |
| 1411 | if (std::regex_match(reader.ptr(), m, re)) { | 1767 | if (std::regex_match(line_reader.ptr(), end, m, re)) { |
| 1412 | auto key = std::string(m[1]); | 1768 | auto key = std::string(m[1]); |
| 1413 | auto val = std::string(m[2]); | 1769 | auto val = std::string(m[2]); |
| 1414 | headers.emplace(key, val); | 1770 | headers.emplace(key, val); |
| @@ -1418,12 +1774,8 @@ inline bool read_headers(Stream &strm, Headers &headers) { | |||
| 1418 | return true; | 1774 | return true; |
| 1419 | } | 1775 | } |
| 1420 | 1776 | ||
| 1421 | typedef std::function<bool(const char *data, size_t data_length)> | ||
| 1422 | ContentReceiverCore; | ||
| 1423 | |||
| 1424 | inline bool read_content_with_length(Stream &strm, uint64_t len, | 1777 | inline bool read_content_with_length(Stream &strm, uint64_t len, |
| 1425 | Progress progress, | 1778 | Progress progress, ContentReceiver out) { |
| 1426 | ContentReceiverCore out) { | ||
| 1427 | char buf[CPPHTTPLIB_RECV_BUFSIZ]; | 1779 | char buf[CPPHTTPLIB_RECV_BUFSIZ]; |
| 1428 | 1780 | ||
| 1429 | uint64_t r = 0; | 1781 | uint64_t r = 0; |
| @@ -1455,7 +1807,7 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) { | |||
| 1455 | } | 1807 | } |
| 1456 | } | 1808 | } |
| 1457 | 1809 | ||
| 1458 | inline bool read_content_without_length(Stream &strm, ContentReceiverCore out) { | 1810 | inline bool read_content_without_length(Stream &strm, ContentReceiver out) { |
| 1459 | char buf[CPPHTTPLIB_RECV_BUFSIZ]; | 1811 | char buf[CPPHTTPLIB_RECV_BUFSIZ]; |
| 1460 | for (;;) { | 1812 | for (;;) { |
| 1461 | auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); | 1813 | auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ); |
| @@ -1470,33 +1822,34 @@ inline bool read_content_without_length(Stream &strm, ContentReceiverCore out) { | |||
| 1470 | return true; | 1822 | return true; |
| 1471 | } | 1823 | } |
| 1472 | 1824 | ||
| 1473 | inline bool read_content_chunked(Stream &strm, ContentReceiverCore out) { | 1825 | inline bool read_content_chunked(Stream &strm, ContentReceiver out) { |
| 1474 | const auto bufsiz = 16; | 1826 | const auto bufsiz = 16; |
| 1475 | char buf[bufsiz]; | 1827 | char buf[bufsiz]; |
| 1476 | 1828 | ||
| 1477 | stream_line_reader reader(strm, buf, bufsiz); | 1829 | stream_line_reader line_reader(strm, buf, bufsiz); |
| 1478 | 1830 | ||
| 1479 | if (!reader.getline()) { return false; } | 1831 | if (!line_reader.getline()) { return false; } |
| 1480 | 1832 | ||
| 1481 | auto chunk_len = std::stoi(reader.ptr(), 0, 16); | 1833 | auto chunk_len = std::stoi(line_reader.ptr(), 0, 16); |
| 1482 | 1834 | ||
| 1483 | while (chunk_len > 0) { | 1835 | while (chunk_len > 0) { |
| 1484 | if (!read_content_with_length(strm, chunk_len, nullptr, out)) { | 1836 | if (!read_content_with_length(strm, chunk_len, nullptr, out)) { |
| 1485 | return false; | 1837 | return false; |
| 1486 | } | 1838 | } |
| 1487 | 1839 | ||
| 1488 | if (!reader.getline()) { return false; } | 1840 | if (!line_reader.getline()) { return false; } |
| 1489 | 1841 | ||
| 1490 | if (strcmp(reader.ptr(), "\r\n")) { break; } | 1842 | if (strcmp(line_reader.ptr(), "\r\n")) { break; } |
| 1491 | 1843 | ||
| 1492 | if (!reader.getline()) { return false; } | 1844 | if (!line_reader.getline()) { return false; } |
| 1493 | 1845 | ||
| 1494 | chunk_len = std::stoi(reader.ptr(), 0, 16); | 1846 | chunk_len = std::stoi(line_reader.ptr(), 0, 16); |
| 1495 | } | 1847 | } |
| 1496 | 1848 | ||
| 1497 | if (chunk_len == 0) { | 1849 | if (chunk_len == 0) { |
| 1498 | // Reader terminator after chunks | 1850 | // Reader terminator after chunks |
| 1499 | if (!reader.getline() || strcmp(reader.ptr(), "\r\n")) return false; | 1851 | if (!line_reader.getline() || strcmp(line_reader.ptr(), "\r\n")) |
| 1852 | return false; | ||
| 1500 | } | 1853 | } |
| 1501 | 1854 | ||
| 1502 | return true; | 1855 | return true; |
| @@ -1509,14 +1862,14 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) { | |||
| 1509 | 1862 | ||
| 1510 | template <typename T> | 1863 | template <typename T> |
| 1511 | bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, | 1864 | bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status, |
| 1512 | Progress progress, ContentReceiverCore receiver) { | 1865 | Progress progress, ContentReceiver receiver) { |
| 1513 | 1866 | ||
| 1514 | ContentReceiverCore out = [&](const char *buf, size_t n) { | 1867 | ContentReceiver out = [&](const char *buf, size_t n) { |
| 1515 | return receiver(buf, n); | 1868 | return receiver(buf, n); |
| 1516 | }; | 1869 | }; |
| 1517 | 1870 | ||
| 1518 | #ifdef CPPHTTPLIB_ZLIB_SUPPORT | 1871 | #ifdef CPPHTTPLIB_ZLIB_SUPPORT |
| 1519 | detail::decompressor decompressor; | 1872 | decompressor decompressor; |
| 1520 | 1873 | ||
| 1521 | if (!decompressor.is_valid()) { | 1874 | if (!decompressor.is_valid()) { |
| 1522 | status = 500; | 1875 | status = 500; |
| @@ -1586,39 +1939,47 @@ inline ssize_t write_content(Stream &strm, ContentProvider content_provider, | |||
| 1586 | size_t end_offset = offset + length; | 1939 | size_t end_offset = offset + length; |
| 1587 | while (offset < end_offset) { | 1940 | while (offset < end_offset) { |
| 1588 | ssize_t written_length = 0; | 1941 | ssize_t written_length = 0; |
| 1589 | content_provider( | 1942 | |
| 1590 | offset, end_offset - offset, | 1943 | DataSink data_sink; |
| 1591 | [&](const char *d, size_t l) { | 1944 | data_sink.write = [&](const char *d, size_t l) { |
| 1592 | offset += l; | 1945 | offset += l; |
| 1593 | written_length = strm.write(d, l); | 1946 | written_length = strm.write(d, l); |
| 1594 | }, | 1947 | }; |
| 1595 | [&](void) { written_length = -1; }); | 1948 | data_sink.done = [&](void) { written_length = -1; }; |
| 1949 | data_sink.is_writable = [&](void) { return strm.is_writable(); }; | ||
| 1950 | |||
| 1951 | content_provider(offset, end_offset - offset, data_sink); | ||
| 1596 | if (written_length < 0) { return written_length; } | 1952 | if (written_length < 0) { return written_length; } |
| 1597 | } | 1953 | } |
| 1598 | return static_cast<ssize_t>(offset - begin_offset); | 1954 | return static_cast<ssize_t>(offset - begin_offset); |
| 1599 | } | 1955 | } |
| 1600 | 1956 | ||
| 1957 | template <typename T> | ||
| 1601 | inline ssize_t write_content_chunked(Stream &strm, | 1958 | inline ssize_t write_content_chunked(Stream &strm, |
| 1602 | ContentProvider content_provider) { | 1959 | ContentProvider content_provider, |
| 1960 | T is_shutting_down) { | ||
| 1603 | size_t offset = 0; | 1961 | size_t offset = 0; |
| 1604 | auto data_available = true; | 1962 | auto data_available = true; |
| 1605 | ssize_t total_written_length = 0; | 1963 | ssize_t total_written_length = 0; |
| 1606 | while (data_available) { | 1964 | while (data_available && !is_shutting_down()) { |
| 1607 | ssize_t written_length = 0; | 1965 | ssize_t written_length = 0; |
| 1608 | content_provider( | 1966 | |
| 1609 | offset, 0, | 1967 | DataSink data_sink; |
| 1610 | [&](const char *d, size_t l) { | 1968 | data_sink.write = [&](const char *d, size_t l) { |
| 1611 | data_available = l > 0; | 1969 | data_available = l > 0; |
| 1612 | offset += l; | 1970 | offset += l; |
| 1613 | 1971 | ||
| 1614 | // Emit chunked response header and footer for each chunk | 1972 | // Emit chunked response header and footer for each chunk |
| 1615 | auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n"; | 1973 | auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n"; |
| 1616 | written_length = strm.write(chunk); | 1974 | written_length = strm.write(chunk); |
| 1617 | }, | 1975 | }; |
| 1618 | [&](void) { | 1976 | data_sink.done = [&](void) { |
| 1619 | data_available = false; | 1977 | data_available = false; |
| 1620 | written_length = strm.write("0\r\n\r\n"); | 1978 | written_length = strm.write("0\r\n\r\n"); |
| 1621 | }); | 1979 | }; |
| 1980 | data_sink.is_writable = [&](void) { return strm.is_writable(); }; | ||
| 1981 | |||
| 1982 | content_provider(offset, 0, data_sink); | ||
| 1622 | 1983 | ||
| 1623 | if (written_length < 0) { return written_length; } | 1984 | if (written_length < 0) { return written_length; } |
| 1624 | total_written_length += written_length; | 1985 | total_written_length += written_length; |
| @@ -1629,17 +1990,12 @@ inline ssize_t write_content_chunked(Stream &strm, | |||
| 1629 | template <typename T> | 1990 | template <typename T> |
| 1630 | inline bool redirect(T &cli, const Request &req, Response &res, | 1991 | inline bool redirect(T &cli, const Request &req, Response &res, |
| 1631 | const std::string &path) { | 1992 | const std::string &path) { |
| 1632 | Request new_req; | 1993 | Request new_req = req; |
| 1633 | new_req.method = req.method; | ||
| 1634 | new_req.path = path; | 1994 | new_req.path = path; |
| 1635 | new_req.headers = req.headers; | 1995 | new_req.redirect_count -= 1; |
| 1636 | new_req.body = req.body; | ||
| 1637 | new_req.redirect_count = req.redirect_count - 1; | ||
| 1638 | new_req.response_handler = req.response_handler; | ||
| 1639 | new_req.content_receiver = req.content_receiver; | ||
| 1640 | new_req.progress = req.progress; | ||
| 1641 | 1996 | ||
| 1642 | Response new_res; | 1997 | Response new_res; |
| 1998 | |||
| 1643 | auto ret = cli.send(new_req, new_res); | 1999 | auto ret = cli.send(new_req, new_res); |
| 1644 | if (ret) { res = new_res; } | 2000 | if (ret) { res = new_res; } |
| 1645 | return ret; | 2001 | return ret; |
| @@ -1656,7 +2012,7 @@ inline std::string encode_url(const std::string &s) { | |||
| 1656 | case '\n': result += "%0A"; break; | 2012 | case '\n': result += "%0A"; break; |
| 1657 | case '\'': result += "%27"; break; | 2013 | case '\'': result += "%27"; break; |
| 1658 | case ',': result += "%2C"; break; | 2014 | case ',': result += "%2C"; break; |
| 1659 | case ':': result += "%3A"; break; | 2015 | // case ':': result += "%3A"; break; // ok? probably... |
| 1660 | case ';': result += "%3B"; break; | 2016 | case ';': result += "%3B"; break; |
| 1661 | default: | 2017 | default: |
| 1662 | auto c = static_cast<uint8_t>(s[i]); | 2018 | auto c = static_cast<uint8_t>(s[i]); |
| @@ -1716,11 +2072,11 @@ inline void parse_query_text(const std::string &s, Params ¶ms) { | |||
| 1716 | split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { | 2072 | split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) { |
| 1717 | std::string key; | 2073 | std::string key; |
| 1718 | std::string val; | 2074 | std::string val; |
| 1719 | split(b, e, '=', [&](const char *b, const char *e) { | 2075 | split(b, e, '=', [&](const char *b2, const char *e2) { |
| 1720 | if (key.empty()) { | 2076 | if (key.empty()) { |
| 1721 | key.assign(b, e); | 2077 | key.assign(b2, e2); |
| 1722 | } else { | 2078 | } else { |
| 1723 | val.assign(b, e); | 2079 | val.assign(b2, e2); |
| 1724 | } | 2080 | } |
| 1725 | }); | 2081 | }); |
| 1726 | params.emplace(key, decode_url(val)); | 2082 | params.emplace(key, decode_url(val)); |
| @@ -1736,112 +2092,207 @@ inline bool parse_multipart_boundary(const std::string &content_type, | |||
| 1736 | return true; | 2092 | return true; |
| 1737 | } | 2093 | } |
| 1738 | 2094 | ||
| 1739 | inline bool parse_multipart_formdata(const std::string &boundary, | 2095 | inline bool parse_range_header(const std::string &s, Ranges &ranges) { |
| 1740 | const std::string &body, | 2096 | static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); |
| 1741 | MultipartFiles &files) { | 2097 | std::smatch m; |
| 1742 | static std::string dash = "--"; | 2098 | if (std::regex_match(s, m, re_first_range)) { |
| 1743 | static std::string crlf = "\r\n"; | 2099 | auto pos = m.position(1); |
| 1744 | 2100 | auto len = m.length(1); | |
| 1745 | static std::regex re_content_type("Content-Type: (.*?)", | 2101 | bool all_valid_ranges = true; |
| 1746 | std::regex_constants::icase); | 2102 | split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) { |
| 1747 | 2103 | if (!all_valid_ranges) return; | |
| 1748 | static std::regex re_content_disposition( | 2104 | static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))"); |
| 1749 | "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", | 2105 | std::cmatch cm; |
| 1750 | std::regex_constants::icase); | 2106 | if (std::regex_match(b, e, cm, re_another_range)) { |
| 1751 | 2107 | ssize_t first = -1; | |
| 1752 | auto dash_boundary = dash + boundary; | 2108 | if (!cm.str(1).empty()) { |
| 1753 | 2109 | first = static_cast<ssize_t>(std::stoll(cm.str(1))); | |
| 1754 | auto pos = body.find(dash_boundary); | 2110 | } |
| 1755 | if (pos != 0) { return false; } | ||
| 1756 | |||
| 1757 | pos += dash_boundary.size(); | ||
| 1758 | 2111 | ||
| 1759 | auto next_pos = body.find(crlf, pos); | 2112 | ssize_t last = -1; |
| 1760 | if (next_pos == std::string::npos) { return false; } | 2113 | if (!cm.str(2).empty()) { |
| 2114 | last = static_cast<ssize_t>(std::stoll(cm.str(2))); | ||
| 2115 | } | ||
| 1761 | 2116 | ||
| 1762 | pos = next_pos + crlf.size(); | 2117 | if (first != -1 && last != -1 && first > last) { |
| 2118 | all_valid_ranges = false; | ||
| 2119 | return; | ||
| 2120 | } | ||
| 2121 | ranges.emplace_back(std::make_pair(first, last)); | ||
| 2122 | } | ||
| 2123 | }); | ||
| 2124 | return all_valid_ranges; | ||
| 2125 | } | ||
| 2126 | return false; | ||
| 2127 | } | ||
| 1763 | 2128 | ||
| 1764 | while (pos < body.size()) { | 2129 | class MultipartFormDataParser { |
| 1765 | next_pos = body.find(crlf, pos); | 2130 | public: |
| 1766 | if (next_pos == std::string::npos) { return false; } | 2131 | MultipartFormDataParser() {} |
| 1767 | 2132 | ||
| 1768 | std::string name; | 2133 | void set_boundary(const std::string &boundary) { boundary_ = boundary; } |
| 1769 | MultipartFile file; | ||
| 1770 | 2134 | ||
| 1771 | auto header = body.substr(pos, (next_pos - pos)); | 2135 | bool is_valid() const { return is_valid_; } |
| 1772 | 2136 | ||
| 1773 | while (pos != next_pos) { | 2137 | template <typename T, typename U> |
| 1774 | std::smatch m; | 2138 | bool parse(const char *buf, size_t n, T content_callback, U header_callback) { |
| 1775 | if (std::regex_match(header, m, re_content_type)) { | 2139 | static const std::regex re_content_type(R"(^Content-Type:\s*(.*?)\s*$)", |
| 1776 | file.content_type = m[1]; | 2140 | std::regex_constants::icase); |
| 1777 | } else if (std::regex_match(header, m, re_content_disposition)) { | 2141 | |
| 1778 | name = m[1]; | 2142 | static const std::regex re_content_disposition( |
| 1779 | file.filename = m[2]; | 2143 | "^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename=" |
| 2144 | "\"(.*?)\")?\\s*$", | ||
| 2145 | std::regex_constants::icase); | ||
| 2146 | |||
| 2147 | buf_.append(buf, n); // TODO: performance improvement | ||
| 2148 | |||
| 2149 | while (!buf_.empty()) { | ||
| 2150 | switch (state_) { | ||
| 2151 | case 0: { // Initial boundary | ||
| 2152 | auto pattern = dash_ + boundary_ + crlf_; | ||
| 2153 | if (pattern.size() > buf_.size()) { return true; } | ||
| 2154 | auto pos = buf_.find(pattern); | ||
| 2155 | if (pos != 0) { | ||
| 2156 | is_done_ = true; | ||
| 2157 | return false; | ||
| 2158 | } | ||
| 2159 | buf_.erase(0, pattern.size()); | ||
| 2160 | off_ += pattern.size(); | ||
| 2161 | state_ = 1; | ||
| 2162 | break; | ||
| 1780 | } | 2163 | } |
| 2164 | case 1: { // New entry | ||
| 2165 | clear_file_info(); | ||
| 2166 | state_ = 2; | ||
| 2167 | break; | ||
| 2168 | } | ||
| 2169 | case 2: { // Headers | ||
| 2170 | auto pos = buf_.find(crlf_); | ||
| 2171 | while (pos != std::string::npos) { | ||
| 2172 | // Empty line | ||
| 2173 | if (pos == 0) { | ||
| 2174 | if (!header_callback(file_)) { | ||
| 2175 | is_valid_ = false; | ||
| 2176 | is_done_ = false; | ||
| 2177 | return false; | ||
| 2178 | } | ||
| 2179 | buf_.erase(0, crlf_.size()); | ||
| 2180 | off_ += crlf_.size(); | ||
| 2181 | state_ = 3; | ||
| 2182 | break; | ||
| 2183 | } | ||
| 1781 | 2184 | ||
| 1782 | pos = next_pos + crlf.size(); | 2185 | auto header = buf_.substr(0, pos); |
| 1783 | 2186 | { | |
| 1784 | next_pos = body.find(crlf, pos); | 2187 | std::smatch m; |
| 1785 | if (next_pos == std::string::npos) { return false; } | 2188 | if (std::regex_match(header, m, re_content_type)) { |
| 1786 | 2189 | file_.content_type = m[1]; | |
| 1787 | header = body.substr(pos, (next_pos - pos)); | 2190 | } else if (std::regex_match(header, m, re_content_disposition)) { |
| 1788 | } | 2191 | file_.name = m[1]; |
| 1789 | 2192 | file_.filename = m[2]; | |
| 1790 | pos = next_pos + crlf.size(); | 2193 | } |
| 2194 | } | ||
| 1791 | 2195 | ||
| 1792 | next_pos = body.find(crlf + dash_boundary, pos); | 2196 | buf_.erase(0, pos + crlf_.size()); |
| 2197 | off_ += pos + crlf_.size(); | ||
| 2198 | pos = buf_.find(crlf_); | ||
| 2199 | } | ||
| 2200 | break; | ||
| 2201 | } | ||
| 2202 | case 3: { // Body | ||
| 2203 | { | ||
| 2204 | auto pattern = crlf_ + dash_; | ||
| 2205 | if (pattern.size() > buf_.size()) { return true; } | ||
| 2206 | |||
| 2207 | auto pos = buf_.find(pattern); | ||
| 2208 | if (pos == std::string::npos) { pos = buf_.size(); } | ||
| 2209 | if (!content_callback(buf_.data(), pos)) { | ||
| 2210 | is_valid_ = false; | ||
| 2211 | is_done_ = false; | ||
| 2212 | return false; | ||
| 2213 | } | ||
| 1793 | 2214 | ||
| 1794 | if (next_pos == std::string::npos) { return false; } | 2215 | off_ += pos; |
| 2216 | buf_.erase(0, pos); | ||
| 2217 | } | ||
| 1795 | 2218 | ||
| 1796 | file.offset = pos; | 2219 | { |
| 1797 | file.length = next_pos - pos; | 2220 | auto pattern = crlf_ + dash_ + boundary_; |
| 2221 | if (pattern.size() > buf_.size()) { return true; } | ||
| 2222 | |||
| 2223 | auto pos = buf_.find(pattern); | ||
| 2224 | if (pos != std::string::npos) { | ||
| 2225 | if (!content_callback(buf_.data(), pos)) { | ||
| 2226 | is_valid_ = false; | ||
| 2227 | is_done_ = false; | ||
| 2228 | return false; | ||
| 2229 | } | ||
| 1798 | 2230 | ||
| 1799 | pos = next_pos + crlf.size() + dash_boundary.size(); | 2231 | off_ += pos + pattern.size(); |
| 2232 | buf_.erase(0, pos + pattern.size()); | ||
| 2233 | state_ = 4; | ||
| 2234 | } else { | ||
| 2235 | if (!content_callback(buf_.data(), pattern.size())) { | ||
| 2236 | is_valid_ = false; | ||
| 2237 | is_done_ = false; | ||
| 2238 | return false; | ||
| 2239 | } | ||
| 1800 | 2240 | ||
| 1801 | next_pos = body.find(crlf, pos); | 2241 | off_ += pattern.size(); |
| 1802 | if (next_pos == std::string::npos) { return false; } | 2242 | buf_.erase(0, pattern.size()); |
| 2243 | } | ||
| 2244 | } | ||
| 2245 | break; | ||
| 2246 | } | ||
| 2247 | case 4: { // Boundary | ||
| 2248 | if (crlf_.size() > buf_.size()) { return true; } | ||
| 2249 | if (buf_.find(crlf_) == 0) { | ||
| 2250 | buf_.erase(0, crlf_.size()); | ||
| 2251 | off_ += crlf_.size(); | ||
| 2252 | state_ = 1; | ||
| 2253 | } else { | ||
| 2254 | auto pattern = dash_ + crlf_; | ||
| 2255 | if (pattern.size() > buf_.size()) { return true; } | ||
| 2256 | if (buf_.find(pattern) == 0) { | ||
| 2257 | buf_.erase(0, pattern.size()); | ||
| 2258 | off_ += pattern.size(); | ||
| 2259 | is_valid_ = true; | ||
| 2260 | state_ = 5; | ||
| 2261 | } else { | ||
| 2262 | is_done_ = true; | ||
| 2263 | return true; | ||
| 2264 | } | ||
| 2265 | } | ||
| 2266 | break; | ||
| 2267 | } | ||
| 2268 | case 5: { // Done | ||
| 2269 | is_valid_ = false; | ||
| 2270 | return false; | ||
| 2271 | } | ||
| 2272 | } | ||
| 2273 | } | ||
| 1803 | 2274 | ||
| 1804 | files.emplace(name, file); | 2275 | return true; |
| 2276 | } | ||
| 1805 | 2277 | ||
| 1806 | pos = next_pos + crlf.size(); | 2278 | private: |
| 2279 | void clear_file_info() { | ||
| 2280 | file_.name.clear(); | ||
| 2281 | file_.filename.clear(); | ||
| 2282 | file_.content_type.clear(); | ||
| 1807 | } | 2283 | } |
| 1808 | 2284 | ||
| 1809 | return true; | 2285 | const std::string dash_ = "--"; |
| 1810 | } | 2286 | const std::string crlf_ = "\r\n"; |
| 2287 | std::string boundary_; | ||
| 1811 | 2288 | ||
| 1812 | inline bool parse_range_header(const std::string &s, Ranges &ranges) { | 2289 | std::string buf_; |
| 1813 | try { | 2290 | size_t state_ = 0; |
| 1814 | static auto re = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))"); | 2291 | size_t is_valid_ = false; |
| 1815 | std::smatch m; | 2292 | size_t is_done_ = false; |
| 1816 | if (std::regex_match(s, m, re)) { | 2293 | size_t off_ = 0; |
| 1817 | auto pos = m.position(1); | 2294 | MultipartFormData file_; |
| 1818 | auto len = m.length(1); | 2295 | }; |
| 1819 | detail::split(&s[pos], &s[pos + len], ',', | ||
| 1820 | [&](const char *b, const char *e) { | ||
| 1821 | static auto re = std::regex(R"(\s*(\d*)-(\d*))"); | ||
| 1822 | std::cmatch m; | ||
| 1823 | if (std::regex_match(b, e, m, re)) { | ||
| 1824 | ssize_t first = -1; | ||
| 1825 | if (!m.str(1).empty()) { | ||
| 1826 | first = static_cast<ssize_t>(std::stoll(m.str(1))); | ||
| 1827 | } | ||
| 1828 | |||
| 1829 | ssize_t last = -1; | ||
| 1830 | if (!m.str(2).empty()) { | ||
| 1831 | last = static_cast<ssize_t>(std::stoll(m.str(2))); | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | if (first != -1 && last != -1 && first > last) { | ||
| 1835 | throw std::runtime_error("invalid range error"); | ||
| 1836 | } | ||
| 1837 | ranges.emplace_back(std::make_pair(first, last)); | ||
| 1838 | } | ||
| 1839 | }); | ||
| 1840 | return true; | ||
| 1841 | } | ||
| 1842 | return false; | ||
| 1843 | } catch (...) { return false; } | ||
| 1844 | } | ||
| 1845 | 2296 | ||
| 1846 | inline std::string to_lower(const char *beg, const char *end) { | 2297 | inline std::string to_lower(const char *beg, const char *end) { |
| 1847 | std::string out; | 2298 | std::string out; |
| @@ -1915,7 +2366,7 @@ bool process_multipart_ranges_data(const Request &req, Response &res, | |||
| 1915 | ctoken("\r\n"); | 2366 | ctoken("\r\n"); |
| 1916 | } | 2367 | } |
| 1917 | 2368 | ||
| 1918 | auto offsets = detail::get_range_offset_and_length(req, res.body.size(), i); | 2369 | auto offsets = get_range_offset_and_length(req, res.body.size(), i); |
| 1919 | auto offset = offsets.first; | 2370 | auto offset = offsets.first; |
| 1920 | auto length = offsets.second; | 2371 | auto length = offsets.second; |
| 1921 | 2372 | ||
| @@ -1978,8 +2429,7 @@ inline bool write_multipart_ranges_data(Stream &strm, const Request &req, | |||
| 1978 | [&](const std::string &token) { strm.write(token); }, | 2429 | [&](const std::string &token) { strm.write(token); }, |
| 1979 | [&](const char *token) { strm.write(token); }, | 2430 | [&](const char *token) { strm.write(token); }, |
| 1980 | [&](size_t offset, size_t length) { | 2431 | [&](size_t offset, size_t length) { |
| 1981 | return detail::write_content(strm, res.content_provider, offset, | 2432 | return write_content(strm, res.content_provider, offset, length) >= 0; |
| 1982 | length) >= 0; | ||
| 1983 | }); | 2433 | }); |
| 1984 | } | 2434 | } |
| 1985 | 2435 | ||
| @@ -1988,11 +2438,56 @@ get_range_offset_and_length(const Request &req, const Response &res, | |||
| 1988 | size_t index) { | 2438 | size_t index) { |
| 1989 | auto r = req.ranges[index]; | 2439 | auto r = req.ranges[index]; |
| 1990 | 2440 | ||
| 1991 | if (r.second == -1) { r.second = res.content_provider_resource_length - 1; } | 2441 | if (r.second == -1) { r.second = res.content_length - 1; } |
| 1992 | 2442 | ||
| 1993 | return std::make_pair(r.first, r.second - r.first + 1); | 2443 | return std::make_pair(r.first, r.second - r.first + 1); |
| 1994 | } | 2444 | } |
| 1995 | 2445 | ||
| 2446 | inline bool expect_content(const Request &req) { | ||
| 2447 | if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || | ||
| 2448 | req.method == "PRI") { | ||
| 2449 | return true; | ||
| 2450 | } | ||
| 2451 | // TODO: check if Content-Length is set | ||
| 2452 | return false; | ||
| 2453 | } | ||
| 2454 | |||
| 2455 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 2456 | template <typename CTX, typename Init, typename Update, typename Final> | ||
| 2457 | inline std::string message_digest(const std::string &s, Init init, | ||
| 2458 | Update update, Final final, | ||
| 2459 | size_t digest_length) { | ||
| 2460 | using namespace std; | ||
| 2461 | |||
| 2462 | std::vector<unsigned char> md(digest_length, 0); | ||
| 2463 | CTX ctx; | ||
| 2464 | init(&ctx); | ||
| 2465 | update(&ctx, s.data(), s.size()); | ||
| 2466 | final(md.data(), &ctx); | ||
| 2467 | |||
| 2468 | stringstream ss; | ||
| 2469 | for (auto c : md) { | ||
| 2470 | ss << setfill('0') << setw(2) << hex << (unsigned int)c; | ||
| 2471 | } | ||
| 2472 | return ss.str(); | ||
| 2473 | } | ||
| 2474 | |||
| 2475 | inline std::string MD5(const std::string &s) { | ||
| 2476 | return message_digest<MD5_CTX>(s, MD5_Init, MD5_Update, MD5_Final, | ||
| 2477 | MD5_DIGEST_LENGTH); | ||
| 2478 | } | ||
| 2479 | |||
| 2480 | inline std::string SHA_256(const std::string &s) { | ||
| 2481 | return message_digest<SHA256_CTX>(s, SHA256_Init, SHA256_Update, SHA256_Final, | ||
| 2482 | SHA256_DIGEST_LENGTH); | ||
| 2483 | } | ||
| 2484 | |||
| 2485 | inline std::string SHA_512(const std::string &s) { | ||
| 2486 | return message_digest<SHA512_CTX>(s, SHA512_Init, SHA512_Update, SHA512_Final, | ||
| 2487 | SHA512_DIGEST_LENGTH); | ||
| 2488 | } | ||
| 2489 | #endif | ||
| 2490 | |||
| 1996 | #ifdef _WIN32 | 2491 | #ifdef _WIN32 |
| 1997 | class WSInit { | 2492 | class WSInit { |
| 1998 | public: | 2493 | public: |
| @@ -2025,9 +2520,103 @@ inline std::pair<std::string, std::string> make_range_header(Ranges ranges) { | |||
| 2025 | 2520 | ||
| 2026 | inline std::pair<std::string, std::string> | 2521 | inline std::pair<std::string, std::string> |
| 2027 | make_basic_authentication_header(const std::string &username, | 2522 | make_basic_authentication_header(const std::string &username, |
| 2028 | const std::string &password) { | 2523 | const std::string &password, |
| 2524 | bool is_proxy = false) { | ||
| 2029 | auto field = "Basic " + detail::base64_encode(username + ":" + password); | 2525 | auto field = "Basic " + detail::base64_encode(username + ":" + password); |
| 2030 | return std::make_pair("Authorization", field); | 2526 | auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; |
| 2527 | return std::make_pair(key, field); | ||
| 2528 | } | ||
| 2529 | |||
| 2530 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 2531 | inline std::pair<std::string, std::string> make_digest_authentication_header( | ||
| 2532 | const Request &req, const std::map<std::string, std::string> &auth, | ||
| 2533 | size_t cnonce_count, const std::string &cnonce, const std::string &username, | ||
| 2534 | const std::string &password, bool is_proxy = false) { | ||
| 2535 | using namespace std; | ||
| 2536 | |||
| 2537 | string nc; | ||
| 2538 | { | ||
| 2539 | stringstream ss; | ||
| 2540 | ss << setfill('0') << setw(8) << hex << cnonce_count; | ||
| 2541 | nc = ss.str(); | ||
| 2542 | } | ||
| 2543 | |||
| 2544 | auto qop = auth.at("qop"); | ||
| 2545 | if (qop.find("auth-int") != std::string::npos) { | ||
| 2546 | qop = "auth-int"; | ||
| 2547 | } else { | ||
| 2548 | qop = "auth"; | ||
| 2549 | } | ||
| 2550 | |||
| 2551 | std::string algo = "MD5"; | ||
| 2552 | if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); } | ||
| 2553 | |||
| 2554 | string response; | ||
| 2555 | { | ||
| 2556 | auto H = algo == "SHA-256" | ||
| 2557 | ? detail::SHA_256 | ||
| 2558 | : algo == "SHA-512" ? detail::SHA_512 : detail::MD5; | ||
| 2559 | |||
| 2560 | auto A1 = username + ":" + auth.at("realm") + ":" + password; | ||
| 2561 | |||
| 2562 | auto A2 = req.method + ":" + req.path; | ||
| 2563 | if (qop == "auth-int") { A2 += ":" + H(req.body); } | ||
| 2564 | |||
| 2565 | response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce + | ||
| 2566 | ":" + qop + ":" + H(A2)); | ||
| 2567 | } | ||
| 2568 | |||
| 2569 | auto field = "Digest username=\"hello\", realm=\"" + auth.at("realm") + | ||
| 2570 | "\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path + | ||
| 2571 | "\", algorithm=" + algo + ", qop=" + qop + ", nc=\"" + nc + | ||
| 2572 | "\", cnonce=\"" + cnonce + "\", response=\"" + response + "\""; | ||
| 2573 | |||
| 2574 | auto key = is_proxy ? "Proxy-Authorization" : "Authorization"; | ||
| 2575 | return std::make_pair(key, field); | ||
| 2576 | } | ||
| 2577 | #endif | ||
| 2578 | |||
| 2579 | inline bool parse_www_authenticate(const httplib::Response &res, | ||
| 2580 | std::map<std::string, std::string> &auth, | ||
| 2581 | bool is_proxy) { | ||
| 2582 | auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate"; | ||
| 2583 | if (res.has_header(auth_key)) { | ||
| 2584 | static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~"); | ||
| 2585 | auto s = res.get_header_value(auth_key); | ||
| 2586 | auto pos = s.find(' '); | ||
| 2587 | if (pos != std::string::npos) { | ||
| 2588 | auto type = s.substr(0, pos); | ||
| 2589 | if (type == "Basic") { | ||
| 2590 | return false; | ||
| 2591 | } else if (type == "Digest") { | ||
| 2592 | s = s.substr(pos + 1); | ||
| 2593 | auto beg = std::sregex_iterator(s.begin(), s.end(), re); | ||
| 2594 | for (auto i = beg; i != std::sregex_iterator(); ++i) { | ||
| 2595 | auto m = *i; | ||
| 2596 | auto key = s.substr(m.position(1), m.length(1)); | ||
| 2597 | auto val = m.length(2) > 0 ? s.substr(m.position(2), m.length(2)) | ||
| 2598 | : s.substr(m.position(3), m.length(3)); | ||
| 2599 | auth[key] = val; | ||
| 2600 | } | ||
| 2601 | return true; | ||
| 2602 | } | ||
| 2603 | } | ||
| 2604 | } | ||
| 2605 | return false; | ||
| 2606 | } | ||
| 2607 | |||
| 2608 | // https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c/440240#answer-440240 | ||
| 2609 | inline std::string random_string(size_t length) { | ||
| 2610 | auto randchar = []() -> char { | ||
| 2611 | const char charset[] = "0123456789" | ||
| 2612 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
| 2613 | "abcdefghijklmnopqrstuvwxyz"; | ||
| 2614 | const size_t max_index = (sizeof(charset) - 1); | ||
| 2615 | return charset[rand() % max_index]; | ||
| 2616 | }; | ||
| 2617 | std::string str(length, 0); | ||
| 2618 | std::generate_n(str.begin(), length, randchar); | ||
| 2619 | return str; | ||
| 2031 | } | 2620 | } |
| 2032 | 2621 | ||
| 2033 | // Request implementation | 2622 | // Request implementation |
| @@ -2068,14 +2657,19 @@ inline size_t Request::get_param_value_count(const char *key) const { | |||
| 2068 | return std::distance(r.first, r.second); | 2657 | return std::distance(r.first, r.second); |
| 2069 | } | 2658 | } |
| 2070 | 2659 | ||
| 2660 | inline bool Request::is_multipart_form_data() const { | ||
| 2661 | const auto &content_type = get_header_value("Content-Type"); | ||
| 2662 | return !content_type.find("multipart/form-data"); | ||
| 2663 | } | ||
| 2664 | |||
| 2071 | inline bool Request::has_file(const char *key) const { | 2665 | inline bool Request::has_file(const char *key) const { |
| 2072 | return files.find(key) != files.end(); | 2666 | return files.find(key) != files.end(); |
| 2073 | } | 2667 | } |
| 2074 | 2668 | ||
| 2075 | inline MultipartFile Request::get_file_value(const char *key) const { | 2669 | inline MultipartFormData Request::get_file_value(const char *key) const { |
| 2076 | auto it = files.find(key); | 2670 | auto it = files.find(key); |
| 2077 | if (it != files.end()) { return it->second; } | 2671 | if (it != files.end()) { return it->second; } |
| 2078 | return MultipartFile(); | 2672 | return MultipartFormData(); |
| 2079 | } | 2673 | } |
| 2080 | 2674 | ||
| 2081 | // Response implementation | 2675 | // Response implementation |
| @@ -2119,40 +2713,47 @@ inline void Response::set_content(const std::string &s, | |||
| 2119 | } | 2713 | } |
| 2120 | 2714 | ||
| 2121 | inline void Response::set_content_provider( | 2715 | inline void Response::set_content_provider( |
| 2122 | size_t length, | 2716 | size_t in_length, |
| 2123 | std::function<void(size_t offset, size_t length, DataSink sink)> provider, | 2717 | std::function<void(size_t offset, size_t length, DataSink &sink)> provider, |
| 2124 | std::function<void()> resource_releaser) { | 2718 | std::function<void()> resource_releaser) { |
| 2125 | assert(length > 0); | 2719 | assert(in_length > 0); |
| 2126 | content_provider_resource_length = length; | 2720 | content_length = in_length; |
| 2127 | content_provider = [provider](size_t offset, size_t length, DataSink sink, | 2721 | content_provider = [provider](size_t offset, size_t length, DataSink &sink) { |
| 2128 | Done) { provider(offset, length, sink); }; | 2722 | provider(offset, length, sink); |
| 2723 | }; | ||
| 2129 | content_provider_resource_releaser = resource_releaser; | 2724 | content_provider_resource_releaser = resource_releaser; |
| 2130 | } | 2725 | } |
| 2131 | 2726 | ||
| 2132 | inline void Response::set_chunked_content_provider( | 2727 | inline void Response::set_chunked_content_provider( |
| 2133 | std::function<void(size_t offset, DataSink sink, Done done)> provider, | 2728 | std::function<void(size_t offset, DataSink &sink)> provider, |
| 2134 | std::function<void()> resource_releaser) { | 2729 | std::function<void()> resource_releaser) { |
| 2135 | content_provider_resource_length = 0; | 2730 | content_length = 0; |
| 2136 | content_provider = [provider](size_t offset, size_t, DataSink sink, | 2731 | content_provider = [provider](size_t offset, size_t, DataSink &sink) { |
| 2137 | Done done) { provider(offset, sink, done); }; | 2732 | provider(offset, sink); |
| 2733 | }; | ||
| 2138 | content_provider_resource_releaser = resource_releaser; | 2734 | content_provider_resource_releaser = resource_releaser; |
| 2139 | } | 2735 | } |
| 2140 | 2736 | ||
| 2141 | // Rstream implementation | 2737 | // Rstream implementation |
| 2738 | inline int Stream::write(const char *ptr) { return write(ptr, strlen(ptr)); } | ||
| 2739 | |||
| 2740 | inline int Stream::write(const std::string &s) { | ||
| 2741 | return write(s.data(), s.size()); | ||
| 2742 | } | ||
| 2743 | |||
| 2142 | template <typename... Args> | 2744 | template <typename... Args> |
| 2143 | inline int Stream::write_format(const char *fmt, const Args &... args) { | 2745 | inline int Stream::write_format(const char *fmt, const Args &... args) { |
| 2144 | const auto bufsiz = 2048; | 2746 | std::array<char, 2048> buf; |
| 2145 | char buf[bufsiz]; | ||
| 2146 | 2747 | ||
| 2147 | #if defined(_MSC_VER) && _MSC_VER < 1900 | 2748 | #if defined(_MSC_VER) && _MSC_VER < 1900 |
| 2148 | auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...); | 2749 | auto n = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...); |
| 2149 | #else | 2750 | #else |
| 2150 | auto n = snprintf(buf, bufsiz - 1, fmt, args...); | 2751 | auto n = snprintf(buf.data(), buf.size() - 1, fmt, args...); |
| 2151 | #endif | 2752 | #endif |
| 2152 | if (n <= 0) { return n; } | 2753 | if (n <= 0) { return n; } |
| 2153 | 2754 | ||
| 2154 | if (n >= bufsiz - 1) { | 2755 | if (n >= static_cast<int>(buf.size()) - 1) { |
| 2155 | std::vector<char> glowable_buf(bufsiz); | 2756 | std::vector<char> glowable_buf(buf.size()); |
| 2156 | 2757 | ||
| 2157 | while (n >= static_cast<int>(glowable_buf.size() - 1)) { | 2758 | while (n >= static_cast<int>(glowable_buf.size() - 1)) { |
| 2158 | glowable_buf.resize(glowable_buf.size() * 2); | 2759 | glowable_buf.resize(glowable_buf.size() * 2); |
| @@ -2165,33 +2766,36 @@ inline int Stream::write_format(const char *fmt, const Args &... args) { | |||
| 2165 | } | 2766 | } |
| 2166 | return write(&glowable_buf[0], n); | 2767 | return write(&glowable_buf[0], n); |
| 2167 | } else { | 2768 | } else { |
| 2168 | return write(buf, n); | 2769 | return write(buf.data(), n); |
| 2169 | } | 2770 | } |
| 2170 | } | 2771 | } |
| 2171 | 2772 | ||
| 2773 | namespace detail { | ||
| 2774 | |||
| 2172 | // Socket stream implementation | 2775 | // Socket stream implementation |
| 2173 | inline SocketStream::SocketStream(socket_t sock) : sock_(sock) {} | 2776 | inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec, |
| 2777 | time_t read_timeout_usec) | ||
| 2778 | : sock_(sock), read_timeout_sec_(read_timeout_sec), | ||
| 2779 | read_timeout_usec_(read_timeout_usec) {} | ||
| 2174 | 2780 | ||
| 2175 | inline SocketStream::~SocketStream() {} | 2781 | inline SocketStream::~SocketStream() {} |
| 2176 | 2782 | ||
| 2177 | inline int SocketStream::read(char *ptr, size_t size) { | 2783 | inline bool SocketStream::is_readable() const { |
| 2178 | if (detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, | 2784 | return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; |
| 2179 | CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) { | ||
| 2180 | return recv(sock_, ptr, static_cast<int>(size), 0); | ||
| 2181 | } | ||
| 2182 | return -1; | ||
| 2183 | } | 2785 | } |
| 2184 | 2786 | ||
| 2185 | inline int SocketStream::write(const char *ptr, size_t size) { | 2787 | inline bool SocketStream::is_writable() const { |
| 2186 | return send(sock_, ptr, static_cast<int>(size), 0); | 2788 | return detail::select_write(sock_, 0, 0) > 0; |
| 2187 | } | 2789 | } |
| 2188 | 2790 | ||
| 2189 | inline int SocketStream::write(const char *ptr) { | 2791 | inline int SocketStream::read(char *ptr, size_t size) { |
| 2190 | return write(ptr, strlen(ptr)); | 2792 | if (is_readable()) { return recv(sock_, ptr, static_cast<int>(size), 0); } |
| 2793 | return -1; | ||
| 2191 | } | 2794 | } |
| 2192 | 2795 | ||
| 2193 | inline int SocketStream::write(const std::string &s) { | 2796 | inline int SocketStream::write(const char *ptr, size_t size) { |
| 2194 | return write(s.data(), s.size()); | 2797 | if (is_writable()) { return send(sock_, ptr, static_cast<int>(size), 0); } |
| 2798 | return -1; | ||
| 2195 | } | 2799 | } |
| 2196 | 2800 | ||
| 2197 | inline std::string SocketStream::get_remote_addr() const { | 2801 | inline std::string SocketStream::get_remote_addr() const { |
| @@ -2199,12 +2803,18 @@ inline std::string SocketStream::get_remote_addr() const { | |||
| 2199 | } | 2803 | } |
| 2200 | 2804 | ||
| 2201 | // Buffer stream implementation | 2805 | // Buffer stream implementation |
| 2806 | inline bool BufferStream::is_readable() const { return true; } | ||
| 2807 | |||
| 2808 | inline bool BufferStream::is_writable() const { return true; } | ||
| 2809 | |||
| 2202 | inline int BufferStream::read(char *ptr, size_t size) { | 2810 | inline int BufferStream::read(char *ptr, size_t size) { |
| 2203 | #if defined(_MSC_VER) && _MSC_VER < 1900 | 2811 | #if defined(_MSC_VER) && _MSC_VER < 1900 |
| 2204 | return static_cast<int>(buffer._Copy_s(ptr, size, size)); | 2812 | int len_read = static_cast<int>(buffer._Copy_s(ptr, size, size, position)); |
| 2205 | #else | 2813 | #else |
| 2206 | return static_cast<int>(buffer.copy(ptr, size)); | 2814 | int len_read = static_cast<int>(buffer.copy(ptr, size, position)); |
| 2207 | #endif | 2815 | #endif |
| 2816 | position += len_read; | ||
| 2817 | return len_read; | ||
| 2208 | } | 2818 | } |
| 2209 | 2819 | ||
| 2210 | inline int BufferStream::write(const char *ptr, size_t size) { | 2820 | inline int BufferStream::write(const char *ptr, size_t size) { |
| @@ -2212,33 +2822,23 @@ inline int BufferStream::write(const char *ptr, size_t size) { | |||
| 2212 | return static_cast<int>(size); | 2822 | return static_cast<int>(size); |
| 2213 | } | 2823 | } |
| 2214 | 2824 | ||
| 2215 | inline int BufferStream::write(const char *ptr) { | ||
| 2216 | return write(ptr, strlen(ptr)); | ||
| 2217 | } | ||
| 2218 | |||
| 2219 | inline int BufferStream::write(const std::string &s) { | ||
| 2220 | return write(s.data(), s.size()); | ||
| 2221 | } | ||
| 2222 | |||
| 2223 | inline std::string BufferStream::get_remote_addr() const { return ""; } | 2825 | inline std::string BufferStream::get_remote_addr() const { return ""; } |
| 2224 | 2826 | ||
| 2225 | inline const std::string &BufferStream::get_buffer() const { return buffer; } | 2827 | inline const std::string &BufferStream::get_buffer() const { return buffer; } |
| 2226 | 2828 | ||
| 2829 | } // namespace detail | ||
| 2830 | |||
| 2227 | // HTTP server implementation | 2831 | // HTTP server implementation |
| 2228 | inline Server::Server() | 2832 | inline Server::Server() |
| 2229 | : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), | 2833 | : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), |
| 2834 | read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND), | ||
| 2835 | read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND), | ||
| 2230 | payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), is_running_(false), | 2836 | payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), is_running_(false), |
| 2231 | svr_sock_(INVALID_SOCKET) { | 2837 | svr_sock_(INVALID_SOCKET) { |
| 2232 | #ifndef _WIN32 | 2838 | #ifndef _WIN32 |
| 2233 | signal(SIGPIPE, SIG_IGN); | 2839 | signal(SIGPIPE, SIG_IGN); |
| 2234 | #endif | 2840 | #endif |
| 2235 | new_task_queue = [] { | 2841 | new_task_queue = [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }; |
| 2236 | #if CPPHTTPLIB_THREAD_POOL_COUNT > 0 | ||
| 2237 | return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); | ||
| 2238 | #else | ||
| 2239 | return new Threads(); | ||
| 2240 | #endif | ||
| 2241 | }; | ||
| 2242 | } | 2842 | } |
| 2243 | 2843 | ||
| 2244 | inline Server::~Server() {} | 2844 | inline Server::~Server() {} |
| @@ -2253,16 +2853,37 @@ inline Server &Server::Post(const char *pattern, Handler handler) { | |||
| 2253 | return *this; | 2853 | return *this; |
| 2254 | } | 2854 | } |
| 2255 | 2855 | ||
| 2856 | inline Server &Server::Post(const char *pattern, | ||
| 2857 | HandlerWithContentReader handler) { | ||
| 2858 | post_handlers_for_content_reader_.push_back( | ||
| 2859 | std::make_pair(std::regex(pattern), handler)); | ||
| 2860 | return *this; | ||
| 2861 | } | ||
| 2862 | |||
| 2256 | inline Server &Server::Put(const char *pattern, Handler handler) { | 2863 | inline Server &Server::Put(const char *pattern, Handler handler) { |
| 2257 | put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); | 2864 | put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); |
| 2258 | return *this; | 2865 | return *this; |
| 2259 | } | 2866 | } |
| 2260 | 2867 | ||
| 2868 | inline Server &Server::Put(const char *pattern, | ||
| 2869 | HandlerWithContentReader handler) { | ||
| 2870 | put_handlers_for_content_reader_.push_back( | ||
| 2871 | std::make_pair(std::regex(pattern), handler)); | ||
| 2872 | return *this; | ||
| 2873 | } | ||
| 2874 | |||
| 2261 | inline Server &Server::Patch(const char *pattern, Handler handler) { | 2875 | inline Server &Server::Patch(const char *pattern, Handler handler) { |
| 2262 | patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); | 2876 | patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); |
| 2263 | return *this; | 2877 | return *this; |
| 2264 | } | 2878 | } |
| 2265 | 2879 | ||
| 2880 | inline Server &Server::Patch(const char *pattern, | ||
| 2881 | HandlerWithContentReader handler) { | ||
| 2882 | patch_handlers_for_content_reader_.push_back( | ||
| 2883 | std::make_pair(std::regex(pattern), handler)); | ||
| 2884 | return *this; | ||
| 2885 | } | ||
| 2886 | |||
| 2266 | inline Server &Server::Delete(const char *pattern, Handler handler) { | 2887 | inline Server &Server::Delete(const char *pattern, Handler handler) { |
| 2267 | delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); | 2888 | delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); |
| 2268 | return *this; | 2889 | return *this; |
| @@ -2273,32 +2894,68 @@ inline Server &Server::Options(const char *pattern, Handler handler) { | |||
| 2273 | return *this; | 2894 | return *this; |
| 2274 | } | 2895 | } |
| 2275 | 2896 | ||
| 2276 | inline bool Server::set_base_dir(const char *path) { | 2897 | inline bool Server::set_base_dir(const char *dir, const char *mount_point) { |
| 2277 | if (detail::is_dir(path)) { | 2898 | return set_mount_point(mount_point, dir); |
| 2278 | base_dir_ = path; | 2899 | } |
| 2279 | return true; | 2900 | |
| 2901 | inline bool Server::set_mount_point(const char *mount_point, const char *dir) { | ||
| 2902 | if (detail::is_dir(dir)) { | ||
| 2903 | std::string mnt = mount_point ? mount_point : "/"; | ||
| 2904 | if (!mnt.empty() && mnt[0] == '/') { | ||
| 2905 | base_dirs_.emplace_back(mnt, dir); | ||
| 2906 | return true; | ||
| 2907 | } | ||
| 2908 | } | ||
| 2909 | return false; | ||
| 2910 | } | ||
| 2911 | |||
| 2912 | inline bool Server::remove_mount_point(const char *mount_point) { | ||
| 2913 | for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) { | ||
| 2914 | if (it->first == mount_point) { | ||
| 2915 | base_dirs_.erase(it); | ||
| 2916 | return true; | ||
| 2917 | } | ||
| 2280 | } | 2918 | } |
| 2281 | return false; | 2919 | return false; |
| 2282 | } | 2920 | } |
| 2283 | 2921 | ||
| 2922 | inline void Server::set_file_extension_and_mimetype_mapping(const char *ext, | ||
| 2923 | const char *mime) { | ||
| 2924 | file_extension_and_mimetype_map_[ext] = mime; | ||
| 2925 | } | ||
| 2926 | |||
| 2284 | inline void Server::set_file_request_handler(Handler handler) { | 2927 | inline void Server::set_file_request_handler(Handler handler) { |
| 2285 | file_request_handler_ = handler; | 2928 | file_request_handler_ = std::move(handler); |
| 2286 | } | 2929 | } |
| 2287 | 2930 | ||
| 2288 | inline void Server::set_error_handler(Handler handler) { | 2931 | inline void Server::set_error_handler(Handler handler) { |
| 2289 | error_handler_ = handler; | 2932 | error_handler_ = std::move(handler); |
| 2290 | } | 2933 | } |
| 2291 | 2934 | ||
| 2292 | inline void Server::set_logger(Logger logger) { logger_ = logger; } | 2935 | inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); } |
| 2936 | |||
| 2937 | inline void | ||
| 2938 | Server::set_expect_100_continue_handler(Expect100ContinueHandler handler) { | ||
| 2939 | expect_100_continue_handler_ = std::move(handler); | ||
| 2940 | } | ||
| 2293 | 2941 | ||
| 2294 | inline void Server::set_keep_alive_max_count(size_t count) { | 2942 | inline void Server::set_keep_alive_max_count(size_t count) { |
| 2295 | keep_alive_max_count_ = count; | 2943 | keep_alive_max_count_ = count; |
| 2296 | } | 2944 | } |
| 2297 | 2945 | ||
| 2946 | inline void Server::set_read_timeout(time_t sec, time_t usec) { | ||
| 2947 | read_timeout_sec_ = sec; | ||
| 2948 | read_timeout_usec_ = usec; | ||
| 2949 | } | ||
| 2950 | |||
| 2298 | inline void Server::set_payload_max_length(size_t length) { | 2951 | inline void Server::set_payload_max_length(size_t length) { |
| 2299 | payload_max_length_ = length; | 2952 | payload_max_length_ = length; |
| 2300 | } | 2953 | } |
| 2301 | 2954 | ||
| 2955 | inline bool Server::bind_to_port(const char *host, int port, int socket_flags) { | ||
| 2956 | if (bind_internal(host, port, socket_flags) < 0) return false; | ||
| 2957 | return true; | ||
| 2958 | } | ||
| 2302 | inline int Server::bind_to_any_port(const char *host, int socket_flags) { | 2959 | inline int Server::bind_to_any_port(const char *host, int socket_flags) { |
| 2303 | return bind_internal(host, 0, socket_flags); | 2960 | return bind_internal(host, 0, socket_flags); |
| 2304 | } | 2961 | } |
| @@ -2306,8 +2963,7 @@ inline int Server::bind_to_any_port(const char *host, int socket_flags) { | |||
| 2306 | inline bool Server::listen_after_bind() { return listen_internal(); } | 2963 | inline bool Server::listen_after_bind() { return listen_internal(); } |
| 2307 | 2964 | ||
| 2308 | inline bool Server::listen(const char *host, int port, int socket_flags) { | 2965 | inline bool Server::listen(const char *host, int port, int socket_flags) { |
| 2309 | if (bind_internal(host, port, socket_flags) < 0) return false; | 2966 | return bind_to_port(host, port, socket_flags) && listen_internal(); |
| 2310 | return listen_internal(); | ||
| 2311 | } | 2967 | } |
| 2312 | 2968 | ||
| 2313 | inline bool Server::is_running() const { return is_running_; } | 2969 | inline bool Server::is_running() const { return is_running_; } |
| @@ -2322,8 +2978,9 @@ inline void Server::stop() { | |||
| 2322 | } | 2978 | } |
| 2323 | 2979 | ||
| 2324 | inline bool Server::parse_request_line(const char *s, Request &req) { | 2980 | inline bool Server::parse_request_line(const char *s, Request &req) { |
| 2325 | static std::regex re("(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " | 2981 | const static std::regex re( |
| 2326 | "(([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); | 2982 | "(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH|PRI) " |
| 2983 | "(([^?]+)(?:\\?(.*?))?) (HTTP/1\\.[01])\r\n"); | ||
| 2327 | 2984 | ||
| 2328 | std::cmatch m; | 2985 | std::cmatch m; |
| 2329 | if (std::regex_match(s, m, re)) { | 2986 | if (std::regex_match(s, m, re)) { |
| @@ -2348,9 +3005,11 @@ inline bool Server::write_response(Stream &strm, bool last_connection, | |||
| 2348 | 3005 | ||
| 2349 | if (400 <= res.status && error_handler_) { error_handler_(req, res); } | 3006 | if (400 <= res.status && error_handler_) { error_handler_(req, res); } |
| 2350 | 3007 | ||
| 3008 | detail::BufferStream bstrm; | ||
| 3009 | |||
| 2351 | // Response line | 3010 | // Response line |
| 2352 | if (!strm.write_format("HTTP/1.1 %d %s\r\n", res.status, | 3011 | if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status, |
| 2353 | detail::status_message(res.status))) { | 3012 | detail::status_message(res.status))) { |
| 2354 | return false; | 3013 | return false; |
| 2355 | } | 3014 | } |
| 2356 | 3015 | ||
| @@ -2363,11 +3022,12 @@ inline bool Server::write_response(Stream &strm, bool last_connection, | |||
| 2363 | res.set_header("Connection", "Keep-Alive"); | 3022 | res.set_header("Connection", "Keep-Alive"); |
| 2364 | } | 3023 | } |
| 2365 | 3024 | ||
| 2366 | if (!res.has_header("Content-Type")) { | 3025 | if (!res.has_header("Content-Type") && |
| 3026 | (!res.body.empty() || res.content_length > 0)) { | ||
| 2367 | res.set_header("Content-Type", "text/plain"); | 3027 | res.set_header("Content-Type", "text/plain"); |
| 2368 | } | 3028 | } |
| 2369 | 3029 | ||
| 2370 | if (!res.has_header("Accept-Ranges")) { | 3030 | if (!res.has_header("Accept-Ranges") && req.method == "HEAD") { |
| 2371 | res.set_header("Accept-Ranges", "bytes"); | 3031 | res.set_header("Accept-Ranges", "bytes"); |
| 2372 | } | 3032 | } |
| 2373 | 3033 | ||
| @@ -2388,17 +3048,17 @@ inline bool Server::write_response(Stream &strm, bool last_connection, | |||
| 2388 | } | 3048 | } |
| 2389 | 3049 | ||
| 2390 | if (res.body.empty()) { | 3050 | if (res.body.empty()) { |
| 2391 | if (res.content_provider_resource_length > 0) { | 3051 | if (res.content_length > 0) { |
| 2392 | size_t length = 0; | 3052 | size_t length = 0; |
| 2393 | if (req.ranges.empty()) { | 3053 | if (req.ranges.empty()) { |
| 2394 | length = res.content_provider_resource_length; | 3054 | length = res.content_length; |
| 2395 | } else if (req.ranges.size() == 1) { | 3055 | } else if (req.ranges.size() == 1) { |
| 2396 | auto offsets = detail::get_range_offset_and_length( | 3056 | auto offsets = |
| 2397 | req, res.content_provider_resource_length, 0); | 3057 | detail::get_range_offset_and_length(req, res.content_length, 0); |
| 2398 | auto offset = offsets.first; | 3058 | auto offset = offsets.first; |
| 2399 | length = offsets.second; | 3059 | length = offsets.second; |
| 2400 | auto content_range = detail::make_content_range_header_field( | 3060 | auto content_range = detail::make_content_range_header_field( |
| 2401 | offset, length, res.content_provider_resource_length); | 3061 | offset, length, res.content_length); |
| 2402 | res.set_header("Content-Range", content_range); | 3062 | res.set_header("Content-Range", content_range); |
| 2403 | } else { | 3063 | } else { |
| 2404 | length = detail::get_multipart_ranges_data_length(req, res, boundary, | 3064 | length = detail::get_multipart_ranges_data_length(req, res, boundary, |
| @@ -2430,7 +3090,7 @@ inline bool Server::write_response(Stream &strm, bool last_connection, | |||
| 2430 | } | 3090 | } |
| 2431 | 3091 | ||
| 2432 | #ifdef CPPHTTPLIB_ZLIB_SUPPORT | 3092 | #ifdef CPPHTTPLIB_ZLIB_SUPPORT |
| 2433 | // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0 | 3093 | // TODO: 'Accept-Encoding' has gzip, not gzip;q=0 |
| 2434 | const auto &encodings = req.get_header_value("Accept-Encoding"); | 3094 | const auto &encodings = req.get_header_value("Accept-Encoding"); |
| 2435 | if (encodings.find("gzip") != std::string::npos && | 3095 | if (encodings.find("gzip") != std::string::npos && |
| 2436 | detail::can_compress(res.get_header_value("Content-Type"))) { | 3096 | detail::can_compress(res.get_header_value("Content-Type"))) { |
| @@ -2444,7 +3104,11 @@ inline bool Server::write_response(Stream &strm, bool last_connection, | |||
| 2444 | res.set_header("Content-Length", length); | 3104 | res.set_header("Content-Length", length); |
| 2445 | } | 3105 | } |
| 2446 | 3106 | ||
| 2447 | if (!detail::write_headers(strm, res, Headers())) { return false; } | 3107 | if (!detail::write_headers(bstrm, res, Headers())) { return false; } |
| 3108 | |||
| 3109 | // Flush buffer | ||
| 3110 | auto &data = bstrm.get_buffer(); | ||
| 3111 | strm.write(data.data(), data.size()); | ||
| 2448 | 3112 | ||
| 2449 | // Body | 3113 | // Body |
| 2450 | if (req.method != "HEAD") { | 3114 | if (req.method != "HEAD") { |
| @@ -2468,15 +3132,15 @@ inline bool | |||
| 2468 | Server::write_content_with_provider(Stream &strm, const Request &req, | 3132 | Server::write_content_with_provider(Stream &strm, const Request &req, |
| 2469 | Response &res, const std::string &boundary, | 3133 | Response &res, const std::string &boundary, |
| 2470 | const std::string &content_type) { | 3134 | const std::string &content_type) { |
| 2471 | if (res.content_provider_resource_length) { | 3135 | if (res.content_length) { |
| 2472 | if (req.ranges.empty()) { | 3136 | if (req.ranges.empty()) { |
| 2473 | if (detail::write_content(strm, res.content_provider, 0, | 3137 | if (detail::write_content(strm, res.content_provider, 0, |
| 2474 | res.content_provider_resource_length) < 0) { | 3138 | res.content_length) < 0) { |
| 2475 | return false; | 3139 | return false; |
| 2476 | } | 3140 | } |
| 2477 | } else if (req.ranges.size() == 1) { | 3141 | } else if (req.ranges.size() == 1) { |
| 2478 | auto offsets = detail::get_range_offset_and_length( | 3142 | auto offsets = |
| 2479 | req, res.content_provider_resource_length, 0); | 3143 | detail::get_range_offset_and_length(req, res.content_length, 0); |
| 2480 | auto offset = offsets.first; | 3144 | auto offset = offsets.first; |
| 2481 | auto length = offsets.second; | 3145 | auto length = offsets.second; |
| 2482 | if (detail::write_content(strm, res.content_provider, offset, length) < | 3146 | if (detail::write_content(strm, res.content_provider, offset, length) < |
| @@ -2490,29 +3154,123 @@ Server::write_content_with_provider(Stream &strm, const Request &req, | |||
| 2490 | } | 3154 | } |
| 2491 | } | 3155 | } |
| 2492 | } else { | 3156 | } else { |
| 2493 | if (detail::write_content_chunked(strm, res.content_provider) < 0) { | 3157 | auto is_shutting_down = [this]() { |
| 3158 | return this->svr_sock_ == INVALID_SOCKET; | ||
| 3159 | }; | ||
| 3160 | if (detail::write_content_chunked(strm, res.content_provider, | ||
| 3161 | is_shutting_down) < 0) { | ||
| 2494 | return false; | 3162 | return false; |
| 2495 | } | 3163 | } |
| 2496 | } | 3164 | } |
| 2497 | return true; | 3165 | return true; |
| 2498 | } | 3166 | } |
| 2499 | 3167 | ||
| 2500 | inline bool Server::handle_file_request(Request &req, Response &res) { | 3168 | inline bool Server::read_content(Stream &strm, bool last_connection, |
| 2501 | if (!base_dir_.empty() && detail::is_valid_path(req.path)) { | 3169 | Request &req, Response &res) { |
| 2502 | std::string path = base_dir_ + req.path; | 3170 | MultipartFormDataMap::iterator cur; |
| 3171 | auto ret = read_content_core( | ||
| 3172 | strm, last_connection, req, res, | ||
| 3173 | // Regular | ||
| 3174 | [&](const char *buf, size_t n) { | ||
| 3175 | if (req.body.size() + n > req.body.max_size()) { return false; } | ||
| 3176 | req.body.append(buf, n); | ||
| 3177 | return true; | ||
| 3178 | }, | ||
| 3179 | // Multipart | ||
| 3180 | [&](const MultipartFormData &file) { | ||
| 3181 | cur = req.files.emplace(file.name, file); | ||
| 3182 | return true; | ||
| 3183 | }, | ||
| 3184 | [&](const char *buf, size_t n) { | ||
| 3185 | auto &content = cur->second.content; | ||
| 3186 | if (content.size() + n > content.max_size()) { return false; } | ||
| 3187 | content.append(buf, n); | ||
| 3188 | return true; | ||
| 3189 | }); | ||
| 2503 | 3190 | ||
| 2504 | if (!path.empty() && path.back() == '/') { path += "index.html"; } | 3191 | const auto &content_type = req.get_header_value("Content-Type"); |
| 3192 | if (!content_type.find("application/x-www-form-urlencoded")) { | ||
| 3193 | detail::parse_query_text(req.body, req.params); | ||
| 3194 | } | ||
| 2505 | 3195 | ||
| 2506 | if (detail::is_file(path)) { | 3196 | return ret; |
| 2507 | detail::read_file(path, res.body); | 3197 | } |
| 2508 | auto type = detail::find_content_type(path); | 3198 | |
| 2509 | if (type) { res.set_header("Content-Type", type); } | 3199 | inline bool Server::read_content_with_content_receiver( |
| 2510 | res.status = 200; | 3200 | Stream &strm, bool last_connection, Request &req, Response &res, |
| 2511 | if (file_request_handler_) { file_request_handler_(req, res); } | 3201 | ContentReceiver receiver, MultipartContentHeader multipart_header, |
| 2512 | return true; | 3202 | ContentReceiver multipart_receiver) { |
| 3203 | return read_content_core(strm, last_connection, req, res, receiver, | ||
| 3204 | multipart_header, multipart_receiver); | ||
| 3205 | } | ||
| 3206 | |||
| 3207 | inline bool Server::read_content_core(Stream &strm, bool last_connection, | ||
| 3208 | Request &req, Response &res, | ||
| 3209 | ContentReceiver receiver, | ||
| 3210 | MultipartContentHeader mulitpart_header, | ||
| 3211 | ContentReceiver multipart_receiver) { | ||
| 3212 | detail::MultipartFormDataParser multipart_form_data_parser; | ||
| 3213 | ContentReceiver out; | ||
| 3214 | |||
| 3215 | if (req.is_multipart_form_data()) { | ||
| 3216 | const auto &content_type = req.get_header_value("Content-Type"); | ||
| 3217 | std::string boundary; | ||
| 3218 | if (!detail::parse_multipart_boundary(content_type, boundary)) { | ||
| 3219 | res.status = 400; | ||
| 3220 | return write_response(strm, last_connection, req, res); | ||
| 2513 | } | 3221 | } |
| 3222 | |||
| 3223 | multipart_form_data_parser.set_boundary(boundary); | ||
| 3224 | out = [&](const char *buf, size_t n) { | ||
| 3225 | return multipart_form_data_parser.parse(buf, n, multipart_receiver, | ||
| 3226 | mulitpart_header); | ||
| 3227 | }; | ||
| 3228 | } else { | ||
| 3229 | out = receiver; | ||
| 3230 | } | ||
| 3231 | |||
| 3232 | if (!detail::read_content(strm, req, payload_max_length_, res.status, | ||
| 3233 | Progress(), out)) { | ||
| 3234 | return write_response(strm, last_connection, req, res); | ||
| 2514 | } | 3235 | } |
| 2515 | 3236 | ||
| 3237 | if (req.is_multipart_form_data()) { | ||
| 3238 | if (!multipart_form_data_parser.is_valid()) { | ||
| 3239 | res.status = 400; | ||
| 3240 | return write_response(strm, last_connection, req, res); | ||
| 3241 | } | ||
| 3242 | } | ||
| 3243 | |||
| 3244 | return true; | ||
| 3245 | } | ||
| 3246 | |||
| 3247 | inline bool Server::handle_file_request(Request &req, Response &res, | ||
| 3248 | bool head) { | ||
| 3249 | for (const auto &kv : base_dirs_) { | ||
| 3250 | const auto &mount_point = kv.first; | ||
| 3251 | const auto &base_dir = kv.second; | ||
| 3252 | |||
| 3253 | // Prefix match | ||
| 3254 | if (!req.path.find(mount_point)) { | ||
| 3255 | std::string sub_path = "/" + req.path.substr(mount_point.size()); | ||
| 3256 | if (detail::is_valid_path(sub_path)) { | ||
| 3257 | auto path = base_dir + sub_path; | ||
| 3258 | if (path.back() == '/') { path += "index.html"; } | ||
| 3259 | |||
| 3260 | if (detail::is_file(path)) { | ||
| 3261 | detail::read_file(path, res.body); | ||
| 3262 | auto type = | ||
| 3263 | detail::find_content_type(path, file_extension_and_mimetype_map_); | ||
| 3264 | if (type) { res.set_header("Content-Type", type); } | ||
| 3265 | res.status = 200; | ||
| 3266 | if (!head && file_request_handler_) { | ||
| 3267 | file_request_handler_(req, res); | ||
| 3268 | } | ||
| 3269 | return true; | ||
| 3270 | } | ||
| 3271 | } | ||
| 3272 | } | ||
| 3273 | } | ||
| 2516 | return false; | 3274 | return false; |
| 2517 | } | 3275 | } |
| 2518 | 3276 | ||
| @@ -2605,9 +3363,51 @@ inline bool Server::listen_internal() { | |||
| 2605 | return ret; | 3363 | return ret; |
| 2606 | } | 3364 | } |
| 2607 | 3365 | ||
| 2608 | inline bool Server::routing(Request &req, Response &res) { | 3366 | inline bool Server::routing(Request &req, Response &res, Stream &strm, |
| 2609 | if (req.method == "GET" && handle_file_request(req, res)) { return true; } | 3367 | bool last_connection) { |
| 3368 | // File handler | ||
| 3369 | bool is_head_request = req.method == "HEAD"; | ||
| 3370 | if ((req.method == "GET" || is_head_request) && | ||
| 3371 | handle_file_request(req, res, is_head_request)) { | ||
| 3372 | return true; | ||
| 3373 | } | ||
| 3374 | |||
| 3375 | if (detail::expect_content(req)) { | ||
| 3376 | // Content reader handler | ||
| 3377 | { | ||
| 3378 | ContentReader reader( | ||
| 3379 | [&](ContentReceiver receiver) { | ||
| 3380 | return read_content_with_content_receiver( | ||
| 3381 | strm, last_connection, req, res, receiver, nullptr, nullptr); | ||
| 3382 | }, | ||
| 3383 | [&](MultipartContentHeader header, ContentReceiver receiver) { | ||
| 3384 | return read_content_with_content_receiver( | ||
| 3385 | strm, last_connection, req, res, nullptr, header, receiver); | ||
| 3386 | }); | ||
| 3387 | |||
| 3388 | if (req.method == "POST") { | ||
| 3389 | if (dispatch_request_for_content_reader( | ||
| 3390 | req, res, reader, post_handlers_for_content_reader_)) { | ||
| 3391 | return true; | ||
| 3392 | } | ||
| 3393 | } else if (req.method == "PUT") { | ||
| 3394 | if (dispatch_request_for_content_reader( | ||
| 3395 | req, res, reader, put_handlers_for_content_reader_)) { | ||
| 3396 | return true; | ||
| 3397 | } | ||
| 3398 | } else if (req.method == "PATCH") { | ||
| 3399 | if (dispatch_request_for_content_reader( | ||
| 3400 | req, res, reader, patch_handlers_for_content_reader_)) { | ||
| 3401 | return true; | ||
| 3402 | } | ||
| 3403 | } | ||
| 3404 | } | ||
| 2610 | 3405 | ||
| 3406 | // Read content into `req.body` | ||
| 3407 | if (!read_content(strm, last_connection, req, res)) { return false; } | ||
| 3408 | } | ||
| 3409 | |||
| 3410 | // Regular handler | ||
| 2611 | if (req.method == "GET" || req.method == "HEAD") { | 3411 | if (req.method == "GET" || req.method == "HEAD") { |
| 2612 | return dispatch_request(req, res, get_handlers_); | 3412 | return dispatch_request(req, res, get_handlers_); |
| 2613 | } else if (req.method == "POST") { | 3413 | } else if (req.method == "POST") { |
| @@ -2640,17 +3440,31 @@ inline bool Server::dispatch_request(Request &req, Response &res, | |||
| 2640 | return false; | 3440 | return false; |
| 2641 | } | 3441 | } |
| 2642 | 3442 | ||
| 3443 | inline bool Server::dispatch_request_for_content_reader( | ||
| 3444 | Request &req, Response &res, ContentReader content_reader, | ||
| 3445 | HandlersForContentReader &handlers) { | ||
| 3446 | for (const auto &x : handlers) { | ||
| 3447 | const auto &pattern = x.first; | ||
| 3448 | const auto &handler = x.second; | ||
| 3449 | |||
| 3450 | if (std::regex_match(req.path, req.matches, pattern)) { | ||
| 3451 | handler(req, res, content_reader); | ||
| 3452 | return true; | ||
| 3453 | } | ||
| 3454 | } | ||
| 3455 | return false; | ||
| 3456 | } | ||
| 3457 | |||
| 2643 | inline bool | 3458 | inline bool |
| 2644 | Server::process_request(Stream &strm, bool last_connection, | 3459 | Server::process_request(Stream &strm, bool last_connection, |
| 2645 | bool &connection_close, | 3460 | bool &connection_close, |
| 2646 | std::function<void(Request &)> setup_request) { | 3461 | const std::function<void(Request &)> &setup_request) { |
| 2647 | const auto bufsiz = 2048; | 3462 | std::array<char, 2048> buf{}; |
| 2648 | char buf[bufsiz]; | ||
| 2649 | 3463 | ||
| 2650 | detail::stream_line_reader reader(strm, buf, bufsiz); | 3464 | detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); |
| 2651 | 3465 | ||
| 2652 | // Connection has been closed on client | 3466 | // Connection has been closed on client |
| 2653 | if (!reader.getline()) { return false; } | 3467 | if (!line_reader.getline()) { return false; } |
| 2654 | 3468 | ||
| 2655 | Request req; | 3469 | Request req; |
| 2656 | Response res; | 3470 | Response res; |
| @@ -2658,7 +3472,7 @@ Server::process_request(Stream &strm, bool last_connection, | |||
| 2658 | res.version = "HTTP/1.1"; | 3472 | res.version = "HTTP/1.1"; |
| 2659 | 3473 | ||
| 2660 | // Check if the request URI doesn't exceed the limit | 3474 | // Check if the request URI doesn't exceed the limit |
| 2661 | if (reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { | 3475 | if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) { |
| 2662 | Headers dummy; | 3476 | Headers dummy; |
| 2663 | detail::read_headers(strm, dummy); | 3477 | detail::read_headers(strm, dummy); |
| 2664 | res.status = 414; | 3478 | res.status = 414; |
| @@ -2666,7 +3480,7 @@ Server::process_request(Stream &strm, bool last_connection, | |||
| 2666 | } | 3480 | } |
| 2667 | 3481 | ||
| 2668 | // Request line and headers | 3482 | // Request line and headers |
| 2669 | if (!parse_request_line(reader.ptr(), req) || | 3483 | if (!parse_request_line(line_reader.ptr(), req) || |
| 2670 | !detail::read_headers(strm, req.headers)) { | 3484 | !detail::read_headers(strm, req.headers)) { |
| 2671 | res.status = 400; | 3485 | res.status = 400; |
| 2672 | return write_response(strm, last_connection, req, res); | 3486 | return write_response(strm, last_connection, req, res); |
| @@ -2683,33 +3497,6 @@ Server::process_request(Stream &strm, bool last_connection, | |||
| 2683 | 3497 | ||
| 2684 | req.set_header("REMOTE_ADDR", strm.get_remote_addr()); | 3498 | req.set_header("REMOTE_ADDR", strm.get_remote_addr()); |
| 2685 | 3499 | ||
| 2686 | // Body | ||
| 2687 | if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" || req.method == "PRI") { | ||
| 2688 | if (!detail::read_content(strm, req, payload_max_length_, res.status, | ||
| 2689 | Progress(), [&](const char *buf, size_t n) { | ||
| 2690 | if (req.body.size() + n > req.body.max_size()) { | ||
| 2691 | return false; | ||
| 2692 | } | ||
| 2693 | req.body.append(buf, n); | ||
| 2694 | return true; | ||
| 2695 | })) { | ||
| 2696 | return write_response(strm, last_connection, req, res); | ||
| 2697 | } | ||
| 2698 | |||
| 2699 | const auto &content_type = req.get_header_value("Content-Type"); | ||
| 2700 | |||
| 2701 | if (!content_type.find("application/x-www-form-urlencoded")) { | ||
| 2702 | detail::parse_query_text(req.body, req.params); | ||
| 2703 | } else if (!content_type.find("multipart/form-data")) { | ||
| 2704 | std::string boundary; | ||
| 2705 | if (!detail::parse_multipart_boundary(content_type, boundary) || | ||
| 2706 | !detail::parse_multipart_formdata(boundary, req.body, req.files)) { | ||
| 2707 | res.status = 400; | ||
| 2708 | return write_response(strm, last_connection, req, res); | ||
| 2709 | } | ||
| 2710 | } | ||
| 2711 | } | ||
| 2712 | |||
| 2713 | if (req.has_header("Range")) { | 3500 | if (req.has_header("Range")) { |
| 2714 | const auto &range_header_value = req.get_header_value("Range"); | 3501 | const auto &range_header_value = req.get_header_value("Range"); |
| 2715 | if (!detail::parse_range_header(range_header_value, req.ranges)) { | 3502 | if (!detail::parse_range_header(range_header_value, req.ranges)) { |
| @@ -2719,7 +3506,23 @@ Server::process_request(Stream &strm, bool last_connection, | |||
| 2719 | 3506 | ||
| 2720 | if (setup_request) { setup_request(req); } | 3507 | if (setup_request) { setup_request(req); } |
| 2721 | 3508 | ||
| 2722 | if (routing(req, res)) { | 3509 | if (req.get_header_value("Expect") == "100-continue") { |
| 3510 | auto status = 100; | ||
| 3511 | if (expect_100_continue_handler_) { | ||
| 3512 | status = expect_100_continue_handler_(req, res); | ||
| 3513 | } | ||
| 3514 | switch (status) { | ||
| 3515 | case 100: | ||
| 3516 | case 417: | ||
| 3517 | strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status, | ||
| 3518 | detail::status_message(status)); | ||
| 3519 | break; | ||
| 3520 | default: return write_response(strm, last_connection, req, res); | ||
| 3521 | } | ||
| 3522 | } | ||
| 3523 | |||
| 3524 | // Rounting | ||
| 3525 | if (routing(req, res, strm, last_connection)) { | ||
| 2723 | if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } | 3526 | if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; } |
| 2724 | } else { | 3527 | } else { |
| 2725 | if (res.status == -1) { res.status = 404; } | 3528 | if (res.status == -1) { res.status = 404; } |
| @@ -2732,7 +3535,7 @@ inline bool Server::is_valid() const { return true; } | |||
| 2732 | 3535 | ||
| 2733 | inline bool Server::process_and_close_socket(socket_t sock) { | 3536 | inline bool Server::process_and_close_socket(socket_t sock) { |
| 2734 | return detail::process_and_close_socket( | 3537 | return detail::process_and_close_socket( |
| 2735 | false, sock, keep_alive_max_count_, | 3538 | false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_, |
| 2736 | [this](Stream &strm, bool last_connection, bool &connection_close) { | 3539 | [this](Stream &strm, bool last_connection, bool &connection_close) { |
| 2737 | return process_request(strm, last_connection, connection_close, | 3540 | return process_request(strm, last_connection, connection_close, |
| 2738 | nullptr); | 3541 | nullptr); |
| @@ -2740,47 +3543,37 @@ inline bool Server::process_and_close_socket(socket_t sock) { | |||
| 2740 | } | 3543 | } |
| 2741 | 3544 | ||
| 2742 | // HTTP client implementation | 3545 | // HTTP client implementation |
| 2743 | inline Client::Client(const char *host, int port, time_t timeout_sec) | 3546 | inline Client::Client(const std::string &host, int port, |
| 2744 | : host_(host), port_(port), timeout_sec_(timeout_sec), | 3547 | const std::string &client_cert_path, |
| 3548 | const std::string &client_key_path) | ||
| 3549 | : host_(host), port_(port), | ||
| 2745 | host_and_port_(host_ + ":" + std::to_string(port_)), | 3550 | host_and_port_(host_ + ":" + std::to_string(port_)), |
| 2746 | keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT), | 3551 | client_cert_path_(client_cert_path), client_key_path_(client_key_path) {} |
| 2747 | follow_location_(false) {} | ||
| 2748 | 3552 | ||
| 2749 | inline Client::~Client() {} | 3553 | inline Client::~Client() {} |
| 2750 | 3554 | ||
| 2751 | inline bool Client::is_valid() const { return true; } | 3555 | inline bool Client::is_valid() const { return true; } |
| 2752 | 3556 | ||
| 2753 | inline socket_t Client::create_client_socket() const { | 3557 | inline socket_t Client::create_client_socket() const { |
| 2754 | return detail::create_socket( | 3558 | if (!proxy_host_.empty()) { |
| 2755 | host_.c_str(), port_, [=](socket_t sock, struct addrinfo &ai) -> bool { | 3559 | return detail::create_client_socket(proxy_host_.c_str(), proxy_port_, |
| 2756 | detail::set_nonblocking(sock, true); | 3560 | timeout_sec_, interface_); |
| 2757 | 3561 | } | |
| 2758 | auto ret = connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen)); | 3562 | return detail::create_client_socket(host_.c_str(), port_, timeout_sec_, |
| 2759 | if (ret < 0) { | 3563 | interface_); |
| 2760 | if (detail::is_connection_error() || | ||
| 2761 | !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) { | ||
| 2762 | detail::close_socket(sock); | ||
| 2763 | return false; | ||
| 2764 | } | ||
| 2765 | } | ||
| 2766 | |||
| 2767 | detail::set_nonblocking(sock, false); | ||
| 2768 | return true; | ||
| 2769 | }); | ||
| 2770 | } | 3564 | } |
| 2771 | 3565 | ||
| 2772 | inline bool Client::read_response_line(Stream &strm, Response &res) { | 3566 | inline bool Client::read_response_line(Stream &strm, Response &res) { |
| 2773 | const auto bufsiz = 2048; | 3567 | std::array<char, 2048> buf; |
| 2774 | char buf[bufsiz]; | ||
| 2775 | 3568 | ||
| 2776 | detail::stream_line_reader reader(strm, buf, bufsiz); | 3569 | detail::stream_line_reader line_reader(strm, buf.data(), buf.size()); |
| 2777 | 3570 | ||
| 2778 | if (!reader.getline()) { return false; } | 3571 | if (!line_reader.getline()) { return false; } |
| 2779 | 3572 | ||
| 2780 | const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); | 3573 | const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n"); |
| 2781 | 3574 | ||
| 2782 | std::cmatch m; | 3575 | std::cmatch m; |
| 2783 | if (std::regex_match(reader.ptr(), m, re)) { | 3576 | if (std::regex_match(line_reader.ptr(), m, re)) { |
| 2784 | res.version = std::string(m[1]); | 3577 | res.version = std::string(m[1]); |
| 2785 | res.status = std::stoi(std::string(m[2])); | 3578 | res.status = std::stoi(std::string(m[2])); |
| 2786 | } | 3579 | } |
| @@ -2789,22 +3582,21 @@ inline bool Client::read_response_line(Stream &strm, Response &res) { | |||
| 2789 | } | 3582 | } |
| 2790 | 3583 | ||
| 2791 | inline bool Client::send(const Request &req, Response &res) { | 3584 | inline bool Client::send(const Request &req, Response &res) { |
| 2792 | if (req.path.empty()) { return false; } | ||
| 2793 | |||
| 2794 | auto sock = create_client_socket(); | 3585 | auto sock = create_client_socket(); |
| 2795 | if (sock == INVALID_SOCKET) { return false; } | 3586 | if (sock == INVALID_SOCKET) { return false; } |
| 2796 | 3587 | ||
| 2797 | auto ret = process_and_close_socket( | 3588 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT |
| 2798 | sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) { | 3589 | if (is_ssl() && !proxy_host_.empty()) { |
| 2799 | return process_request(strm, req, res, last_connection, | 3590 | bool error; |
| 2800 | connection_close); | 3591 | if (!connect(sock, res, error)) { return error; } |
| 2801 | }); | ||
| 2802 | |||
| 2803 | if (ret && follow_location_ && (300 < res.status && res.status < 400)) { | ||
| 2804 | ret = redirect(req, res); | ||
| 2805 | } | 3592 | } |
| 3593 | #endif | ||
| 2806 | 3594 | ||
| 2807 | return ret; | 3595 | return process_and_close_socket( |
| 3596 | sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) { | ||
| 3597 | return handle_request(strm, req, res, last_connection, | ||
| 3598 | connection_close); | ||
| 3599 | }); | ||
| 2808 | } | 3600 | } |
| 2809 | 3601 | ||
| 2810 | inline bool Client::send(const std::vector<Request> &requests, | 3602 | inline bool Client::send(const std::vector<Request> &requests, |
| @@ -2814,32 +3606,136 @@ inline bool Client::send(const std::vector<Request> &requests, | |||
| 2814 | auto sock = create_client_socket(); | 3606 | auto sock = create_client_socket(); |
| 2815 | if (sock == INVALID_SOCKET) { return false; } | 3607 | if (sock == INVALID_SOCKET) { return false; } |
| 2816 | 3608 | ||
| 2817 | if (!process_and_close_socket( | 3609 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT |
| 2818 | sock, requests.size() - i, | 3610 | if (is_ssl() && !proxy_host_.empty()) { |
| 2819 | [&](Stream &strm, bool last_connection, bool &connection_close) -> bool { | 3611 | Response res; |
| 2820 | auto &req = requests[i]; | 3612 | bool error; |
| 2821 | auto res = Response(); | 3613 | if (!connect(sock, res, error)) { return false; } |
| 2822 | i++; | 3614 | } |
| 3615 | #endif | ||
| 3616 | |||
| 3617 | if (!process_and_close_socket(sock, requests.size() - i, | ||
| 3618 | [&](Stream &strm, bool last_connection, | ||
| 3619 | bool &connection_close) -> bool { | ||
| 3620 | auto &req = requests[i++]; | ||
| 3621 | auto res = Response(); | ||
| 3622 | auto ret = handle_request(strm, req, res, | ||
| 3623 | last_connection, | ||
| 3624 | connection_close); | ||
| 3625 | if (ret) { | ||
| 3626 | responses.emplace_back(std::move(res)); | ||
| 3627 | } | ||
| 3628 | return ret; | ||
| 3629 | })) { | ||
| 3630 | return false; | ||
| 3631 | } | ||
| 3632 | } | ||
| 2823 | 3633 | ||
| 2824 | if (req.path.empty()) { return false; } | 3634 | return true; |
| 2825 | auto ret = process_request(strm, req, res, last_connection, | 3635 | } |
| 2826 | connection_close); | 3636 | |
| 3637 | inline bool Client::handle_request(Stream &strm, const Request &req, | ||
| 3638 | Response &res, bool last_connection, | ||
| 3639 | bool &connection_close) { | ||
| 3640 | if (req.path.empty()) { return false; } | ||
| 3641 | |||
| 3642 | bool ret; | ||
| 3643 | |||
| 3644 | if (!is_ssl() && !proxy_host_.empty()) { | ||
| 3645 | auto req2 = req; | ||
| 3646 | req2.path = "http://" + host_and_port_ + req.path; | ||
| 3647 | ret = process_request(strm, req2, res, last_connection, connection_close); | ||
| 3648 | } else { | ||
| 3649 | ret = process_request(strm, req, res, last_connection, connection_close); | ||
| 3650 | } | ||
| 3651 | |||
| 3652 | if (!ret) { return false; } | ||
| 3653 | |||
| 3654 | if (300 < res.status && res.status < 400 && follow_location_) { | ||
| 3655 | ret = redirect(req, res); | ||
| 3656 | } | ||
| 2827 | 3657 | ||
| 2828 | if (ret && follow_location_ && | 3658 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT |
| 2829 | (300 < res.status && res.status < 400)) { | 3659 | if (res.status == 401 || res.status == 407) { |
| 2830 | ret = redirect(req, res); | 3660 | auto is_proxy = res.status == 407; |
| 2831 | } | 3661 | const auto &username = |
| 3662 | is_proxy ? proxy_digest_auth_username_ : digest_auth_username_; | ||
| 3663 | const auto &password = | ||
| 3664 | is_proxy ? proxy_digest_auth_password_ : digest_auth_password_; | ||
| 3665 | |||
| 3666 | if (!username.empty() && !password.empty()) { | ||
| 3667 | std::map<std::string, std::string> auth; | ||
| 3668 | if (parse_www_authenticate(res, auth, is_proxy)) { | ||
| 3669 | Request new_req = req; | ||
| 3670 | auto key = is_proxy ? "Proxy-Authorization" : "WWW-Authorization"; | ||
| 3671 | new_req.headers.erase(key); | ||
| 3672 | new_req.headers.insert(make_digest_authentication_header( | ||
| 3673 | req, auth, 1, random_string(10), username, password, is_proxy)); | ||
| 3674 | |||
| 3675 | Response new_res; | ||
| 3676 | |||
| 3677 | ret = send(new_req, new_res); | ||
| 3678 | if (ret) { res = new_res; } | ||
| 3679 | } | ||
| 3680 | } | ||
| 3681 | } | ||
| 3682 | #endif | ||
| 3683 | |||
| 3684 | return ret; | ||
| 3685 | } | ||
| 2832 | 3686 | ||
| 2833 | if (ret) { responses.emplace_back(std::move(res)); } | 3687 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT |
| 3688 | inline bool Client::connect(socket_t sock, Response &res, bool &error) { | ||
| 3689 | error = true; | ||
| 3690 | Response res2; | ||
| 3691 | |||
| 3692 | if (!detail::process_socket( | ||
| 3693 | true, sock, 1, read_timeout_sec_, read_timeout_usec_, | ||
| 3694 | [&](Stream &strm, bool /*last_connection*/, bool &connection_close) { | ||
| 3695 | Request req2; | ||
| 3696 | req2.method = "CONNECT"; | ||
| 3697 | req2.path = host_and_port_; | ||
| 3698 | return process_request(strm, req2, res2, false, connection_close); | ||
| 3699 | })) { | ||
| 3700 | detail::close_socket(sock); | ||
| 3701 | error = false; | ||
| 3702 | return false; | ||
| 3703 | } | ||
| 2834 | 3704 | ||
| 2835 | return ret; | 3705 | if (res2.status == 407) { |
| 2836 | })) { | 3706 | if (!proxy_digest_auth_username_.empty() && |
| 3707 | !proxy_digest_auth_password_.empty()) { | ||
| 3708 | std::map<std::string, std::string> auth; | ||
| 3709 | if (parse_www_authenticate(res2, auth, true)) { | ||
| 3710 | Response res3; | ||
| 3711 | if (!detail::process_socket( | ||
| 3712 | true, sock, 1, read_timeout_sec_, read_timeout_usec_, | ||
| 3713 | [&](Stream &strm, bool /*last_connection*/, | ||
| 3714 | bool &connection_close) { | ||
| 3715 | Request req3; | ||
| 3716 | req3.method = "CONNECT"; | ||
| 3717 | req3.path = host_and_port_; | ||
| 3718 | req3.headers.insert(make_digest_authentication_header( | ||
| 3719 | req3, auth, 1, random_string(10), | ||
| 3720 | proxy_digest_auth_username_, proxy_digest_auth_password_, | ||
| 3721 | true)); | ||
| 3722 | return process_request(strm, req3, res3, false, | ||
| 3723 | connection_close); | ||
| 3724 | })) { | ||
| 3725 | detail::close_socket(sock); | ||
| 3726 | error = false; | ||
| 3727 | return false; | ||
| 3728 | } | ||
| 3729 | } | ||
| 3730 | } else { | ||
| 3731 | res = res2; | ||
| 2837 | return false; | 3732 | return false; |
| 2838 | } | 3733 | } |
| 2839 | } | 3734 | } |
| 2840 | 3735 | ||
| 2841 | return true; | 3736 | return true; |
| 2842 | } | 3737 | } |
| 3738 | #endif | ||
| 2843 | 3739 | ||
| 2844 | inline bool Client::redirect(const Request &req, Response &res) { | 3740 | inline bool Client::redirect(const Request &req, Response &res) { |
| 2845 | if (req.redirect_count == 0) { return false; } | 3741 | if (req.redirect_count == 0) { return false; } |
| @@ -2847,46 +3743,47 @@ inline bool Client::redirect(const Request &req, Response &res) { | |||
| 2847 | auto location = res.get_header_value("location"); | 3743 | auto location = res.get_header_value("location"); |
| 2848 | if (location.empty()) { return false; } | 3744 | if (location.empty()) { return false; } |
| 2849 | 3745 | ||
| 2850 | std::regex re( | 3746 | const static std::regex re( |
| 2851 | R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); | 3747 | R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)"); |
| 2852 | 3748 | ||
| 3749 | std::smatch m; | ||
| 3750 | if (!regex_match(location, m, re)) { return false; } | ||
| 3751 | |||
| 2853 | auto scheme = is_ssl() ? "https" : "http"; | 3752 | auto scheme = is_ssl() ? "https" : "http"; |
| 2854 | 3753 | ||
| 2855 | std::smatch m; | 3754 | auto next_scheme = m[1].str(); |
| 2856 | if (regex_match(location, m, re)) { | 3755 | auto next_host = m[2].str(); |
| 2857 | auto next_scheme = m[1].str(); | 3756 | auto next_path = m[3].str(); |
| 2858 | auto next_host = m[2].str(); | 3757 | if (next_scheme.empty()) { next_scheme = scheme; } |
| 2859 | auto next_path = m[3].str(); | 3758 | if (next_scheme.empty()) { next_scheme = scheme; } |
| 2860 | if (next_host.empty()) { next_host = host_; } | 3759 | if (next_host.empty()) { next_host = host_; } |
| 2861 | if (next_path.empty()) { next_path = "/"; } | 3760 | if (next_path.empty()) { next_path = "/"; } |
| 2862 | 3761 | ||
| 2863 | if (next_scheme == scheme && next_host == host_) { | 3762 | if (next_scheme == scheme && next_host == host_) { |
| 2864 | return detail::redirect(*this, req, res, next_path); | 3763 | return detail::redirect(*this, req, res, next_path); |
| 2865 | } else { | 3764 | } else { |
| 2866 | if (next_scheme == "https") { | 3765 | if (next_scheme == "https") { |
| 2867 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | 3766 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT |
| 2868 | SSLClient cli(next_host.c_str()); | 3767 | SSLClient cli(next_host.c_str()); |
| 2869 | cli.follow_location(true); | 3768 | cli.copy_settings(*this); |
| 2870 | return detail::redirect(cli, req, res, next_path); | 3769 | return detail::redirect(cli, req, res, next_path); |
| 2871 | #else | 3770 | #else |
| 2872 | return false; | 3771 | return false; |
| 2873 | #endif | 3772 | #endif |
| 2874 | } else { | 3773 | } else { |
| 2875 | Client cli(next_host.c_str()); | 3774 | Client cli(next_host.c_str()); |
| 2876 | cli.follow_location(true); | 3775 | cli.copy_settings(*this); |
| 2877 | return detail::redirect(cli, req, res, next_path); | 3776 | return detail::redirect(cli, req, res, next_path); |
| 2878 | } | ||
| 2879 | } | 3777 | } |
| 2880 | } | 3778 | } |
| 2881 | return false; | ||
| 2882 | } | 3779 | } |
| 2883 | 3780 | ||
| 2884 | inline void Client::write_request(Stream &strm, const Request &req, | 3781 | inline bool Client::write_request(Stream &strm, const Request &req, |
| 2885 | bool last_connection) { | 3782 | bool last_connection) { |
| 2886 | BufferStream bstrm; | 3783 | detail::BufferStream bstrm; |
| 2887 | 3784 | ||
| 2888 | // Request line | 3785 | // Request line |
| 2889 | auto path = detail::encode_url(req.path); | 3786 | const auto &path = detail::encode_url(req.path); |
| 2890 | 3787 | ||
| 2891 | bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); | 3788 | bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str()); |
| 2892 | 3789 | ||
| @@ -2913,11 +3810,14 @@ inline void Client::write_request(Stream &strm, const Request &req, | |||
| 2913 | if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); } | 3810 | if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); } |
| 2914 | 3811 | ||
| 2915 | if (!req.has_header("User-Agent")) { | 3812 | if (!req.has_header("User-Agent")) { |
| 2916 | headers.emplace("User-Agent", "cpp-httplib/0.2"); | 3813 | headers.emplace("User-Agent", "cpp-httplib/0.5"); |
| 2917 | } | 3814 | } |
| 2918 | 3815 | ||
| 2919 | if (req.body.empty()) { | 3816 | if (req.body.empty()) { |
| 2920 | if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH") { | 3817 | if (req.content_provider) { |
| 3818 | auto length = std::to_string(req.content_length); | ||
| 3819 | headers.emplace("Content-Length", length); | ||
| 3820 | } else { | ||
| 2921 | headers.emplace("Content-Length", "0"); | 3821 | headers.emplace("Content-Length", "0"); |
| 2922 | } | 3822 | } |
| 2923 | } else { | 3823 | } else { |
| @@ -2931,21 +3831,100 @@ inline void Client::write_request(Stream &strm, const Request &req, | |||
| 2931 | } | 3831 | } |
| 2932 | } | 3832 | } |
| 2933 | 3833 | ||
| 2934 | detail::write_headers(bstrm, req, headers); | 3834 | if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) { |
| 3835 | headers.insert(make_basic_authentication_header( | ||
| 3836 | basic_auth_username_, basic_auth_password_, false)); | ||
| 3837 | } | ||
| 2935 | 3838 | ||
| 2936 | // Body | 3839 | if (!proxy_basic_auth_username_.empty() && |
| 2937 | if (!req.body.empty()) { bstrm.write(req.body); } | 3840 | !proxy_basic_auth_password_.empty()) { |
| 3841 | headers.insert(make_basic_authentication_header( | ||
| 3842 | proxy_basic_auth_username_, proxy_basic_auth_password_, true)); | ||
| 3843 | } | ||
| 3844 | |||
| 3845 | detail::write_headers(bstrm, req, headers); | ||
| 2938 | 3846 | ||
| 2939 | // Flush buffer | 3847 | // Flush buffer |
| 2940 | auto &data = bstrm.get_buffer(); | 3848 | auto &data = bstrm.get_buffer(); |
| 2941 | strm.write(data.data(), data.size()); | 3849 | strm.write(data.data(), data.size()); |
| 3850 | |||
| 3851 | // Body | ||
| 3852 | if (req.body.empty()) { | ||
| 3853 | if (req.content_provider) { | ||
| 3854 | size_t offset = 0; | ||
| 3855 | size_t end_offset = req.content_length; | ||
| 3856 | |||
| 3857 | DataSink data_sink; | ||
| 3858 | data_sink.write = [&](const char *d, size_t l) { | ||
| 3859 | auto written_length = strm.write(d, l); | ||
| 3860 | offset += written_length; | ||
| 3861 | }; | ||
| 3862 | data_sink.is_writable = [&](void) { return strm.is_writable(); }; | ||
| 3863 | |||
| 3864 | while (offset < end_offset) { | ||
| 3865 | req.content_provider(offset, end_offset - offset, data_sink); | ||
| 3866 | } | ||
| 3867 | } | ||
| 3868 | } else { | ||
| 3869 | strm.write(req.body); | ||
| 3870 | } | ||
| 3871 | |||
| 3872 | return true; | ||
| 3873 | } | ||
| 3874 | |||
| 3875 | inline std::shared_ptr<Response> Client::send_with_content_provider( | ||
| 3876 | const char *method, const char *path, const Headers &headers, | ||
| 3877 | const std::string &body, size_t content_length, | ||
| 3878 | ContentProvider content_provider, const char *content_type) { | ||
| 3879 | Request req; | ||
| 3880 | req.method = method; | ||
| 3881 | req.headers = headers; | ||
| 3882 | req.path = path; | ||
| 3883 | |||
| 3884 | req.headers.emplace("Content-Type", content_type); | ||
| 3885 | |||
| 3886 | #ifdef CPPHTTPLIB_ZLIB_SUPPORT | ||
| 3887 | if (compress_) { | ||
| 3888 | if (content_provider) { | ||
| 3889 | size_t offset = 0; | ||
| 3890 | |||
| 3891 | DataSink data_sink; | ||
| 3892 | data_sink.write = [&](const char *data, size_t data_len) { | ||
| 3893 | req.body.append(data, data_len); | ||
| 3894 | offset += data_len; | ||
| 3895 | }; | ||
| 3896 | data_sink.is_writable = [&](void) { return true; }; | ||
| 3897 | |||
| 3898 | while (offset < content_length) { | ||
| 3899 | content_provider(offset, content_length - offset, data_sink); | ||
| 3900 | } | ||
| 3901 | } else { | ||
| 3902 | req.body = body; | ||
| 3903 | } | ||
| 3904 | |||
| 3905 | if (!detail::compress(req.body)) { return nullptr; } | ||
| 3906 | req.headers.emplace("Content-Encoding", "gzip"); | ||
| 3907 | } else | ||
| 3908 | #endif | ||
| 3909 | { | ||
| 3910 | if (content_provider) { | ||
| 3911 | req.content_length = content_length; | ||
| 3912 | req.content_provider = content_provider; | ||
| 3913 | } else { | ||
| 3914 | req.body = body; | ||
| 3915 | } | ||
| 3916 | } | ||
| 3917 | |||
| 3918 | auto res = std::make_shared<Response>(); | ||
| 3919 | |||
| 3920 | return send(req, *res) ? res : nullptr; | ||
| 2942 | } | 3921 | } |
| 2943 | 3922 | ||
| 2944 | inline bool Client::process_request(Stream &strm, const Request &req, | 3923 | inline bool Client::process_request(Stream &strm, const Request &req, |
| 2945 | Response &res, bool last_connection, | 3924 | Response &res, bool last_connection, |
| 2946 | bool &connection_close) { | 3925 | bool &connection_close) { |
| 2947 | // Send request | 3926 | // Send request |
| 2948 | write_request(strm, req, last_connection); | 3927 | if (!write_request(strm, req, last_connection)) { return false; } |
| 2949 | 3928 | ||
| 2950 | // Receive response and headers | 3929 | // Receive response and headers |
| 2951 | if (!read_response_line(strm, res) || | 3930 | if (!read_response_line(strm, res) || |
| @@ -2963,21 +3942,16 @@ inline bool Client::process_request(Stream &strm, const Request &req, | |||
| 2963 | } | 3942 | } |
| 2964 | 3943 | ||
| 2965 | // Body | 3944 | // Body |
| 2966 | if (req.method != "HEAD") { | 3945 | if (req.method != "HEAD" && req.method != "CONNECT") { |
| 2967 | detail::ContentReceiverCore out = [&](const char *buf, size_t n) { | 3946 | ContentReceiver out = [&](const char *buf, size_t n) { |
| 2968 | if (res.body.size() + n > res.body.max_size()) { return false; } | 3947 | if (res.body.size() + n > res.body.max_size()) { return false; } |
| 2969 | res.body.append(buf, n); | 3948 | res.body.append(buf, n); |
| 2970 | return true; | 3949 | return true; |
| 2971 | }; | 3950 | }; |
| 2972 | 3951 | ||
| 2973 | if (req.content_receiver) { | 3952 | if (req.content_receiver) { |
| 2974 | auto offset = std::make_shared<size_t>(); | 3953 | out = [&](const char *buf, size_t n) { |
| 2975 | auto length = get_header_value_uint64(res.headers, "Content-Length", 0); | 3954 | return req.content_receiver(buf, n); |
| 2976 | auto receiver = req.content_receiver; | ||
| 2977 | out = [offset, length, receiver](const char *buf, size_t n) { | ||
| 2978 | auto ret = receiver(buf, n, *offset, length); | ||
| 2979 | (*offset) += n; | ||
| 2980 | return ret; | ||
| 2981 | }; | 3955 | }; |
| 2982 | } | 3956 | } |
| 2983 | 3957 | ||
| @@ -2988,6 +3962,9 @@ inline bool Client::process_request(Stream &strm, const Request &req, | |||
| 2988 | } | 3962 | } |
| 2989 | } | 3963 | } |
| 2990 | 3964 | ||
| 3965 | // Log | ||
| 3966 | if (logger_) { logger_(req, res); } | ||
| 3967 | |||
| 2991 | return true; | 3968 | return true; |
| 2992 | } | 3969 | } |
| 2993 | 3970 | ||
| @@ -2997,25 +3974,25 @@ inline bool Client::process_and_close_socket( | |||
| 2997 | bool &connection_close)> | 3974 | bool &connection_close)> |
| 2998 | callback) { | 3975 | callback) { |
| 2999 | request_count = std::min(request_count, keep_alive_max_count_); | 3976 | request_count = std::min(request_count, keep_alive_max_count_); |
| 3000 | return detail::process_and_close_socket(true, sock, request_count, callback); | 3977 | return detail::process_and_close_socket(true, sock, request_count, |
| 3978 | read_timeout_sec_, read_timeout_usec_, | ||
| 3979 | callback); | ||
| 3001 | } | 3980 | } |
| 3002 | 3981 | ||
| 3003 | inline bool Client::is_ssl() const { return false; } | 3982 | inline bool Client::is_ssl() const { return false; } |
| 3004 | 3983 | ||
| 3005 | inline std::shared_ptr<Response> Client::Get(const char *path) { | 3984 | inline std::shared_ptr<Response> Client::Get(const char *path) { |
| 3006 | Progress dummy; | 3985 | return Get(path, Headers(), Progress()); |
| 3007 | return Get(path, Headers(), dummy); | ||
| 3008 | } | 3986 | } |
| 3009 | 3987 | ||
| 3010 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3988 | inline std::shared_ptr<Response> Client::Get(const char *path, |
| 3011 | Progress progress) { | 3989 | Progress progress) { |
| 3012 | return Get(path, Headers(), progress); | 3990 | return Get(path, Headers(), std::move(progress)); |
| 3013 | } | 3991 | } |
| 3014 | 3992 | ||
| 3015 | inline std::shared_ptr<Response> Client::Get(const char *path, | 3993 | inline std::shared_ptr<Response> Client::Get(const char *path, |
| 3016 | const Headers &headers) { | 3994 | const Headers &headers) { |
| 3017 | Progress dummy; | 3995 | return Get(path, headers, Progress()); |
| 3018 | return Get(path, headers, dummy); | ||
| 3019 | } | 3996 | } |
| 3020 | 3997 | ||
| 3021 | inline std::shared_ptr<Response> | 3998 | inline std::shared_ptr<Response> |
| @@ -3024,7 +4001,7 @@ Client::Get(const char *path, const Headers &headers, Progress progress) { | |||
| 3024 | req.method = "GET"; | 4001 | req.method = "GET"; |
| 3025 | req.path = path; | 4002 | req.path = path; |
| 3026 | req.headers = headers; | 4003 | req.headers = headers; |
| 3027 | req.progress = progress; | 4004 | req.progress = std::move(progress); |
| 3028 | 4005 | ||
| 3029 | auto res = std::make_shared<Response>(); | 4006 | auto res = std::make_shared<Response>(); |
| 3030 | return send(req, *res) ? res : nullptr; | 4007 | return send(req, *res) ? res : nullptr; |
| @@ -3032,36 +4009,36 @@ Client::Get(const char *path, const Headers &headers, Progress progress) { | |||
| 3032 | 4009 | ||
| 3033 | inline std::shared_ptr<Response> Client::Get(const char *path, | 4010 | inline std::shared_ptr<Response> Client::Get(const char *path, |
| 3034 | ContentReceiver content_receiver) { | 4011 | ContentReceiver content_receiver) { |
| 3035 | Progress dummy; | 4012 | return Get(path, Headers(), nullptr, std::move(content_receiver), Progress()); |
| 3036 | return Get(path, Headers(), nullptr, content_receiver, dummy); | ||
| 3037 | } | 4013 | } |
| 3038 | 4014 | ||
| 3039 | inline std::shared_ptr<Response> Client::Get(const char *path, | 4015 | inline std::shared_ptr<Response> Client::Get(const char *path, |
| 3040 | ContentReceiver content_receiver, | 4016 | ContentReceiver content_receiver, |
| 3041 | Progress progress) { | 4017 | Progress progress) { |
| 3042 | return Get(path, Headers(), nullptr, content_receiver, progress); | 4018 | return Get(path, Headers(), nullptr, std::move(content_receiver), |
| 4019 | std::move(progress)); | ||
| 3043 | } | 4020 | } |
| 3044 | 4021 | ||
| 3045 | inline std::shared_ptr<Response> Client::Get(const char *path, | 4022 | inline std::shared_ptr<Response> Client::Get(const char *path, |
| 3046 | const Headers &headers, | 4023 | const Headers &headers, |
| 3047 | ContentReceiver content_receiver) { | 4024 | ContentReceiver content_receiver) { |
| 3048 | Progress dummy; | 4025 | return Get(path, headers, nullptr, std::move(content_receiver), Progress()); |
| 3049 | return Get(path, headers, nullptr, content_receiver, dummy); | ||
| 3050 | } | 4026 | } |
| 3051 | 4027 | ||
| 3052 | inline std::shared_ptr<Response> Client::Get(const char *path, | 4028 | inline std::shared_ptr<Response> Client::Get(const char *path, |
| 3053 | const Headers &headers, | 4029 | const Headers &headers, |
| 3054 | ContentReceiver content_receiver, | 4030 | ContentReceiver content_receiver, |
| 3055 | Progress progress) { | 4031 | Progress progress) { |
| 3056 | return Get(path, headers, nullptr, content_receiver, progress); | 4032 | return Get(path, headers, nullptr, std::move(content_receiver), |
| 4033 | std::move(progress)); | ||
| 3057 | } | 4034 | } |
| 3058 | 4035 | ||
| 3059 | inline std::shared_ptr<Response> Client::Get(const char *path, | 4036 | inline std::shared_ptr<Response> Client::Get(const char *path, |
| 3060 | const Headers &headers, | 4037 | const Headers &headers, |
| 3061 | ResponseHandler response_handler, | 4038 | ResponseHandler response_handler, |
| 3062 | ContentReceiver content_receiver) { | 4039 | ContentReceiver content_receiver) { |
| 3063 | Progress dummy; | 4040 | return Get(path, headers, std::move(response_handler), content_receiver, |
| 3064 | return Get(path, headers, response_handler, content_receiver, dummy); | 4041 | Progress()); |
| 3065 | } | 4042 | } |
| 3066 | 4043 | ||
| 3067 | inline std::shared_ptr<Response> Client::Get(const char *path, | 4044 | inline std::shared_ptr<Response> Client::Get(const char *path, |
| @@ -3073,9 +4050,9 @@ inline std::shared_ptr<Response> Client::Get(const char *path, | |||
| 3073 | req.method = "GET"; | 4050 | req.method = "GET"; |
| 3074 | req.path = path; | 4051 | req.path = path; |
| 3075 | req.headers = headers; | 4052 | req.headers = headers; |
| 3076 | req.response_handler = response_handler; | 4053 | req.response_handler = std::move(response_handler); |
| 3077 | req.content_receiver = content_receiver; | 4054 | req.content_receiver = std::move(content_receiver); |
| 3078 | req.progress = progress; | 4055 | req.progress = std::move(progress); |
| 3079 | 4056 | ||
| 3080 | auto res = std::make_shared<Response>(); | 4057 | auto res = std::make_shared<Response>(); |
| 3081 | return send(req, *res) ? res : nullptr; | 4058 | return send(req, *res) ? res : nullptr; |
| @@ -3107,17 +4084,8 @@ inline std::shared_ptr<Response> Client::Post(const char *path, | |||
| 3107 | const Headers &headers, | 4084 | const Headers &headers, |
| 3108 | const std::string &body, | 4085 | const std::string &body, |
| 3109 | const char *content_type) { | 4086 | const char *content_type) { |
| 3110 | Request req; | 4087 | return send_with_content_provider("POST", path, headers, body, 0, nullptr, |
| 3111 | req.method = "POST"; | 4088 | content_type); |
| 3112 | req.headers = headers; | ||
| 3113 | req.path = path; | ||
| 3114 | |||
| 3115 | req.headers.emplace("Content-Type", content_type); | ||
| 3116 | req.body = body; | ||
| 3117 | |||
| 3118 | auto res = std::make_shared<Response>(); | ||
| 3119 | |||
| 3120 | return send(req, *res) ? res : nullptr; | ||
| 3121 | } | 4089 | } |
| 3122 | 4090 | ||
| 3123 | inline std::shared_ptr<Response> Client::Post(const char *path, | 4091 | inline std::shared_ptr<Response> Client::Post(const char *path, |
| @@ -3125,6 +4093,21 @@ inline std::shared_ptr<Response> Client::Post(const char *path, | |||
| 3125 | return Post(path, Headers(), params); | 4093 | return Post(path, Headers(), params); |
| 3126 | } | 4094 | } |
| 3127 | 4095 | ||
| 4096 | inline std::shared_ptr<Response> Client::Post(const char *path, | ||
| 4097 | size_t content_length, | ||
| 4098 | ContentProvider content_provider, | ||
| 4099 | const char *content_type) { | ||
| 4100 | return Post(path, Headers(), content_length, content_provider, content_type); | ||
| 4101 | } | ||
| 4102 | |||
| 4103 | inline std::shared_ptr<Response> | ||
| 4104 | Client::Post(const char *path, const Headers &headers, size_t content_length, | ||
| 4105 | ContentProvider content_provider, const char *content_type) { | ||
| 4106 | return send_with_content_provider("POST", path, headers, std::string(), | ||
| 4107 | content_length, content_provider, | ||
| 4108 | content_type); | ||
| 4109 | } | ||
| 4110 | |||
| 3128 | inline std::shared_ptr<Response> | 4111 | inline std::shared_ptr<Response> |
| 3129 | Client::Post(const char *path, const Headers &headers, const Params ¶ms) { | 4112 | Client::Post(const char *path, const Headers &headers, const Params ¶ms) { |
| 3130 | std::string query; | 4113 | std::string query; |
| @@ -3146,35 +4129,28 @@ Client::Post(const char *path, const MultipartFormDataItems &items) { | |||
| 3146 | inline std::shared_ptr<Response> | 4129 | inline std::shared_ptr<Response> |
| 3147 | Client::Post(const char *path, const Headers &headers, | 4130 | Client::Post(const char *path, const Headers &headers, |
| 3148 | const MultipartFormDataItems &items) { | 4131 | const MultipartFormDataItems &items) { |
| 3149 | Request req; | ||
| 3150 | req.method = "POST"; | ||
| 3151 | req.headers = headers; | ||
| 3152 | req.path = path; | ||
| 3153 | |||
| 3154 | auto boundary = detail::make_multipart_data_boundary(); | 4132 | auto boundary = detail::make_multipart_data_boundary(); |
| 3155 | 4133 | ||
| 3156 | req.headers.emplace("Content-Type", | 4134 | std::string body; |
| 3157 | "multipart/form-data; boundary=" + boundary); | ||
| 3158 | 4135 | ||
| 3159 | for (const auto &item : items) { | 4136 | for (const auto &item : items) { |
| 3160 | req.body += "--" + boundary + "\r\n"; | 4137 | body += "--" + boundary + "\r\n"; |
| 3161 | req.body += "Content-Disposition: form-data; name=\"" + item.name + "\""; | 4138 | body += "Content-Disposition: form-data; name=\"" + item.name + "\""; |
| 3162 | if (!item.filename.empty()) { | 4139 | if (!item.filename.empty()) { |
| 3163 | req.body += "; filename=\"" + item.filename + "\""; | 4140 | body += "; filename=\"" + item.filename + "\""; |
| 3164 | } | 4141 | } |
| 3165 | req.body += "\r\n"; | 4142 | body += "\r\n"; |
| 3166 | if (!item.content_type.empty()) { | 4143 | if (!item.content_type.empty()) { |
| 3167 | req.body += "Content-Type: " + item.content_type + "\r\n"; | 4144 | body += "Content-Type: " + item.content_type + "\r\n"; |
| 3168 | } | 4145 | } |
| 3169 | req.body += "\r\n"; | 4146 | body += "\r\n"; |
| 3170 | req.body += item.content + "\r\n"; | 4147 | body += item.content + "\r\n"; |
| 3171 | } | 4148 | } |
| 3172 | 4149 | ||
| 3173 | req.body += "--" + boundary + "--\r\n"; | 4150 | body += "--" + boundary + "--\r\n"; |
| 3174 | 4151 | ||
| 3175 | auto res = std::make_shared<Response>(); | 4152 | std::string content_type = "multipart/form-data; boundary=" + boundary; |
| 3176 | 4153 | return Post(path, headers, body, content_type.c_str()); | |
| 3177 | return send(req, *res) ? res : nullptr; | ||
| 3178 | } | 4154 | } |
| 3179 | 4155 | ||
| 3180 | inline std::shared_ptr<Response> Client::Put(const char *path, | 4156 | inline std::shared_ptr<Response> Client::Put(const char *path, |
| @@ -3187,17 +4163,41 @@ inline std::shared_ptr<Response> Client::Put(const char *path, | |||
| 3187 | const Headers &headers, | 4163 | const Headers &headers, |
| 3188 | const std::string &body, | 4164 | const std::string &body, |
| 3189 | const char *content_type) { | 4165 | const char *content_type) { |
| 3190 | Request req; | 4166 | return send_with_content_provider("PUT", path, headers, body, 0, nullptr, |
| 3191 | req.method = "PUT"; | 4167 | content_type); |
| 3192 | req.headers = headers; | 4168 | } |
| 3193 | req.path = path; | ||
| 3194 | 4169 | ||
| 3195 | req.headers.emplace("Content-Type", content_type); | 4170 | inline std::shared_ptr<Response> Client::Put(const char *path, |
| 3196 | req.body = body; | 4171 | size_t content_length, |
| 4172 | ContentProvider content_provider, | ||
| 4173 | const char *content_type) { | ||
| 4174 | return Put(path, Headers(), content_length, content_provider, content_type); | ||
| 4175 | } | ||
| 3197 | 4176 | ||
| 3198 | auto res = std::make_shared<Response>(); | 4177 | inline std::shared_ptr<Response> |
| 4178 | Client::Put(const char *path, const Headers &headers, size_t content_length, | ||
| 4179 | ContentProvider content_provider, const char *content_type) { | ||
| 4180 | return send_with_content_provider("PUT", path, headers, std::string(), | ||
| 4181 | content_length, content_provider, | ||
| 4182 | content_type); | ||
| 4183 | } | ||
| 3199 | 4184 | ||
| 3200 | return send(req, *res) ? res : nullptr; | 4185 | inline std::shared_ptr<Response> Client::Put(const char *path, |
| 4186 | const Params ¶ms) { | ||
| 4187 | return Put(path, Headers(), params); | ||
| 4188 | } | ||
| 4189 | |||
| 4190 | inline std::shared_ptr<Response> | ||
| 4191 | Client::Put(const char *path, const Headers &headers, const Params ¶ms) { | ||
| 4192 | std::string query; | ||
| 4193 | for (auto it = params.begin(); it != params.end(); ++it) { | ||
| 4194 | if (it != params.begin()) { query += "&"; } | ||
| 4195 | query += it->first; | ||
| 4196 | query += "="; | ||
| 4197 | query += detail::encode_url(it->second); | ||
| 4198 | } | ||
| 4199 | |||
| 4200 | return Put(path, headers, query, "application/x-www-form-urlencoded"); | ||
| 3201 | } | 4201 | } |
| 3202 | 4202 | ||
| 3203 | inline std::shared_ptr<Response> Client::Patch(const char *path, | 4203 | inline std::shared_ptr<Response> Client::Patch(const char *path, |
| @@ -3210,17 +4210,23 @@ inline std::shared_ptr<Response> Client::Patch(const char *path, | |||
| 3210 | const Headers &headers, | 4210 | const Headers &headers, |
| 3211 | const std::string &body, | 4211 | const std::string &body, |
| 3212 | const char *content_type) { | 4212 | const char *content_type) { |
| 3213 | Request req; | 4213 | return send_with_content_provider("PATCH", path, headers, body, 0, nullptr, |
| 3214 | req.method = "PATCH"; | 4214 | content_type); |
| 3215 | req.headers = headers; | 4215 | } |
| 3216 | req.path = path; | ||
| 3217 | |||
| 3218 | req.headers.emplace("Content-Type", content_type); | ||
| 3219 | req.body = body; | ||
| 3220 | 4216 | ||
| 3221 | auto res = std::make_shared<Response>(); | 4217 | inline std::shared_ptr<Response> Client::Patch(const char *path, |
| 4218 | size_t content_length, | ||
| 4219 | ContentProvider content_provider, | ||
| 4220 | const char *content_type) { | ||
| 4221 | return Patch(path, Headers(), content_length, content_provider, content_type); | ||
| 4222 | } | ||
| 3222 | 4223 | ||
| 3223 | return send(req, *res) ? res : nullptr; | 4224 | inline std::shared_ptr<Response> |
| 4225 | Client::Patch(const char *path, const Headers &headers, size_t content_length, | ||
| 4226 | ContentProvider content_provider, const char *content_type) { | ||
| 4227 | return send_with_content_provider("PATCH", path, headers, std::string(), | ||
| 4228 | content_length, content_provider, | ||
| 4229 | content_type); | ||
| 3224 | } | 4230 | } |
| 3225 | 4231 | ||
| 3226 | inline std::shared_ptr<Response> Client::Delete(const char *path) { | 4232 | inline std::shared_ptr<Response> Client::Delete(const char *path) { |
| @@ -3271,11 +4277,58 @@ inline std::shared_ptr<Response> Client::Options(const char *path, | |||
| 3271 | return send(req, *res) ? res : nullptr; | 4277 | return send(req, *res) ? res : nullptr; |
| 3272 | } | 4278 | } |
| 3273 | 4279 | ||
| 4280 | inline void Client::set_timeout_sec(time_t timeout_sec) { | ||
| 4281 | timeout_sec_ = timeout_sec; | ||
| 4282 | } | ||
| 4283 | |||
| 4284 | inline void Client::set_read_timeout(time_t sec, time_t usec) { | ||
| 4285 | read_timeout_sec_ = sec; | ||
| 4286 | read_timeout_usec_ = usec; | ||
| 4287 | } | ||
| 4288 | |||
| 3274 | inline void Client::set_keep_alive_max_count(size_t count) { | 4289 | inline void Client::set_keep_alive_max_count(size_t count) { |
| 3275 | keep_alive_max_count_ = count; | 4290 | keep_alive_max_count_ = count; |
| 3276 | } | 4291 | } |
| 3277 | 4292 | ||
| 3278 | inline void Client::follow_location(bool on) { follow_location_ = on; } | 4293 | inline void Client::set_basic_auth(const char *username, const char *password) { |
| 4294 | basic_auth_username_ = username; | ||
| 4295 | basic_auth_password_ = password; | ||
| 4296 | } | ||
| 4297 | |||
| 4298 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 4299 | inline void Client::set_digest_auth(const char *username, | ||
| 4300 | const char *password) { | ||
| 4301 | digest_auth_username_ = username; | ||
| 4302 | digest_auth_password_ = password; | ||
| 4303 | } | ||
| 4304 | #endif | ||
| 4305 | |||
| 4306 | inline void Client::set_follow_location(bool on) { follow_location_ = on; } | ||
| 4307 | |||
| 4308 | inline void Client::set_compress(bool on) { compress_ = on; } | ||
| 4309 | |||
| 4310 | inline void Client::set_interface(const char *intf) { interface_ = intf; } | ||
| 4311 | |||
| 4312 | inline void Client::set_proxy(const char *host, int port) { | ||
| 4313 | proxy_host_ = host; | ||
| 4314 | proxy_port_ = port; | ||
| 4315 | } | ||
| 4316 | |||
| 4317 | inline void Client::set_proxy_basic_auth(const char *username, | ||
| 4318 | const char *password) { | ||
| 4319 | proxy_basic_auth_username_ = username; | ||
| 4320 | proxy_basic_auth_password_ = password; | ||
| 4321 | } | ||
| 4322 | |||
| 4323 | #ifdef CPPHTTPLIB_OPENSSL_SUPPORT | ||
| 4324 | inline void Client::set_proxy_digest_auth(const char *username, | ||
| 4325 | const char *password) { | ||
| 4326 | proxy_digest_auth_username_ = username; | ||
| 4327 | proxy_digest_auth_password_ = password; | ||
| 4328 | } | ||
| 4329 | #endif | ||
| 4330 | |||
| 4331 | inline void Client::set_logger(Logger logger) { logger_ = std::move(logger); } | ||
| 3279 | 4332 | ||
| 3280 | /* | 4333 | /* |
| 3281 | * SSL Implementation | 4334 | * SSL Implementation |
| @@ -3284,11 +4337,10 @@ inline void Client::follow_location(bool on) { follow_location_ = on; } | |||
| 3284 | namespace detail { | 4337 | namespace detail { |
| 3285 | 4338 | ||
| 3286 | template <typename U, typename V, typename T> | 4339 | template <typename U, typename V, typename T> |
| 3287 | inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock, | 4340 | inline bool process_and_close_socket_ssl( |
| 3288 | size_t keep_alive_max_count, | 4341 | bool is_client_request, socket_t sock, size_t keep_alive_max_count, |
| 3289 | SSL_CTX *ctx, std::mutex &ctx_mutex, | 4342 | time_t read_timeout_sec, time_t read_timeout_usec, SSL_CTX *ctx, |
| 3290 | U SSL_connect_or_accept, V setup, | 4343 | std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup, T callback) { |
| 3291 | T callback) { | ||
| 3292 | assert(keep_alive_max_count > 0); | 4344 | assert(keep_alive_max_count > 0); |
| 3293 | 4345 | ||
| 3294 | SSL *ssl = nullptr; | 4346 | SSL *ssl = nullptr; |
| @@ -3316,7 +4368,7 @@ inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock, | |||
| 3316 | return false; | 4368 | return false; |
| 3317 | } | 4369 | } |
| 3318 | 4370 | ||
| 3319 | bool ret = false; | 4371 | auto ret = false; |
| 3320 | 4372 | ||
| 3321 | if (SSL_connect_or_accept(ssl) == 1) { | 4373 | if (SSL_connect_or_accept(ssl) == 1) { |
| 3322 | if (keep_alive_max_count > 1) { | 4374 | if (keep_alive_max_count > 1) { |
| @@ -3325,7 +4377,7 @@ inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock, | |||
| 3325 | (is_client_request || | 4377 | (is_client_request || |
| 3326 | detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, | 4378 | detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, |
| 3327 | CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) { | 4379 | CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) { |
| 3328 | SSLSocketStream strm(sock, ssl); | 4380 | SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec); |
| 3329 | auto last_connection = count == 1; | 4381 | auto last_connection = count == 1; |
| 3330 | auto connection_close = false; | 4382 | auto connection_close = false; |
| 3331 | 4383 | ||
| @@ -3335,7 +4387,7 @@ inline bool process_and_close_socket_ssl(bool is_client_request, socket_t sock, | |||
| 3335 | count--; | 4387 | count--; |
| 3336 | } | 4388 | } |
| 3337 | } else { | 4389 | } else { |
| 3338 | SSLSocketStream strm(sock, ssl); | 4390 | SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec); |
| 3339 | auto dummy_connection_close = false; | 4391 | auto dummy_connection_close = false; |
| 3340 | ret = callback(ssl, strm, true, dummy_connection_close); | 4392 | ret = callback(ssl, strm, true, dummy_connection_close); |
| 3341 | } | 4393 | } |
| @@ -3382,11 +4434,20 @@ private: | |||
| 3382 | class SSLInit { | 4434 | class SSLInit { |
| 3383 | public: | 4435 | public: |
| 3384 | SSLInit() { | 4436 | SSLInit() { |
| 4437 | #if OPENSSL_VERSION_NUMBER < 0x1010001fL | ||
| 3385 | SSL_load_error_strings(); | 4438 | SSL_load_error_strings(); |
| 3386 | SSL_library_init(); | 4439 | SSL_library_init(); |
| 4440 | #else | ||
| 4441 | OPENSSL_init_ssl( | ||
| 4442 | OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); | ||
| 4443 | #endif | ||
| 3387 | } | 4444 | } |
| 3388 | 4445 | ||
| 3389 | ~SSLInit() { ERR_free_strings(); } | 4446 | ~SSLInit() { |
| 4447 | #if OPENSSL_VERSION_NUMBER < 0x1010001fL | ||
| 4448 | ERR_free_strings(); | ||
| 4449 | #endif | ||
| 4450 | } | ||
| 3390 | 4451 | ||
| 3391 | private: | 4452 | private: |
| 3392 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | 4453 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
| @@ -3394,41 +4455,44 @@ private: | |||
| 3394 | #endif | 4455 | #endif |
| 3395 | }; | 4456 | }; |
| 3396 | 4457 | ||
| 3397 | static SSLInit sslinit_; | ||
| 3398 | |||
| 3399 | } // namespace detail | ||
| 3400 | |||
| 3401 | // SSL socket stream implementation | 4458 | // SSL socket stream implementation |
| 3402 | inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl) | 4459 | inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl, |
| 3403 | : sock_(sock), ssl_(ssl) {} | 4460 | time_t read_timeout_sec, |
| 4461 | time_t read_timeout_usec) | ||
| 4462 | : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec), | ||
| 4463 | read_timeout_usec_(read_timeout_usec) {} | ||
| 3404 | 4464 | ||
| 3405 | inline SSLSocketStream::~SSLSocketStream() {} | 4465 | inline SSLSocketStream::~SSLSocketStream() {} |
| 3406 | 4466 | ||
| 4467 | inline bool SSLSocketStream::is_readable() const { | ||
| 4468 | return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0; | ||
| 4469 | } | ||
| 4470 | |||
| 4471 | inline bool SSLSocketStream::is_writable() const { | ||
| 4472 | return detail::select_write(sock_, 0, 0) > 0; | ||
| 4473 | } | ||
| 4474 | |||
| 3407 | inline int SSLSocketStream::read(char *ptr, size_t size) { | 4475 | inline int SSLSocketStream::read(char *ptr, size_t size) { |
| 3408 | if (SSL_pending(ssl_) > 0 || | 4476 | if (SSL_pending(ssl_) > 0 || |
| 3409 | detail::select_read(sock_, CPPHTTPLIB_READ_TIMEOUT_SECOND, | 4477 | select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) { |
| 3410 | CPPHTTPLIB_READ_TIMEOUT_USECOND) > 0) { | ||
| 3411 | return SSL_read(ssl_, ptr, static_cast<int>(size)); | 4478 | return SSL_read(ssl_, ptr, static_cast<int>(size)); |
| 3412 | } | 4479 | } |
| 3413 | return -1; | 4480 | return -1; |
| 3414 | } | 4481 | } |
| 3415 | 4482 | ||
| 3416 | inline int SSLSocketStream::write(const char *ptr, size_t size) { | 4483 | inline int SSLSocketStream::write(const char *ptr, size_t size) { |
| 3417 | return SSL_write(ssl_, ptr, static_cast<int>(size)); | 4484 | if (is_writable()) { return SSL_write(ssl_, ptr, static_cast<int>(size)); } |
| 3418 | } | 4485 | return -1; |
| 3419 | |||
| 3420 | inline int SSLSocketStream::write(const char *ptr) { | ||
| 3421 | return write(ptr, strlen(ptr)); | ||
| 3422 | } | ||
| 3423 | |||
| 3424 | inline int SSLSocketStream::write(const std::string &s) { | ||
| 3425 | return write(s.data(), s.size()); | ||
| 3426 | } | 4486 | } |
| 3427 | 4487 | ||
| 3428 | inline std::string SSLSocketStream::get_remote_addr() const { | 4488 | inline std::string SSLSocketStream::get_remote_addr() const { |
| 3429 | return detail::get_remote_addr(sock_); | 4489 | return detail::get_remote_addr(sock_); |
| 3430 | } | 4490 | } |
| 3431 | 4491 | ||
| 4492 | static SSLInit sslinit_; | ||
| 4493 | |||
| 4494 | } // namespace detail | ||
| 4495 | |||
| 3432 | // SSL HTTP server implementation | 4496 | // SSL HTTP server implementation |
| 3433 | inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, | 4497 | inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path, |
| 3434 | const char *client_ca_cert_file_path, | 4498 | const char *client_ca_cert_file_path, |
| @@ -3476,8 +4540,8 @@ inline bool SSLServer::is_valid() const { return ctx_; } | |||
| 3476 | 4540 | ||
| 3477 | inline bool SSLServer::process_and_close_socket(socket_t sock) { | 4541 | inline bool SSLServer::process_and_close_socket(socket_t sock) { |
| 3478 | return detail::process_and_close_socket_ssl( | 4542 | return detail::process_and_close_socket_ssl( |
| 3479 | false, sock, keep_alive_max_count_, ctx_, ctx_mutex_, SSL_accept, | 4543 | false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_, |
| 3480 | [](SSL * /*ssl*/) { return true; }, | 4544 | ctx_, ctx_mutex_, SSL_accept, [](SSL * /*ssl*/) { return true; }, |
| 3481 | [this](SSL *ssl, Stream &strm, bool last_connection, | 4545 | [this](SSL *ssl, Stream &strm, bool last_connection, |
| 3482 | bool &connection_close) { | 4546 | bool &connection_close) { |
| 3483 | return process_request(strm, last_connection, connection_close, | 4547 | return process_request(strm, last_connection, connection_close, |
| @@ -3486,21 +4550,21 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) { | |||
| 3486 | } | 4550 | } |
| 3487 | 4551 | ||
| 3488 | // SSL HTTP client implementation | 4552 | // SSL HTTP client implementation |
| 3489 | inline SSLClient::SSLClient(const char *host, int port, time_t timeout_sec, | 4553 | inline SSLClient::SSLClient(const std::string &host, int port, |
| 3490 | const char *client_cert_path, | 4554 | const std::string &client_cert_path, |
| 3491 | const char *client_key_path) | 4555 | const std::string &client_key_path) |
| 3492 | : Client(host, port, timeout_sec) { | 4556 | : Client(host, port, client_cert_path, client_key_path) { |
| 3493 | ctx_ = SSL_CTX_new(SSLv23_client_method()); | 4557 | ctx_ = SSL_CTX_new(SSLv23_client_method()); |
| 3494 | 4558 | ||
| 3495 | detail::split(&host_[0], &host_[host_.size()], '.', | 4559 | detail::split(&host_[0], &host_[host_.size()], '.', |
| 3496 | [&](const char *b, const char *e) { | 4560 | [&](const char *b, const char *e) { |
| 3497 | host_components_.emplace_back(std::string(b, e)); | 4561 | host_components_.emplace_back(std::string(b, e)); |
| 3498 | }); | 4562 | }); |
| 3499 | if (client_cert_path && client_key_path) { | 4563 | if (!client_cert_path.empty() && !client_key_path.empty()) { |
| 3500 | if (SSL_CTX_use_certificate_file(ctx_, client_cert_path, | 4564 | if (SSL_CTX_use_certificate_file(ctx_, client_cert_path.c_str(), |
| 3501 | SSL_FILETYPE_PEM) != 1 || | 4565 | SSL_FILETYPE_PEM) != 1 || |
| 3502 | SSL_CTX_use_PrivateKey_file(ctx_, client_key_path, SSL_FILETYPE_PEM) != | 4566 | SSL_CTX_use_PrivateKey_file(ctx_, client_key_path.c_str(), |
| 3503 | 1) { | 4567 | SSL_FILETYPE_PEM) != 1) { |
| 3504 | SSL_CTX_free(ctx_); | 4568 | SSL_CTX_free(ctx_); |
| 3505 | ctx_ = nullptr; | 4569 | ctx_ = nullptr; |
| 3506 | } | 4570 | } |
| @@ -3527,9 +4591,7 @@ inline long SSLClient::get_openssl_verify_result() const { | |||
| 3527 | return verify_result_; | 4591 | return verify_result_; |
| 3528 | } | 4592 | } |
| 3529 | 4593 | ||
| 3530 | inline SSL_CTX* SSLClient::ssl_context() const noexcept { | 4594 | inline SSL_CTX *SSLClient::ssl_context() const noexcept { return ctx_; } |
| 3531 | return ctx_; | ||
| 3532 | } | ||
| 3533 | 4595 | ||
| 3534 | inline bool SSLClient::process_and_close_socket( | 4596 | inline bool SSLClient::process_and_close_socket( |
| 3535 | socket_t sock, size_t request_count, | 4597 | socket_t sock, size_t request_count, |
| @@ -3541,7 +4603,8 @@ inline bool SSLClient::process_and_close_socket( | |||
| 3541 | 4603 | ||
| 3542 | return is_valid() && | 4604 | return is_valid() && |
| 3543 | detail::process_and_close_socket_ssl( | 4605 | detail::process_and_close_socket_ssl( |
| 3544 | true, sock, request_count, ctx_, ctx_mutex_, | 4606 | true, sock, request_count, read_timeout_sec_, read_timeout_usec_, |
| 4607 | ctx_, ctx_mutex_, | ||
| 3545 | [&](SSL *ssl) { | 4608 | [&](SSL *ssl) { |
| 3546 | if (ca_cert_file_path_.empty()) { | 4609 | if (ca_cert_file_path_.empty()) { |
| 3547 | SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); | 4610 | SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr); |
| @@ -3712,6 +4775,8 @@ inline bool SSLClient::check_host_name(const char *pattern, | |||
| 3712 | } | 4775 | } |
| 3713 | #endif | 4776 | #endif |
| 3714 | 4777 | ||
| 4778 | // ---------------------------------------------------------------------------- | ||
| 4779 | |||
| 3715 | } // namespace httplib | 4780 | } // namespace httplib |
| 3716 | 4781 | ||
| 3717 | #endif // CPPHTTPLIB_HTTPLIB_H | 4782 | #endif // CPPHTTPLIB_HTTPLIB_H |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 8f2591d53..04bc3128f 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -120,7 +120,7 @@ private: | |||
| 120 | duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); | 120 | duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin); |
| 121 | entry.log_class = log_class; | 121 | entry.log_class = log_class; |
| 122 | entry.log_level = log_level; | 122 | entry.log_level = log_level; |
| 123 | entry.filename = Common::TrimSourcePath(filename); | 123 | entry.filename = filename; |
| 124 | entry.line_num = line_nr; | 124 | entry.line_num = line_nr; |
| 125 | entry.function = function; | 125 | entry.function = function; |
| 126 | entry.message = std::move(message); | 126 | entry.message = std::move(message); |
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index fca0267a1..fc338c70d 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h | |||
| @@ -23,7 +23,7 @@ struct Entry { | |||
| 23 | std::chrono::microseconds timestamp; | 23 | std::chrono::microseconds timestamp; |
| 24 | Class log_class; | 24 | Class log_class; |
| 25 | Level log_level; | 25 | Level log_level; |
| 26 | std::string filename; | 26 | const char* filename; |
| 27 | unsigned int line_num; | 27 | unsigned int line_num; |
| 28 | std::string function; | 28 | std::string function; |
| 29 | std::string message; | 29 | std::string message; |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 259708116..13a4f1e30 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -9,6 +9,15 @@ | |||
| 9 | 9 | ||
| 10 | namespace Log { | 10 | namespace Log { |
| 11 | 11 | ||
| 12 | // trims up to and including the last of ../, ..\, src/, src\ in a string | ||
| 13 | constexpr const char* TrimSourcePath(std::string_view source) { | ||
| 14 | const auto rfind = [source](const std::string_view match) { | ||
| 15 | return source.rfind(match) == source.npos ? 0 : (source.rfind(match) + match.size()); | ||
| 16 | }; | ||
| 17 | auto idx = std::max({rfind("src/"), rfind("src\\"), rfind("../"), rfind("..\\")}); | ||
| 18 | return source.data() + idx; | ||
| 19 | } | ||
| 20 | |||
| 12 | /// Specifies the severity or level of detail of the log message. | 21 | /// Specifies the severity or level of detail of the log message. |
| 13 | enum class Level : u8 { | 22 | enum class Level : u8 { |
| 14 | Trace, ///< Extremely detailed and repetitive debugging information that is likely to | 23 | 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 | |||
| 141 | 150 | ||
| 142 | #ifdef _DEBUG | 151 | #ifdef _DEBUG |
| 143 | #define LOG_TRACE(log_class, ...) \ | 152 | #define LOG_TRACE(log_class, ...) \ |
| 144 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \ | 153 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, \ |
| 145 | __func__, __VA_ARGS__) | 154 | ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |
| 146 | #else | 155 | #else |
| 147 | #define LOG_TRACE(log_class, fmt, ...) (void(0)) | 156 | #define LOG_TRACE(log_class, fmt, ...) (void(0)) |
| 148 | #endif | 157 | #endif |
| 149 | 158 | ||
| 150 | #define LOG_DEBUG(log_class, ...) \ | 159 | #define LOG_DEBUG(log_class, ...) \ |
| 151 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \ | 160 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, \ |
| 152 | __func__, __VA_ARGS__) | 161 | ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |
| 153 | #define LOG_INFO(log_class, ...) \ | 162 | #define LOG_INFO(log_class, ...) \ |
| 154 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \ | 163 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, \ |
| 155 | __func__, __VA_ARGS__) | 164 | ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |
| 156 | #define LOG_WARNING(log_class, ...) \ | 165 | #define LOG_WARNING(log_class, ...) \ |
| 157 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \ | 166 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, \ |
| 158 | __func__, __VA_ARGS__) | 167 | ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |
| 159 | #define LOG_ERROR(log_class, ...) \ | 168 | #define LOG_ERROR(log_class, ...) \ |
| 160 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \ | 169 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, \ |
| 161 | __func__, __VA_ARGS__) | 170 | ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |
| 162 | #define LOG_CRITICAL(log_class, ...) \ | 171 | #define LOG_CRITICAL(log_class, ...) \ |
| 163 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \ | 172 | ::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, \ |
| 164 | __func__, __VA_ARGS__) | 173 | ::Log::TrimSourcePath(__FILE__), __LINE__, __func__, __VA_ARGS__) |
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 959f278aa..84883a1d3 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -223,26 +223,4 @@ std::u16string UTF16StringFromFixedZeroTerminatedBuffer(std::u16string_view buff | |||
| 223 | return std::u16string(buffer.begin(), buffer.begin() + len); | 223 | return std::u16string(buffer.begin(), buffer.begin() + len); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | const char* TrimSourcePath(const char* path, const char* root) { | ||
| 227 | const char* p = path; | ||
| 228 | |||
| 229 | while (*p != '\0') { | ||
| 230 | const char* next_slash = p; | ||
| 231 | while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') { | ||
| 232 | ++next_slash; | ||
| 233 | } | ||
| 234 | |||
| 235 | bool is_src = Common::ComparePartialString(p, next_slash, root); | ||
| 236 | p = next_slash; | ||
| 237 | |||
| 238 | if (*p != '\0') { | ||
| 239 | ++p; | ||
| 240 | } | ||
| 241 | if (is_src) { | ||
| 242 | path = p; | ||
| 243 | } | ||
| 244 | } | ||
| 245 | return path; | ||
| 246 | } | ||
| 247 | |||
| 248 | } // namespace Common | 226 | } // namespace Common |
diff --git a/src/common/thread.h b/src/common/thread.h index 0cfd98be6..2fc071685 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -28,6 +28,15 @@ public: | |||
| 28 | is_set = false; | 28 | is_set = false; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | template <class Duration> | ||
| 32 | bool WaitFor(const std::chrono::duration<Duration>& time) { | ||
| 33 | std::unique_lock lk{mutex}; | ||
| 34 | if (!condvar.wait_for(lk, time, [this] { return is_set; })) | ||
| 35 | return false; | ||
| 36 | is_set = false; | ||
| 37 | return true; | ||
| 38 | } | ||
| 39 | |||
| 31 | template <class Clock, class Duration> | 40 | template <class Clock, class Duration> |
| 32 | bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) { | 41 | bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) { |
| 33 | std::unique_lock lk{mutex}; | 42 | std::unique_lock lk{mutex}; |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1a3647a67..26612e692 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -15,14 +15,14 @@ add_library(core STATIC | |||
| 15 | constants.h | 15 | constants.h |
| 16 | core.cpp | 16 | core.cpp |
| 17 | core.h | 17 | core.h |
| 18 | core_cpu.cpp | 18 | core_manager.cpp |
| 19 | core_cpu.h | 19 | core_manager.h |
| 20 | core_timing.cpp | 20 | core_timing.cpp |
| 21 | core_timing.h | 21 | core_timing.h |
| 22 | core_timing_util.cpp | 22 | core_timing_util.cpp |
| 23 | core_timing_util.h | 23 | core_timing_util.h |
| 24 | cpu_core_manager.cpp | 24 | cpu_manager.cpp |
| 25 | cpu_core_manager.h | 25 | cpu_manager.h |
| 26 | crypto/aes_util.cpp | 26 | crypto/aes_util.cpp |
| 27 | crypto/aes_util.h | 27 | crypto/aes_util.h |
| 28 | crypto/encryption_layer.cpp | 28 | crypto/encryption_layer.cpp |
| @@ -158,6 +158,8 @@ add_library(core STATIC | |||
| 158 | hle/kernel/mutex.h | 158 | hle/kernel/mutex.h |
| 159 | hle/kernel/object.cpp | 159 | hle/kernel/object.cpp |
| 160 | hle/kernel/object.h | 160 | hle/kernel/object.h |
| 161 | hle/kernel/physical_core.cpp | ||
| 162 | hle/kernel/physical_core.h | ||
| 161 | hle/kernel/process.cpp | 163 | hle/kernel/process.cpp |
| 162 | hle/kernel/process.h | 164 | hle/kernel/process.h |
| 163 | hle/kernel/process_capability.cpp | 165 | hle/kernel/process_capability.cpp |
| @@ -179,14 +181,16 @@ add_library(core STATIC | |||
| 179 | hle/kernel/svc.cpp | 181 | hle/kernel/svc.cpp |
| 180 | hle/kernel/svc.h | 182 | hle/kernel/svc.h |
| 181 | hle/kernel/svc_wrap.h | 183 | hle/kernel/svc_wrap.h |
| 184 | hle/kernel/synchronization_object.cpp | ||
| 185 | hle/kernel/synchronization_object.h | ||
| 186 | hle/kernel/synchronization.cpp | ||
| 187 | hle/kernel/synchronization.h | ||
| 182 | hle/kernel/thread.cpp | 188 | hle/kernel/thread.cpp |
| 183 | hle/kernel/thread.h | 189 | hle/kernel/thread.h |
| 184 | hle/kernel/transfer_memory.cpp | 190 | hle/kernel/transfer_memory.cpp |
| 185 | hle/kernel/transfer_memory.h | 191 | hle/kernel/transfer_memory.h |
| 186 | hle/kernel/vm_manager.cpp | 192 | hle/kernel/vm_manager.cpp |
| 187 | hle/kernel/vm_manager.h | 193 | hle/kernel/vm_manager.h |
| 188 | hle/kernel/wait_object.cpp | ||
| 189 | hle/kernel/wait_object.h | ||
| 190 | hle/kernel/writable_event.cpp | 194 | hle/kernel/writable_event.cpp |
| 191 | hle/kernel/writable_event.h | 195 | hle/kernel/writable_event.h |
| 192 | hle/lock.cpp | 196 | hle/lock.cpp |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index e825c0526..29eaf74e5 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -10,11 +10,13 @@ | |||
| 10 | #include "common/microprofile.h" | 10 | #include "common/microprofile.h" |
| 11 | #include "core/arm/dynarmic/arm_dynarmic.h" | 11 | #include "core/arm/dynarmic/arm_dynarmic.h" |
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/core_cpu.h" | 13 | #include "core/core_manager.h" |
| 14 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 15 | #include "core/core_timing_util.h" | 15 | #include "core/core_timing_util.h" |
| 16 | #include "core/gdbstub/gdbstub.h" | 16 | #include "core/gdbstub/gdbstub.h" |
| 17 | #include "core/hardware_properties.h" | ||
| 17 | #include "core/hle/kernel/process.h" | 18 | #include "core/hle/kernel/process.h" |
| 19 | #include "core/hle/kernel/scheduler.h" | ||
| 18 | #include "core/hle/kernel/svc.h" | 20 | #include "core/hle/kernel/svc.h" |
| 19 | #include "core/hle/kernel/vm_manager.h" | 21 | #include "core/hle/kernel/vm_manager.h" |
| 20 | #include "core/memory.h" | 22 | #include "core/memory.h" |
| @@ -87,7 +89,7 @@ public: | |||
| 87 | if (GDBStub::IsServerEnabled()) { | 89 | if (GDBStub::IsServerEnabled()) { |
| 88 | parent.jit->HaltExecution(); | 90 | parent.jit->HaltExecution(); |
| 89 | parent.SetPC(pc); | 91 | parent.SetPC(pc); |
| 90 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 92 | Kernel::Thread* const thread = parent.system.CurrentScheduler().GetCurrentThread(); |
| 91 | parent.SaveContext(thread->GetContext()); | 93 | parent.SaveContext(thread->GetContext()); |
| 92 | GDBStub::Break(); | 94 | GDBStub::Break(); |
| 93 | GDBStub::SendTrap(thread, 5); | 95 | GDBStub::SendTrap(thread, 5); |
| @@ -152,7 +154,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit(Common::PageTable& pag | |||
| 152 | config.tpidr_el0 = &cb->tpidr_el0; | 154 | config.tpidr_el0 = &cb->tpidr_el0; |
| 153 | config.dczid_el0 = 4; | 155 | config.dczid_el0 = 4; |
| 154 | config.ctr_el0 = 0x8444c004; | 156 | config.ctr_el0 = 0x8444c004; |
| 155 | config.cntfrq_el0 = Timing::CNTFREQ; | 157 | config.cntfrq_el0 = Hardware::CNTFREQ; |
| 156 | 158 | ||
| 157 | // Unpredictable instructions | 159 | // Unpredictable instructions |
| 158 | config.define_unpredictable_behaviour = true; | 160 | config.define_unpredictable_behaviour = true; |
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index abd59ff4b..94570e520 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp | |||
| @@ -2,10 +2,24 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #ifdef ARCHITECTURE_x86_64 | ||
| 6 | #include "core/arm/dynarmic/arm_dynarmic.h" | ||
| 7 | #endif | ||
| 5 | #include "core/arm/exclusive_monitor.h" | 8 | #include "core/arm/exclusive_monitor.h" |
| 9 | #include "core/memory.h" | ||
| 6 | 10 | ||
| 7 | namespace Core { | 11 | namespace Core { |
| 8 | 12 | ||
| 9 | ExclusiveMonitor::~ExclusiveMonitor() = default; | 13 | ExclusiveMonitor::~ExclusiveMonitor() = default; |
| 10 | 14 | ||
| 15 | std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, | ||
| 16 | std::size_t num_cores) { | ||
| 17 | #ifdef ARCHITECTURE_x86_64 | ||
| 18 | return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores); | ||
| 19 | #else | ||
| 20 | // TODO(merry): Passthrough exclusive monitor | ||
| 21 | return nullptr; | ||
| 22 | #endif | ||
| 23 | } | ||
| 24 | |||
| 11 | } // namespace Core | 25 | } // namespace Core |
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index f59aca667..4ef418b90 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h | |||
| @@ -4,8 +4,14 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | |||
| 7 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 8 | 10 | ||
| 11 | namespace Memory { | ||
| 12 | class Memory; | ||
| 13 | } | ||
| 14 | |||
| 9 | namespace Core { | 15 | namespace Core { |
| 10 | 16 | ||
| 11 | class ExclusiveMonitor { | 17 | class ExclusiveMonitor { |
| @@ -22,4 +28,7 @@ public: | |||
| 22 | virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0; | 28 | virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0; |
| 23 | }; | 29 | }; |
| 24 | 30 | ||
| 31 | std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, | ||
| 32 | std::size_t num_cores); | ||
| 33 | |||
| 25 | } // namespace Core | 34 | } // namespace Core |
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 48182c99a..f99ad5802 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/arm/unicorn/arm_unicorn.h" | 9 | #include "core/arm/unicorn/arm_unicorn.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/core_timing.h" | 11 | #include "core/core_timing.h" |
| 12 | #include "core/hle/kernel/scheduler.h" | ||
| 12 | #include "core/hle/kernel/svc.h" | 13 | #include "core/hle/kernel/svc.h" |
| 13 | 14 | ||
| 14 | namespace Core { | 15 | namespace Core { |
| @@ -177,7 +178,7 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { | |||
| 177 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); | 178 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); |
| 178 | } | 179 | } |
| 179 | 180 | ||
| 180 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 181 | Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread(); |
| 181 | SaveContext(thread->GetContext()); | 182 | SaveContext(thread->GetContext()); |
| 182 | if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { | 183 | if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) { |
| 183 | last_bkpt_hit = false; | 184 | last_bkpt_hit = false; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index d697b80ef..0eb0c0dca 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -11,9 +11,9 @@ | |||
| 11 | #include "common/string_util.h" | 11 | #include "common/string_util.h" |
| 12 | #include "core/arm/exclusive_monitor.h" | 12 | #include "core/arm/exclusive_monitor.h" |
| 13 | #include "core/core.h" | 13 | #include "core/core.h" |
| 14 | #include "core/core_cpu.h" | 14 | #include "core/core_manager.h" |
| 15 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 16 | #include "core/cpu_core_manager.h" | 16 | #include "core/cpu_manager.h" |
| 17 | #include "core/file_sys/bis_factory.h" | 17 | #include "core/file_sys/bis_factory.h" |
| 18 | #include "core/file_sys/card_image.h" | 18 | #include "core/file_sys/card_image.h" |
| 19 | #include "core/file_sys/mode.h" | 19 | #include "core/file_sys/mode.h" |
| @@ -28,6 +28,7 @@ | |||
| 28 | #include "core/hardware_interrupt_manager.h" | 28 | #include "core/hardware_interrupt_manager.h" |
| 29 | #include "core/hle/kernel/client_port.h" | 29 | #include "core/hle/kernel/client_port.h" |
| 30 | #include "core/hle/kernel/kernel.h" | 30 | #include "core/hle/kernel/kernel.h" |
| 31 | #include "core/hle/kernel/physical_core.h" | ||
| 31 | #include "core/hle/kernel/process.h" | 32 | #include "core/hle/kernel/process.h" |
| 32 | #include "core/hle/kernel/scheduler.h" | 33 | #include "core/hle/kernel/scheduler.h" |
| 33 | #include "core/hle/kernel/thread.h" | 34 | #include "core/hle/kernel/thread.h" |
| @@ -113,16 +114,25 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 113 | struct System::Impl { | 114 | struct System::Impl { |
| 114 | explicit Impl(System& system) | 115 | explicit Impl(System& system) |
| 115 | : kernel{system}, fs_controller{system}, memory{system}, | 116 | : kernel{system}, fs_controller{system}, memory{system}, |
| 116 | cpu_core_manager{system}, reporter{system}, applet_manager{system} {} | 117 | cpu_manager{system}, reporter{system}, applet_manager{system} {} |
| 117 | 118 | ||
| 118 | Cpu& CurrentCpuCore() { | 119 | CoreManager& CurrentCoreManager() { |
| 119 | return cpu_core_manager.GetCurrentCore(); | 120 | return cpu_manager.GetCurrentCoreManager(); |
| 121 | } | ||
| 122 | |||
| 123 | Kernel::PhysicalCore& CurrentPhysicalCore() { | ||
| 124 | const auto index = cpu_manager.GetActiveCoreIndex(); | ||
| 125 | return kernel.PhysicalCore(index); | ||
| 126 | } | ||
| 127 | |||
| 128 | Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { | ||
| 129 | return kernel.PhysicalCore(index); | ||
| 120 | } | 130 | } |
| 121 | 131 | ||
| 122 | ResultStatus RunLoop(bool tight_loop) { | 132 | ResultStatus RunLoop(bool tight_loop) { |
| 123 | status = ResultStatus::Success; | 133 | status = ResultStatus::Success; |
| 124 | 134 | ||
| 125 | cpu_core_manager.RunLoop(tight_loop); | 135 | cpu_manager.RunLoop(tight_loop); |
| 126 | 136 | ||
| 127 | return status; | 137 | return status; |
| 128 | } | 138 | } |
| @@ -131,8 +141,8 @@ struct System::Impl { | |||
| 131 | LOG_DEBUG(HW_Memory, "initialized OK"); | 141 | LOG_DEBUG(HW_Memory, "initialized OK"); |
| 132 | 142 | ||
| 133 | core_timing.Initialize(); | 143 | core_timing.Initialize(); |
| 134 | cpu_core_manager.Initialize(); | ||
| 135 | kernel.Initialize(); | 144 | kernel.Initialize(); |
| 145 | cpu_manager.Initialize(); | ||
| 136 | 146 | ||
| 137 | const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( | 147 | const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( |
| 138 | std::chrono::system_clock::now().time_since_epoch()); | 148 | std::chrono::system_clock::now().time_since_epoch()); |
| @@ -205,7 +215,6 @@ struct System::Impl { | |||
| 205 | // Main process has been loaded and been made current. | 215 | // Main process has been loaded and been made current. |
| 206 | // Begin GPU and CPU execution. | 216 | // Begin GPU and CPU execution. |
| 207 | gpu_core->Start(); | 217 | gpu_core->Start(); |
| 208 | cpu_core_manager.StartThreads(); | ||
| 209 | 218 | ||
| 210 | // Initialize cheat engine | 219 | // Initialize cheat engine |
| 211 | if (cheat_engine) { | 220 | if (cheat_engine) { |
| @@ -259,7 +268,9 @@ struct System::Impl { | |||
| 259 | is_powered_on = false; | 268 | is_powered_on = false; |
| 260 | exit_lock = false; | 269 | exit_lock = false; |
| 261 | 270 | ||
| 262 | gpu_core->WaitIdle(); | 271 | if (gpu_core) { |
| 272 | gpu_core->WaitIdle(); | ||
| 273 | } | ||
| 263 | 274 | ||
| 264 | // Shutdown emulation session | 275 | // Shutdown emulation session |
| 265 | renderer.reset(); | 276 | renderer.reset(); |
| @@ -272,7 +283,7 @@ struct System::Impl { | |||
| 272 | gpu_core.reset(); | 283 | gpu_core.reset(); |
| 273 | 284 | ||
| 274 | // Close all CPU/threading state | 285 | // Close all CPU/threading state |
| 275 | cpu_core_manager.Shutdown(); | 286 | cpu_manager.Shutdown(); |
| 276 | 287 | ||
| 277 | // Shutdown kernel and core timing | 288 | // Shutdown kernel and core timing |
| 278 | kernel.Shutdown(); | 289 | kernel.Shutdown(); |
| @@ -342,7 +353,7 @@ struct System::Impl { | |||
| 342 | std::unique_ptr<Tegra::GPU> gpu_core; | 353 | std::unique_ptr<Tegra::GPU> gpu_core; |
| 343 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; | 354 | std::unique_ptr<Hardware::InterruptManager> interrupt_manager; |
| 344 | Memory::Memory memory; | 355 | Memory::Memory memory; |
| 345 | CpuCoreManager cpu_core_manager; | 356 | CpuManager cpu_manager; |
| 346 | bool is_powered_on = false; | 357 | bool is_powered_on = false; |
| 347 | bool exit_lock = false; | 358 | bool exit_lock = false; |
| 348 | 359 | ||
| @@ -377,12 +388,12 @@ struct System::Impl { | |||
| 377 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 388 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| 378 | System::~System() = default; | 389 | System::~System() = default; |
| 379 | 390 | ||
| 380 | Cpu& System::CurrentCpuCore() { | 391 | CoreManager& System::CurrentCoreManager() { |
| 381 | return impl->CurrentCpuCore(); | 392 | return impl->CurrentCoreManager(); |
| 382 | } | 393 | } |
| 383 | 394 | ||
| 384 | const Cpu& System::CurrentCpuCore() const { | 395 | const CoreManager& System::CurrentCoreManager() const { |
| 385 | return impl->CurrentCpuCore(); | 396 | return impl->CurrentCoreManager(); |
| 386 | } | 397 | } |
| 387 | 398 | ||
| 388 | System::ResultStatus System::RunLoop(bool tight_loop) { | 399 | System::ResultStatus System::RunLoop(bool tight_loop) { |
| @@ -394,7 +405,7 @@ System::ResultStatus System::SingleStep() { | |||
| 394 | } | 405 | } |
| 395 | 406 | ||
| 396 | void System::InvalidateCpuInstructionCaches() { | 407 | void System::InvalidateCpuInstructionCaches() { |
| 397 | impl->cpu_core_manager.InvalidateAllInstructionCaches(); | 408 | impl->kernel.InvalidateAllInstructionCaches(); |
| 398 | } | 409 | } |
| 399 | 410 | ||
| 400 | System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { | 411 | System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { |
| @@ -406,13 +417,11 @@ bool System::IsPoweredOn() const { | |||
| 406 | } | 417 | } |
| 407 | 418 | ||
| 408 | void System::PrepareReschedule() { | 419 | void System::PrepareReschedule() { |
| 409 | CurrentCpuCore().PrepareReschedule(); | 420 | impl->CurrentPhysicalCore().Stop(); |
| 410 | } | 421 | } |
| 411 | 422 | ||
| 412 | void System::PrepareReschedule(const u32 core_index) { | 423 | void System::PrepareReschedule(const u32 core_index) { |
| 413 | if (core_index < GlobalScheduler().CpuCoresCount()) { | 424 | impl->kernel.PrepareReschedule(core_index); |
| 414 | CpuCore(core_index).PrepareReschedule(); | ||
| 415 | } | ||
| 416 | } | 425 | } |
| 417 | 426 | ||
| 418 | PerfStatsResults System::GetAndResetPerfStats() { | 427 | PerfStatsResults System::GetAndResetPerfStats() { |
| @@ -428,31 +437,31 @@ const TelemetrySession& System::TelemetrySession() const { | |||
| 428 | } | 437 | } |
| 429 | 438 | ||
| 430 | ARM_Interface& System::CurrentArmInterface() { | 439 | ARM_Interface& System::CurrentArmInterface() { |
| 431 | return CurrentCpuCore().ArmInterface(); | 440 | return impl->CurrentPhysicalCore().ArmInterface(); |
| 432 | } | 441 | } |
| 433 | 442 | ||
| 434 | const ARM_Interface& System::CurrentArmInterface() const { | 443 | const ARM_Interface& System::CurrentArmInterface() const { |
| 435 | return CurrentCpuCore().ArmInterface(); | 444 | return impl->CurrentPhysicalCore().ArmInterface(); |
| 436 | } | 445 | } |
| 437 | 446 | ||
| 438 | std::size_t System::CurrentCoreIndex() const { | 447 | std::size_t System::CurrentCoreIndex() const { |
| 439 | return CurrentCpuCore().CoreIndex(); | 448 | return impl->cpu_manager.GetActiveCoreIndex(); |
| 440 | } | 449 | } |
| 441 | 450 | ||
| 442 | Kernel::Scheduler& System::CurrentScheduler() { | 451 | Kernel::Scheduler& System::CurrentScheduler() { |
| 443 | return CurrentCpuCore().Scheduler(); | 452 | return impl->CurrentPhysicalCore().Scheduler(); |
| 444 | } | 453 | } |
| 445 | 454 | ||
| 446 | const Kernel::Scheduler& System::CurrentScheduler() const { | 455 | const Kernel::Scheduler& System::CurrentScheduler() const { |
| 447 | return CurrentCpuCore().Scheduler(); | 456 | return impl->CurrentPhysicalCore().Scheduler(); |
| 448 | } | 457 | } |
| 449 | 458 | ||
| 450 | Kernel::Scheduler& System::Scheduler(std::size_t core_index) { | 459 | Kernel::Scheduler& System::Scheduler(std::size_t core_index) { |
| 451 | return CpuCore(core_index).Scheduler(); | 460 | return impl->GetPhysicalCore(core_index).Scheduler(); |
| 452 | } | 461 | } |
| 453 | 462 | ||
| 454 | const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { | 463 | const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const { |
| 455 | return CpuCore(core_index).Scheduler(); | 464 | return impl->GetPhysicalCore(core_index).Scheduler(); |
| 456 | } | 465 | } |
| 457 | 466 | ||
| 458 | /// Gets the global scheduler | 467 | /// Gets the global scheduler |
| @@ -474,28 +483,28 @@ const Kernel::Process* System::CurrentProcess() const { | |||
| 474 | } | 483 | } |
| 475 | 484 | ||
| 476 | ARM_Interface& System::ArmInterface(std::size_t core_index) { | 485 | ARM_Interface& System::ArmInterface(std::size_t core_index) { |
| 477 | return CpuCore(core_index).ArmInterface(); | 486 | return impl->GetPhysicalCore(core_index).ArmInterface(); |
| 478 | } | 487 | } |
| 479 | 488 | ||
| 480 | const ARM_Interface& System::ArmInterface(std::size_t core_index) const { | 489 | const ARM_Interface& System::ArmInterface(std::size_t core_index) const { |
| 481 | return CpuCore(core_index).ArmInterface(); | 490 | return impl->GetPhysicalCore(core_index).ArmInterface(); |
| 482 | } | 491 | } |
| 483 | 492 | ||
| 484 | Cpu& System::CpuCore(std::size_t core_index) { | 493 | CoreManager& System::GetCoreManager(std::size_t core_index) { |
| 485 | return impl->cpu_core_manager.GetCore(core_index); | 494 | return impl->cpu_manager.GetCoreManager(core_index); |
| 486 | } | 495 | } |
| 487 | 496 | ||
| 488 | const Cpu& System::CpuCore(std::size_t core_index) const { | 497 | const CoreManager& System::GetCoreManager(std::size_t core_index) const { |
| 489 | ASSERT(core_index < NUM_CPU_CORES); | 498 | ASSERT(core_index < NUM_CPU_CORES); |
| 490 | return impl->cpu_core_manager.GetCore(core_index); | 499 | return impl->cpu_manager.GetCoreManager(core_index); |
| 491 | } | 500 | } |
| 492 | 501 | ||
| 493 | ExclusiveMonitor& System::Monitor() { | 502 | ExclusiveMonitor& System::Monitor() { |
| 494 | return impl->cpu_core_manager.GetExclusiveMonitor(); | 503 | return impl->kernel.GetExclusiveMonitor(); |
| 495 | } | 504 | } |
| 496 | 505 | ||
| 497 | const ExclusiveMonitor& System::Monitor() const { | 506 | const ExclusiveMonitor& System::Monitor() const { |
| 498 | return impl->cpu_core_manager.GetExclusiveMonitor(); | 507 | return impl->kernel.GetExclusiveMonitor(); |
| 499 | } | 508 | } |
| 500 | 509 | ||
| 501 | Memory::Memory& System::Memory() { | 510 | Memory::Memory& System::Memory() { |
diff --git a/src/core/core.h b/src/core/core.h index e240c5c58..e69d68fcf 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -93,7 +93,7 @@ class Memory; | |||
| 93 | namespace Core { | 93 | namespace Core { |
| 94 | 94 | ||
| 95 | class ARM_Interface; | 95 | class ARM_Interface; |
| 96 | class Cpu; | 96 | class CoreManager; |
| 97 | class ExclusiveMonitor; | 97 | class ExclusiveMonitor; |
| 98 | class FrameLimiter; | 98 | class FrameLimiter; |
| 99 | class PerfStats; | 99 | class PerfStats; |
| @@ -218,10 +218,10 @@ public: | |||
| 218 | const ARM_Interface& ArmInterface(std::size_t core_index) const; | 218 | const ARM_Interface& ArmInterface(std::size_t core_index) const; |
| 219 | 219 | ||
| 220 | /// Gets a CPU interface to the CPU core with the specified index | 220 | /// Gets a CPU interface to the CPU core with the specified index |
| 221 | Cpu& CpuCore(std::size_t core_index); | 221 | CoreManager& GetCoreManager(std::size_t core_index); |
| 222 | 222 | ||
| 223 | /// Gets a CPU interface to the CPU core with the specified index | 223 | /// Gets a CPU interface to the CPU core with the specified index |
| 224 | const Cpu& CpuCore(std::size_t core_index) const; | 224 | const CoreManager& GetCoreManager(std::size_t core_index) const; |
| 225 | 225 | ||
| 226 | /// Gets a reference to the exclusive monitor | 226 | /// Gets a reference to the exclusive monitor |
| 227 | ExclusiveMonitor& Monitor(); | 227 | ExclusiveMonitor& Monitor(); |
| @@ -364,10 +364,10 @@ private: | |||
| 364 | System(); | 364 | System(); |
| 365 | 365 | ||
| 366 | /// Returns the currently running CPU core | 366 | /// Returns the currently running CPU core |
| 367 | Cpu& CurrentCpuCore(); | 367 | CoreManager& CurrentCoreManager(); |
| 368 | 368 | ||
| 369 | /// Returns the currently running CPU core | 369 | /// Returns the currently running CPU core |
| 370 | const Cpu& CurrentCpuCore() const; | 370 | const CoreManager& CurrentCoreManager() const; |
| 371 | 371 | ||
| 372 | /** | 372 | /** |
| 373 | * Initialize the emulated system. | 373 | * Initialize the emulated system. |
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp deleted file mode 100644 index 630cd4feb..000000000 --- a/src/core/core_cpu.cpp +++ /dev/null | |||
| @@ -1,127 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <condition_variable> | ||
| 6 | #include <mutex> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #ifdef ARCHITECTURE_x86_64 | ||
| 10 | #include "core/arm/dynarmic/arm_dynarmic.h" | ||
| 11 | #endif | ||
| 12 | #include "core/arm/exclusive_monitor.h" | ||
| 13 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/core_cpu.h" | ||
| 16 | #include "core/core_timing.h" | ||
| 17 | #include "core/hle/kernel/scheduler.h" | ||
| 18 | #include "core/hle/kernel/thread.h" | ||
| 19 | #include "core/hle/lock.h" | ||
| 20 | #include "core/settings.h" | ||
| 21 | |||
| 22 | namespace Core { | ||
| 23 | |||
| 24 | void CpuBarrier::NotifyEnd() { | ||
| 25 | std::unique_lock lock{mutex}; | ||
| 26 | end = true; | ||
| 27 | condition.notify_all(); | ||
| 28 | } | ||
| 29 | |||
| 30 | bool CpuBarrier::Rendezvous() { | ||
| 31 | if (!Settings::values.use_multi_core) { | ||
| 32 | // Meaningless when running in single-core mode | ||
| 33 | return true; | ||
| 34 | } | ||
| 35 | |||
| 36 | if (!end) { | ||
| 37 | std::unique_lock lock{mutex}; | ||
| 38 | |||
| 39 | --cores_waiting; | ||
| 40 | if (!cores_waiting) { | ||
| 41 | cores_waiting = NUM_CPU_CORES; | ||
| 42 | condition.notify_all(); | ||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | condition.wait(lock); | ||
| 47 | return true; | ||
| 48 | } | ||
| 49 | |||
| 50 | return false; | ||
| 51 | } | ||
| 52 | |||
| 53 | Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, | ||
| 54 | std::size_t core_index) | ||
| 55 | : cpu_barrier{cpu_barrier}, global_scheduler{system.GlobalScheduler()}, | ||
| 56 | core_timing{system.CoreTiming()}, core_index{core_index} { | ||
| 57 | #ifdef ARCHITECTURE_x86_64 | ||
| 58 | arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); | ||
| 59 | #else | ||
| 60 | arm_interface = std::make_unique<ARM_Unicorn>(system); | ||
| 61 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||
| 62 | #endif | ||
| 63 | |||
| 64 | scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index); | ||
| 65 | } | ||
| 66 | |||
| 67 | Cpu::~Cpu() = default; | ||
| 68 | |||
| 69 | std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor( | ||
| 70 | [[maybe_unused]] Memory::Memory& memory, [[maybe_unused]] std::size_t num_cores) { | ||
| 71 | #ifdef ARCHITECTURE_x86_64 | ||
| 72 | return std::make_unique<DynarmicExclusiveMonitor>(memory, num_cores); | ||
| 73 | #else | ||
| 74 | // TODO(merry): Passthrough exclusive monitor | ||
| 75 | return nullptr; | ||
| 76 | #endif | ||
| 77 | } | ||
| 78 | |||
| 79 | void Cpu::RunLoop(bool tight_loop) { | ||
| 80 | // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step | ||
| 81 | if (!cpu_barrier.Rendezvous()) { | ||
| 82 | // If rendezvous failed, session has been killed | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | Reschedule(); | ||
| 87 | |||
| 88 | // If we don't have a currently active thread then don't execute instructions, | ||
| 89 | // instead advance to the next event and try to yield to the next thread | ||
| 90 | if (Kernel::GetCurrentThread() == nullptr) { | ||
| 91 | LOG_TRACE(Core, "Core-{} idling", core_index); | ||
| 92 | core_timing.Idle(); | ||
| 93 | } else { | ||
| 94 | if (tight_loop) { | ||
| 95 | arm_interface->Run(); | ||
| 96 | } else { | ||
| 97 | arm_interface->Step(); | ||
| 98 | } | ||
| 99 | // We are stopping a run, exclusive state must be cleared | ||
| 100 | arm_interface->ClearExclusiveState(); | ||
| 101 | } | ||
| 102 | core_timing.Advance(); | ||
| 103 | |||
| 104 | Reschedule(); | ||
| 105 | } | ||
| 106 | |||
| 107 | void Cpu::SingleStep() { | ||
| 108 | return RunLoop(false); | ||
| 109 | } | ||
| 110 | |||
| 111 | void Cpu::PrepareReschedule() { | ||
| 112 | arm_interface->PrepareReschedule(); | ||
| 113 | } | ||
| 114 | |||
| 115 | void Cpu::Reschedule() { | ||
| 116 | // Lock the global kernel mutex when we manipulate the HLE state | ||
| 117 | std::lock_guard lock(HLE::g_hle_lock); | ||
| 118 | |||
| 119 | global_scheduler.SelectThread(core_index); | ||
| 120 | scheduler->TryDoContextSwitch(); | ||
| 121 | } | ||
| 122 | |||
| 123 | void Cpu::Shutdown() { | ||
| 124 | scheduler->Shutdown(); | ||
| 125 | } | ||
| 126 | |||
| 127 | } // namespace Core | ||
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h deleted file mode 100644 index 78f5021a2..000000000 --- a/src/core/core_cpu.h +++ /dev/null | |||
| @@ -1,120 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <condition_variable> | ||
| 9 | #include <cstddef> | ||
| 10 | #include <memory> | ||
| 11 | #include <mutex> | ||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | class GlobalScheduler; | ||
| 16 | class Scheduler; | ||
| 17 | } // namespace Kernel | ||
| 18 | |||
| 19 | namespace Core { | ||
| 20 | class System; | ||
| 21 | } | ||
| 22 | |||
| 23 | namespace Core::Timing { | ||
| 24 | class CoreTiming; | ||
| 25 | } | ||
| 26 | |||
| 27 | namespace Memory { | ||
| 28 | class Memory; | ||
| 29 | } | ||
| 30 | |||
| 31 | namespace Core { | ||
| 32 | |||
| 33 | class ARM_Interface; | ||
| 34 | class ExclusiveMonitor; | ||
| 35 | |||
| 36 | constexpr unsigned NUM_CPU_CORES{4}; | ||
| 37 | |||
| 38 | class CpuBarrier { | ||
| 39 | public: | ||
| 40 | bool IsAlive() const { | ||
| 41 | return !end; | ||
| 42 | } | ||
| 43 | |||
| 44 | void NotifyEnd(); | ||
| 45 | |||
| 46 | bool Rendezvous(); | ||
| 47 | |||
| 48 | private: | ||
| 49 | unsigned cores_waiting{NUM_CPU_CORES}; | ||
| 50 | std::mutex mutex; | ||
| 51 | std::condition_variable condition; | ||
| 52 | std::atomic<bool> end{}; | ||
| 53 | }; | ||
| 54 | |||
| 55 | class Cpu { | ||
| 56 | public: | ||
| 57 | Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, | ||
| 58 | std::size_t core_index); | ||
| 59 | ~Cpu(); | ||
| 60 | |||
| 61 | void RunLoop(bool tight_loop = true); | ||
| 62 | |||
| 63 | void SingleStep(); | ||
| 64 | |||
| 65 | void PrepareReschedule(); | ||
| 66 | |||
| 67 | ARM_Interface& ArmInterface() { | ||
| 68 | return *arm_interface; | ||
| 69 | } | ||
| 70 | |||
| 71 | const ARM_Interface& ArmInterface() const { | ||
| 72 | return *arm_interface; | ||
| 73 | } | ||
| 74 | |||
| 75 | Kernel::Scheduler& Scheduler() { | ||
| 76 | return *scheduler; | ||
| 77 | } | ||
| 78 | |||
| 79 | const Kernel::Scheduler& Scheduler() const { | ||
| 80 | return *scheduler; | ||
| 81 | } | ||
| 82 | |||
| 83 | bool IsMainCore() const { | ||
| 84 | return core_index == 0; | ||
| 85 | } | ||
| 86 | |||
| 87 | std::size_t CoreIndex() const { | ||
| 88 | return core_index; | ||
| 89 | } | ||
| 90 | |||
| 91 | void Shutdown(); | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Creates an exclusive monitor to handle exclusive reads/writes. | ||
| 95 | * | ||
| 96 | * @param memory The current memory subsystem that the monitor may wish | ||
| 97 | * to keep track of. | ||
| 98 | * | ||
| 99 | * @param num_cores The number of cores to assume about the CPU. | ||
| 100 | * | ||
| 101 | * @returns The constructed exclusive monitor instance, or nullptr if the current | ||
| 102 | * CPU backend is unable to use an exclusive monitor. | ||
| 103 | */ | ||
| 104 | static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, | ||
| 105 | std::size_t num_cores); | ||
| 106 | |||
| 107 | private: | ||
| 108 | void Reschedule(); | ||
| 109 | |||
| 110 | std::unique_ptr<ARM_Interface> arm_interface; | ||
| 111 | CpuBarrier& cpu_barrier; | ||
| 112 | Kernel::GlobalScheduler& global_scheduler; | ||
| 113 | std::unique_ptr<Kernel::Scheduler> scheduler; | ||
| 114 | Timing::CoreTiming& core_timing; | ||
| 115 | |||
| 116 | std::atomic<bool> reschedule_pending = false; | ||
| 117 | std::size_t core_index; | ||
| 118 | }; | ||
| 119 | |||
| 120 | } // namespace Core | ||
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp new file mode 100644 index 000000000..8eacf92dd --- /dev/null +++ b/src/core/core_manager.cpp | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <condition_variable> | ||
| 6 | #include <mutex> | ||
| 7 | |||
| 8 | #include "common/logging/log.h" | ||
| 9 | #ifdef ARCHITECTURE_x86_64 | ||
| 10 | #include "core/arm/dynarmic/arm_dynarmic.h" | ||
| 11 | #endif | ||
| 12 | #include "core/arm/exclusive_monitor.h" | ||
| 13 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/core_manager.h" | ||
| 16 | #include "core/core_timing.h" | ||
| 17 | #include "core/hle/kernel/kernel.h" | ||
| 18 | #include "core/hle/kernel/physical_core.h" | ||
| 19 | #include "core/hle/kernel/scheduler.h" | ||
| 20 | #include "core/hle/kernel/thread.h" | ||
| 21 | #include "core/hle/lock.h" | ||
| 22 | #include "core/settings.h" | ||
| 23 | |||
| 24 | namespace Core { | ||
| 25 | |||
| 26 | CoreManager::CoreManager(System& system, std::size_t core_index) | ||
| 27 | : global_scheduler{system.GlobalScheduler()}, physical_core{system.Kernel().PhysicalCore( | ||
| 28 | core_index)}, | ||
| 29 | core_timing{system.CoreTiming()}, core_index{core_index} {} | ||
| 30 | |||
| 31 | CoreManager::~CoreManager() = default; | ||
| 32 | |||
| 33 | void CoreManager::RunLoop(bool tight_loop) { | ||
| 34 | Reschedule(); | ||
| 35 | |||
| 36 | // If we don't have a currently active thread then don't execute instructions, | ||
| 37 | // instead advance to the next event and try to yield to the next thread | ||
| 38 | if (Kernel::GetCurrentThread() == nullptr) { | ||
| 39 | LOG_TRACE(Core, "Core-{} idling", core_index); | ||
| 40 | core_timing.Idle(); | ||
| 41 | } else { | ||
| 42 | if (tight_loop) { | ||
| 43 | physical_core.Run(); | ||
| 44 | } else { | ||
| 45 | physical_core.Step(); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | core_timing.Advance(); | ||
| 49 | |||
| 50 | Reschedule(); | ||
| 51 | } | ||
| 52 | |||
| 53 | void CoreManager::SingleStep() { | ||
| 54 | return RunLoop(false); | ||
| 55 | } | ||
| 56 | |||
| 57 | void CoreManager::PrepareReschedule() { | ||
| 58 | physical_core.Stop(); | ||
| 59 | } | ||
| 60 | |||
| 61 | void CoreManager::Reschedule() { | ||
| 62 | // Lock the global kernel mutex when we manipulate the HLE state | ||
| 63 | std::lock_guard lock(HLE::g_hle_lock); | ||
| 64 | |||
| 65 | global_scheduler.SelectThread(core_index); | ||
| 66 | |||
| 67 | physical_core.Scheduler().TryDoContextSwitch(); | ||
| 68 | } | ||
| 69 | |||
| 70 | } // namespace Core | ||
diff --git a/src/core/core_manager.h b/src/core/core_manager.h new file mode 100644 index 000000000..b14e723d7 --- /dev/null +++ b/src/core/core_manager.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <cstddef> | ||
| 9 | #include <memory> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Kernel { | ||
| 13 | class GlobalScheduler; | ||
| 14 | class PhysicalCore; | ||
| 15 | } // namespace Kernel | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Core::Timing { | ||
| 22 | class CoreTiming; | ||
| 23 | } | ||
| 24 | |||
| 25 | namespace Memory { | ||
| 26 | class Memory; | ||
| 27 | } | ||
| 28 | |||
| 29 | namespace Core { | ||
| 30 | |||
| 31 | constexpr unsigned NUM_CPU_CORES{4}; | ||
| 32 | |||
| 33 | class CoreManager { | ||
| 34 | public: | ||
| 35 | CoreManager(System& system, std::size_t core_index); | ||
| 36 | ~CoreManager(); | ||
| 37 | |||
| 38 | void RunLoop(bool tight_loop = true); | ||
| 39 | |||
| 40 | void SingleStep(); | ||
| 41 | |||
| 42 | void PrepareReschedule(); | ||
| 43 | |||
| 44 | bool IsMainCore() const { | ||
| 45 | return core_index == 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | std::size_t CoreIndex() const { | ||
| 49 | return core_index; | ||
| 50 | } | ||
| 51 | |||
| 52 | private: | ||
| 53 | void Reschedule(); | ||
| 54 | |||
| 55 | Kernel::GlobalScheduler& global_scheduler; | ||
| 56 | Kernel::PhysicalCore& physical_core; | ||
| 57 | Timing::CoreTiming& core_timing; | ||
| 58 | |||
| 59 | std::atomic<bool> reschedule_pending = false; | ||
| 60 | std::size_t core_index; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } // namespace Core | ||
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index aa09fa453..46d4178c4 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/thread.h" | 13 | #include "common/thread.h" |
| 14 | #include "core/core_timing_util.h" | 14 | #include "core/core_timing_util.h" |
| 15 | #include "core/hardware_properties.h" | ||
| 15 | 16 | ||
| 16 | namespace Core::Timing { | 17 | namespace Core::Timing { |
| 17 | 18 | ||
| @@ -215,7 +216,7 @@ void CoreTiming::Idle() { | |||
| 215 | } | 216 | } |
| 216 | 217 | ||
| 217 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { | 218 | std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { |
| 218 | return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE}; | 219 | return std::chrono::microseconds{GetTicks() * 1000000 / Hardware::BASE_CLOCK_RATE}; |
| 219 | } | 220 | } |
| 220 | 221 | ||
| 221 | s64 CoreTiming::GetDowncount() const { | 222 | s64 CoreTiming::GetDowncount() const { |
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index a10472a95..de50d3b14 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | namespace Core::Timing { | 12 | namespace Core::Timing { |
| 13 | 13 | ||
| 14 | constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / BASE_CLOCK_RATE; | 14 | constexpr u64 MAX_VALUE_TO_MULTIPLY = std::numeric_limits<s64>::max() / Hardware::BASE_CLOCK_RATE; |
| 15 | 15 | ||
| 16 | s64 msToCycles(std::chrono::milliseconds ms) { | 16 | s64 msToCycles(std::chrono::milliseconds ms) { |
| 17 | if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) { | 17 | if (static_cast<u64>(ms.count() / 1000) > MAX_VALUE_TO_MULTIPLY) { |
| @@ -20,9 +20,9 @@ s64 msToCycles(std::chrono::milliseconds ms) { | |||
| 20 | } | 20 | } |
| 21 | if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) { | 21 | if (static_cast<u64>(ms.count()) > MAX_VALUE_TO_MULTIPLY) { |
| 22 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | 22 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); |
| 23 | return BASE_CLOCK_RATE * (ms.count() / 1000); | 23 | return Hardware::BASE_CLOCK_RATE * (ms.count() / 1000); |
| 24 | } | 24 | } |
| 25 | return (BASE_CLOCK_RATE * ms.count()) / 1000; | 25 | return (Hardware::BASE_CLOCK_RATE * ms.count()) / 1000; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | s64 usToCycles(std::chrono::microseconds us) { | 28 | s64 usToCycles(std::chrono::microseconds us) { |
| @@ -32,9 +32,9 @@ s64 usToCycles(std::chrono::microseconds us) { | |||
| 32 | } | 32 | } |
| 33 | if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) { | 33 | if (static_cast<u64>(us.count()) > MAX_VALUE_TO_MULTIPLY) { |
| 34 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | 34 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); |
| 35 | return BASE_CLOCK_RATE * (us.count() / 1000000); | 35 | return Hardware::BASE_CLOCK_RATE * (us.count() / 1000000); |
| 36 | } | 36 | } |
| 37 | return (BASE_CLOCK_RATE * us.count()) / 1000000; | 37 | return (Hardware::BASE_CLOCK_RATE * us.count()) / 1000000; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | s64 nsToCycles(std::chrono::nanoseconds ns) { | 40 | s64 nsToCycles(std::chrono::nanoseconds ns) { |
| @@ -44,14 +44,14 @@ s64 nsToCycles(std::chrono::nanoseconds ns) { | |||
| 44 | } | 44 | } |
| 45 | if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) { | 45 | if (static_cast<u64>(ns.count()) > MAX_VALUE_TO_MULTIPLY) { |
| 46 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); | 46 | LOG_DEBUG(Core_Timing, "Time very big, do rounding"); |
| 47 | return BASE_CLOCK_RATE * (ns.count() / 1000000000); | 47 | return Hardware::BASE_CLOCK_RATE * (ns.count() / 1000000000); |
| 48 | } | 48 | } |
| 49 | return (BASE_CLOCK_RATE * ns.count()) / 1000000000; | 49 | return (Hardware::BASE_CLOCK_RATE * ns.count()) / 1000000000; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | u64 CpuCyclesToClockCycles(u64 ticks) { | 52 | u64 CpuCyclesToClockCycles(u64 ticks) { |
| 53 | const u128 temporal = Common::Multiply64Into128(ticks, CNTFREQ); | 53 | const u128 temporal = Common::Multiply64Into128(ticks, Hardware::CNTFREQ); |
| 54 | return Common::Divide128On32(temporal, static_cast<u32>(BASE_CLOCK_RATE)).first; | 54 | return Common::Divide128On32(temporal, static_cast<u32>(Hardware::BASE_CLOCK_RATE)).first; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | } // namespace Core::Timing | 57 | } // namespace Core::Timing |
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index cdd84d70f..addc72b19 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h | |||
| @@ -6,28 +6,24 @@ | |||
| 6 | 6 | ||
| 7 | #include <chrono> | 7 | #include <chrono> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hardware_properties.h" | ||
| 9 | 10 | ||
| 10 | namespace Core::Timing { | 11 | namespace Core::Timing { |
| 11 | 12 | ||
| 12 | // The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz | ||
| 13 | // The exact value used is of course unverified. | ||
| 14 | constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch clock speed is 1020MHz un/docked | ||
| 15 | constexpr u64 CNTFREQ = 19200000; // Value from fusee. | ||
| 16 | |||
| 17 | s64 msToCycles(std::chrono::milliseconds ms); | 13 | s64 msToCycles(std::chrono::milliseconds ms); |
| 18 | s64 usToCycles(std::chrono::microseconds us); | 14 | s64 usToCycles(std::chrono::microseconds us); |
| 19 | s64 nsToCycles(std::chrono::nanoseconds ns); | 15 | s64 nsToCycles(std::chrono::nanoseconds ns); |
| 20 | 16 | ||
| 21 | inline std::chrono::milliseconds CyclesToMs(s64 cycles) { | 17 | inline std::chrono::milliseconds CyclesToMs(s64 cycles) { |
| 22 | return std::chrono::milliseconds(cycles * 1000 / BASE_CLOCK_RATE); | 18 | return std::chrono::milliseconds(cycles * 1000 / Hardware::BASE_CLOCK_RATE); |
| 23 | } | 19 | } |
| 24 | 20 | ||
| 25 | inline std::chrono::nanoseconds CyclesToNs(s64 cycles) { | 21 | inline std::chrono::nanoseconds CyclesToNs(s64 cycles) { |
| 26 | return std::chrono::nanoseconds(cycles * 1000000000 / BASE_CLOCK_RATE); | 22 | return std::chrono::nanoseconds(cycles * 1000000000 / Hardware::BASE_CLOCK_RATE); |
| 27 | } | 23 | } |
| 28 | 24 | ||
| 29 | inline std::chrono::microseconds CyclesToUs(s64 cycles) { | 25 | inline std::chrono::microseconds CyclesToUs(s64 cycles) { |
| 30 | return std::chrono::microseconds(cycles * 1000000 / BASE_CLOCK_RATE); | 26 | return std::chrono::microseconds(cycles * 1000000 / Hardware::BASE_CLOCK_RATE); |
| 31 | } | 27 | } |
| 32 | 28 | ||
| 33 | u64 CpuCyclesToClockCycles(u64 ticks); | 29 | u64 CpuCyclesToClockCycles(u64 ticks); |
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp deleted file mode 100644 index f04a34133..000000000 --- a/src/core/cpu_core_manager.cpp +++ /dev/null | |||
| @@ -1,152 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "core/arm/exclusive_monitor.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/core_cpu.h" | ||
| 9 | #include "core/core_timing.h" | ||
| 10 | #include "core/cpu_core_manager.h" | ||
| 11 | #include "core/gdbstub/gdbstub.h" | ||
| 12 | #include "core/settings.h" | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | namespace { | ||
| 16 | void RunCpuCore(const System& system, Cpu& cpu_state) { | ||
| 17 | while (system.IsPoweredOn()) { | ||
| 18 | cpu_state.RunLoop(true); | ||
| 19 | } | ||
| 20 | } | ||
| 21 | } // Anonymous namespace | ||
| 22 | |||
| 23 | CpuCoreManager::CpuCoreManager(System& system) : system{system} {} | ||
| 24 | CpuCoreManager::~CpuCoreManager() = default; | ||
| 25 | |||
| 26 | void CpuCoreManager::Initialize() { | ||
| 27 | barrier = std::make_unique<CpuBarrier>(); | ||
| 28 | exclusive_monitor = Cpu::MakeExclusiveMonitor(system.Memory(), cores.size()); | ||
| 29 | |||
| 30 | for (std::size_t index = 0; index < cores.size(); ++index) { | ||
| 31 | cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | void CpuCoreManager::StartThreads() { | ||
| 36 | // Create threads for CPU cores 1-3, and build thread_to_cpu map | ||
| 37 | // CPU core 0 is run on the main thread | ||
| 38 | thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); | ||
| 39 | if (!Settings::values.use_multi_core) { | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | |||
| 43 | for (std::size_t index = 0; index < core_threads.size(); ++index) { | ||
| 44 | core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system), | ||
| 45 | std::ref(*cores[index + 1])); | ||
| 46 | thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get(); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void CpuCoreManager::Shutdown() { | ||
| 51 | barrier->NotifyEnd(); | ||
| 52 | if (Settings::values.use_multi_core) { | ||
| 53 | for (auto& thread : core_threads) { | ||
| 54 | thread->join(); | ||
| 55 | thread.reset(); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | thread_to_cpu.clear(); | ||
| 60 | for (auto& cpu_core : cores) { | ||
| 61 | cpu_core->Shutdown(); | ||
| 62 | cpu_core.reset(); | ||
| 63 | } | ||
| 64 | |||
| 65 | exclusive_monitor.reset(); | ||
| 66 | barrier.reset(); | ||
| 67 | } | ||
| 68 | |||
| 69 | Cpu& CpuCoreManager::GetCore(std::size_t index) { | ||
| 70 | return *cores.at(index); | ||
| 71 | } | ||
| 72 | |||
| 73 | const Cpu& CpuCoreManager::GetCore(std::size_t index) const { | ||
| 74 | return *cores.at(index); | ||
| 75 | } | ||
| 76 | |||
| 77 | ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() { | ||
| 78 | return *exclusive_monitor; | ||
| 79 | } | ||
| 80 | |||
| 81 | const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const { | ||
| 82 | return *exclusive_monitor; | ||
| 83 | } | ||
| 84 | |||
| 85 | Cpu& CpuCoreManager::GetCurrentCore() { | ||
| 86 | if (Settings::values.use_multi_core) { | ||
| 87 | const auto& search = thread_to_cpu.find(std::this_thread::get_id()); | ||
| 88 | ASSERT(search != thread_to_cpu.end()); | ||
| 89 | ASSERT(search->second); | ||
| 90 | return *search->second; | ||
| 91 | } | ||
| 92 | |||
| 93 | // Otherwise, use single-threaded mode active_core variable | ||
| 94 | return *cores[active_core]; | ||
| 95 | } | ||
| 96 | |||
| 97 | const Cpu& CpuCoreManager::GetCurrentCore() const { | ||
| 98 | if (Settings::values.use_multi_core) { | ||
| 99 | const auto& search = thread_to_cpu.find(std::this_thread::get_id()); | ||
| 100 | ASSERT(search != thread_to_cpu.end()); | ||
| 101 | ASSERT(search->second); | ||
| 102 | return *search->second; | ||
| 103 | } | ||
| 104 | |||
| 105 | // Otherwise, use single-threaded mode active_core variable | ||
| 106 | return *cores[active_core]; | ||
| 107 | } | ||
| 108 | |||
| 109 | void CpuCoreManager::RunLoop(bool tight_loop) { | ||
| 110 | // Update thread_to_cpu in case Core 0 is run from a different host thread | ||
| 111 | thread_to_cpu[std::this_thread::get_id()] = cores[0].get(); | ||
| 112 | |||
| 113 | if (GDBStub::IsServerEnabled()) { | ||
| 114 | GDBStub::HandlePacket(); | ||
| 115 | |||
| 116 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | ||
| 117 | // execute. Otherwise, get out of the loop function. | ||
| 118 | if (GDBStub::GetCpuHaltFlag()) { | ||
| 119 | if (GDBStub::GetCpuStepFlag()) { | ||
| 120 | tight_loop = false; | ||
| 121 | } else { | ||
| 122 | return; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | auto& core_timing = system.CoreTiming(); | ||
| 128 | core_timing.ResetRun(); | ||
| 129 | bool keep_running{}; | ||
| 130 | do { | ||
| 131 | keep_running = false; | ||
| 132 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | ||
| 133 | core_timing.SwitchContext(active_core); | ||
| 134 | if (core_timing.CanCurrentContextRun()) { | ||
| 135 | cores[active_core]->RunLoop(tight_loop); | ||
| 136 | } | ||
| 137 | keep_running |= core_timing.CanCurrentContextRun(); | ||
| 138 | } | ||
| 139 | } while (keep_running); | ||
| 140 | |||
| 141 | if (GDBStub::IsServerEnabled()) { | ||
| 142 | GDBStub::SetCpuStepFlag(false); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | void CpuCoreManager::InvalidateAllInstructionCaches() { | ||
| 147 | for (auto& cpu : cores) { | ||
| 148 | cpu->ArmInterface().ClearInstructionCache(); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | } // namespace Core | ||
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h deleted file mode 100644 index 2cbbf8216..000000000 --- a/src/core/cpu_core_manager.h +++ /dev/null | |||
| @@ -1,62 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <map> | ||
| 9 | #include <memory> | ||
| 10 | #include <thread> | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | |||
| 14 | class Cpu; | ||
| 15 | class CpuBarrier; | ||
| 16 | class ExclusiveMonitor; | ||
| 17 | class System; | ||
| 18 | |||
| 19 | class CpuCoreManager { | ||
| 20 | public: | ||
| 21 | explicit CpuCoreManager(System& system); | ||
| 22 | CpuCoreManager(const CpuCoreManager&) = delete; | ||
| 23 | CpuCoreManager(CpuCoreManager&&) = delete; | ||
| 24 | |||
| 25 | ~CpuCoreManager(); | ||
| 26 | |||
| 27 | CpuCoreManager& operator=(const CpuCoreManager&) = delete; | ||
| 28 | CpuCoreManager& operator=(CpuCoreManager&&) = delete; | ||
| 29 | |||
| 30 | void Initialize(); | ||
| 31 | void StartThreads(); | ||
| 32 | void Shutdown(); | ||
| 33 | |||
| 34 | Cpu& GetCore(std::size_t index); | ||
| 35 | const Cpu& GetCore(std::size_t index) const; | ||
| 36 | |||
| 37 | Cpu& GetCurrentCore(); | ||
| 38 | const Cpu& GetCurrentCore() const; | ||
| 39 | |||
| 40 | ExclusiveMonitor& GetExclusiveMonitor(); | ||
| 41 | const ExclusiveMonitor& GetExclusiveMonitor() const; | ||
| 42 | |||
| 43 | void RunLoop(bool tight_loop); | ||
| 44 | |||
| 45 | void InvalidateAllInstructionCaches(); | ||
| 46 | |||
| 47 | private: | ||
| 48 | static constexpr std::size_t NUM_CPU_CORES = 4; | ||
| 49 | |||
| 50 | std::unique_ptr<ExclusiveMonitor> exclusive_monitor; | ||
| 51 | std::unique_ptr<CpuBarrier> barrier; | ||
| 52 | std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores; | ||
| 53 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads; | ||
| 54 | std::size_t active_core{}; ///< Active core, only used in single thread mode | ||
| 55 | |||
| 56 | /// Map of guest threads to CPU cores | ||
| 57 | std::map<std::thread::id, Cpu*> thread_to_cpu; | ||
| 58 | |||
| 59 | System& system; | ||
| 60 | }; | ||
| 61 | |||
| 62 | } // namespace Core | ||
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp new file mode 100644 index 000000000..70ddbdcca --- /dev/null +++ b/src/core/cpu_manager.cpp | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/arm/exclusive_monitor.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/core_manager.h" | ||
| 8 | #include "core/core_timing.h" | ||
| 9 | #include "core/cpu_manager.h" | ||
| 10 | #include "core/gdbstub/gdbstub.h" | ||
| 11 | |||
| 12 | namespace Core { | ||
| 13 | |||
| 14 | CpuManager::CpuManager(System& system) : system{system} {} | ||
| 15 | CpuManager::~CpuManager() = default; | ||
| 16 | |||
| 17 | void CpuManager::Initialize() { | ||
| 18 | for (std::size_t index = 0; index < core_managers.size(); ++index) { | ||
| 19 | core_managers[index] = std::make_unique<CoreManager>(system, index); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | void CpuManager::Shutdown() { | ||
| 24 | for (auto& cpu_core : core_managers) { | ||
| 25 | cpu_core.reset(); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | CoreManager& CpuManager::GetCoreManager(std::size_t index) { | ||
| 30 | return *core_managers.at(index); | ||
| 31 | } | ||
| 32 | |||
| 33 | const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { | ||
| 34 | return *core_managers.at(index); | ||
| 35 | } | ||
| 36 | |||
| 37 | CoreManager& CpuManager::GetCurrentCoreManager() { | ||
| 38 | // Otherwise, use single-threaded mode active_core variable | ||
| 39 | return *core_managers[active_core]; | ||
| 40 | } | ||
| 41 | |||
| 42 | const CoreManager& CpuManager::GetCurrentCoreManager() const { | ||
| 43 | // Otherwise, use single-threaded mode active_core variable | ||
| 44 | return *core_managers[active_core]; | ||
| 45 | } | ||
| 46 | |||
| 47 | void CpuManager::RunLoop(bool tight_loop) { | ||
| 48 | if (GDBStub::IsServerEnabled()) { | ||
| 49 | GDBStub::HandlePacket(); | ||
| 50 | |||
| 51 | // If the loop is halted and we want to step, use a tiny (1) number of instructions to | ||
| 52 | // execute. Otherwise, get out of the loop function. | ||
| 53 | if (GDBStub::GetCpuHaltFlag()) { | ||
| 54 | if (GDBStub::GetCpuStepFlag()) { | ||
| 55 | tight_loop = false; | ||
| 56 | } else { | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | auto& core_timing = system.CoreTiming(); | ||
| 63 | core_timing.ResetRun(); | ||
| 64 | bool keep_running{}; | ||
| 65 | do { | ||
| 66 | keep_running = false; | ||
| 67 | for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { | ||
| 68 | core_timing.SwitchContext(active_core); | ||
| 69 | if (core_timing.CanCurrentContextRun()) { | ||
| 70 | core_managers[active_core]->RunLoop(tight_loop); | ||
| 71 | } | ||
| 72 | keep_running |= core_timing.CanCurrentContextRun(); | ||
| 73 | } | ||
| 74 | } while (keep_running); | ||
| 75 | |||
| 76 | if (GDBStub::IsServerEnabled()) { | ||
| 77 | GDBStub::SetCpuStepFlag(false); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | } // namespace Core | ||
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h new file mode 100644 index 000000000..97554d1bb --- /dev/null +++ b/src/core/cpu_manager.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <memory> | ||
| 9 | #include "core/hardware_properties.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | |||
| 13 | class CoreManager; | ||
| 14 | class System; | ||
| 15 | |||
| 16 | class CpuManager { | ||
| 17 | public: | ||
| 18 | explicit CpuManager(System& system); | ||
| 19 | CpuManager(const CpuManager&) = delete; | ||
| 20 | CpuManager(CpuManager&&) = delete; | ||
| 21 | |||
| 22 | ~CpuManager(); | ||
| 23 | |||
| 24 | CpuManager& operator=(const CpuManager&) = delete; | ||
| 25 | CpuManager& operator=(CpuManager&&) = delete; | ||
| 26 | |||
| 27 | void Initialize(); | ||
| 28 | void Shutdown(); | ||
| 29 | |||
| 30 | CoreManager& GetCoreManager(std::size_t index); | ||
| 31 | const CoreManager& GetCoreManager(std::size_t index) const; | ||
| 32 | |||
| 33 | CoreManager& GetCurrentCoreManager(); | ||
| 34 | const CoreManager& GetCurrentCoreManager() const; | ||
| 35 | |||
| 36 | std::size_t GetActiveCoreIndex() const { | ||
| 37 | return active_core; | ||
| 38 | } | ||
| 39 | |||
| 40 | void RunLoop(bool tight_loop); | ||
| 41 | |||
| 42 | private: | ||
| 43 | std::array<std::unique_ptr<CoreManager>, Hardware::NUM_CPU_CORES> core_managers; | ||
| 44 | std::size_t active_core{}; ///< Active core, only used in single thread mode | ||
| 45 | |||
| 46 | System& system; | ||
| 47 | }; | ||
| 48 | |||
| 49 | } // namespace Core | ||
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 4a9912641..3376eedc5 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -75,6 +75,13 @@ public: | |||
| 75 | return nullptr; | 75 | return nullptr; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | /// Returns if window is shown (not minimized) | ||
| 79 | virtual bool IsShown() const = 0; | ||
| 80 | |||
| 81 | /// Retrieves Vulkan specific handlers from the window | ||
| 82 | virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 83 | void* surface) const = 0; | ||
| 84 | |||
| 78 | /** | 85 | /** |
| 79 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) | 86 | * Signal that a touch pressed event has occurred (e.g. mouse click pressed) |
| 80 | * @param framebuffer_x Framebuffer x-coordinate that was pressed | 87 | * @param framebuffer_x Framebuffer x-coordinate that was pressed |
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index d6d2cf3f0..2dc795d56 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp | |||
| @@ -27,9 +27,9 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) { | |||
| 27 | // so just calculate them both even if the other isn't showing. | 27 | // so just calculate them both even if the other isn't showing. |
| 28 | FramebufferLayout res{width, height}; | 28 | FramebufferLayout res{width, height}; |
| 29 | 29 | ||
| 30 | const float emulation_aspect_ratio{static_cast<float>(ScreenUndocked::Height) / | 30 | const float window_aspect_ratio = static_cast<float>(height) / width; |
| 31 | ScreenUndocked::Width}; | 31 | const float emulation_aspect_ratio = EmulationAspectRatio( |
| 32 | const auto window_aspect_ratio = static_cast<float>(height) / width; | 32 | static_cast<AspectRatio>(Settings::values.aspect_ratio), window_aspect_ratio); |
| 33 | 33 | ||
| 34 | const Common::Rectangle<u32> screen_window_area{0, 0, width, height}; | 34 | const Common::Rectangle<u32> screen_window_area{0, 0, width, height}; |
| 35 | Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio); | 35 | Common::Rectangle<u32> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio); |
| @@ -58,4 +58,19 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) { | |||
| 58 | return DefaultFrameLayout(width, height); | 58 | return DefaultFrameLayout(width, height); |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio) { | ||
| 62 | switch (aspect) { | ||
| 63 | case AspectRatio::Default: | ||
| 64 | return static_cast<float>(ScreenUndocked::Height) / ScreenUndocked::Width; | ||
| 65 | case AspectRatio::R4_3: | ||
| 66 | return 3.0f / 4.0f; | ||
| 67 | case AspectRatio::R21_9: | ||
| 68 | return 9.0f / 21.0f; | ||
| 69 | case AspectRatio::StretchToWindow: | ||
| 70 | return window_aspect_ratio; | ||
| 71 | default: | ||
| 72 | return static_cast<float>(ScreenUndocked::Height) / ScreenUndocked::Width; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 61 | } // namespace Layout | 76 | } // namespace Layout |
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index d2370adde..1d39c1faf 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h | |||
| @@ -18,6 +18,13 @@ enum ScreenDocked : u32 { | |||
| 18 | HeightDocked = 1080, | 18 | HeightDocked = 1080, |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | enum class AspectRatio { | ||
| 22 | Default, | ||
| 23 | R4_3, | ||
| 24 | R21_9, | ||
| 25 | StretchToWindow, | ||
| 26 | }; | ||
| 27 | |||
| 21 | /// Describes the layout of the window framebuffer | 28 | /// Describes the layout of the window framebuffer |
| 22 | struct FramebufferLayout { | 29 | struct FramebufferLayout { |
| 23 | u32 width{ScreenUndocked::Width}; | 30 | u32 width{ScreenUndocked::Width}; |
| @@ -48,4 +55,12 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height); | |||
| 48 | */ | 55 | */ |
| 49 | FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale); | 56 | FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale); |
| 50 | 57 | ||
| 58 | /** | ||
| 59 | * Convenience method to determine emulation aspect ratio | ||
| 60 | * @param aspect Represents the index of aspect ratio stored in Settings::values.aspect_ratio | ||
| 61 | * @param window_aspect_ratio Current window aspect ratio | ||
| 62 | * @return Emulation render window aspect ratio | ||
| 63 | */ | ||
| 64 | float EmulationAspectRatio(AspectRatio aspect, float window_aspect_ratio); | ||
| 65 | |||
| 51 | } // namespace Layout | 66 | } // namespace Layout |
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 7c11d7546..2b098b7c6 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h | |||
| @@ -15,6 +15,13 @@ | |||
| 15 | 15 | ||
| 16 | namespace Input { | 16 | namespace Input { |
| 17 | 17 | ||
| 18 | enum class AnalogDirection : u8 { | ||
| 19 | RIGHT, | ||
| 20 | LEFT, | ||
| 21 | UP, | ||
| 22 | DOWN, | ||
| 23 | }; | ||
| 24 | |||
| 18 | /// An abstract class template for an input device (a button, an analog input, etc.). | 25 | /// An abstract class template for an input device (a button, an analog input, etc.). |
| 19 | template <typename StatusType> | 26 | template <typename StatusType> |
| 20 | class InputDevice { | 27 | class InputDevice { |
| @@ -23,6 +30,9 @@ public: | |||
| 23 | virtual StatusType GetStatus() const { | 30 | virtual StatusType GetStatus() const { |
| 24 | return {}; | 31 | return {}; |
| 25 | } | 32 | } |
| 33 | virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { | ||
| 34 | return {}; | ||
| 35 | } | ||
| 26 | }; | 36 | }; |
| 27 | 37 | ||
| 28 | /// An abstract class template for a factory that can create input devices. | 38 | /// An abstract class template for a factory that can create input devices. |
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 37cb28848..67e95999d 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -35,7 +35,7 @@ | |||
| 35 | #include "common/swap.h" | 35 | #include "common/swap.h" |
| 36 | #include "core/arm/arm_interface.h" | 36 | #include "core/arm/arm_interface.h" |
| 37 | #include "core/core.h" | 37 | #include "core/core.h" |
| 38 | #include "core/core_cpu.h" | 38 | #include "core/core_manager.h" |
| 39 | #include "core/gdbstub/gdbstub.h" | 39 | #include "core/gdbstub/gdbstub.h" |
| 40 | #include "core/hle/kernel/process.h" | 40 | #include "core/hle/kernel/process.h" |
| 41 | #include "core/hle/kernel/scheduler.h" | 41 | #include "core/hle/kernel/scheduler.h" |
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h new file mode 100644 index 000000000..213461b6a --- /dev/null +++ b/src/core/hardware_properties.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <tuple> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | |||
| 13 | namespace Hardware { | ||
| 14 | |||
| 15 | // The below clock rate is based on Switch's clockspeed being widely known as 1.020GHz | ||
| 16 | // The exact value used is of course unverified. | ||
| 17 | constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch cpu frequency is 1020MHz un/docked | ||
| 18 | constexpr u64 CNTFREQ = 19200000; // Switch's hardware clock speed | ||
| 19 | constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores | ||
| 20 | |||
| 21 | } // namespace Hardware | ||
| 22 | |||
| 23 | struct EmuThreadHandle { | ||
| 24 | u32 host_handle; | ||
| 25 | u32 guest_handle; | ||
| 26 | |||
| 27 | u64 GetRaw() const { | ||
| 28 | return (static_cast<u64>(host_handle) << 32) | guest_handle; | ||
| 29 | } | ||
| 30 | |||
| 31 | bool operator==(const EmuThreadHandle& rhs) const { | ||
| 32 | return std::tie(host_handle, guest_handle) == std::tie(rhs.host_handle, rhs.guest_handle); | ||
| 33 | } | ||
| 34 | |||
| 35 | bool operator!=(const EmuThreadHandle& rhs) const { | ||
| 36 | return !operator==(rhs); | ||
| 37 | } | ||
| 38 | |||
| 39 | static constexpr EmuThreadHandle InvalidHandle() { | ||
| 40 | constexpr u32 invalid_handle = 0xFFFFFFFF; | ||
| 41 | return {invalid_handle, invalid_handle}; | ||
| 42 | } | ||
| 43 | }; | ||
| 44 | |||
| 45 | } // namespace Core | ||
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index db189c8e3..8475b698c 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/core_cpu.h" | ||
| 12 | #include "core/hle/kernel/address_arbiter.h" | 11 | #include "core/hle/kernel/address_arbiter.h" |
| 13 | #include "core/hle/kernel/errors.h" | 12 | #include "core/hle/kernel/errors.h" |
| 14 | #include "core/hle/kernel/scheduler.h" | 13 | #include "core/hle/kernel/scheduler.h" |
| @@ -202,42 +201,39 @@ void AddressArbiter::HandleWakeupThread(std::shared_ptr<Thread> thread) { | |||
| 202 | void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) { | 201 | void AddressArbiter::InsertThread(std::shared_ptr<Thread> thread) { |
| 203 | const VAddr arb_addr = thread->GetArbiterWaitAddress(); | 202 | const VAddr arb_addr = thread->GetArbiterWaitAddress(); |
| 204 | std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; | 203 | std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; |
| 205 | auto it = thread_list.begin(); | 204 | |
| 206 | while (it != thread_list.end()) { | 205 | const auto iter = |
| 207 | const std::shared_ptr<Thread>& current_thread = *it; | 206 | std::find_if(thread_list.cbegin(), thread_list.cend(), [&thread](const auto& entry) { |
| 208 | if (current_thread->GetPriority() >= thread->GetPriority()) { | 207 | return entry->GetPriority() >= thread->GetPriority(); |
| 209 | thread_list.insert(it, thread); | 208 | }); |
| 210 | return; | 209 | |
| 211 | } | 210 | if (iter == thread_list.cend()) { |
| 212 | ++it; | 211 | thread_list.push_back(std::move(thread)); |
| 212 | } else { | ||
| 213 | thread_list.insert(iter, std::move(thread)); | ||
| 213 | } | 214 | } |
| 214 | thread_list.push_back(std::move(thread)); | ||
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) { | 217 | void AddressArbiter::RemoveThread(std::shared_ptr<Thread> thread) { |
| 218 | const VAddr arb_addr = thread->GetArbiterWaitAddress(); | 218 | const VAddr arb_addr = thread->GetArbiterWaitAddress(); |
| 219 | std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; | 219 | std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[arb_addr]; |
| 220 | auto it = thread_list.begin(); | 220 | |
| 221 | while (it != thread_list.end()) { | 221 | const auto iter = std::find_if(thread_list.cbegin(), thread_list.cend(), |
| 222 | const std::shared_ptr<Thread>& current_thread = *it; | 222 | [&thread](const auto& entry) { return thread == entry; }); |
| 223 | if (current_thread.get() == thread.get()) { | 223 | |
| 224 | thread_list.erase(it); | 224 | ASSERT(iter != thread_list.cend()); |
| 225 | return; | 225 | |
| 226 | } | 226 | thread_list.erase(iter); |
| 227 | ++it; | ||
| 228 | } | ||
| 229 | UNREACHABLE(); | ||
| 230 | } | 227 | } |
| 231 | 228 | ||
| 232 | std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) { | 229 | std::vector<std::shared_ptr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress( |
| 233 | std::vector<std::shared_ptr<Thread>> result; | 230 | VAddr address) const { |
| 234 | std::list<std::shared_ptr<Thread>>& thread_list = arb_threads[address]; | 231 | const auto iter = arb_threads.find(address); |
| 235 | auto it = thread_list.begin(); | 232 | if (iter == arb_threads.cend()) { |
| 236 | while (it != thread_list.end()) { | 233 | return {}; |
| 237 | std::shared_ptr<Thread> current_thread = *it; | ||
| 238 | result.push_back(std::move(current_thread)); | ||
| 239 | ++it; | ||
| 240 | } | 234 | } |
| 241 | return result; | 235 | |
| 236 | const std::list<std::shared_ptr<Thread>>& thread_list = iter->second; | ||
| 237 | return {thread_list.cbegin(), thread_list.cend()}; | ||
| 242 | } | 238 | } |
| 243 | } // namespace Kernel | 239 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 386983e54..f958eee5a 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h | |||
| @@ -86,7 +86,7 @@ private: | |||
| 86 | void RemoveThread(std::shared_ptr<Thread> thread); | 86 | void RemoveThread(std::shared_ptr<Thread> thread); |
| 87 | 87 | ||
| 88 | // Gets the threads waiting on an address. | 88 | // Gets the threads waiting on an address. |
| 89 | std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address); | 89 | std::vector<std::shared_ptr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const; |
| 90 | 90 | ||
| 91 | /// List of threads waiting for a address arbiter | 91 | /// List of threads waiting for a address arbiter |
| 92 | std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads; | 92 | std::unordered_map<VAddr, std::list<std::shared_ptr<Thread>>> arb_threads; |
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 4669a14ad..6d66276bc 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | namespace Kernel { | 13 | namespace Kernel { |
| 14 | 14 | ||
| 15 | ClientSession::ClientSession(KernelCore& kernel) : WaitObject{kernel} {} | 15 | ClientSession::ClientSession(KernelCore& kernel) : SynchronizationObject{kernel} {} |
| 16 | 16 | ||
| 17 | ClientSession::~ClientSession() { | 17 | ClientSession::~ClientSession() { |
| 18 | // This destructor will be called automatically when the last ClientSession handle is closed by | 18 | // This destructor will be called automatically when the last ClientSession handle is closed by |
| @@ -31,6 +31,11 @@ void ClientSession::Acquire(Thread* thread) { | |||
| 31 | UNIMPLEMENTED(); | 31 | UNIMPLEMENTED(); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | bool ClientSession::IsSignaled() const { | ||
| 35 | UNIMPLEMENTED(); | ||
| 36 | return true; | ||
| 37 | } | ||
| 38 | |||
| 34 | ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel, | 39 | ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel, |
| 35 | std::shared_ptr<Session> parent, | 40 | std::shared_ptr<Session> parent, |
| 36 | std::string name) { | 41 | std::string name) { |
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index b4289a9a8..d15b09554 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | 9 | ||
| 10 | #include "core/hle/kernel/wait_object.h" | 10 | #include "core/hle/kernel/synchronization_object.h" |
| 11 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 12 | 12 | ||
| 13 | union ResultCode; | 13 | union ResultCode; |
| @@ -22,7 +22,7 @@ class KernelCore; | |||
| 22 | class Session; | 22 | class Session; |
| 23 | class Thread; | 23 | class Thread; |
| 24 | 24 | ||
| 25 | class ClientSession final : public WaitObject { | 25 | class ClientSession final : public SynchronizationObject { |
| 26 | public: | 26 | public: |
| 27 | explicit ClientSession(KernelCore& kernel); | 27 | explicit ClientSession(KernelCore& kernel); |
| 28 | ~ClientSession() override; | 28 | ~ClientSession() override; |
| @@ -48,6 +48,8 @@ public: | |||
| 48 | 48 | ||
| 49 | void Acquire(Thread* thread) override; | 49 | void Acquire(Thread* thread) override; |
| 50 | 50 | ||
| 51 | bool IsSignaled() const override; | ||
| 52 | |||
| 51 | private: | 53 | private: |
| 52 | static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel, | 54 | static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel, |
| 53 | std::shared_ptr<Session> parent, | 55 | std::shared_ptr<Session> parent, |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 2db28dcf0..c558a2f33 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -47,15 +47,15 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread( | |||
| 47 | const std::string& reason, u64 timeout, WakeupCallback&& callback, | 47 | const std::string& reason, u64 timeout, WakeupCallback&& callback, |
| 48 | std::shared_ptr<WritableEvent> writable_event) { | 48 | std::shared_ptr<WritableEvent> writable_event) { |
| 49 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. | 49 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. |
| 50 | thread->SetWakeupCallback([context = *this, callback](ThreadWakeupReason reason, | 50 | thread->SetWakeupCallback( |
| 51 | std::shared_ptr<Thread> thread, | 51 | [context = *this, callback](ThreadWakeupReason reason, std::shared_ptr<Thread> thread, |
| 52 | std::shared_ptr<WaitObject> object, | 52 | std::shared_ptr<SynchronizationObject> object, |
| 53 | std::size_t index) mutable -> bool { | 53 | std::size_t index) mutable -> bool { |
| 54 | ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent); | 54 | ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent); |
| 55 | callback(thread, context, reason); | 55 | callback(thread, context, reason); |
| 56 | context.WriteToOutgoingCommandBuffer(*thread); | 56 | context.WriteToOutgoingCommandBuffer(*thread); |
| 57 | return true; | 57 | return true; |
| 58 | }); | 58 | }); |
| 59 | 59 | ||
| 60 | auto& kernel = Core::System::GetInstance().Kernel(); | 60 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 61 | if (!writable_event) { | 61 | if (!writable_event) { |
| @@ -67,7 +67,7 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread( | |||
| 67 | const auto readable_event{writable_event->GetReadableEvent()}; | 67 | const auto readable_event{writable_event->GetReadableEvent()}; |
| 68 | writable_event->Clear(); | 68 | writable_event->Clear(); |
| 69 | thread->SetStatus(ThreadStatus::WaitHLEEvent); | 69 | thread->SetStatus(ThreadStatus::WaitHLEEvent); |
| 70 | thread->SetWaitObjects({readable_event}); | 70 | thread->SetSynchronizationObjects({readable_event}); |
| 71 | readable_event->AddWaitingThread(thread); | 71 | readable_event->AddWaitingThread(thread); |
| 72 | 72 | ||
| 73 | if (timeout > 0) { | 73 | if (timeout > 0) { |
| @@ -284,13 +284,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { | |||
| 284 | 284 | ||
| 285 | std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { | 285 | std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { |
| 286 | std::vector<u8> buffer; | 286 | std::vector<u8> buffer; |
| 287 | const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; | 287 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && |
| 288 | BufferDescriptorA()[buffer_index].Size()}; | ||
| 288 | auto& memory = Core::System::GetInstance().Memory(); | 289 | auto& memory = Core::System::GetInstance().Memory(); |
| 289 | 290 | ||
| 290 | if (is_buffer_a) { | 291 | if (is_buffer_a) { |
| 292 | ASSERT_MSG(BufferDescriptorA().size() > buffer_index, | ||
| 293 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | ||
| 291 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); | 294 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); |
| 292 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); | 295 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 293 | } else { | 296 | } else { |
| 297 | ASSERT_MSG(BufferDescriptorX().size() > buffer_index, | ||
| 298 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | ||
| 294 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); | 299 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); |
| 295 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); | 300 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 296 | } | 301 | } |
| @@ -305,7 +310,8 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | |||
| 305 | return 0; | 310 | return 0; |
| 306 | } | 311 | } |
| 307 | 312 | ||
| 308 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; | 313 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && |
| 314 | BufferDescriptorB()[buffer_index].Size()}; | ||
| 309 | const std::size_t buffer_size{GetWriteBufferSize(buffer_index)}; | 315 | const std::size_t buffer_size{GetWriteBufferSize(buffer_index)}; |
| 310 | if (size > buffer_size) { | 316 | if (size > buffer_size) { |
| 311 | LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, | 317 | LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, |
| @@ -315,8 +321,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | |||
| 315 | 321 | ||
| 316 | auto& memory = Core::System::GetInstance().Memory(); | 322 | auto& memory = Core::System::GetInstance().Memory(); |
| 317 | if (is_buffer_b) { | 323 | if (is_buffer_b) { |
| 324 | ASSERT_MSG(BufferDescriptorB().size() > buffer_index, | ||
| 325 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | ||
| 326 | ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size, | ||
| 327 | "BufferDescriptorB buffer_index {} is not large enough", buffer_index); | ||
| 318 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); | 328 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); |
| 319 | } else { | 329 | } else { |
| 330 | ASSERT_MSG(BufferDescriptorC().size() > buffer_index, | ||
| 331 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | ||
| 332 | ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size, | ||
| 333 | "BufferDescriptorC buffer_index {} is not large enough", buffer_index); | ||
| 320 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); | 334 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); |
| 321 | } | 335 | } |
| 322 | 336 | ||
| @@ -324,15 +338,35 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | |||
| 324 | } | 338 | } |
| 325 | 339 | ||
| 326 | std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { | 340 | std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { |
| 327 | const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; | 341 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && |
| 328 | return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() | 342 | BufferDescriptorA()[buffer_index].Size()}; |
| 329 | : BufferDescriptorX()[buffer_index].Size(); | 343 | if (is_buffer_a) { |
| 344 | ASSERT_MSG(BufferDescriptorA().size() > buffer_index, | ||
| 345 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | ||
| 346 | ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0, | ||
| 347 | "BufferDescriptorA buffer_index {} is empty", buffer_index); | ||
| 348 | return BufferDescriptorA()[buffer_index].Size(); | ||
| 349 | } else { | ||
| 350 | ASSERT_MSG(BufferDescriptorX().size() > buffer_index, | ||
| 351 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | ||
| 352 | ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0, | ||
| 353 | "BufferDescriptorX buffer_index {} is empty", buffer_index); | ||
| 354 | return BufferDescriptorX()[buffer_index].Size(); | ||
| 355 | } | ||
| 330 | } | 356 | } |
| 331 | 357 | ||
| 332 | std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { | 358 | std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { |
| 333 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; | 359 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && |
| 334 | return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() | 360 | BufferDescriptorB()[buffer_index].Size()}; |
| 335 | : BufferDescriptorC()[buffer_index].Size(); | 361 | if (is_buffer_b) { |
| 362 | ASSERT_MSG(BufferDescriptorB().size() > buffer_index, | ||
| 363 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | ||
| 364 | return BufferDescriptorB()[buffer_index].Size(); | ||
| 365 | } else { | ||
| 366 | ASSERT_MSG(BufferDescriptorC().size() > buffer_index, | ||
| 367 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | ||
| 368 | return BufferDescriptorC()[buffer_index].Size(); | ||
| 369 | } | ||
| 336 | } | 370 | } |
| 337 | 371 | ||
| 338 | std::string HLERequestContext::Description() const { | 372 | std::string HLERequestContext::Description() const { |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1d0783bd3..4eb1d8703 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -3,13 +3,15 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <atomic> | 5 | #include <atomic> |
| 6 | #include <functional> | ||
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | #include <mutex> | 8 | #include <mutex> |
| 8 | #include <utility> | 9 | #include <utility> |
| 9 | 10 | ||
| 10 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 11 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 12 | 13 | #include "core/arm/arm_interface.h" | |
| 14 | #include "core/arm/exclusive_monitor.h" | ||
| 13 | #include "core/core.h" | 15 | #include "core/core.h" |
| 14 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 15 | #include "core/core_timing_util.h" | 17 | #include "core/core_timing_util.h" |
| @@ -17,9 +19,11 @@ | |||
| 17 | #include "core/hle/kernel/errors.h" | 19 | #include "core/hle/kernel/errors.h" |
| 18 | #include "core/hle/kernel/handle_table.h" | 20 | #include "core/hle/kernel/handle_table.h" |
| 19 | #include "core/hle/kernel/kernel.h" | 21 | #include "core/hle/kernel/kernel.h" |
| 22 | #include "core/hle/kernel/physical_core.h" | ||
| 20 | #include "core/hle/kernel/process.h" | 23 | #include "core/hle/kernel/process.h" |
| 21 | #include "core/hle/kernel/resource_limit.h" | 24 | #include "core/hle/kernel/resource_limit.h" |
| 22 | #include "core/hle/kernel/scheduler.h" | 25 | #include "core/hle/kernel/scheduler.h" |
| 26 | #include "core/hle/kernel/synchronization.h" | ||
| 23 | #include "core/hle/kernel/thread.h" | 27 | #include "core/hle/kernel/thread.h" |
| 24 | #include "core/hle/lock.h" | 28 | #include "core/hle/lock.h" |
| 25 | #include "core/hle/result.h" | 29 | #include "core/hle/result.h" |
| @@ -51,10 +55,10 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ | |||
| 51 | if (thread->GetStatus() == ThreadStatus::WaitSynch || | 55 | if (thread->GetStatus() == ThreadStatus::WaitSynch || |
| 52 | thread->GetStatus() == ThreadStatus::WaitHLEEvent) { | 56 | thread->GetStatus() == ThreadStatus::WaitHLEEvent) { |
| 53 | // Remove the thread from each of its waiting objects' waitlists | 57 | // Remove the thread from each of its waiting objects' waitlists |
| 54 | for (const auto& object : thread->GetWaitObjects()) { | 58 | for (const auto& object : thread->GetSynchronizationObjects()) { |
| 55 | object->RemoveWaitingThread(thread); | 59 | object->RemoveWaitingThread(thread); |
| 56 | } | 60 | } |
| 57 | thread->ClearWaitObjects(); | 61 | thread->ClearSynchronizationObjects(); |
| 58 | 62 | ||
| 59 | // Invoke the wakeup callback before clearing the wait objects | 63 | // Invoke the wakeup callback before clearing the wait objects |
| 60 | if (thread->HasWakeupCallback()) { | 64 | if (thread->HasWakeupCallback()) { |
| @@ -93,11 +97,13 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ | |||
| 93 | } | 97 | } |
| 94 | 98 | ||
| 95 | struct KernelCore::Impl { | 99 | struct KernelCore::Impl { |
| 96 | explicit Impl(Core::System& system) : system{system}, global_scheduler{system} {} | 100 | explicit Impl(Core::System& system) |
| 101 | : system{system}, global_scheduler{system}, synchronization{system} {} | ||
| 97 | 102 | ||
| 98 | void Initialize(KernelCore& kernel) { | 103 | void Initialize(KernelCore& kernel) { |
| 99 | Shutdown(); | 104 | Shutdown(); |
| 100 | 105 | ||
| 106 | InitializePhysicalCores(); | ||
| 101 | InitializeSystemResourceLimit(kernel); | 107 | InitializeSystemResourceLimit(kernel); |
| 102 | InitializeThreads(); | 108 | InitializeThreads(); |
| 103 | InitializePreemption(); | 109 | InitializePreemption(); |
| @@ -121,6 +127,21 @@ struct KernelCore::Impl { | |||
| 121 | global_scheduler.Shutdown(); | 127 | global_scheduler.Shutdown(); |
| 122 | 128 | ||
| 123 | named_ports.clear(); | 129 | named_ports.clear(); |
| 130 | |||
| 131 | for (auto& core : cores) { | ||
| 132 | core.Shutdown(); | ||
| 133 | } | ||
| 134 | cores.clear(); | ||
| 135 | |||
| 136 | exclusive_monitor.reset(); | ||
| 137 | } | ||
| 138 | |||
| 139 | void InitializePhysicalCores() { | ||
| 140 | exclusive_monitor = | ||
| 141 | Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount()); | ||
| 142 | for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) { | ||
| 143 | cores.emplace_back(system, i, *exclusive_monitor); | ||
| 144 | } | ||
| 124 | } | 145 | } |
| 125 | 146 | ||
| 126 | // Creates the default system resource limit | 147 | // Creates the default system resource limit |
| @@ -172,6 +193,7 @@ struct KernelCore::Impl { | |||
| 172 | std::vector<std::shared_ptr<Process>> process_list; | 193 | std::vector<std::shared_ptr<Process>> process_list; |
| 173 | Process* current_process = nullptr; | 194 | Process* current_process = nullptr; |
| 174 | Kernel::GlobalScheduler global_scheduler; | 195 | Kernel::GlobalScheduler global_scheduler; |
| 196 | Kernel::Synchronization synchronization; | ||
| 175 | 197 | ||
| 176 | std::shared_ptr<ResourceLimit> system_resource_limit; | 198 | std::shared_ptr<ResourceLimit> system_resource_limit; |
| 177 | 199 | ||
| @@ -186,6 +208,9 @@ struct KernelCore::Impl { | |||
| 186 | /// the ConnectToPort SVC. | 208 | /// the ConnectToPort SVC. |
| 187 | NamedPortTable named_ports; | 209 | NamedPortTable named_ports; |
| 188 | 210 | ||
| 211 | std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; | ||
| 212 | std::vector<Kernel::PhysicalCore> cores; | ||
| 213 | |||
| 189 | // System context | 214 | // System context |
| 190 | Core::System& system; | 215 | Core::System& system; |
| 191 | }; | 216 | }; |
| @@ -240,6 +265,42 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const { | |||
| 240 | return impl->global_scheduler; | 265 | return impl->global_scheduler; |
| 241 | } | 266 | } |
| 242 | 267 | ||
| 268 | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { | ||
| 269 | return impl->cores[id]; | ||
| 270 | } | ||
| 271 | |||
| 272 | const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { | ||
| 273 | return impl->cores[id]; | ||
| 274 | } | ||
| 275 | |||
| 276 | Kernel::Synchronization& KernelCore::Synchronization() { | ||
| 277 | return impl->synchronization; | ||
| 278 | } | ||
| 279 | |||
| 280 | const Kernel::Synchronization& KernelCore::Synchronization() const { | ||
| 281 | return impl->synchronization; | ||
| 282 | } | ||
| 283 | |||
| 284 | Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { | ||
| 285 | return *impl->exclusive_monitor; | ||
| 286 | } | ||
| 287 | |||
| 288 | const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const { | ||
| 289 | return *impl->exclusive_monitor; | ||
| 290 | } | ||
| 291 | |||
| 292 | void KernelCore::InvalidateAllInstructionCaches() { | ||
| 293 | for (std::size_t i = 0; i < impl->global_scheduler.CpuCoresCount(); i++) { | ||
| 294 | PhysicalCore(i).ArmInterface().ClearInstructionCache(); | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | void KernelCore::PrepareReschedule(std::size_t id) { | ||
| 299 | if (id < impl->global_scheduler.CpuCoresCount()) { | ||
| 300 | impl->cores[id].Stop(); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 243 | void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { | 304 | void KernelCore::AddNamedPort(std::string name, std::shared_ptr<ClientPort> port) { |
| 244 | impl->named_ports.emplace(std::move(name), std::move(port)); | 305 | impl->named_ports.emplace(std::move(name), std::move(port)); |
| 245 | } | 306 | } |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 3bf0068ed..1eede3063 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -11,8 +11,9 @@ | |||
| 11 | #include "core/hle/kernel/object.h" | 11 | #include "core/hle/kernel/object.h" |
| 12 | 12 | ||
| 13 | namespace Core { | 13 | namespace Core { |
| 14 | class ExclusiveMonitor; | ||
| 14 | class System; | 15 | class System; |
| 15 | } | 16 | } // namespace Core |
| 16 | 17 | ||
| 17 | namespace Core::Timing { | 18 | namespace Core::Timing { |
| 18 | class CoreTiming; | 19 | class CoreTiming; |
| @@ -25,8 +26,10 @@ class AddressArbiter; | |||
| 25 | class ClientPort; | 26 | class ClientPort; |
| 26 | class GlobalScheduler; | 27 | class GlobalScheduler; |
| 27 | class HandleTable; | 28 | class HandleTable; |
| 29 | class PhysicalCore; | ||
| 28 | class Process; | 30 | class Process; |
| 29 | class ResourceLimit; | 31 | class ResourceLimit; |
| 32 | class Synchronization; | ||
| 30 | class Thread; | 33 | class Thread; |
| 31 | 34 | ||
| 32 | /// Represents a single instance of the kernel. | 35 | /// Represents a single instance of the kernel. |
| @@ -84,6 +87,27 @@ public: | |||
| 84 | /// Gets the sole instance of the global scheduler | 87 | /// Gets the sole instance of the global scheduler |
| 85 | const Kernel::GlobalScheduler& GlobalScheduler() const; | 88 | const Kernel::GlobalScheduler& GlobalScheduler() const; |
| 86 | 89 | ||
| 90 | /// Gets the an instance of the respective physical CPU core. | ||
| 91 | Kernel::PhysicalCore& PhysicalCore(std::size_t id); | ||
| 92 | |||
| 93 | /// Gets the an instance of the respective physical CPU core. | ||
| 94 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; | ||
| 95 | |||
| 96 | /// Gets the an instance of the Synchronization Interface. | ||
| 97 | Kernel::Synchronization& Synchronization(); | ||
| 98 | |||
| 99 | /// Gets the an instance of the Synchronization Interface. | ||
| 100 | const Kernel::Synchronization& Synchronization() const; | ||
| 101 | |||
| 102 | /// Stops execution of 'id' core, in order to reschedule a new thread. | ||
| 103 | void PrepareReschedule(std::size_t id); | ||
| 104 | |||
| 105 | Core::ExclusiveMonitor& GetExclusiveMonitor(); | ||
| 106 | |||
| 107 | const Core::ExclusiveMonitor& GetExclusiveMonitor() const; | ||
| 108 | |||
| 109 | void InvalidateAllInstructionCaches(); | ||
| 110 | |||
| 87 | /// Adds a port to the named port table | 111 | /// Adds a port to the named port table |
| 88 | void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port); | 112 | void AddNamedPort(std::string name, std::shared_ptr<ClientPort> port); |
| 89 | 113 | ||
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp new file mode 100644 index 000000000..9303dd273 --- /dev/null +++ b/src/core/hle/kernel/physical_core.cpp | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/arm/arm_interface.h" | ||
| 7 | #ifdef ARCHITECTURE_x86_64 | ||
| 8 | #include "core/arm/dynarmic/arm_dynarmic.h" | ||
| 9 | #endif | ||
| 10 | #include "core/arm/exclusive_monitor.h" | ||
| 11 | #include "core/arm/unicorn/arm_unicorn.h" | ||
| 12 | #include "core/core.h" | ||
| 13 | #include "core/hle/kernel/physical_core.h" | ||
| 14 | #include "core/hle/kernel/scheduler.h" | ||
| 15 | #include "core/hle/kernel/thread.h" | ||
| 16 | |||
| 17 | namespace Kernel { | ||
| 18 | |||
| 19 | PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, | ||
| 20 | Core::ExclusiveMonitor& exclusive_monitor) | ||
| 21 | : core_index{id} { | ||
| 22 | #ifdef ARCHITECTURE_x86_64 | ||
| 23 | arm_interface = std::make_unique<Core::ARM_Dynarmic>(system, exclusive_monitor, core_index); | ||
| 24 | #else | ||
| 25 | arm_interface = std::make_shared<Core::ARM_Unicorn>(system); | ||
| 26 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||
| 27 | #endif | ||
| 28 | |||
| 29 | scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index); | ||
| 30 | } | ||
| 31 | |||
| 32 | PhysicalCore::~PhysicalCore() = default; | ||
| 33 | |||
| 34 | void PhysicalCore::Run() { | ||
| 35 | arm_interface->Run(); | ||
| 36 | arm_interface->ClearExclusiveState(); | ||
| 37 | } | ||
| 38 | |||
| 39 | void PhysicalCore::Step() { | ||
| 40 | arm_interface->Step(); | ||
| 41 | } | ||
| 42 | |||
| 43 | void PhysicalCore::Stop() { | ||
| 44 | arm_interface->PrepareReschedule(); | ||
| 45 | } | ||
| 46 | |||
| 47 | void PhysicalCore::Shutdown() { | ||
| 48 | scheduler->Shutdown(); | ||
| 49 | } | ||
| 50 | |||
| 51 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h new file mode 100644 index 000000000..4c32c0f1b --- /dev/null +++ b/src/core/hle/kernel/physical_core.h | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | class Scheduler; | ||
| 12 | } // namespace Kernel | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | class ARM_Interface; | ||
| 16 | class ExclusiveMonitor; | ||
| 17 | class System; | ||
| 18 | } // namespace Core | ||
| 19 | |||
| 20 | namespace Kernel { | ||
| 21 | |||
| 22 | class PhysicalCore { | ||
| 23 | public: | ||
| 24 | PhysicalCore(Core::System& system, std::size_t id, Core::ExclusiveMonitor& exclusive_monitor); | ||
| 25 | ~PhysicalCore(); | ||
| 26 | |||
| 27 | PhysicalCore(const PhysicalCore&) = delete; | ||
| 28 | PhysicalCore& operator=(const PhysicalCore&) = delete; | ||
| 29 | |||
| 30 | PhysicalCore(PhysicalCore&&) = default; | ||
| 31 | PhysicalCore& operator=(PhysicalCore&&) = default; | ||
| 32 | |||
| 33 | /// Execute current jit state | ||
| 34 | void Run(); | ||
| 35 | /// Execute a single instruction in current jit. | ||
| 36 | void Step(); | ||
| 37 | /// Stop JIT execution/exit | ||
| 38 | void Stop(); | ||
| 39 | |||
| 40 | // Shutdown this physical core. | ||
| 41 | void Shutdown(); | ||
| 42 | |||
| 43 | Core::ARM_Interface& ArmInterface() { | ||
| 44 | return *arm_interface; | ||
| 45 | } | ||
| 46 | |||
| 47 | const Core::ARM_Interface& ArmInterface() const { | ||
| 48 | return *arm_interface; | ||
| 49 | } | ||
| 50 | |||
| 51 | bool IsMainCore() const { | ||
| 52 | return core_index == 0; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool IsSystemCore() const { | ||
| 56 | return core_index == 3; | ||
| 57 | } | ||
| 58 | |||
| 59 | std::size_t CoreIndex() const { | ||
| 60 | return core_index; | ||
| 61 | } | ||
| 62 | |||
| 63 | Kernel::Scheduler& Scheduler() { | ||
| 64 | return *scheduler; | ||
| 65 | } | ||
| 66 | |||
| 67 | const Kernel::Scheduler& Scheduler() const { | ||
| 68 | return *scheduler; | ||
| 69 | } | ||
| 70 | |||
| 71 | private: | ||
| 72 | std::size_t core_index; | ||
| 73 | std::unique_ptr<Core::ARM_Interface> arm_interface; | ||
| 74 | std::unique_ptr<Kernel::Scheduler> scheduler; | ||
| 75 | }; | ||
| 76 | |||
| 77 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b9035a0be..2fcb7326c 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -337,7 +337,7 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { | |||
| 337 | } | 337 | } |
| 338 | 338 | ||
| 339 | Process::Process(Core::System& system) | 339 | Process::Process(Core::System& system) |
| 340 | : WaitObject{system.Kernel()}, vm_manager{system}, | 340 | : SynchronizationObject{system.Kernel()}, vm_manager{system}, |
| 341 | address_arbiter{system}, mutex{system}, system{system} {} | 341 | address_arbiter{system}, mutex{system}, system{system} {} |
| 342 | 342 | ||
| 343 | Process::~Process() = default; | 343 | Process::~Process() = default; |
| @@ -357,7 +357,7 @@ void Process::ChangeStatus(ProcessStatus new_status) { | |||
| 357 | 357 | ||
| 358 | status = new_status; | 358 | status = new_status; |
| 359 | is_signaled = true; | 359 | is_signaled = true; |
| 360 | WakeupAllWaitingThreads(); | 360 | Signal(); |
| 361 | } | 361 | } |
| 362 | 362 | ||
| 363 | void Process::AllocateMainThreadStack(u64 stack_size) { | 363 | void Process::AllocateMainThreadStack(u64 stack_size) { |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 3483fa19d..4887132a7 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -15,8 +15,8 @@ | |||
| 15 | #include "core/hle/kernel/handle_table.h" | 15 | #include "core/hle/kernel/handle_table.h" |
| 16 | #include "core/hle/kernel/mutex.h" | 16 | #include "core/hle/kernel/mutex.h" |
| 17 | #include "core/hle/kernel/process_capability.h" | 17 | #include "core/hle/kernel/process_capability.h" |
| 18 | #include "core/hle/kernel/synchronization_object.h" | ||
| 18 | #include "core/hle/kernel/vm_manager.h" | 19 | #include "core/hle/kernel/vm_manager.h" |
| 19 | #include "core/hle/kernel/wait_object.h" | ||
| 20 | #include "core/hle/result.h" | 20 | #include "core/hle/result.h" |
| 21 | 21 | ||
| 22 | namespace Core { | 22 | namespace Core { |
| @@ -60,7 +60,7 @@ enum class ProcessStatus { | |||
| 60 | DebugBreak, | 60 | DebugBreak, |
| 61 | }; | 61 | }; |
| 62 | 62 | ||
| 63 | class Process final : public WaitObject { | 63 | class Process final : public SynchronizationObject { |
| 64 | public: | 64 | public: |
| 65 | explicit Process(Core::System& system); | 65 | explicit Process(Core::System& system); |
| 66 | ~Process() override; | 66 | ~Process() override; |
| @@ -359,10 +359,6 @@ private: | |||
| 359 | /// specified by metadata provided to the process during loading. | 359 | /// specified by metadata provided to the process during loading. |
| 360 | bool is_64bit_process = true; | 360 | bool is_64bit_process = true; |
| 361 | 361 | ||
| 362 | /// Whether or not this process is signaled. This occurs | ||
| 363 | /// upon the process changing to a different state. | ||
| 364 | bool is_signaled = false; | ||
| 365 | |||
| 366 | /// Total running time for the process in ticks. | 362 | /// Total running time for the process in ticks. |
| 367 | u64 total_process_running_time_ticks = 0; | 363 | u64 total_process_running_time_ticks = 0; |
| 368 | 364 | ||
diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index d8ac97aa1..9d3d3a81b 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp | |||
| @@ -11,30 +11,30 @@ | |||
| 11 | 11 | ||
| 12 | namespace Kernel { | 12 | namespace Kernel { |
| 13 | 13 | ||
| 14 | ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {} | 14 | ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel} {} |
| 15 | ReadableEvent::~ReadableEvent() = default; | 15 | ReadableEvent::~ReadableEvent() = default; |
| 16 | 16 | ||
| 17 | bool ReadableEvent::ShouldWait(const Thread* thread) const { | 17 | bool ReadableEvent::ShouldWait(const Thread* thread) const { |
| 18 | return !signaled; | 18 | return !is_signaled; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | void ReadableEvent::Acquire(Thread* thread) { | 21 | void ReadableEvent::Acquire(Thread* thread) { |
| 22 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 22 | ASSERT_MSG(IsSignaled(), "object unavailable!"); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | void ReadableEvent::Signal() { | 25 | void ReadableEvent::Signal() { |
| 26 | if (!signaled) { | 26 | if (!is_signaled) { |
| 27 | signaled = true; | 27 | is_signaled = true; |
| 28 | WakeupAllWaitingThreads(); | 28 | SynchronizationObject::Signal(); |
| 29 | }; | 29 | }; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | void ReadableEvent::Clear() { | 32 | void ReadableEvent::Clear() { |
| 33 | signaled = false; | 33 | is_signaled = false; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | ResultCode ReadableEvent::Reset() { | 36 | ResultCode ReadableEvent::Reset() { |
| 37 | if (!signaled) { | 37 | if (!is_signaled) { |
| 38 | return ERR_INVALID_STATE; | 38 | return ERR_INVALID_STATE; |
| 39 | } | 39 | } |
| 40 | 40 | ||
diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 11ff71c3a..3264dd066 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/kernel/object.h" | 7 | #include "core/hle/kernel/object.h" |
| 8 | #include "core/hle/kernel/wait_object.h" | 8 | #include "core/hle/kernel/synchronization_object.h" |
| 9 | 9 | ||
| 10 | union ResultCode; | 10 | union ResultCode; |
| 11 | 11 | ||
| @@ -14,7 +14,7 @@ namespace Kernel { | |||
| 14 | class KernelCore; | 14 | class KernelCore; |
| 15 | class WritableEvent; | 15 | class WritableEvent; |
| 16 | 16 | ||
| 17 | class ReadableEvent final : public WaitObject { | 17 | class ReadableEvent final : public SynchronizationObject { |
| 18 | friend class WritableEvent; | 18 | friend class WritableEvent; |
| 19 | 19 | ||
| 20 | public: | 20 | public: |
| @@ -46,13 +46,11 @@ public: | |||
| 46 | /// then ERR_INVALID_STATE will be returned. | 46 | /// then ERR_INVALID_STATE will be returned. |
| 47 | ResultCode Reset(); | 47 | ResultCode Reset(); |
| 48 | 48 | ||
| 49 | void Signal() override; | ||
| 50 | |||
| 49 | private: | 51 | private: |
| 50 | explicit ReadableEvent(KernelCore& kernel); | 52 | explicit ReadableEvent(KernelCore& kernel); |
| 51 | 53 | ||
| 52 | void Signal(); | ||
| 53 | |||
| 54 | bool signaled{}; | ||
| 55 | |||
| 56 | std::string name; ///< Name of event (optional) | 54 | std::string name; ///< Name of event (optional) |
| 57 | }; | 55 | }; |
| 58 | 56 | ||
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index d36fcd7d9..86f1421bf 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -14,7 +14,6 @@ | |||
| 14 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 15 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/core.h" | 16 | #include "core/core.h" |
| 17 | #include "core/core_cpu.h" | ||
| 18 | #include "core/core_timing.h" | 17 | #include "core/core_timing.h" |
| 19 | #include "core/hle/kernel/kernel.h" | 18 | #include "core/hle/kernel/kernel.h" |
| 20 | #include "core/hle/kernel/process.h" | 19 | #include "core/hle/kernel/process.h" |
| @@ -125,8 +124,8 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) { | |||
| 125 | "Thread yielding without being in front"); | 124 | "Thread yielding without being in front"); |
| 126 | scheduled_queue[core_id].yield(priority); | 125 | scheduled_queue[core_id].yield(priority); |
| 127 | 126 | ||
| 128 | std::array<Thread*, NUM_CPU_CORES> current_threads; | 127 | std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads; |
| 129 | for (u32 i = 0; i < NUM_CPU_CORES; i++) { | 128 | for (std::size_t i = 0; i < current_threads.size(); i++) { |
| 130 | current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front(); | 129 | current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front(); |
| 131 | } | 130 | } |
| 132 | 131 | ||
| @@ -178,8 +177,8 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread | |||
| 178 | // function... | 177 | // function... |
| 179 | if (scheduled_queue[core_id].empty()) { | 178 | if (scheduled_queue[core_id].empty()) { |
| 180 | // Here, "current_threads" is calculated after the ""yield"", unlike yield -1 | 179 | // Here, "current_threads" is calculated after the ""yield"", unlike yield -1 |
| 181 | std::array<Thread*, NUM_CPU_CORES> current_threads; | 180 | std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads; |
| 182 | for (u32 i = 0; i < NUM_CPU_CORES; i++) { | 181 | for (std::size_t i = 0; i < current_threads.size(); i++) { |
| 183 | current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front(); | 182 | current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front(); |
| 184 | } | 183 | } |
| 185 | for (auto& thread : suggested_queue[core_id]) { | 184 | for (auto& thread : suggested_queue[core_id]) { |
| @@ -209,7 +208,7 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread | |||
| 209 | } | 208 | } |
| 210 | 209 | ||
| 211 | void GlobalScheduler::PreemptThreads() { | 210 | void GlobalScheduler::PreemptThreads() { |
| 212 | for (std::size_t core_id = 0; core_id < NUM_CPU_CORES; core_id++) { | 211 | for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { |
| 213 | const u32 priority = preemption_priorities[core_id]; | 212 | const u32 priority = preemption_priorities[core_id]; |
| 214 | 213 | ||
| 215 | if (scheduled_queue[core_id].size(priority) > 0) { | 214 | if (scheduled_queue[core_id].size(priority) > 0) { |
| @@ -350,7 +349,7 @@ bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread, | |||
| 350 | } | 349 | } |
| 351 | 350 | ||
| 352 | void GlobalScheduler::Shutdown() { | 351 | void GlobalScheduler::Shutdown() { |
| 353 | for (std::size_t core = 0; core < NUM_CPU_CORES; core++) { | 352 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 354 | scheduled_queue[core].clear(); | 353 | scheduled_queue[core].clear(); |
| 355 | suggested_queue[core].clear(); | 354 | suggested_queue[core].clear(); |
| 356 | } | 355 | } |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 14b77960a..96db049cb 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/multi_level_queue.h" | 12 | #include "common/multi_level_queue.h" |
| 13 | #include "core/hardware_properties.h" | ||
| 13 | #include "core/hle/kernel/thread.h" | 14 | #include "core/hle/kernel/thread.h" |
| 14 | 15 | ||
| 15 | namespace Core { | 16 | namespace Core { |
| @@ -23,8 +24,6 @@ class Process; | |||
| 23 | 24 | ||
| 24 | class GlobalScheduler final { | 25 | class GlobalScheduler final { |
| 25 | public: | 26 | public: |
| 26 | static constexpr u32 NUM_CPU_CORES = 4; | ||
| 27 | |||
| 28 | explicit GlobalScheduler(Core::System& system); | 27 | explicit GlobalScheduler(Core::System& system); |
| 29 | ~GlobalScheduler(); | 28 | ~GlobalScheduler(); |
| 30 | 29 | ||
| @@ -125,7 +124,7 @@ public: | |||
| 125 | void PreemptThreads(); | 124 | void PreemptThreads(); |
| 126 | 125 | ||
| 127 | u32 CpuCoresCount() const { | 126 | u32 CpuCoresCount() const { |
| 128 | return NUM_CPU_CORES; | 127 | return Core::Hardware::NUM_CPU_CORES; |
| 129 | } | 128 | } |
| 130 | 129 | ||
| 131 | void SetReselectionPending() { | 130 | void SetReselectionPending() { |
| @@ -149,13 +148,15 @@ private: | |||
| 149 | bool AskForReselectionOrMarkRedundant(Thread* current_thread, const Thread* winner); | 148 | bool AskForReselectionOrMarkRedundant(Thread* current_thread, const Thread* winner); |
| 150 | 149 | ||
| 151 | static constexpr u32 min_regular_priority = 2; | 150 | static constexpr u32 min_regular_priority = 2; |
| 152 | std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> scheduled_queue; | 151 | std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, Core::Hardware::NUM_CPU_CORES> |
| 153 | std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> suggested_queue; | 152 | scheduled_queue; |
| 153 | std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, Core::Hardware::NUM_CPU_CORES> | ||
| 154 | suggested_queue; | ||
| 154 | std::atomic<bool> is_reselection_pending{false}; | 155 | std::atomic<bool> is_reselection_pending{false}; |
| 155 | 156 | ||
| 156 | // The priority levels at which the global scheduler preempts threads every 10 ms. They are | 157 | // The priority levels at which the global scheduler preempts threads every 10 ms. They are |
| 157 | // ordered from Core 0 to Core 3. | 158 | // ordered from Core 0 to Core 3. |
| 158 | std::array<u32, NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; | 159 | std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; |
| 159 | 160 | ||
| 160 | /// Lists all thread ids that aren't deleted/etc. | 161 | /// Lists all thread ids that aren't deleted/etc. |
| 161 | std::vector<std::shared_ptr<Thread>> thread_list; | 162 | std::vector<std::shared_ptr<Thread>> thread_list; |
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index a4ccfa35e..a549ae9d7 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | namespace Kernel { | 14 | namespace Kernel { |
| 15 | 15 | ||
| 16 | ServerPort::ServerPort(KernelCore& kernel) : WaitObject{kernel} {} | 16 | ServerPort::ServerPort(KernelCore& kernel) : SynchronizationObject{kernel} {} |
| 17 | ServerPort::~ServerPort() = default; | 17 | ServerPort::~ServerPort() = default; |
| 18 | 18 | ||
| 19 | ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { | 19 | ResultVal<std::shared_ptr<ServerSession>> ServerPort::Accept() { |
| @@ -39,6 +39,10 @@ void ServerPort::Acquire(Thread* thread) { | |||
| 39 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 39 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | bool ServerPort::IsSignaled() const { | ||
| 43 | return !pending_sessions.empty(); | ||
| 44 | } | ||
| 45 | |||
| 42 | ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions, | 46 | ServerPort::PortPair ServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions, |
| 43 | std::string name) { | 47 | std::string name) { |
| 44 | std::shared_ptr<ServerPort> server_port = std::make_shared<ServerPort>(kernel); | 48 | std::shared_ptr<ServerPort> server_port = std::make_shared<ServerPort>(kernel); |
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 8be8a75ea..41b191b86 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/hle/kernel/object.h" | 12 | #include "core/hle/kernel/object.h" |
| 13 | #include "core/hle/kernel/wait_object.h" | 13 | #include "core/hle/kernel/synchronization_object.h" |
| 14 | #include "core/hle/result.h" | 14 | #include "core/hle/result.h" |
| 15 | 15 | ||
| 16 | namespace Kernel { | 16 | namespace Kernel { |
| @@ -20,7 +20,7 @@ class KernelCore; | |||
| 20 | class ServerSession; | 20 | class ServerSession; |
| 21 | class SessionRequestHandler; | 21 | class SessionRequestHandler; |
| 22 | 22 | ||
| 23 | class ServerPort final : public WaitObject { | 23 | class ServerPort final : public SynchronizationObject { |
| 24 | public: | 24 | public: |
| 25 | explicit ServerPort(KernelCore& kernel); | 25 | explicit ServerPort(KernelCore& kernel); |
| 26 | ~ServerPort() override; | 26 | ~ServerPort() override; |
| @@ -82,6 +82,8 @@ public: | |||
| 82 | bool ShouldWait(const Thread* thread) const override; | 82 | bool ShouldWait(const Thread* thread) const override; |
| 83 | void Acquire(Thread* thread) override; | 83 | void Acquire(Thread* thread) override; |
| 84 | 84 | ||
| 85 | bool IsSignaled() const override; | ||
| 86 | |||
| 85 | private: | 87 | private: |
| 86 | /// ServerSessions waiting to be accepted by the port | 88 | /// ServerSessions waiting to be accepted by the port |
| 87 | std::vector<std::shared_ptr<ServerSession>> pending_sessions; | 89 | std::vector<std::shared_ptr<ServerSession>> pending_sessions; |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 7825e1ec4..4604e35c5 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -24,7 +24,7 @@ | |||
| 24 | 24 | ||
| 25 | namespace Kernel { | 25 | namespace Kernel { |
| 26 | 26 | ||
| 27 | ServerSession::ServerSession(KernelCore& kernel) : WaitObject{kernel} {} | 27 | ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} |
| 28 | ServerSession::~ServerSession() = default; | 28 | ServerSession::~ServerSession() = default; |
| 29 | 29 | ||
| 30 | ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel, | 30 | ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel, |
| @@ -50,6 +50,16 @@ bool ServerSession::ShouldWait(const Thread* thread) const { | |||
| 50 | return pending_requesting_threads.empty() || currently_handling != nullptr; | 50 | return pending_requesting_threads.empty() || currently_handling != nullptr; |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | bool ServerSession::IsSignaled() const { | ||
| 54 | // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. | ||
| 55 | if (!parent->Client()) { | ||
| 56 | return true; | ||
| 57 | } | ||
| 58 | |||
| 59 | // Wait if we have no pending requests, or if we're currently handling a request. | ||
| 60 | return !pending_requesting_threads.empty() && currently_handling == nullptr; | ||
| 61 | } | ||
| 62 | |||
| 53 | void ServerSession::Acquire(Thread* thread) { | 63 | void ServerSession::Acquire(Thread* thread) { |
| 54 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 64 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 55 | // We are now handling a request, pop it from the stack. | 65 | // We are now handling a request, pop it from the stack. |
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index d6e48109e..77e4f6721 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | 11 | ||
| 12 | #include "common/threadsafe_queue.h" | 12 | #include "common/threadsafe_queue.h" |
| 13 | #include "core/hle/kernel/wait_object.h" | 13 | #include "core/hle/kernel/synchronization_object.h" |
| 14 | #include "core/hle/result.h" | 14 | #include "core/hle/result.h" |
| 15 | 15 | ||
| 16 | namespace Memory { | 16 | namespace Memory { |
| @@ -41,7 +41,7 @@ class Thread; | |||
| 41 | * After the server replies to the request, the response is marshalled back to the caller's | 41 | * After the server replies to the request, the response is marshalled back to the caller's |
| 42 | * TLS buffer and control is transferred back to it. | 42 | * TLS buffer and control is transferred back to it. |
| 43 | */ | 43 | */ |
| 44 | class ServerSession final : public WaitObject { | 44 | class ServerSession final : public SynchronizationObject { |
| 45 | public: | 45 | public: |
| 46 | explicit ServerSession(KernelCore& kernel); | 46 | explicit ServerSession(KernelCore& kernel); |
| 47 | ~ServerSession() override; | 47 | ~ServerSession() override; |
| @@ -73,6 +73,8 @@ public: | |||
| 73 | return parent.get(); | 73 | return parent.get(); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | bool IsSignaled() const override; | ||
| 77 | |||
| 76 | /** | 78 | /** |
| 77 | * Sets the HLE handler for the session. This handler will be called to service IPC requests | 79 | * Sets the HLE handler for the session. This handler will be called to service IPC requests |
| 78 | * instead of the regular IPC machinery. (The regular IPC machinery is currently not | 80 | * instead of the regular IPC machinery. (The regular IPC machinery is currently not |
diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index dee6e2b72..e4dd53e24 100644 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | namespace Kernel { | 10 | namespace Kernel { |
| 11 | 11 | ||
| 12 | Session::Session(KernelCore& kernel) : WaitObject{kernel} {} | 12 | Session::Session(KernelCore& kernel) : SynchronizationObject{kernel} {} |
| 13 | Session::~Session() = default; | 13 | Session::~Session() = default; |
| 14 | 14 | ||
| 15 | Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { | 15 | Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { |
| @@ -29,6 +29,11 @@ bool Session::ShouldWait(const Thread* thread) const { | |||
| 29 | return {}; | 29 | return {}; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | bool Session::IsSignaled() const { | ||
| 33 | UNIMPLEMENTED(); | ||
| 34 | return true; | ||
| 35 | } | ||
| 36 | |||
| 32 | void Session::Acquire(Thread* thread) { | 37 | void Session::Acquire(Thread* thread) { |
| 33 | UNIMPLEMENTED(); | 38 | UNIMPLEMENTED(); |
| 34 | } | 39 | } |
diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 15a5ac15f..7cd9c0d77 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | 10 | ||
| 11 | #include "core/hle/kernel/wait_object.h" | 11 | #include "core/hle/kernel/synchronization_object.h" |
| 12 | 12 | ||
| 13 | namespace Kernel { | 13 | namespace Kernel { |
| 14 | 14 | ||
| @@ -19,7 +19,7 @@ class ServerSession; | |||
| 19 | * Parent structure to link the client and server endpoints of a session with their associated | 19 | * Parent structure to link the client and server endpoints of a session with their associated |
| 20 | * client port. | 20 | * client port. |
| 21 | */ | 21 | */ |
| 22 | class Session final : public WaitObject { | 22 | class Session final : public SynchronizationObject { |
| 23 | public: | 23 | public: |
| 24 | explicit Session(KernelCore& kernel); | 24 | explicit Session(KernelCore& kernel); |
| 25 | ~Session() override; | 25 | ~Session() override; |
| @@ -39,6 +39,8 @@ public: | |||
| 39 | 39 | ||
| 40 | bool ShouldWait(const Thread* thread) const override; | 40 | bool ShouldWait(const Thread* thread) const override; |
| 41 | 41 | ||
| 42 | bool IsSignaled() const override; | ||
| 43 | |||
| 42 | void Acquire(Thread* thread) override; | 44 | void Acquire(Thread* thread) override; |
| 43 | 45 | ||
| 44 | std::shared_ptr<ClientSession> Client() { | 46 | std::shared_ptr<ClientSession> Client() { |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index dbcdb0b88..fd91779a3 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -15,7 +15,7 @@ | |||
| 15 | #include "common/string_util.h" | 15 | #include "common/string_util.h" |
| 16 | #include "core/arm/exclusive_monitor.h" | 16 | #include "core/arm/exclusive_monitor.h" |
| 17 | #include "core/core.h" | 17 | #include "core/core.h" |
| 18 | #include "core/core_cpu.h" | 18 | #include "core/core_manager.h" |
| 19 | #include "core/core_timing.h" | 19 | #include "core/core_timing.h" |
| 20 | #include "core/core_timing_util.h" | 20 | #include "core/core_timing_util.h" |
| 21 | #include "core/hle/kernel/address_arbiter.h" | 21 | #include "core/hle/kernel/address_arbiter.h" |
| @@ -32,6 +32,7 @@ | |||
| 32 | #include "core/hle/kernel/shared_memory.h" | 32 | #include "core/hle/kernel/shared_memory.h" |
| 33 | #include "core/hle/kernel/svc.h" | 33 | #include "core/hle/kernel/svc.h" |
| 34 | #include "core/hle/kernel/svc_wrap.h" | 34 | #include "core/hle/kernel/svc_wrap.h" |
| 35 | #include "core/hle/kernel/synchronization.h" | ||
| 35 | #include "core/hle/kernel/thread.h" | 36 | #include "core/hle/kernel/thread.h" |
| 36 | #include "core/hle/kernel/transfer_memory.h" | 37 | #include "core/hle/kernel/transfer_memory.h" |
| 37 | #include "core/hle/kernel/writable_event.h" | 38 | #include "core/hle/kernel/writable_event.h" |
| @@ -433,22 +434,6 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han | |||
| 433 | return ERR_INVALID_HANDLE; | 434 | return ERR_INVALID_HANDLE; |
| 434 | } | 435 | } |
| 435 | 436 | ||
| 436 | /// Default thread wakeup callback for WaitSynchronization | ||
| 437 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | ||
| 438 | std::shared_ptr<WaitObject> object, std::size_t index) { | ||
| 439 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); | ||
| 440 | |||
| 441 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 442 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 443 | return true; | ||
| 444 | } | ||
| 445 | |||
| 446 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 447 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 448 | thread->SetWaitSynchronizationOutput(static_cast<u32>(index)); | ||
| 449 | return true; | ||
| 450 | }; | ||
| 451 | |||
| 452 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds | 437 | /// Wait for the given handles to synchronize, timeout after the specified nanoseconds |
| 453 | static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, | 438 | static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, |
| 454 | u64 handle_count, s64 nano_seconds) { | 439 | u64 handle_count, s64 nano_seconds) { |
| @@ -472,14 +457,14 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr | |||
| 472 | } | 457 | } |
| 473 | 458 | ||
| 474 | auto* const thread = system.CurrentScheduler().GetCurrentThread(); | 459 | auto* const thread = system.CurrentScheduler().GetCurrentThread(); |
| 475 | 460 | auto& kernel = system.Kernel(); | |
| 476 | using ObjectPtr = Thread::ThreadWaitObjects::value_type; | 461 | using ObjectPtr = Thread::ThreadSynchronizationObjects::value_type; |
| 477 | Thread::ThreadWaitObjects objects(handle_count); | 462 | Thread::ThreadSynchronizationObjects objects(handle_count); |
| 478 | const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); | 463 | const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| 479 | 464 | ||
| 480 | for (u64 i = 0; i < handle_count; ++i) { | 465 | for (u64 i = 0; i < handle_count; ++i) { |
| 481 | const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); | 466 | const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); |
| 482 | const auto object = handle_table.Get<WaitObject>(handle); | 467 | const auto object = handle_table.Get<SynchronizationObject>(handle); |
| 483 | 468 | ||
| 484 | if (object == nullptr) { | 469 | if (object == nullptr) { |
| 485 | LOG_ERROR(Kernel_SVC, "Object is a nullptr"); | 470 | LOG_ERROR(Kernel_SVC, "Object is a nullptr"); |
| @@ -488,47 +473,10 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr | |||
| 488 | 473 | ||
| 489 | objects[i] = object; | 474 | objects[i] = object; |
| 490 | } | 475 | } |
| 491 | 476 | auto& synchronization = kernel.Synchronization(); | |
| 492 | // Find the first object that is acquirable in the provided list of objects | 477 | const auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds); |
| 493 | auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { | 478 | *index = handle_result; |
| 494 | return !object->ShouldWait(thread); | 479 | return result; |
| 495 | }); | ||
| 496 | |||
| 497 | if (itr != objects.end()) { | ||
| 498 | // We found a ready object, acquire it and set the result value | ||
| 499 | WaitObject* object = itr->get(); | ||
| 500 | object->Acquire(thread); | ||
| 501 | *index = static_cast<s32>(std::distance(objects.begin(), itr)); | ||
| 502 | return RESULT_SUCCESS; | ||
| 503 | } | ||
| 504 | |||
| 505 | // No objects were ready to be acquired, prepare to suspend the thread. | ||
| 506 | |||
| 507 | // If a timeout value of 0 was provided, just return the Timeout error code instead of | ||
| 508 | // suspending the thread. | ||
| 509 | if (nano_seconds == 0) { | ||
| 510 | return RESULT_TIMEOUT; | ||
| 511 | } | ||
| 512 | |||
| 513 | if (thread->IsSyncCancelled()) { | ||
| 514 | thread->SetSyncCancelled(false); | ||
| 515 | return ERR_SYNCHRONIZATION_CANCELED; | ||
| 516 | } | ||
| 517 | |||
| 518 | for (auto& object : objects) { | ||
| 519 | object->AddWaitingThread(SharedFrom(thread)); | ||
| 520 | } | ||
| 521 | |||
| 522 | thread->SetWaitObjects(std::move(objects)); | ||
| 523 | thread->SetStatus(ThreadStatus::WaitSynch); | ||
| 524 | |||
| 525 | // Create an event to wake the thread up after the specified nanosecond delay has passed | ||
| 526 | thread->WakeAfterDelay(nano_seconds); | ||
| 527 | thread->SetWakeupCallback(DefaultThreadWakeupCallback); | ||
| 528 | |||
| 529 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 530 | |||
| 531 | return RESULT_TIMEOUT; | ||
| 532 | } | 480 | } |
| 533 | 481 | ||
| 534 | /// Resumes a thread waiting on WaitSynchronization | 482 | /// Resumes a thread waiting on WaitSynchronization |
| @@ -1863,10 +1811,14 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd | |||
| 1863 | } | 1811 | } |
| 1864 | 1812 | ||
| 1865 | auto& kernel = system.Kernel(); | 1813 | auto& kernel = system.Kernel(); |
| 1866 | auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); | 1814 | auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms); |
| 1815 | |||
| 1816 | if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) { | ||
| 1817 | return reserve_result; | ||
| 1818 | } | ||
| 1867 | 1819 | ||
| 1868 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | 1820 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| 1869 | const auto result = handle_table.Create(std::move(transfer_mem_handle)); | 1821 | const auto result{handle_table.Create(std::move(transfer_mem_handle))}; |
| 1870 | if (result.Failed()) { | 1822 | if (result.Failed()) { |
| 1871 | return result.Code(); | 1823 | return result.Code(); |
| 1872 | } | 1824 | } |
diff --git a/src/core/hle/kernel/synchronization.cpp b/src/core/hle/kernel/synchronization.cpp new file mode 100644 index 000000000..dc37fad1a --- /dev/null +++ b/src/core/hle/kernel/synchronization.cpp | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/core.h" | ||
| 6 | #include "core/hle/kernel/errors.h" | ||
| 7 | #include "core/hle/kernel/handle_table.h" | ||
| 8 | #include "core/hle/kernel/kernel.h" | ||
| 9 | #include "core/hle/kernel/scheduler.h" | ||
| 10 | #include "core/hle/kernel/synchronization.h" | ||
| 11 | #include "core/hle/kernel/synchronization_object.h" | ||
| 12 | #include "core/hle/kernel/thread.h" | ||
| 13 | |||
| 14 | namespace Kernel { | ||
| 15 | |||
| 16 | /// Default thread wakeup callback for WaitSynchronization | ||
| 17 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | ||
| 18 | std::shared_ptr<SynchronizationObject> object, | ||
| 19 | std::size_t index) { | ||
| 20 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); | ||
| 21 | |||
| 22 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 23 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 24 | return true; | ||
| 25 | } | ||
| 26 | |||
| 27 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 28 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 29 | thread->SetWaitSynchronizationOutput(static_cast<u32>(index)); | ||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | Synchronization::Synchronization(Core::System& system) : system{system} {} | ||
| 34 | |||
| 35 | void Synchronization::SignalObject(SynchronizationObject& obj) const { | ||
| 36 | if (obj.IsSignaled()) { | ||
| 37 | obj.WakeupAllWaitingThreads(); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | std::pair<ResultCode, Handle> Synchronization::WaitFor( | ||
| 42 | std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds) { | ||
| 43 | auto* const thread = system.CurrentScheduler().GetCurrentThread(); | ||
| 44 | // Find the first object that is acquirable in the provided list of objects | ||
| 45 | const auto itr = std::find_if(sync_objects.begin(), sync_objects.end(), | ||
| 46 | [thread](const std::shared_ptr<SynchronizationObject>& object) { | ||
| 47 | return object->IsSignaled(); | ||
| 48 | }); | ||
| 49 | |||
| 50 | if (itr != sync_objects.end()) { | ||
| 51 | // We found a ready object, acquire it and set the result value | ||
| 52 | SynchronizationObject* object = itr->get(); | ||
| 53 | object->Acquire(thread); | ||
| 54 | const u32 index = static_cast<s32>(std::distance(sync_objects.begin(), itr)); | ||
| 55 | return {RESULT_SUCCESS, index}; | ||
| 56 | } | ||
| 57 | |||
| 58 | // No objects were ready to be acquired, prepare to suspend the thread. | ||
| 59 | |||
| 60 | // If a timeout value of 0 was provided, just return the Timeout error code instead of | ||
| 61 | // suspending the thread. | ||
| 62 | if (nano_seconds == 0) { | ||
| 63 | return {RESULT_TIMEOUT, InvalidHandle}; | ||
| 64 | } | ||
| 65 | |||
| 66 | if (thread->IsSyncCancelled()) { | ||
| 67 | thread->SetSyncCancelled(false); | ||
| 68 | return {ERR_SYNCHRONIZATION_CANCELED, InvalidHandle}; | ||
| 69 | } | ||
| 70 | |||
| 71 | for (auto& object : sync_objects) { | ||
| 72 | object->AddWaitingThread(SharedFrom(thread)); | ||
| 73 | } | ||
| 74 | |||
| 75 | thread->SetSynchronizationObjects(std::move(sync_objects)); | ||
| 76 | thread->SetStatus(ThreadStatus::WaitSynch); | ||
| 77 | |||
| 78 | // Create an event to wake the thread up after the specified nanosecond delay has passed | ||
| 79 | thread->WakeAfterDelay(nano_seconds); | ||
| 80 | thread->SetWakeupCallback(DefaultThreadWakeupCallback); | ||
| 81 | |||
| 82 | system.PrepareReschedule(thread->GetProcessorID()); | ||
| 83 | |||
| 84 | return {RESULT_TIMEOUT, InvalidHandle}; | ||
| 85 | } | ||
| 86 | |||
| 87 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/synchronization.h b/src/core/hle/kernel/synchronization.h new file mode 100644 index 000000000..379f4b1d3 --- /dev/null +++ b/src/core/hle/kernel/synchronization.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "core/hle/kernel/object.h" | ||
| 12 | #include "core/hle/result.h" | ||
| 13 | |||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } // namespace Core | ||
| 17 | |||
| 18 | namespace Kernel { | ||
| 19 | |||
| 20 | class SynchronizationObject; | ||
| 21 | |||
| 22 | /** | ||
| 23 | * The 'Synchronization' class is an interface for handling synchronization methods | ||
| 24 | * used by Synchronization objects and synchronization SVCs. This centralizes processing of | ||
| 25 | * such | ||
| 26 | */ | ||
| 27 | class Synchronization { | ||
| 28 | public: | ||
| 29 | explicit Synchronization(Core::System& system); | ||
| 30 | |||
| 31 | /// Signals a synchronization object, waking up all its waiting threads | ||
| 32 | void SignalObject(SynchronizationObject& obj) const; | ||
| 33 | |||
| 34 | /// Tries to see if waiting for any of the sync_objects is necessary, if not | ||
| 35 | /// it returns Success and the handle index of the signaled sync object. In | ||
| 36 | /// case not, the current thread will be locked and wait for nano_seconds or | ||
| 37 | /// for a synchronization object to signal. | ||
| 38 | std::pair<ResultCode, Handle> WaitFor( | ||
| 39 | std::vector<std::shared_ptr<SynchronizationObject>>& sync_objects, s64 nano_seconds); | ||
| 40 | |||
| 41 | private: | ||
| 42 | Core::System& system; | ||
| 43 | }; | ||
| 44 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/synchronization_object.cpp index 745f2c4e8..43f3eef18 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/synchronization_object.cpp | |||
| @@ -7,24 +7,29 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/core_cpu.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/object.h" | 11 | #include "core/hle/kernel/object.h" |
| 13 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/kernel/synchronization.h" | ||
| 14 | #include "core/hle/kernel/synchronization_object.h" | ||
| 14 | #include "core/hle/kernel/thread.h" | 15 | #include "core/hle/kernel/thread.h" |
| 15 | 16 | ||
| 16 | namespace Kernel { | 17 | namespace Kernel { |
| 17 | 18 | ||
| 18 | WaitObject::WaitObject(KernelCore& kernel) : Object{kernel} {} | 19 | SynchronizationObject::SynchronizationObject(KernelCore& kernel) : Object{kernel} {} |
| 19 | WaitObject::~WaitObject() = default; | 20 | SynchronizationObject::~SynchronizationObject() = default; |
| 20 | 21 | ||
| 21 | void WaitObject::AddWaitingThread(std::shared_ptr<Thread> thread) { | 22 | void SynchronizationObject::Signal() { |
| 23 | kernel.Synchronization().SignalObject(*this); | ||
| 24 | } | ||
| 25 | |||
| 26 | void SynchronizationObject::AddWaitingThread(std::shared_ptr<Thread> thread) { | ||
| 22 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | 27 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); |
| 23 | if (itr == waiting_threads.end()) | 28 | if (itr == waiting_threads.end()) |
| 24 | waiting_threads.push_back(std::move(thread)); | 29 | waiting_threads.push_back(std::move(thread)); |
| 25 | } | 30 | } |
| 26 | 31 | ||
| 27 | void WaitObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) { | 32 | void SynchronizationObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) { |
| 28 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); | 33 | auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); |
| 29 | // If a thread passed multiple handles to the same object, | 34 | // If a thread passed multiple handles to the same object, |
| 30 | // the kernel might attempt to remove the thread from the object's | 35 | // the kernel might attempt to remove the thread from the object's |
| @@ -33,7 +38,7 @@ void WaitObject::RemoveWaitingThread(std::shared_ptr<Thread> thread) { | |||
| 33 | waiting_threads.erase(itr); | 38 | waiting_threads.erase(itr); |
| 34 | } | 39 | } |
| 35 | 40 | ||
| 36 | std::shared_ptr<Thread> WaitObject::GetHighestPriorityReadyThread() const { | 41 | std::shared_ptr<Thread> SynchronizationObject::GetHighestPriorityReadyThread() const { |
| 37 | Thread* candidate = nullptr; | 42 | Thread* candidate = nullptr; |
| 38 | u32 candidate_priority = THREADPRIO_LOWEST + 1; | 43 | u32 candidate_priority = THREADPRIO_LOWEST + 1; |
| 39 | 44 | ||
| @@ -51,23 +56,14 @@ std::shared_ptr<Thread> WaitObject::GetHighestPriorityReadyThread() const { | |||
| 51 | if (ShouldWait(thread.get())) | 56 | if (ShouldWait(thread.get())) |
| 52 | continue; | 57 | continue; |
| 53 | 58 | ||
| 54 | // A thread is ready to run if it's either in ThreadStatus::WaitSynch | 59 | candidate = thread.get(); |
| 55 | // and the rest of the objects it is waiting on are ready. | 60 | candidate_priority = thread->GetPriority(); |
| 56 | bool ready_to_run = true; | ||
| 57 | if (thread_status == ThreadStatus::WaitSynch) { | ||
| 58 | ready_to_run = thread->AllWaitObjectsReady(); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (ready_to_run) { | ||
| 62 | candidate = thread.get(); | ||
| 63 | candidate_priority = thread->GetPriority(); | ||
| 64 | } | ||
| 65 | } | 61 | } |
| 66 | 62 | ||
| 67 | return SharedFrom(candidate); | 63 | return SharedFrom(candidate); |
| 68 | } | 64 | } |
| 69 | 65 | ||
| 70 | void WaitObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) { | 66 | void SynchronizationObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) { |
| 71 | ASSERT(!ShouldWait(thread.get())); | 67 | ASSERT(!ShouldWait(thread.get())); |
| 72 | 68 | ||
| 73 | if (!thread) { | 69 | if (!thread) { |
| @@ -75,7 +71,7 @@ void WaitObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) { | |||
| 75 | } | 71 | } |
| 76 | 72 | ||
| 77 | if (thread->IsSleepingOnWait()) { | 73 | if (thread->IsSleepingOnWait()) { |
| 78 | for (const auto& object : thread->GetWaitObjects()) { | 74 | for (const auto& object : thread->GetSynchronizationObjects()) { |
| 79 | ASSERT(!object->ShouldWait(thread.get())); | 75 | ASSERT(!object->ShouldWait(thread.get())); |
| 80 | object->Acquire(thread.get()); | 76 | object->Acquire(thread.get()); |
| 81 | } | 77 | } |
| @@ -83,9 +79,9 @@ void WaitObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) { | |||
| 83 | Acquire(thread.get()); | 79 | Acquire(thread.get()); |
| 84 | } | 80 | } |
| 85 | 81 | ||
| 86 | const std::size_t index = thread->GetWaitObjectIndex(SharedFrom(this)); | 82 | const std::size_t index = thread->GetSynchronizationObjectIndex(SharedFrom(this)); |
| 87 | 83 | ||
| 88 | thread->ClearWaitObjects(); | 84 | thread->ClearSynchronizationObjects(); |
| 89 | 85 | ||
| 90 | thread->CancelWakeupTimer(); | 86 | thread->CancelWakeupTimer(); |
| 91 | 87 | ||
| @@ -96,17 +92,17 @@ void WaitObject::WakeupWaitingThread(std::shared_ptr<Thread> thread) { | |||
| 96 | } | 92 | } |
| 97 | if (resume) { | 93 | if (resume) { |
| 98 | thread->ResumeFromWait(); | 94 | thread->ResumeFromWait(); |
| 99 | Core::System::GetInstance().PrepareReschedule(thread->GetProcessorID()); | 95 | kernel.PrepareReschedule(thread->GetProcessorID()); |
| 100 | } | 96 | } |
| 101 | } | 97 | } |
| 102 | 98 | ||
| 103 | void WaitObject::WakeupAllWaitingThreads() { | 99 | void SynchronizationObject::WakeupAllWaitingThreads() { |
| 104 | while (auto thread = GetHighestPriorityReadyThread()) { | 100 | while (auto thread = GetHighestPriorityReadyThread()) { |
| 105 | WakeupWaitingThread(thread); | 101 | WakeupWaitingThread(thread); |
| 106 | } | 102 | } |
| 107 | } | 103 | } |
| 108 | 104 | ||
| 109 | const std::vector<std::shared_ptr<Thread>>& WaitObject::GetWaitingThreads() const { | 105 | const std::vector<std::shared_ptr<Thread>>& SynchronizationObject::GetWaitingThreads() const { |
| 110 | return waiting_threads; | 106 | return waiting_threads; |
| 111 | } | 107 | } |
| 112 | 108 | ||
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/synchronization_object.h index 9a17958a4..741c31faf 100644 --- a/src/core/hle/kernel/wait_object.h +++ b/src/core/hle/kernel/synchronization_object.h | |||
| @@ -15,10 +15,10 @@ class KernelCore; | |||
| 15 | class Thread; | 15 | class Thread; |
| 16 | 16 | ||
| 17 | /// Class that represents a Kernel object that a thread can be waiting on | 17 | /// Class that represents a Kernel object that a thread can be waiting on |
| 18 | class WaitObject : public Object { | 18 | class SynchronizationObject : public Object { |
| 19 | public: | 19 | public: |
| 20 | explicit WaitObject(KernelCore& kernel); | 20 | explicit SynchronizationObject(KernelCore& kernel); |
| 21 | ~WaitObject() override; | 21 | ~SynchronizationObject() override; |
| 22 | 22 | ||
| 23 | /** | 23 | /** |
| 24 | * Check if the specified thread should wait until the object is available | 24 | * Check if the specified thread should wait until the object is available |
| @@ -30,6 +30,13 @@ public: | |||
| 30 | /// Acquire/lock the object for the specified thread if it is available | 30 | /// Acquire/lock the object for the specified thread if it is available |
| 31 | virtual void Acquire(Thread* thread) = 0; | 31 | virtual void Acquire(Thread* thread) = 0; |
| 32 | 32 | ||
| 33 | /// Signal this object | ||
| 34 | virtual void Signal(); | ||
| 35 | |||
| 36 | virtual bool IsSignaled() const { | ||
| 37 | return is_signaled; | ||
| 38 | } | ||
| 39 | |||
| 33 | /** | 40 | /** |
| 34 | * Add a thread to wait on this object | 41 | * Add a thread to wait on this object |
| 35 | * @param thread Pointer to thread to add | 42 | * @param thread Pointer to thread to add |
| @@ -60,16 +67,20 @@ public: | |||
| 60 | /// Get a const reference to the waiting threads list for debug use | 67 | /// Get a const reference to the waiting threads list for debug use |
| 61 | const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; | 68 | const std::vector<std::shared_ptr<Thread>>& GetWaitingThreads() const; |
| 62 | 69 | ||
| 70 | protected: | ||
| 71 | bool is_signaled{}; // Tells if this sync object is signalled; | ||
| 72 | |||
| 63 | private: | 73 | private: |
| 64 | /// Threads waiting for this object to become available | 74 | /// Threads waiting for this object to become available |
| 65 | std::vector<std::shared_ptr<Thread>> waiting_threads; | 75 | std::vector<std::shared_ptr<Thread>> waiting_threads; |
| 66 | }; | 76 | }; |
| 67 | 77 | ||
| 68 | // Specialization of DynamicObjectCast for WaitObjects | 78 | // Specialization of DynamicObjectCast for SynchronizationObjects |
| 69 | template <> | 79 | template <> |
| 70 | inline std::shared_ptr<WaitObject> DynamicObjectCast<WaitObject>(std::shared_ptr<Object> object) { | 80 | inline std::shared_ptr<SynchronizationObject> DynamicObjectCast<SynchronizationObject>( |
| 81 | std::shared_ptr<Object> object) { | ||
| 71 | if (object != nullptr && object->IsWaitable()) { | 82 | if (object != nullptr && object->IsWaitable()) { |
| 72 | return std::static_pointer_cast<WaitObject>(object); | 83 | return std::static_pointer_cast<SynchronizationObject>(object); |
| 73 | } | 84 | } |
| 74 | return nullptr; | 85 | return nullptr; |
| 75 | } | 86 | } |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index e84e5ce0d..ae5f2c8bd 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -13,9 +13,9 @@ | |||
| 13 | #include "common/thread_queue_list.h" | 13 | #include "common/thread_queue_list.h" |
| 14 | #include "core/arm/arm_interface.h" | 14 | #include "core/arm/arm_interface.h" |
| 15 | #include "core/core.h" | 15 | #include "core/core.h" |
| 16 | #include "core/core_cpu.h" | ||
| 17 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 18 | #include "core/core_timing_util.h" | 17 | #include "core/core_timing_util.h" |
| 18 | #include "core/hardware_properties.h" | ||
| 19 | #include "core/hle/kernel/errors.h" | 19 | #include "core/hle/kernel/errors.h" |
| 20 | #include "core/hle/kernel/handle_table.h" | 20 | #include "core/hle/kernel/handle_table.h" |
| 21 | #include "core/hle/kernel/kernel.h" | 21 | #include "core/hle/kernel/kernel.h" |
| @@ -32,11 +32,15 @@ bool Thread::ShouldWait(const Thread* thread) const { | |||
| 32 | return status != ThreadStatus::Dead; | 32 | return status != ThreadStatus::Dead; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | bool Thread::IsSignaled() const { | ||
| 36 | return status == ThreadStatus::Dead; | ||
| 37 | } | ||
| 38 | |||
| 35 | void Thread::Acquire(Thread* thread) { | 39 | void Thread::Acquire(Thread* thread) { |
| 36 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 40 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 37 | } | 41 | } |
| 38 | 42 | ||
| 39 | Thread::Thread(KernelCore& kernel) : WaitObject{kernel} {} | 43 | Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} |
| 40 | Thread::~Thread() = default; | 44 | Thread::~Thread() = default; |
| 41 | 45 | ||
| 42 | void Thread::Stop() { | 46 | void Thread::Stop() { |
| @@ -46,7 +50,7 @@ void Thread::Stop() { | |||
| 46 | kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); | 50 | kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); |
| 47 | callback_handle = 0; | 51 | callback_handle = 0; |
| 48 | SetStatus(ThreadStatus::Dead); | 52 | SetStatus(ThreadStatus::Dead); |
| 49 | WakeupAllWaitingThreads(); | 53 | Signal(); |
| 50 | 54 | ||
| 51 | // Clean up any dangling references in objects that this thread was waiting for | 55 | // Clean up any dangling references in objects that this thread was waiting for |
| 52 | for (auto& wait_object : wait_objects) { | 56 | for (auto& wait_object : wait_objects) { |
| @@ -216,7 +220,7 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { | |||
| 216 | context.cpu_registers[1] = output; | 220 | context.cpu_registers[1] = output; |
| 217 | } | 221 | } |
| 218 | 222 | ||
| 219 | s32 Thread::GetWaitObjectIndex(std::shared_ptr<WaitObject> object) const { | 223 | s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const { |
| 220 | ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); | 224 | ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); |
| 221 | const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); | 225 | const auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); |
| 222 | return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1); | 226 | return static_cast<s32>(std::distance(match, wait_objects.rend()) - 1); |
| @@ -337,14 +341,16 @@ void Thread::ChangeCore(u32 core, u64 mask) { | |||
| 337 | SetCoreAndAffinityMask(core, mask); | 341 | SetCoreAndAffinityMask(core, mask); |
| 338 | } | 342 | } |
| 339 | 343 | ||
| 340 | bool Thread::AllWaitObjectsReady() const { | 344 | bool Thread::AllSynchronizationObjectsReady() const { |
| 341 | return std::none_of( | 345 | return std::none_of(wait_objects.begin(), wait_objects.end(), |
| 342 | wait_objects.begin(), wait_objects.end(), | 346 | [this](const std::shared_ptr<SynchronizationObject>& object) { |
| 343 | [this](const std::shared_ptr<WaitObject>& object) { return object->ShouldWait(this); }); | 347 | return object->ShouldWait(this); |
| 348 | }); | ||
| 344 | } | 349 | } |
| 345 | 350 | ||
| 346 | bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 351 | bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, |
| 347 | std::shared_ptr<WaitObject> object, std::size_t index) { | 352 | std::shared_ptr<SynchronizationObject> object, |
| 353 | std::size_t index) { | ||
| 348 | ASSERT(wakeup_callback); | 354 | ASSERT(wakeup_callback); |
| 349 | return wakeup_callback(reason, std::move(thread), std::move(object), index); | 355 | return wakeup_callback(reason, std::move(thread), std::move(object), index); |
| 350 | } | 356 | } |
| @@ -356,7 +362,7 @@ void Thread::SetActivity(ThreadActivity value) { | |||
| 356 | // Set status if not waiting | 362 | // Set status if not waiting |
| 357 | if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { | 363 | if (status == ThreadStatus::Ready || status == ThreadStatus::Running) { |
| 358 | SetStatus(ThreadStatus::Paused); | 364 | SetStatus(ThreadStatus::Paused); |
| 359 | Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); | 365 | kernel.PrepareReschedule(processor_id); |
| 360 | } | 366 | } |
| 361 | } else if (status == ThreadStatus::Paused) { | 367 | } else if (status == ThreadStatus::Paused) { |
| 362 | // Ready to reschedule | 368 | // Ready to reschedule |
| @@ -426,7 +432,7 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { | |||
| 426 | const s32 old_core = processor_id; | 432 | const s32 old_core = processor_id; |
| 427 | if (processor_id >= 0 && ((affinity_mask >> processor_id) & 1) == 0) { | 433 | if (processor_id >= 0 && ((affinity_mask >> processor_id) & 1) == 0) { |
| 428 | if (static_cast<s32>(ideal_core) < 0) { | 434 | if (static_cast<s32>(ideal_core) < 0) { |
| 429 | processor_id = HighestSetCore(affinity_mask, GlobalScheduler::NUM_CPU_CORES); | 435 | processor_id = HighestSetCore(affinity_mask, Core::Hardware::NUM_CPU_CORES); |
| 430 | } else { | 436 | } else { |
| 431 | processor_id = ideal_core; | 437 | processor_id = ideal_core; |
| 432 | } | 438 | } |
| @@ -450,7 +456,7 @@ void Thread::AdjustSchedulingOnStatus(u32 old_flags) { | |||
| 450 | scheduler.Unschedule(current_priority, static_cast<u32>(processor_id), this); | 456 | scheduler.Unschedule(current_priority, static_cast<u32>(processor_id), this); |
| 451 | } | 457 | } |
| 452 | 458 | ||
| 453 | for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) { | 459 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 454 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | 460 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { |
| 455 | scheduler.Unsuggest(current_priority, core, this); | 461 | scheduler.Unsuggest(current_priority, core, this); |
| 456 | } | 462 | } |
| @@ -461,7 +467,7 @@ void Thread::AdjustSchedulingOnStatus(u32 old_flags) { | |||
| 461 | scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this); | 467 | scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this); |
| 462 | } | 468 | } |
| 463 | 469 | ||
| 464 | for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) { | 470 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 465 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | 471 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { |
| 466 | scheduler.Suggest(current_priority, core, this); | 472 | scheduler.Suggest(current_priority, core, this); |
| 467 | } | 473 | } |
| @@ -475,12 +481,12 @@ void Thread::AdjustSchedulingOnPriority(u32 old_priority) { | |||
| 475 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) { | 481 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) { |
| 476 | return; | 482 | return; |
| 477 | } | 483 | } |
| 478 | auto& scheduler = Core::System::GetInstance().GlobalScheduler(); | 484 | auto& scheduler = kernel.GlobalScheduler(); |
| 479 | if (processor_id >= 0) { | 485 | if (processor_id >= 0) { |
| 480 | scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this); | 486 | scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this); |
| 481 | } | 487 | } |
| 482 | 488 | ||
| 483 | for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) { | 489 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 484 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | 490 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { |
| 485 | scheduler.Unsuggest(old_priority, core, this); | 491 | scheduler.Unsuggest(old_priority, core, this); |
| 486 | } | 492 | } |
| @@ -497,7 +503,7 @@ void Thread::AdjustSchedulingOnPriority(u32 old_priority) { | |||
| 497 | } | 503 | } |
| 498 | } | 504 | } |
| 499 | 505 | ||
| 500 | for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) { | 506 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 501 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { | 507 | if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) { |
| 502 | scheduler.Suggest(current_priority, core, this); | 508 | scheduler.Suggest(current_priority, core, this); |
| 503 | } | 509 | } |
| @@ -507,13 +513,13 @@ void Thread::AdjustSchedulingOnPriority(u32 old_priority) { | |||
| 507 | } | 513 | } |
| 508 | 514 | ||
| 509 | void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) { | 515 | void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) { |
| 510 | auto& scheduler = Core::System::GetInstance().GlobalScheduler(); | 516 | auto& scheduler = kernel.GlobalScheduler(); |
| 511 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable || | 517 | if (GetSchedulingStatus() != ThreadSchedStatus::Runnable || |
| 512 | current_priority >= THREADPRIO_COUNT) { | 518 | current_priority >= THREADPRIO_COUNT) { |
| 513 | return; | 519 | return; |
| 514 | } | 520 | } |
| 515 | 521 | ||
| 516 | for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) { | 522 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 517 | if (((old_affinity_mask >> core) & 1) != 0) { | 523 | if (((old_affinity_mask >> core) & 1) != 0) { |
| 518 | if (core == static_cast<u32>(old_core)) { | 524 | if (core == static_cast<u32>(old_core)) { |
| 519 | scheduler.Unschedule(current_priority, core, this); | 525 | scheduler.Unschedule(current_priority, core, this); |
| @@ -523,7 +529,7 @@ void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) { | |||
| 523 | } | 529 | } |
| 524 | } | 530 | } |
| 525 | 531 | ||
| 526 | for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) { | 532 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 527 | if (((affinity_mask >> core) & 1) != 0) { | 533 | if (((affinity_mask >> core) & 1) != 0) { |
| 528 | if (core == static_cast<u32>(processor_id)) { | 534 | if (core == static_cast<u32>(processor_id)) { |
| 529 | scheduler.Schedule(current_priority, core, this); | 535 | scheduler.Schedule(current_priority, core, this); |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 3bcf9e137..7a4916318 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/arm/arm_interface.h" | 12 | #include "core/arm/arm_interface.h" |
| 13 | #include "core/hle/kernel/object.h" | 13 | #include "core/hle/kernel/object.h" |
| 14 | #include "core/hle/kernel/wait_object.h" | 14 | #include "core/hle/kernel/synchronization_object.h" |
| 15 | #include "core/hle/result.h" | 15 | #include "core/hle/result.h" |
| 16 | 16 | ||
| 17 | namespace Kernel { | 17 | namespace Kernel { |
| @@ -95,7 +95,7 @@ enum class ThreadSchedMasks : u32 { | |||
| 95 | ForcePauseMask = 0x0070, | 95 | ForcePauseMask = 0x0070, |
| 96 | }; | 96 | }; |
| 97 | 97 | ||
| 98 | class Thread final : public WaitObject { | 98 | class Thread final : public SynchronizationObject { |
| 99 | public: | 99 | public: |
| 100 | explicit Thread(KernelCore& kernel); | 100 | explicit Thread(KernelCore& kernel); |
| 101 | ~Thread() override; | 101 | ~Thread() override; |
| @@ -104,11 +104,11 @@ public: | |||
| 104 | 104 | ||
| 105 | using ThreadContext = Core::ARM_Interface::ThreadContext; | 105 | using ThreadContext = Core::ARM_Interface::ThreadContext; |
| 106 | 106 | ||
| 107 | using ThreadWaitObjects = std::vector<std::shared_ptr<WaitObject>>; | 107 | using ThreadSynchronizationObjects = std::vector<std::shared_ptr<SynchronizationObject>>; |
| 108 | 108 | ||
| 109 | using WakeupCallback = | 109 | using WakeupCallback = |
| 110 | std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 110 | std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, |
| 111 | std::shared_ptr<WaitObject> object, std::size_t index)>; | 111 | std::shared_ptr<SynchronizationObject> object, std::size_t index)>; |
| 112 | 112 | ||
| 113 | /** | 113 | /** |
| 114 | * Creates and returns a new thread. The new thread is immediately scheduled | 114 | * Creates and returns a new thread. The new thread is immediately scheduled |
| @@ -146,6 +146,7 @@ public: | |||
| 146 | 146 | ||
| 147 | bool ShouldWait(const Thread* thread) const override; | 147 | bool ShouldWait(const Thread* thread) const override; |
| 148 | void Acquire(Thread* thread) override; | 148 | void Acquire(Thread* thread) override; |
| 149 | bool IsSignaled() const override; | ||
| 149 | 150 | ||
| 150 | /** | 151 | /** |
| 151 | * Gets the thread's current priority | 152 | * Gets the thread's current priority |
| @@ -233,7 +234,7 @@ public: | |||
| 233 | * | 234 | * |
| 234 | * @param object Object to query the index of. | 235 | * @param object Object to query the index of. |
| 235 | */ | 236 | */ |
| 236 | s32 GetWaitObjectIndex(std::shared_ptr<WaitObject> object) const; | 237 | s32 GetSynchronizationObjectIndex(std::shared_ptr<SynchronizationObject> object) const; |
| 237 | 238 | ||
| 238 | /** | 239 | /** |
| 239 | * Stops a thread, invalidating it from further use | 240 | * Stops a thread, invalidating it from further use |
| @@ -314,15 +315,15 @@ public: | |||
| 314 | return owner_process; | 315 | return owner_process; |
| 315 | } | 316 | } |
| 316 | 317 | ||
| 317 | const ThreadWaitObjects& GetWaitObjects() const { | 318 | const ThreadSynchronizationObjects& GetSynchronizationObjects() const { |
| 318 | return wait_objects; | 319 | return wait_objects; |
| 319 | } | 320 | } |
| 320 | 321 | ||
| 321 | void SetWaitObjects(ThreadWaitObjects objects) { | 322 | void SetSynchronizationObjects(ThreadSynchronizationObjects objects) { |
| 322 | wait_objects = std::move(objects); | 323 | wait_objects = std::move(objects); |
| 323 | } | 324 | } |
| 324 | 325 | ||
| 325 | void ClearWaitObjects() { | 326 | void ClearSynchronizationObjects() { |
| 326 | for (const auto& waiting_object : wait_objects) { | 327 | for (const auto& waiting_object : wait_objects) { |
| 327 | waiting_object->RemoveWaitingThread(SharedFrom(this)); | 328 | waiting_object->RemoveWaitingThread(SharedFrom(this)); |
| 328 | } | 329 | } |
| @@ -330,7 +331,7 @@ public: | |||
| 330 | } | 331 | } |
| 331 | 332 | ||
| 332 | /// Determines whether all the objects this thread is waiting on are ready. | 333 | /// Determines whether all the objects this thread is waiting on are ready. |
| 333 | bool AllWaitObjectsReady() const; | 334 | bool AllSynchronizationObjectsReady() const; |
| 334 | 335 | ||
| 335 | const MutexWaitingThreads& GetMutexWaitingThreads() const { | 336 | const MutexWaitingThreads& GetMutexWaitingThreads() const { |
| 336 | return wait_mutex_threads; | 337 | return wait_mutex_threads; |
| @@ -395,7 +396,7 @@ public: | |||
| 395 | * will cause an assertion to trigger. | 396 | * will cause an assertion to trigger. |
| 396 | */ | 397 | */ |
| 397 | bool InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, | 398 | bool InvokeWakeupCallback(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, |
| 398 | std::shared_ptr<WaitObject> object, std::size_t index); | 399 | std::shared_ptr<SynchronizationObject> object, std::size_t index); |
| 399 | 400 | ||
| 400 | u32 GetIdealCore() const { | 401 | u32 GetIdealCore() const { |
| 401 | return ideal_core; | 402 | return ideal_core; |
| @@ -494,7 +495,7 @@ private: | |||
| 494 | 495 | ||
| 495 | /// Objects that the thread is waiting on, in the same order as they were | 496 | /// Objects that the thread is waiting on, in the same order as they were |
| 496 | /// passed to WaitSynchronization. | 497 | /// passed to WaitSynchronization. |
| 497 | ThreadWaitObjects wait_objects; | 498 | ThreadSynchronizationObjects wait_objects; |
| 498 | 499 | ||
| 499 | /// List of threads that are waiting for a mutex that is held by this thread. | 500 | /// List of threads that are waiting for a mutex that is held by this thread. |
| 500 | MutexWaitingThreads wait_mutex_threads; | 501 | MutexWaitingThreads wait_mutex_threads; |
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp index f0e73f57b..f2d3f8b49 100644 --- a/src/core/hle/kernel/transfer_memory.cpp +++ b/src/core/hle/kernel/transfer_memory.cpp | |||
| @@ -8,15 +8,23 @@ | |||
| 8 | #include "core/hle/kernel/shared_memory.h" | 8 | #include "core/hle/kernel/shared_memory.h" |
| 9 | #include "core/hle/kernel/transfer_memory.h" | 9 | #include "core/hle/kernel/transfer_memory.h" |
| 10 | #include "core/hle/result.h" | 10 | #include "core/hle/result.h" |
| 11 | #include "core/memory.h" | ||
| 11 | 12 | ||
| 12 | namespace Kernel { | 13 | namespace Kernel { |
| 13 | 14 | ||
| 14 | TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {} | 15 | TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory) |
| 15 | TransferMemory::~TransferMemory() = default; | 16 | : Object{kernel}, memory{memory} {} |
| 16 | 17 | ||
| 17 | std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, | 18 | TransferMemory::~TransferMemory() { |
| 18 | u64 size, MemoryPermission permissions) { | 19 | // Release memory region when transfer memory is destroyed |
| 19 | std::shared_ptr<TransferMemory> transfer_memory{std::make_shared<TransferMemory>(kernel)}; | 20 | Reset(); |
| 21 | } | ||
| 22 | |||
| 23 | std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory, | ||
| 24 | VAddr base_address, u64 size, | ||
| 25 | MemoryPermission permissions) { | ||
| 26 | std::shared_ptr<TransferMemory> transfer_memory{ | ||
| 27 | std::make_shared<TransferMemory>(kernel, memory)}; | ||
| 20 | 28 | ||
| 21 | transfer_memory->base_address = base_address; | 29 | transfer_memory->base_address = base_address; |
| 22 | transfer_memory->memory_size = size; | 30 | transfer_memory->memory_size = size; |
| @@ -27,7 +35,7 @@ std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr | |||
| 27 | } | 35 | } |
| 28 | 36 | ||
| 29 | const u8* TransferMemory::GetPointer() const { | 37 | const u8* TransferMemory::GetPointer() const { |
| 30 | return backing_block.get()->data(); | 38 | return memory.GetPointer(base_address); |
| 31 | } | 39 | } |
| 32 | 40 | ||
| 33 | u64 TransferMemory::GetSize() const { | 41 | u64 TransferMemory::GetSize() const { |
| @@ -62,6 +70,52 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p | |||
| 62 | return RESULT_SUCCESS; | 70 | return RESULT_SUCCESS; |
| 63 | } | 71 | } |
| 64 | 72 | ||
| 73 | ResultCode TransferMemory::Reserve() { | ||
| 74 | auto& vm_manager{owner_process->VMManager()}; | ||
| 75 | const auto check_range_result{vm_manager.CheckRangeState( | ||
| 76 | base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, | ||
| 77 | MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All, | ||
| 78 | VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, | ||
| 79 | MemoryAttribute::IpcAndDeviceMapped)}; | ||
| 80 | |||
| 81 | if (check_range_result.Failed()) { | ||
| 82 | return check_range_result.Code(); | ||
| 83 | } | ||
| 84 | |||
| 85 | auto [state_, permissions_, attribute] = *check_range_result; | ||
| 86 | |||
| 87 | if (const auto result{vm_manager.ReprotectRange( | ||
| 88 | base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))}; | ||
| 89 | result.IsError()) { | ||
| 90 | return result; | ||
| 91 | } | ||
| 92 | |||
| 93 | return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, | ||
| 94 | attribute | MemoryAttribute::Locked); | ||
| 95 | } | ||
| 96 | |||
| 97 | ResultCode TransferMemory::Reset() { | ||
| 98 | auto& vm_manager{owner_process->VMManager()}; | ||
| 99 | if (const auto result{vm_manager.CheckRangeState( | ||
| 100 | base_address, memory_size, | ||
| 101 | MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, | ||
| 102 | MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None, | ||
| 103 | VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, | ||
| 104 | MemoryAttribute::IpcAndDeviceMapped)}; | ||
| 105 | result.Failed()) { | ||
| 106 | return result.Code(); | ||
| 107 | } | ||
| 108 | |||
| 109 | if (const auto result{ | ||
| 110 | vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)}; | ||
| 111 | result.IsError()) { | ||
| 112 | return result; | ||
| 113 | } | ||
| 114 | |||
| 115 | return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, | ||
| 116 | MemoryAttribute::None); | ||
| 117 | } | ||
| 118 | |||
| 65 | ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) { | 119 | ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) { |
| 66 | if (memory_size != size) { | 120 | if (memory_size != size) { |
| 67 | return ERR_INVALID_SIZE; | 121 | return ERR_INVALID_SIZE; |
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h index 0a6e15d18..6e388536a 100644 --- a/src/core/hle/kernel/transfer_memory.h +++ b/src/core/hle/kernel/transfer_memory.h | |||
| @@ -11,6 +11,10 @@ | |||
| 11 | 11 | ||
| 12 | union ResultCode; | 12 | union ResultCode; |
| 13 | 13 | ||
| 14 | namespace Memory { | ||
| 15 | class Memory; | ||
| 16 | } | ||
| 17 | |||
| 14 | namespace Kernel { | 18 | namespace Kernel { |
| 15 | 19 | ||
| 16 | class KernelCore; | 20 | class KernelCore; |
| @@ -26,12 +30,13 @@ enum class MemoryPermission : u32; | |||
| 26 | /// | 30 | /// |
| 27 | class TransferMemory final : public Object { | 31 | class TransferMemory final : public Object { |
| 28 | public: | 32 | public: |
| 29 | explicit TransferMemory(KernelCore& kernel); | 33 | explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory); |
| 30 | ~TransferMemory() override; | 34 | ~TransferMemory() override; |
| 31 | 35 | ||
| 32 | static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; | 36 | static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; |
| 33 | 37 | ||
| 34 | static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size, | 38 | static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory, |
| 39 | VAddr base_address, u64 size, | ||
| 35 | MemoryPermission permissions); | 40 | MemoryPermission permissions); |
| 36 | 41 | ||
| 37 | TransferMemory(const TransferMemory&) = delete; | 42 | TransferMemory(const TransferMemory&) = delete; |
| @@ -80,6 +85,14 @@ public: | |||
| 80 | /// | 85 | /// |
| 81 | ResultCode UnmapMemory(VAddr address, u64 size); | 86 | ResultCode UnmapMemory(VAddr address, u64 size); |
| 82 | 87 | ||
| 88 | /// Reserves the region to be used for the transfer memory, called after the transfer memory is | ||
| 89 | /// created. | ||
| 90 | ResultCode Reserve(); | ||
| 91 | |||
| 92 | /// Resets the region previously used for the transfer memory, called after the transfer memory | ||
| 93 | /// is closed. | ||
| 94 | ResultCode Reset(); | ||
| 95 | |||
| 83 | private: | 96 | private: |
| 84 | /// Memory block backing this instance. | 97 | /// Memory block backing this instance. |
| 85 | std::shared_ptr<PhysicalMemory> backing_block; | 98 | std::shared_ptr<PhysicalMemory> backing_block; |
| @@ -98,6 +111,8 @@ private: | |||
| 98 | 111 | ||
| 99 | /// Whether or not this transfer memory instance has mapped memory. | 112 | /// Whether or not this transfer memory instance has mapped memory. |
| 100 | bool is_mapped = false; | 113 | bool is_mapped = false; |
| 114 | |||
| 115 | Memory::Memory& memory; | ||
| 101 | }; | 116 | }; |
| 102 | 117 | ||
| 103 | } // namespace Kernel | 118 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 0b3500fce..024c22901 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -544,7 +544,8 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const { | |||
| 544 | 544 | ||
| 545 | ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, | 545 | ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, |
| 546 | MemoryAttribute attribute) { | 546 | MemoryAttribute attribute) { |
| 547 | constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped; | 547 | constexpr auto ignore_mask = |
| 548 | MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped | MemoryAttribute::Locked; | ||
| 548 | constexpr auto attribute_mask = ~ignore_mask; | 549 | constexpr auto attribute_mask = ~ignore_mask; |
| 549 | 550 | ||
| 550 | const auto result = CheckRangeState( | 551 | const auto result = CheckRangeState( |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 850a7ebc3..90b4b006a 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -98,6 +98,8 @@ enum class MemoryAttribute : u32 { | |||
| 98 | DeviceMapped = 4, | 98 | DeviceMapped = 4, |
| 99 | /// Uncached memory | 99 | /// Uncached memory |
| 100 | Uncached = 8, | 100 | Uncached = 8, |
| 101 | |||
| 102 | IpcAndDeviceMapped = LockedForIPC | DeviceMapped, | ||
| 101 | }; | 103 | }; |
| 102 | 104 | ||
| 103 | constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { | 105 | constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { |
| @@ -654,6 +656,35 @@ public: | |||
| 654 | /// is scheduled. | 656 | /// is scheduled. |
| 655 | Common::PageTable page_table{Memory::PAGE_BITS}; | 657 | Common::PageTable page_table{Memory::PAGE_BITS}; |
| 656 | 658 | ||
| 659 | using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>; | ||
| 660 | |||
| 661 | /// Checks if an address range adheres to the specified states provided. | ||
| 662 | /// | ||
| 663 | /// @param address The starting address of the address range. | ||
| 664 | /// @param size The size of the address range. | ||
| 665 | /// @param state_mask The memory state mask. | ||
| 666 | /// @param state The state to compare the individual VMA states against, | ||
| 667 | /// which is done in the form of: (vma.state & state_mask) != state. | ||
| 668 | /// @param permission_mask The memory permissions mask. | ||
| 669 | /// @param permissions The permission to compare the individual VMA permissions against, | ||
| 670 | /// which is done in the form of: | ||
| 671 | /// (vma.permission & permission_mask) != permission. | ||
| 672 | /// @param attribute_mask The memory attribute mask. | ||
| 673 | /// @param attribute The memory attributes to compare the individual VMA attributes | ||
| 674 | /// against, which is done in the form of: | ||
| 675 | /// (vma.attributes & attribute_mask) != attribute. | ||
| 676 | /// @param ignore_mask The memory attributes to ignore during the check. | ||
| 677 | /// | ||
| 678 | /// @returns If successful, returns a tuple containing the memory attributes | ||
| 679 | /// (with ignored bits specified by ignore_mask unset), memory permissions, and | ||
| 680 | /// memory state across the memory range. | ||
| 681 | /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE. | ||
| 682 | /// | ||
| 683 | CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, | ||
| 684 | VMAPermission permission_mask, VMAPermission permissions, | ||
| 685 | MemoryAttribute attribute_mask, MemoryAttribute attribute, | ||
| 686 | MemoryAttribute ignore_mask) const; | ||
| 687 | |||
| 657 | private: | 688 | private: |
| 658 | using VMAIter = VMAMap::iterator; | 689 | using VMAIter = VMAMap::iterator; |
| 659 | 690 | ||
| @@ -707,35 +738,6 @@ private: | |||
| 707 | /// Clears out the page table | 738 | /// Clears out the page table |
| 708 | void ClearPageTable(); | 739 | void ClearPageTable(); |
| 709 | 740 | ||
| 710 | using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>; | ||
| 711 | |||
| 712 | /// Checks if an address range adheres to the specified states provided. | ||
| 713 | /// | ||
| 714 | /// @param address The starting address of the address range. | ||
| 715 | /// @param size The size of the address range. | ||
| 716 | /// @param state_mask The memory state mask. | ||
| 717 | /// @param state The state to compare the individual VMA states against, | ||
| 718 | /// which is done in the form of: (vma.state & state_mask) != state. | ||
| 719 | /// @param permission_mask The memory permissions mask. | ||
| 720 | /// @param permissions The permission to compare the individual VMA permissions against, | ||
| 721 | /// which is done in the form of: | ||
| 722 | /// (vma.permission & permission_mask) != permission. | ||
| 723 | /// @param attribute_mask The memory attribute mask. | ||
| 724 | /// @param attribute The memory attributes to compare the individual VMA attributes | ||
| 725 | /// against, which is done in the form of: | ||
| 726 | /// (vma.attributes & attribute_mask) != attribute. | ||
| 727 | /// @param ignore_mask The memory attributes to ignore during the check. | ||
| 728 | /// | ||
| 729 | /// @returns If successful, returns a tuple containing the memory attributes | ||
| 730 | /// (with ignored bits specified by ignore_mask unset), memory permissions, and | ||
| 731 | /// memory state across the memory range. | ||
| 732 | /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE. | ||
| 733 | /// | ||
| 734 | CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, | ||
| 735 | VMAPermission permission_mask, VMAPermission permissions, | ||
| 736 | MemoryAttribute attribute_mask, MemoryAttribute attribute, | ||
| 737 | MemoryAttribute ignore_mask) const; | ||
| 738 | |||
| 739 | /// Gets the amount of memory currently mapped (state != Unmapped) in a range. | 741 | /// Gets the amount of memory currently mapped (state != Unmapped) in a range. |
| 740 | ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; | 742 | ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; |
| 741 | 743 | ||
diff --git a/src/core/hle/kernel/writable_event.cpp b/src/core/hle/kernel/writable_event.cpp index c9332e3e1..fc2f7c424 100644 --- a/src/core/hle/kernel/writable_event.cpp +++ b/src/core/hle/kernel/writable_event.cpp | |||
| @@ -22,7 +22,6 @@ EventPair WritableEvent::CreateEventPair(KernelCore& kernel, std::string name) { | |||
| 22 | writable_event->name = name + ":Writable"; | 22 | writable_event->name = name + ":Writable"; |
| 23 | writable_event->readable = readable_event; | 23 | writable_event->readable = readable_event; |
| 24 | readable_event->name = name + ":Readable"; | 24 | readable_event->name = name + ":Readable"; |
| 25 | readable_event->signaled = false; | ||
| 26 | 25 | ||
| 27 | return {std::move(readable_event), std::move(writable_event)}; | 26 | return {std::move(readable_event), std::move(writable_event)}; |
| 28 | } | 27 | } |
| @@ -40,7 +39,7 @@ void WritableEvent::Clear() { | |||
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | bool WritableEvent::IsSignaled() const { | 41 | bool WritableEvent::IsSignaled() const { |
| 43 | return readable->signaled; | 42 | return readable->IsSignaled(); |
| 44 | } | 43 | } |
| 45 | 44 | ||
| 46 | } // namespace Kernel | 45 | } // namespace Kernel |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 95aa5d23d..cc978713b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -709,8 +709,34 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) { | |||
| 709 | apm_sys->SetCpuBoostMode(ctx); | 709 | apm_sys->SetCpuBoostMode(ctx); |
| 710 | } | 710 | } |
| 711 | 711 | ||
| 712 | IStorage::IStorage(std::vector<u8> buffer) | 712 | IStorageImpl::~IStorageImpl() = default; |
| 713 | : ServiceFramework("IStorage"), buffer(std::move(buffer)) { | 713 | |
| 714 | class StorageDataImpl final : public IStorageImpl { | ||
| 715 | public: | ||
| 716 | explicit StorageDataImpl(std::vector<u8>&& buffer) : buffer{std::move(buffer)} {} | ||
| 717 | |||
| 718 | std::vector<u8>& GetData() override { | ||
| 719 | return buffer; | ||
| 720 | } | ||
| 721 | |||
| 722 | const std::vector<u8>& GetData() const override { | ||
| 723 | return buffer; | ||
| 724 | } | ||
| 725 | |||
| 726 | std::size_t GetSize() const override { | ||
| 727 | return buffer.size(); | ||
| 728 | } | ||
| 729 | |||
| 730 | private: | ||
| 731 | std::vector<u8> buffer; | ||
| 732 | }; | ||
| 733 | |||
| 734 | IStorage::IStorage(std::vector<u8>&& buffer) | ||
| 735 | : ServiceFramework("IStorage"), impl{std::make_shared<StorageDataImpl>(std::move(buffer))} { | ||
| 736 | Register(); | ||
| 737 | } | ||
| 738 | |||
| 739 | void IStorage::Register() { | ||
| 714 | // clang-format off | 740 | // clang-format off |
| 715 | static const FunctionInfo functions[] = { | 741 | static const FunctionInfo functions[] = { |
| 716 | {0, &IStorage::Open, "Open"}, | 742 | {0, &IStorage::Open, "Open"}, |
| @@ -723,8 +749,13 @@ IStorage::IStorage(std::vector<u8> buffer) | |||
| 723 | 749 | ||
| 724 | IStorage::~IStorage() = default; | 750 | IStorage::~IStorage() = default; |
| 725 | 751 | ||
| 726 | const std::vector<u8>& IStorage::GetData() const { | 752 | void IStorage::Open(Kernel::HLERequestContext& ctx) { |
| 727 | return buffer; | 753 | LOG_DEBUG(Service_AM, "called"); |
| 754 | |||
| 755 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 756 | |||
| 757 | rb.Push(RESULT_SUCCESS); | ||
| 758 | rb.PushIpcInterface<IStorageAccessor>(*this); | ||
| 728 | } | 759 | } |
| 729 | 760 | ||
| 730 | void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { | 761 | void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { |
| @@ -816,7 +847,7 @@ private: | |||
| 816 | LOG_DEBUG(Service_AM, "called"); | 847 | LOG_DEBUG(Service_AM, "called"); |
| 817 | 848 | ||
| 818 | IPC::RequestParser rp{ctx}; | 849 | IPC::RequestParser rp{ctx}; |
| 819 | applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>()); | 850 | applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>()); |
| 820 | 851 | ||
| 821 | IPC::ResponseBuilder rb{ctx, 2}; | 852 | IPC::ResponseBuilder rb{ctx, 2}; |
| 822 | rb.Push(RESULT_SUCCESS); | 853 | rb.Push(RESULT_SUCCESS); |
| @@ -825,26 +856,25 @@ private: | |||
| 825 | void PopOutData(Kernel::HLERequestContext& ctx) { | 856 | void PopOutData(Kernel::HLERequestContext& ctx) { |
| 826 | LOG_DEBUG(Service_AM, "called"); | 857 | LOG_DEBUG(Service_AM, "called"); |
| 827 | 858 | ||
| 828 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 829 | |||
| 830 | const auto storage = applet->GetBroker().PopNormalDataToGame(); | 859 | const auto storage = applet->GetBroker().PopNormalDataToGame(); |
| 831 | if (storage == nullptr) { | 860 | if (storage == nullptr) { |
| 832 | LOG_ERROR(Service_AM, | 861 | LOG_ERROR(Service_AM, |
| 833 | "storage is a nullptr. There is no data in the current normal channel"); | 862 | "storage is a nullptr. There is no data in the current normal channel"); |
| 834 | 863 | IPC::ResponseBuilder rb{ctx, 2}; | |
| 835 | rb.Push(ERR_NO_DATA_IN_CHANNEL); | 864 | rb.Push(ERR_NO_DATA_IN_CHANNEL); |
| 836 | return; | 865 | return; |
| 837 | } | 866 | } |
| 838 | 867 | ||
| 868 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 839 | rb.Push(RESULT_SUCCESS); | 869 | rb.Push(RESULT_SUCCESS); |
| 840 | rb.PushIpcInterface<IStorage>(std::move(*storage)); | 870 | rb.PushIpcInterface<IStorage>(std::move(storage)); |
| 841 | } | 871 | } |
| 842 | 872 | ||
| 843 | void PushInteractiveInData(Kernel::HLERequestContext& ctx) { | 873 | void PushInteractiveInData(Kernel::HLERequestContext& ctx) { |
| 844 | LOG_DEBUG(Service_AM, "called"); | 874 | LOG_DEBUG(Service_AM, "called"); |
| 845 | 875 | ||
| 846 | IPC::RequestParser rp{ctx}; | 876 | IPC::RequestParser rp{ctx}; |
| 847 | applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>()); | 877 | applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>()); |
| 848 | 878 | ||
| 849 | ASSERT(applet->IsInitialized()); | 879 | ASSERT(applet->IsInitialized()); |
| 850 | applet->ExecuteInteractive(); | 880 | applet->ExecuteInteractive(); |
| @@ -857,19 +887,18 @@ private: | |||
| 857 | void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { | 887 | void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { |
| 858 | LOG_DEBUG(Service_AM, "called"); | 888 | LOG_DEBUG(Service_AM, "called"); |
| 859 | 889 | ||
| 860 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 861 | |||
| 862 | const auto storage = applet->GetBroker().PopInteractiveDataToGame(); | 890 | const auto storage = applet->GetBroker().PopInteractiveDataToGame(); |
| 863 | if (storage == nullptr) { | 891 | if (storage == nullptr) { |
| 864 | LOG_ERROR(Service_AM, | 892 | LOG_ERROR(Service_AM, |
| 865 | "storage is a nullptr. There is no data in the current interactive channel"); | 893 | "storage is a nullptr. There is no data in the current interactive channel"); |
| 866 | 894 | IPC::ResponseBuilder rb{ctx, 2}; | |
| 867 | rb.Push(ERR_NO_DATA_IN_CHANNEL); | 895 | rb.Push(ERR_NO_DATA_IN_CHANNEL); |
| 868 | return; | 896 | return; |
| 869 | } | 897 | } |
| 870 | 898 | ||
| 899 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 871 | rb.Push(RESULT_SUCCESS); | 900 | rb.Push(RESULT_SUCCESS); |
| 872 | rb.PushIpcInterface<IStorage>(std::move(*storage)); | 901 | rb.PushIpcInterface<IStorage>(std::move(storage)); |
| 873 | } | 902 | } |
| 874 | 903 | ||
| 875 | void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { | 904 | void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) { |
| @@ -891,15 +920,6 @@ private: | |||
| 891 | std::shared_ptr<Applets::Applet> applet; | 920 | std::shared_ptr<Applets::Applet> applet; |
| 892 | }; | 921 | }; |
| 893 | 922 | ||
| 894 | void IStorage::Open(Kernel::HLERequestContext& ctx) { | ||
| 895 | LOG_DEBUG(Service_AM, "called"); | ||
| 896 | |||
| 897 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 898 | |||
| 899 | rb.Push(RESULT_SUCCESS); | ||
| 900 | rb.PushIpcInterface<IStorageAccessor>(*this); | ||
| 901 | } | ||
| 902 | |||
| 903 | IStorageAccessor::IStorageAccessor(IStorage& storage) | 923 | IStorageAccessor::IStorageAccessor(IStorage& storage) |
| 904 | : ServiceFramework("IStorageAccessor"), backing(storage) { | 924 | : ServiceFramework("IStorageAccessor"), backing(storage) { |
| 905 | // clang-format off | 925 | // clang-format off |
| @@ -921,7 +941,7 @@ void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) { | |||
| 921 | IPC::ResponseBuilder rb{ctx, 4}; | 941 | IPC::ResponseBuilder rb{ctx, 4}; |
| 922 | 942 | ||
| 923 | rb.Push(RESULT_SUCCESS); | 943 | rb.Push(RESULT_SUCCESS); |
| 924 | rb.Push(static_cast<u64>(backing.buffer.size())); | 944 | rb.Push(static_cast<u64>(backing.GetSize())); |
| 925 | } | 945 | } |
| 926 | 946 | ||
| 927 | void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { | 947 | void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { |
| @@ -932,17 +952,17 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { | |||
| 932 | 952 | ||
| 933 | LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); | 953 | LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); |
| 934 | 954 | ||
| 935 | if (data.size() > backing.buffer.size() - offset) { | 955 | if (data.size() > backing.GetSize() - offset) { |
| 936 | LOG_ERROR(Service_AM, | 956 | LOG_ERROR(Service_AM, |
| 937 | "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}", | 957 | "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}", |
| 938 | backing.buffer.size(), data.size(), offset); | 958 | backing.GetSize(), data.size(), offset); |
| 939 | 959 | ||
| 940 | IPC::ResponseBuilder rb{ctx, 2}; | 960 | IPC::ResponseBuilder rb{ctx, 2}; |
| 941 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); | 961 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); |
| 942 | return; | 962 | return; |
| 943 | } | 963 | } |
| 944 | 964 | ||
| 945 | std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); | 965 | std::memcpy(backing.GetData().data() + offset, data.data(), data.size()); |
| 946 | 966 | ||
| 947 | IPC::ResponseBuilder rb{ctx, 2}; | 967 | IPC::ResponseBuilder rb{ctx, 2}; |
| 948 | rb.Push(RESULT_SUCCESS); | 968 | rb.Push(RESULT_SUCCESS); |
| @@ -956,16 +976,16 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) { | |||
| 956 | 976 | ||
| 957 | LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); | 977 | LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); |
| 958 | 978 | ||
| 959 | if (size > backing.buffer.size() - offset) { | 979 | if (size > backing.GetSize() - offset) { |
| 960 | LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}", | 980 | LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}", |
| 961 | backing.buffer.size(), size, offset); | 981 | backing.GetSize(), size, offset); |
| 962 | 982 | ||
| 963 | IPC::ResponseBuilder rb{ctx, 2}; | 983 | IPC::ResponseBuilder rb{ctx, 2}; |
| 964 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); | 984 | rb.Push(ERR_SIZE_OUT_OF_BOUNDS); |
| 965 | return; | 985 | return; |
| 966 | } | 986 | } |
| 967 | 987 | ||
| 968 | ctx.WriteBuffer(backing.buffer.data() + offset, size); | 988 | ctx.WriteBuffer(backing.GetData().data() + offset, size); |
| 969 | 989 | ||
| 970 | IPC::ResponseBuilder rb{ctx, 2}; | 990 | IPC::ResponseBuilder rb{ctx, 2}; |
| 971 | rb.Push(RESULT_SUCCESS); | 991 | rb.Push(RESULT_SUCCESS); |
| @@ -1031,7 +1051,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex | |||
| 1031 | rp.SetCurrentOffset(3); | 1051 | rp.SetCurrentOffset(3); |
| 1032 | const auto handle{rp.Pop<Kernel::Handle>()}; | 1052 | const auto handle{rp.Pop<Kernel::Handle>()}; |
| 1033 | 1053 | ||
| 1034 | const auto transfer_mem = | 1054 | auto transfer_mem = |
| 1035 | system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); | 1055 | system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle); |
| 1036 | 1056 | ||
| 1037 | if (transfer_mem == nullptr) { | 1057 | if (transfer_mem == nullptr) { |
| @@ -1047,7 +1067,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex | |||
| 1047 | 1067 | ||
| 1048 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 1068 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 1049 | rb.Push(RESULT_SUCCESS); | 1069 | rb.Push(RESULT_SUCCESS); |
| 1050 | rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory))); | 1070 | rb.PushIpcInterface<IStorage>(std::move(memory)); |
| 1051 | } | 1071 | } |
| 1052 | 1072 | ||
| 1053 | IApplicationFunctions::IApplicationFunctions(Core::System& system_) | 1073 | IApplicationFunctions::IApplicationFunctions(Core::System& system_) |
| @@ -1189,13 +1209,11 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { | |||
| 1189 | u64 build_id{}; | 1209 | u64 build_id{}; |
| 1190 | std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); | 1210 | std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); |
| 1191 | 1211 | ||
| 1192 | const auto data = | 1212 | auto data = backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id}); |
| 1193 | backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id}); | ||
| 1194 | |||
| 1195 | if (data.has_value()) { | 1213 | if (data.has_value()) { |
| 1196 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 1214 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 1197 | rb.Push(RESULT_SUCCESS); | 1215 | rb.Push(RESULT_SUCCESS); |
| 1198 | rb.PushIpcInterface<AM::IStorage>(*data); | 1216 | rb.PushIpcInterface<IStorage>(std::move(*data)); |
| 1199 | launch_popped_application_specific = true; | 1217 | launch_popped_application_specific = true; |
| 1200 | return; | 1218 | return; |
| 1201 | } | 1219 | } |
| @@ -1218,7 +1236,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { | |||
| 1218 | std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); | 1236 | std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser)); |
| 1219 | std::memcpy(buffer.data(), ¶ms, buffer.size()); | 1237 | std::memcpy(buffer.data(), ¶ms, buffer.size()); |
| 1220 | 1238 | ||
| 1221 | rb.PushIpcInterface<AM::IStorage>(buffer); | 1239 | rb.PushIpcInterface<IStorage>(std::move(buffer)); |
| 1222 | launch_popped_account_preselect = true; | 1240 | launch_popped_account_preselect = true; |
| 1223 | return; | 1241 | return; |
| 1224 | } | 1242 | } |
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 448817be9..0b9a4332d 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -12,7 +12,8 @@ | |||
| 12 | 12 | ||
| 13 | namespace Kernel { | 13 | namespace Kernel { |
| 14 | class KernelCore; | 14 | class KernelCore; |
| 15 | } | 15 | class TransferMemory; |
| 16 | } // namespace Kernel | ||
| 16 | 17 | ||
| 17 | namespace Service::NVFlinger { | 18 | namespace Service::NVFlinger { |
| 18 | class NVFlinger; | 19 | class NVFlinger; |
| @@ -188,19 +189,36 @@ private: | |||
| 188 | std::shared_ptr<AppletMessageQueue> msg_queue; | 189 | std::shared_ptr<AppletMessageQueue> msg_queue; |
| 189 | }; | 190 | }; |
| 190 | 191 | ||
| 192 | class IStorageImpl { | ||
| 193 | public: | ||
| 194 | virtual ~IStorageImpl(); | ||
| 195 | virtual std::vector<u8>& GetData() = 0; | ||
| 196 | virtual const std::vector<u8>& GetData() const = 0; | ||
| 197 | virtual std::size_t GetSize() const = 0; | ||
| 198 | }; | ||
| 199 | |||
| 191 | class IStorage final : public ServiceFramework<IStorage> { | 200 | class IStorage final : public ServiceFramework<IStorage> { |
| 192 | public: | 201 | public: |
| 193 | explicit IStorage(std::vector<u8> buffer); | 202 | explicit IStorage(std::vector<u8>&& buffer); |
| 194 | ~IStorage() override; | 203 | ~IStorage() override; |
| 195 | 204 | ||
| 196 | const std::vector<u8>& GetData() const; | 205 | std::vector<u8>& GetData() { |
| 206 | return impl->GetData(); | ||
| 207 | } | ||
| 208 | |||
| 209 | const std::vector<u8>& GetData() const { | ||
| 210 | return impl->GetData(); | ||
| 211 | } | ||
| 212 | |||
| 213 | std::size_t GetSize() const { | ||
| 214 | return impl->GetSize(); | ||
| 215 | } | ||
| 197 | 216 | ||
| 198 | private: | 217 | private: |
| 218 | void Register(); | ||
| 199 | void Open(Kernel::HLERequestContext& ctx); | 219 | void Open(Kernel::HLERequestContext& ctx); |
| 200 | 220 | ||
| 201 | std::vector<u8> buffer; | 221 | std::shared_ptr<IStorageImpl> impl; |
| 202 | |||
| 203 | friend class IStorageAccessor; | ||
| 204 | }; | 222 | }; |
| 205 | 223 | ||
| 206 | class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { | 224 | class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { |
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 92f995f8f..c3261f3e6 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -50,16 +50,17 @@ AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() co | |||
| 50 | return {std::move(out_normal), std::move(out_interactive)}; | 50 | return {std::move(out_normal), std::move(out_interactive)}; |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { | 53 | std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { |
| 54 | if (out_channel.empty()) | 54 | if (out_channel.empty()) |
| 55 | return nullptr; | 55 | return nullptr; |
| 56 | 56 | ||
| 57 | auto out = std::move(out_channel.front()); | 57 | auto out = std::move(out_channel.front()); |
| 58 | out_channel.pop_front(); | 58 | out_channel.pop_front(); |
| 59 | pop_out_data_event.writable->Clear(); | ||
| 59 | return out; | 60 | return out; |
| 60 | } | 61 | } |
| 61 | 62 | ||
| 62 | std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { | 63 | std::shared_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { |
| 63 | if (in_channel.empty()) | 64 | if (in_channel.empty()) |
| 64 | return nullptr; | 65 | return nullptr; |
| 65 | 66 | ||
| @@ -68,16 +69,17 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() { | |||
| 68 | return out; | 69 | return out; |
| 69 | } | 70 | } |
| 70 | 71 | ||
| 71 | std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { | 72 | std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() { |
| 72 | if (out_interactive_channel.empty()) | 73 | if (out_interactive_channel.empty()) |
| 73 | return nullptr; | 74 | return nullptr; |
| 74 | 75 | ||
| 75 | auto out = std::move(out_interactive_channel.front()); | 76 | auto out = std::move(out_interactive_channel.front()); |
| 76 | out_interactive_channel.pop_front(); | 77 | out_interactive_channel.pop_front(); |
| 78 | pop_interactive_out_data_event.writable->Clear(); | ||
| 77 | return out; | 79 | return out; |
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { | 82 | std::shared_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { |
| 81 | if (in_interactive_channel.empty()) | 83 | if (in_interactive_channel.empty()) |
| 82 | return nullptr; | 84 | return nullptr; |
| 83 | 85 | ||
| @@ -86,21 +88,21 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() { | |||
| 86 | return out; | 88 | return out; |
| 87 | } | 89 | } |
| 88 | 90 | ||
| 89 | void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { | 91 | void AppletDataBroker::PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage) { |
| 90 | in_channel.push_back(std::make_unique<IStorage>(storage)); | 92 | in_channel.emplace_back(std::move(storage)); |
| 91 | } | 93 | } |
| 92 | 94 | ||
| 93 | void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { | 95 | void AppletDataBroker::PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage) { |
| 94 | out_channel.push_back(std::make_unique<IStorage>(storage)); | 96 | out_channel.emplace_back(std::move(storage)); |
| 95 | pop_out_data_event.writable->Signal(); | 97 | pop_out_data_event.writable->Signal(); |
| 96 | } | 98 | } |
| 97 | 99 | ||
| 98 | void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { | 100 | void AppletDataBroker::PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage) { |
| 99 | in_interactive_channel.push_back(std::make_unique<IStorage>(storage)); | 101 | in_interactive_channel.emplace_back(std::move(storage)); |
| 100 | } | 102 | } |
| 101 | 103 | ||
| 102 | void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { | 104 | void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage) { |
| 103 | out_interactive_channel.push_back(std::make_unique<IStorage>(storage)); | 105 | out_interactive_channel.emplace_back(std::move(storage)); |
| 104 | pop_interactive_out_data_event.writable->Signal(); | 106 | pop_interactive_out_data_event.writable->Signal(); |
| 105 | } | 107 | } |
| 106 | 108 | ||
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index 16e61fc6f..e75be86a2 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -72,17 +72,17 @@ public: | |||
| 72 | // Retrieves but does not pop the data sent to applet. | 72 | // Retrieves but does not pop the data sent to applet. |
| 73 | RawChannelData PeekDataToAppletForDebug() const; | 73 | RawChannelData PeekDataToAppletForDebug() const; |
| 74 | 74 | ||
| 75 | std::unique_ptr<IStorage> PopNormalDataToGame(); | 75 | std::shared_ptr<IStorage> PopNormalDataToGame(); |
| 76 | std::unique_ptr<IStorage> PopNormalDataToApplet(); | 76 | std::shared_ptr<IStorage> PopNormalDataToApplet(); |
| 77 | 77 | ||
| 78 | std::unique_ptr<IStorage> PopInteractiveDataToGame(); | 78 | std::shared_ptr<IStorage> PopInteractiveDataToGame(); |
| 79 | std::unique_ptr<IStorage> PopInteractiveDataToApplet(); | 79 | std::shared_ptr<IStorage> PopInteractiveDataToApplet(); |
| 80 | 80 | ||
| 81 | void PushNormalDataFromGame(IStorage storage); | 81 | void PushNormalDataFromGame(std::shared_ptr<IStorage>&& storage); |
| 82 | void PushNormalDataFromApplet(IStorage storage); | 82 | void PushNormalDataFromApplet(std::shared_ptr<IStorage>&& storage); |
| 83 | 83 | ||
| 84 | void PushInteractiveDataFromGame(IStorage storage); | 84 | void PushInteractiveDataFromGame(std::shared_ptr<IStorage>&& storage); |
| 85 | void PushInteractiveDataFromApplet(IStorage storage); | 85 | void PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&& storage); |
| 86 | 86 | ||
| 87 | void SignalStateChanged() const; | 87 | void SignalStateChanged() const; |
| 88 | 88 | ||
| @@ -94,16 +94,16 @@ private: | |||
| 94 | // Queues are named from applet's perspective | 94 | // Queues are named from applet's perspective |
| 95 | 95 | ||
| 96 | // PopNormalDataToApplet and PushNormalDataFromGame | 96 | // PopNormalDataToApplet and PushNormalDataFromGame |
| 97 | std::deque<std::unique_ptr<IStorage>> in_channel; | 97 | std::deque<std::shared_ptr<IStorage>> in_channel; |
| 98 | 98 | ||
| 99 | // PopNormalDataToGame and PushNormalDataFromApplet | 99 | // PopNormalDataToGame and PushNormalDataFromApplet |
| 100 | std::deque<std::unique_ptr<IStorage>> out_channel; | 100 | std::deque<std::shared_ptr<IStorage>> out_channel; |
| 101 | 101 | ||
| 102 | // PopInteractiveDataToApplet and PushInteractiveDataFromGame | 102 | // PopInteractiveDataToApplet and PushInteractiveDataFromGame |
| 103 | std::deque<std::unique_ptr<IStorage>> in_interactive_channel; | 103 | std::deque<std::shared_ptr<IStorage>> in_interactive_channel; |
| 104 | 104 | ||
| 105 | // PopInteractiveDataToGame and PushInteractiveDataFromApplet | 105 | // PopInteractiveDataToGame and PushInteractiveDataFromApplet |
| 106 | std::deque<std::unique_ptr<IStorage>> out_interactive_channel; | 106 | std::deque<std::shared_ptr<IStorage>> out_interactive_channel; |
| 107 | 107 | ||
| 108 | Kernel::EventPair state_changed_event; | 108 | Kernel::EventPair state_changed_event; |
| 109 | 109 | ||
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index eab0d42c9..f12fd7f89 100644 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/error.cpp | |||
| @@ -186,7 +186,7 @@ void Error::Execute() { | |||
| 186 | 186 | ||
| 187 | void Error::DisplayCompleted() { | 187 | void Error::DisplayCompleted() { |
| 188 | complete = true; | 188 | complete = true; |
| 189 | broker.PushNormalDataFromApplet(IStorage{{}}); | 189 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{})); |
| 190 | broker.SignalStateChanged(); | 190 | broker.SignalStateChanged(); |
| 191 | } | 191 | } |
| 192 | 192 | ||
diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp index 328438a1d..104501ac5 100644 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ b/src/core/hle/service/am/applets/general_backend.cpp | |||
| @@ -20,7 +20,7 @@ namespace Service::AM::Applets { | |||
| 20 | constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; | 20 | constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; |
| 21 | 21 | ||
| 22 | static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { | 22 | static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { |
| 23 | std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet(); | 23 | std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet(); |
| 24 | for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { | 24 | for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { |
| 25 | const auto data = storage->GetData(); | 25 | const auto data = storage->GetData(); |
| 26 | LOG_INFO(Service_AM, | 26 | LOG_INFO(Service_AM, |
| @@ -148,7 +148,7 @@ void Auth::AuthFinished(bool successful) { | |||
| 148 | std::vector<u8> out(sizeof(Return)); | 148 | std::vector<u8> out(sizeof(Return)); |
| 149 | std::memcpy(out.data(), &return_, sizeof(Return)); | 149 | std::memcpy(out.data(), &return_, sizeof(Return)); |
| 150 | 150 | ||
| 151 | broker.PushNormalDataFromApplet(IStorage{out}); | 151 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out))); |
| 152 | broker.SignalStateChanged(); | 152 | broker.SignalStateChanged(); |
| 153 | } | 153 | } |
| 154 | 154 | ||
| @@ -198,7 +198,7 @@ void PhotoViewer::Execute() { | |||
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | void PhotoViewer::ViewFinished() { | 200 | void PhotoViewer::ViewFinished() { |
| 201 | broker.PushNormalDataFromApplet(IStorage{{}}); | 201 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{})); |
| 202 | broker.SignalStateChanged(); | 202 | broker.SignalStateChanged(); |
| 203 | } | 203 | } |
| 204 | 204 | ||
| @@ -234,8 +234,8 @@ void StubApplet::ExecuteInteractive() { | |||
| 234 | LOG_WARNING(Service_AM, "called (STUBBED)"); | 234 | LOG_WARNING(Service_AM, "called (STUBBED)"); |
| 235 | LogCurrentStorage(broker, "ExecuteInteractive"); | 235 | LogCurrentStorage(broker, "ExecuteInteractive"); |
| 236 | 236 | ||
| 237 | broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); | 237 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000))); |
| 238 | broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); | 238 | broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000))); |
| 239 | broker.SignalStateChanged(); | 239 | broker.SignalStateChanged(); |
| 240 | } | 240 | } |
| 241 | 241 | ||
| @@ -243,8 +243,8 @@ void StubApplet::Execute() { | |||
| 243 | LOG_WARNING(Service_AM, "called (STUBBED)"); | 243 | LOG_WARNING(Service_AM, "called (STUBBED)"); |
| 244 | LogCurrentStorage(broker, "Execute"); | 244 | LogCurrentStorage(broker, "Execute"); |
| 245 | 245 | ||
| 246 | broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)}); | 246 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000))); |
| 247 | broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)}); | 247 | broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::vector<u8>(0x1000))); |
| 248 | broker.SignalStateChanged(); | 248 | broker.SignalStateChanged(); |
| 249 | } | 249 | } |
| 250 | 250 | ||
diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index 3eba696ca..70cc23552 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp | |||
| @@ -50,7 +50,7 @@ void ProfileSelect::ExecuteInteractive() { | |||
| 50 | 50 | ||
| 51 | void ProfileSelect::Execute() { | 51 | void ProfileSelect::Execute() { |
| 52 | if (complete) { | 52 | if (complete) { |
| 53 | broker.PushNormalDataFromApplet(IStorage{final_data}); | 53 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data))); |
| 54 | return; | 54 | return; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| @@ -71,7 +71,7 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) { | |||
| 71 | 71 | ||
| 72 | final_data = std::vector<u8>(sizeof(UserSelectionOutput)); | 72 | final_data = std::vector<u8>(sizeof(UserSelectionOutput)); |
| 73 | std::memcpy(final_data.data(), &output, final_data.size()); | 73 | std::memcpy(final_data.data(), &output, final_data.size()); |
| 74 | broker.PushNormalDataFromApplet(IStorage{final_data}); | 74 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data))); |
| 75 | broker.SignalStateChanged(); | 75 | broker.SignalStateChanged(); |
| 76 | } | 76 | } |
| 77 | 77 | ||
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 748559cd0..54e63c138 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp | |||
| @@ -102,7 +102,8 @@ void SoftwareKeyboard::ExecuteInteractive() { | |||
| 102 | 102 | ||
| 103 | void SoftwareKeyboard::Execute() { | 103 | void SoftwareKeyboard::Execute() { |
| 104 | if (complete) { | 104 | if (complete) { |
| 105 | broker.PushNormalDataFromApplet(IStorage{final_data}); | 105 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(final_data))); |
| 106 | broker.SignalStateChanged(); | ||
| 106 | return; | 107 | return; |
| 107 | } | 108 | } |
| 108 | 109 | ||
| @@ -119,7 +120,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { | |||
| 119 | std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE); | 120 | std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE); |
| 120 | 121 | ||
| 121 | if (config.utf_8) { | 122 | if (config.utf_8) { |
| 122 | const u64 size = text->size() + 8; | 123 | const u64 size = text->size() + sizeof(u64); |
| 123 | const auto new_text = Common::UTF16ToUTF8(*text); | 124 | const auto new_text = Common::UTF16ToUTF8(*text); |
| 124 | 125 | ||
| 125 | std::memcpy(output_sub.data(), &size, sizeof(u64)); | 126 | std::memcpy(output_sub.data(), &size, sizeof(u64)); |
| @@ -130,7 +131,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { | |||
| 130 | std::memcpy(output_main.data() + 4, new_text.data(), | 131 | std::memcpy(output_main.data() + 4, new_text.data(), |
| 131 | std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); | 132 | std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4)); |
| 132 | } else { | 133 | } else { |
| 133 | const u64 size = text->size() * 2 + 8; | 134 | const u64 size = text->size() * 2 + sizeof(u64); |
| 134 | std::memcpy(output_sub.data(), &size, sizeof(u64)); | 135 | std::memcpy(output_sub.data(), &size, sizeof(u64)); |
| 135 | std::memcpy(output_sub.data() + 8, text->data(), | 136 | std::memcpy(output_sub.data() + 8, text->data(), |
| 136 | std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); | 137 | std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); |
| @@ -144,15 +145,15 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { | |||
| 144 | final_data = output_main; | 145 | final_data = output_main; |
| 145 | 146 | ||
| 146 | if (complete) { | 147 | if (complete) { |
| 147 | broker.PushNormalDataFromApplet(IStorage{output_main}); | 148 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main))); |
| 148 | broker.SignalStateChanged(); | 149 | broker.SignalStateChanged(); |
| 149 | } else { | 150 | } else { |
| 150 | broker.PushInteractiveDataFromApplet(IStorage{output_sub}); | 151 | broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(std::move(output_sub))); |
| 151 | } | 152 | } |
| 152 | } else { | 153 | } else { |
| 153 | output_main[0] = 1; | 154 | output_main[0] = 1; |
| 154 | complete = true; | 155 | complete = true; |
| 155 | broker.PushNormalDataFromApplet(IStorage{output_main}); | 156 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(output_main))); |
| 156 | broker.SignalStateChanged(); | 157 | broker.SignalStateChanged(); |
| 157 | } | 158 | } |
| 158 | } | 159 | } |
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 5546ef6e8..12443c910 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp | |||
| @@ -284,7 +284,7 @@ void WebBrowser::Finalize() { | |||
| 284 | std::vector<u8> data(sizeof(WebCommonReturnValue)); | 284 | std::vector<u8> data(sizeof(WebCommonReturnValue)); |
| 285 | std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue)); | 285 | std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue)); |
| 286 | 286 | ||
| 287 | broker.PushNormalDataFromApplet(IStorage{data}); | 287 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data))); |
| 288 | broker.SignalStateChanged(); | 288 | broker.SignalStateChanged(); |
| 289 | 289 | ||
| 290 | if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { | 290 | if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { |
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index cb839e4a2..d19513cbb 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -170,8 +170,10 @@ public: | |||
| 170 | {3, nullptr, "SetContextForMultiStream"}, | 170 | {3, nullptr, "SetContextForMultiStream"}, |
| 171 | {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, | 171 | {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, |
| 172 | {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, | 172 | {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, |
| 173 | {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, | 173 | {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"}, |
| 174 | {7, nullptr, "DecodeInterleavedForMultiStream"}, | 174 | {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, |
| 175 | {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, | ||
| 176 | {9, nullptr, "DecodeInterleavedForMultiStream"}, | ||
| 175 | }; | 177 | }; |
| 176 | // clang-format on | 178 | // clang-format on |
| 177 | 179 | ||
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index 6f5ea095a..def3410cc 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp | |||
| @@ -117,13 +117,13 @@ bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, | |||
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | bool NullBackend::Clear(u64 title_id) { | 119 | bool NullBackend::Clear(u64 title_id) { |
| 120 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}"); | 120 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id); |
| 121 | 121 | ||
| 122 | return true; | 122 | return true; |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { | 125 | void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) { |
| 126 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id, | 126 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id, |
| 127 | Common::HexToString(passphrase)); | 127 | Common::HexToString(passphrase)); |
| 128 | } | 128 | } |
| 129 | 129 | ||
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index 67e39a5c4..f589864ee 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp | |||
| @@ -200,7 +200,8 @@ private: | |||
| 200 | DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds, | 200 | DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds, |
| 201 | const std::string& content_type_name) { | 201 | const std::string& content_type_name) { |
| 202 | if (client == nullptr) { | 202 | if (client == nullptr) { |
| 203 | client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT, timeout_seconds); | 203 | client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT); |
| 204 | client->set_timeout_sec(timeout_seconds); | ||
| 204 | } | 205 | } |
| 205 | 206 | ||
| 206 | httplib::Headers headers{ | 207 | httplib::Headers headers{ |
| @@ -448,8 +449,8 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) | |||
| 448 | 449 | ||
| 449 | Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global, | 450 | Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global, |
| 450 | std::map<std::string, EventStatus>& games) { | 451 | std::map<std::string, EventStatus>& games) { |
| 451 | httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT), | 452 | httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT)}; |
| 452 | static_cast<int>(TIMEOUT_SECONDS)}; | 453 | client.set_timeout_sec(static_cast<int>(TIMEOUT_SECONDS)); |
| 453 | 454 | ||
| 454 | httplib::Headers headers{ | 455 | httplib::Headers headers{ |
| 455 | {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)}, | 456 | {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)}, |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 55d62fc5e..e6811d5b5 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -420,7 +420,7 @@ public: | |||
| 420 | return; | 420 | return; |
| 421 | } | 421 | } |
| 422 | 422 | ||
| 423 | IFile file(result.Unwrap()); | 423 | auto file = std::make_shared<IFile>(result.Unwrap()); |
| 424 | 424 | ||
| 425 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 425 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 426 | rb.Push(RESULT_SUCCESS); | 426 | rb.Push(RESULT_SUCCESS); |
| @@ -445,7 +445,7 @@ public: | |||
| 445 | return; | 445 | return; |
| 446 | } | 446 | } |
| 447 | 447 | ||
| 448 | IDirectory directory(result.Unwrap()); | 448 | auto directory = std::make_shared<IDirectory>(result.Unwrap()); |
| 449 | 449 | ||
| 450 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 450 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 451 | rb.Push(RESULT_SUCCESS); | 451 | rb.Push(RESULT_SUCCESS); |
| @@ -794,8 +794,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) { | |||
| 794 | void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { | 794 | void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { |
| 795 | LOG_DEBUG(Service_FS, "called"); | 795 | LOG_DEBUG(Service_FS, "called"); |
| 796 | 796 | ||
| 797 | IFileSystem filesystem(fsc.OpenSDMC().Unwrap(), | 797 | auto filesystem = std::make_shared<IFileSystem>( |
| 798 | SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); | 798 | fsc.OpenSDMC().Unwrap(), SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); |
| 799 | 799 | ||
| 800 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 800 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 801 | rb.Push(RESULT_SUCCESS); | 801 | rb.Push(RESULT_SUCCESS); |
| @@ -846,7 +846,8 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 846 | id = FileSys::StorageId::NandSystem; | 846 | id = FileSys::StorageId::NandSystem; |
| 847 | } | 847 | } |
| 848 | 848 | ||
| 849 | IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id)); | 849 | auto filesystem = |
| 850 | std::make_shared<IFileSystem>(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id)); | ||
| 850 | 851 | ||
| 851 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 852 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 852 | rb.Push(RESULT_SUCCESS); | 853 | rb.Push(RESULT_SUCCESS); |
| @@ -898,7 +899,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | |||
| 898 | return; | 899 | return; |
| 899 | } | 900 | } |
| 900 | 901 | ||
| 901 | IStorage storage(std::move(romfs.Unwrap())); | 902 | auto storage = std::make_shared<IStorage>(std::move(romfs.Unwrap())); |
| 902 | 903 | ||
| 903 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 904 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 904 | rb.Push(RESULT_SUCCESS); | 905 | rb.Push(RESULT_SUCCESS); |
| @@ -937,7 +938,8 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { | |||
| 937 | 938 | ||
| 938 | FileSys::PatchManager pm{title_id}; | 939 | FileSys::PatchManager pm{title_id}; |
| 939 | 940 | ||
| 940 | IStorage storage(pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); | 941 | auto storage = std::make_shared<IStorage>( |
| 942 | pm.PatchRomFS(std::move(data.Unwrap()), 0, FileSys::ContentRecordType::Data)); | ||
| 941 | 943 | ||
| 942 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 944 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 943 | rb.Push(RESULT_SUCCESS); | 945 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 4d952adc0..15c09f04c 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -250,6 +250,10 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | |||
| 250 | auto& rstick_entry = npad_pad_states[controller_idx].r_stick; | 250 | auto& rstick_entry = npad_pad_states[controller_idx].r_stick; |
| 251 | const auto& button_state = buttons[controller_idx]; | 251 | const auto& button_state = buttons[controller_idx]; |
| 252 | const auto& analog_state = sticks[controller_idx]; | 252 | const auto& analog_state = sticks[controller_idx]; |
| 253 | const auto [stick_l_x_f, stick_l_y_f] = | ||
| 254 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | ||
| 255 | const auto [stick_r_x_f, stick_r_y_f] = | ||
| 256 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 253 | 257 | ||
| 254 | using namespace Settings::NativeButton; | 258 | using namespace Settings::NativeButton; |
| 255 | pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); | 259 | pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); |
| @@ -270,23 +274,32 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | |||
| 270 | pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); | 274 | pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); |
| 271 | pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); | 275 | pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); |
| 272 | 276 | ||
| 273 | pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus()); | 277 | pad_state.l_stick_right.Assign( |
| 274 | pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus()); | 278 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( |
| 275 | pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus()); | 279 | Input::AnalogDirection::RIGHT)); |
| 276 | pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus()); | 280 | pad_state.l_stick_left.Assign( |
| 277 | 281 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( | |
| 278 | pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus()); | 282 | Input::AnalogDirection::LEFT)); |
| 279 | pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus()); | 283 | pad_state.l_stick_up.Assign( |
| 280 | pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus()); | 284 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( |
| 281 | pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus()); | 285 | Input::AnalogDirection::UP)); |
| 286 | pad_state.l_stick_down.Assign( | ||
| 287 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( | ||
| 288 | Input::AnalogDirection::DOWN)); | ||
| 289 | |||
| 290 | pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 291 | ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); | ||
| 292 | pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 293 | ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); | ||
| 294 | pad_state.r_stick_right.Assign( | ||
| 295 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 296 | ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); | ||
| 297 | pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | ||
| 298 | ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); | ||
| 282 | 299 | ||
| 283 | pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); | 300 | pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); |
| 284 | pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); | 301 | pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); |
| 285 | 302 | ||
| 286 | const auto [stick_l_x_f, stick_l_y_f] = | ||
| 287 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | ||
| 288 | const auto [stick_r_x_f, stick_r_y_f] = | ||
| 289 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 290 | lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | 303 | lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); |
| 291 | lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | 304 | lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); |
| 292 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | 305 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 89bf8b815..e6b56a9f9 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "core/core_timing_util.h" | 10 | #include "core/core_timing_util.h" |
| 11 | #include "core/frontend/emu_window.h" | 11 | #include "core/frontend/emu_window.h" |
| 12 | #include "core/frontend/input.h" | 12 | #include "core/frontend/input.h" |
| 13 | #include "core/hardware_properties.h" | ||
| 13 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| 14 | #include "core/hle/kernel/client_port.h" | 15 | #include "core/hle/kernel/client_port.h" |
| 15 | #include "core/hle/kernel/client_session.h" | 16 | #include "core/hle/kernel/client_session.h" |
| @@ -37,11 +38,11 @@ namespace Service::HID { | |||
| 37 | 38 | ||
| 38 | // Updating period for each HID device. | 39 | // Updating period for each HID device. |
| 39 | // TODO(ogniK): Find actual polling rate of hid | 40 | // TODO(ogniK): Find actual polling rate of hid |
| 40 | constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66); | 41 | constexpr s64 pad_update_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 66); |
| 41 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = | 42 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = |
| 42 | static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); | 43 | static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); |
| 43 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = | 44 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = |
| 44 | static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); | 45 | static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); |
| 45 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 46 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 46 | 47 | ||
| 47 | IAppletResource::IAppletResource(Core::System& system) | 48 | IAppletResource::IAppletResource(Core::System& system) |
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index ed5059047..92adde6d4 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp | |||
| @@ -129,12 +129,20 @@ public: | |||
| 129 | {304, nullptr, "Disconnect"}, | 129 | {304, nullptr, "Disconnect"}, |
| 130 | {400, nullptr, "Initialize"}, | 130 | {400, nullptr, "Initialize"}, |
| 131 | {401, nullptr, "Finalize"}, | 131 | {401, nullptr, "Finalize"}, |
| 132 | {402, nullptr, "SetOperationMode"}, | 132 | {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+ |
| 133 | }; | 133 | }; |
| 134 | // clang-format on | 134 | // clang-format on |
| 135 | 135 | ||
| 136 | RegisterHandlers(functions); | 136 | RegisterHandlers(functions); |
| 137 | } | 137 | } |
| 138 | |||
| 139 | void Initialize2(Kernel::HLERequestContext& ctx) { | ||
| 140 | LOG_WARNING(Service_LDN, "(STUBBED) called"); | ||
| 141 | // Result success seem make this services start network and continue. | ||
| 142 | // If we just pass result error then it will stop and maybe try again and again. | ||
| 143 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 144 | rb.Push(RESULT_UNKNOWN); | ||
| 145 | } | ||
| 138 | }; | 146 | }; |
| 139 | 147 | ||
| 140 | class LDNS final : public ServiceFramework<LDNS> { | 148 | class LDNS final : public ServiceFramework<LDNS> { |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 6d8bca8bb..f1966ac0e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | |||
| @@ -44,6 +44,8 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve | |||
| 44 | return GetWaitbase(input, output); | 44 | return GetWaitbase(input, output); |
| 45 | case IoctlCommand::IocChannelSetTimeoutCommand: | 45 | case IoctlCommand::IocChannelSetTimeoutCommand: |
| 46 | return ChannelSetTimeout(input, output); | 46 | return ChannelSetTimeout(input, output); |
| 47 | case IoctlCommand::IocChannelSetTimeslice: | ||
| 48 | return ChannelSetTimeslice(input, output); | ||
| 47 | default: | 49 | default: |
| 48 | break; | 50 | break; |
| 49 | } | 51 | } |
| @@ -228,4 +230,14 @@ u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& | |||
| 228 | return 0; | 230 | return 0; |
| 229 | } | 231 | } |
| 230 | 232 | ||
| 233 | u32 nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 234 | IoctlSetTimeslice params{}; | ||
| 235 | std::memcpy(¶ms, input.data(), sizeof(IoctlSetTimeslice)); | ||
| 236 | LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice); | ||
| 237 | |||
| 238 | channel_timeslice = params.timeslice; | ||
| 239 | |||
| 240 | return 0; | ||
| 241 | } | ||
| 242 | |||
| 231 | } // namespace Service::Nvidia::Devices | 243 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index d056dd046..2ac74743f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h | |||
| @@ -48,6 +48,7 @@ private: | |||
| 48 | IocAllocObjCtxCommand = 0xC0104809, | 48 | IocAllocObjCtxCommand = 0xC0104809, |
| 49 | IocChannelGetWaitbaseCommand = 0xC0080003, | 49 | IocChannelGetWaitbaseCommand = 0xC0080003, |
| 50 | IocChannelSetTimeoutCommand = 0x40044803, | 50 | IocChannelSetTimeoutCommand = 0x40044803, |
| 51 | IocChannelSetTimeslice = 0xC004481D, | ||
| 51 | }; | 52 | }; |
| 52 | 53 | ||
| 53 | enum class CtxObjects : u32_le { | 54 | enum class CtxObjects : u32_le { |
| @@ -101,6 +102,11 @@ private: | |||
| 101 | static_assert(sizeof(IoctlChannelSetPriority) == 4, | 102 | static_assert(sizeof(IoctlChannelSetPriority) == 4, |
| 102 | "IoctlChannelSetPriority is incorrect size"); | 103 | "IoctlChannelSetPriority is incorrect size"); |
| 103 | 104 | ||
| 105 | struct IoctlSetTimeslice { | ||
| 106 | u32_le timeslice; | ||
| 107 | }; | ||
| 108 | static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size"); | ||
| 109 | |||
| 104 | struct IoctlEventIdControl { | 110 | struct IoctlEventIdControl { |
| 105 | u32_le cmd; // 0=disable, 1=enable, 2=clear | 111 | u32_le cmd; // 0=disable, 1=enable, 2=clear |
| 106 | u32_le id; | 112 | u32_le id; |
| @@ -174,6 +180,7 @@ private: | |||
| 174 | u64_le user_data{}; | 180 | u64_le user_data{}; |
| 175 | IoctlZCullBind zcull_params{}; | 181 | IoctlZCullBind zcull_params{}; |
| 176 | u32_le channel_priority{}; | 182 | u32_le channel_priority{}; |
| 183 | u32_le channel_timeslice{}; | ||
| 177 | 184 | ||
| 178 | u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); | 185 | u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); |
| 179 | u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output); | 186 | u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output); |
| @@ -188,6 +195,7 @@ private: | |||
| 188 | const std::vector<u8>& input2, IoctlVersion version); | 195 | const std::vector<u8>& input2, IoctlVersion version); |
| 189 | u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); | 196 | u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); |
| 190 | u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); | 197 | u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); |
| 198 | u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 191 | 199 | ||
| 192 | std::shared_ptr<nvmap> nvmap_dev; | 200 | std::shared_ptr<nvmap> nvmap_dev; |
| 193 | u32 assigned_syncpoints{}; | 201 | u32 assigned_syncpoints{}; |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 62752e419..134152210 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/core_timing.h" | 13 | #include "core/core_timing.h" |
| 14 | #include "core/core_timing_util.h" | 14 | #include "core/core_timing_util.h" |
| 15 | #include "core/hardware_properties.h" | ||
| 15 | #include "core/hle/kernel/kernel.h" | 16 | #include "core/hle/kernel/kernel.h" |
| 16 | #include "core/hle/kernel/readable_event.h" | 17 | #include "core/hle/kernel/readable_event.h" |
| 17 | #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" | 18 | #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" |
| @@ -26,8 +27,8 @@ | |||
| 26 | 27 | ||
| 27 | namespace Service::NVFlinger { | 28 | namespace Service::NVFlinger { |
| 28 | 29 | ||
| 29 | constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); | 30 | constexpr s64 frame_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); |
| 30 | constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30); | 31 | constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 30); |
| 31 | 32 | ||
| 32 | NVFlinger::NVFlinger(Core::System& system) : system(system) { | 33 | NVFlinger::NVFlinger(Core::System& system) : system(system) { |
| 33 | displays.emplace_back(0, "Default", system); | 34 | displays.emplace_back(0, "Default", system); |
| @@ -222,7 +223,7 @@ void NVFlinger::Compose() { | |||
| 222 | 223 | ||
| 223 | s64 NVFlinger::GetNextTicks() const { | 224 | s64 NVFlinger::GetNextTicks() const { |
| 224 | constexpr s64 max_hertz = 120LL; | 225 | constexpr s64 max_hertz = 120LL; |
| 225 | return (Core::Timing::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; | 226 | return (Core::Hardware::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; |
| 226 | } | 227 | } |
| 227 | 228 | ||
| 228 | } // namespace Service::NVFlinger | 229 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 5eb26caf8..8f1be0e48 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp | |||
| @@ -50,16 +50,16 @@ private: | |||
| 50 | IPC::RequestParser rp{ctx}; | 50 | IPC::RequestParser rp{ctx}; |
| 51 | const auto process_id = rp.PopRaw<u64>(); | 51 | const auto process_id = rp.PopRaw<u64>(); |
| 52 | 52 | ||
| 53 | const auto data1 = ctx.ReadBuffer(0); | 53 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; |
| 54 | const auto data2 = ctx.ReadBuffer(1); | 54 | if (Type == Core::Reporter::PlayReportType::New) { |
| 55 | data.emplace_back(ctx.ReadBuffer(1)); | ||
| 56 | } | ||
| 55 | 57 | ||
| 56 | LOG_DEBUG(Service_PREPO, | 58 | LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}", |
| 57 | "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}", | 59 | static_cast<u8>(Type), process_id, data[0].size()); |
| 58 | static_cast<u8>(Type), process_id, data1.size(), data2.size()); | ||
| 59 | 60 | ||
| 60 | const auto& reporter{system.GetReporter()}; | 61 | const auto& reporter{system.GetReporter()}; |
| 61 | reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, | 62 | reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id); |
| 62 | process_id); | ||
| 63 | 63 | ||
| 64 | IPC::ResponseBuilder rb{ctx, 2}; | 64 | IPC::ResponseBuilder rb{ctx, 2}; |
| 65 | rb.Push(RESULT_SUCCESS); | 65 | rb.Push(RESULT_SUCCESS); |
| @@ -70,19 +70,19 @@ private: | |||
| 70 | IPC::RequestParser rp{ctx}; | 70 | IPC::RequestParser rp{ctx}; |
| 71 | const auto user_id = rp.PopRaw<u128>(); | 71 | const auto user_id = rp.PopRaw<u128>(); |
| 72 | const auto process_id = rp.PopRaw<u64>(); | 72 | const auto process_id = rp.PopRaw<u64>(); |
| 73 | 73 | std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; | |
| 74 | const auto data1 = ctx.ReadBuffer(0); | 74 | if (Type == Core::Reporter::PlayReportType::New) { |
| 75 | const auto data2 = ctx.ReadBuffer(1); | 75 | data.emplace_back(ctx.ReadBuffer(1)); |
| 76 | } | ||
| 76 | 77 | ||
| 77 | LOG_DEBUG( | 78 | LOG_DEBUG( |
| 78 | Service_PREPO, | 79 | Service_PREPO, |
| 79 | "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, " | 80 | "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}", |
| 80 | "data2_size={:016X}", | 81 | static_cast<u8>(Type), user_id[1], user_id[0], process_id, data[0].size()); |
| 81 | static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size()); | ||
| 82 | 82 | ||
| 83 | const auto& reporter{system.GetReporter()}; | 83 | const auto& reporter{system.GetReporter()}; |
| 84 | reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, | 84 | reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id, |
| 85 | process_id, user_id); | 85 | user_id); |
| 86 | 86 | ||
| 87 | IPC::ResponseBuilder rb{ctx, 2}; | 87 | IPC::ResponseBuilder rb{ctx, 2}; |
| 88 | rb.Push(RESULT_SUCCESS); | 88 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 884ad173b..f67fab2f9 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -42,6 +42,26 @@ void BSD::Socket(Kernel::HLERequestContext& ctx) { | |||
| 42 | rb.Push<u32>(0); // bsd errno | 42 | rb.Push<u32>(0); // bsd errno |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | void BSD::Select(Kernel::HLERequestContext& ctx) { | ||
| 46 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 47 | |||
| 48 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 49 | |||
| 50 | rb.Push(RESULT_SUCCESS); | ||
| 51 | rb.Push<u32>(0); // ret | ||
| 52 | rb.Push<u32>(0); // bsd errno | ||
| 53 | } | ||
| 54 | |||
| 55 | void BSD::Bind(Kernel::HLERequestContext& ctx) { | ||
| 56 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 57 | |||
| 58 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 59 | |||
| 60 | rb.Push(RESULT_SUCCESS); | ||
| 61 | rb.Push<u32>(0); // ret | ||
| 62 | rb.Push<u32>(0); // bsd errno | ||
| 63 | } | ||
| 64 | |||
| 45 | void BSD::Connect(Kernel::HLERequestContext& ctx) { | 65 | void BSD::Connect(Kernel::HLERequestContext& ctx) { |
| 46 | LOG_WARNING(Service, "(STUBBED) called"); | 66 | LOG_WARNING(Service, "(STUBBED) called"); |
| 47 | 67 | ||
| @@ -52,6 +72,26 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) { | |||
| 52 | rb.Push<u32>(0); // bsd errno | 72 | rb.Push<u32>(0); // bsd errno |
| 53 | } | 73 | } |
| 54 | 74 | ||
| 75 | void BSD::Listen(Kernel::HLERequestContext& ctx) { | ||
| 76 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 77 | |||
| 78 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 79 | |||
| 80 | rb.Push(RESULT_SUCCESS); | ||
| 81 | rb.Push<u32>(0); // ret | ||
| 82 | rb.Push<u32>(0); // bsd errno | ||
| 83 | } | ||
| 84 | |||
| 85 | void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) { | ||
| 86 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 87 | |||
| 88 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 89 | |||
| 90 | rb.Push(RESULT_SUCCESS); | ||
| 91 | rb.Push<u32>(0); // ret | ||
| 92 | rb.Push<u32>(0); // bsd errno | ||
| 93 | } | ||
| 94 | |||
| 55 | void BSD::SendTo(Kernel::HLERequestContext& ctx) { | 95 | void BSD::SendTo(Kernel::HLERequestContext& ctx) { |
| 56 | LOG_WARNING(Service, "(STUBBED) called"); | 96 | LOG_WARNING(Service, "(STUBBED) called"); |
| 57 | 97 | ||
| @@ -80,7 +120,7 @@ BSD::BSD(const char* name) : ServiceFramework(name) { | |||
| 80 | {2, &BSD::Socket, "Socket"}, | 120 | {2, &BSD::Socket, "Socket"}, |
| 81 | {3, nullptr, "SocketExempt"}, | 121 | {3, nullptr, "SocketExempt"}, |
| 82 | {4, nullptr, "Open"}, | 122 | {4, nullptr, "Open"}, |
| 83 | {5, nullptr, "Select"}, | 123 | {5, &BSD::Select, "Select"}, |
| 84 | {6, nullptr, "Poll"}, | 124 | {6, nullptr, "Poll"}, |
| 85 | {7, nullptr, "Sysctl"}, | 125 | {7, nullptr, "Sysctl"}, |
| 86 | {8, nullptr, "Recv"}, | 126 | {8, nullptr, "Recv"}, |
| @@ -88,15 +128,15 @@ BSD::BSD(const char* name) : ServiceFramework(name) { | |||
| 88 | {10, nullptr, "Send"}, | 128 | {10, nullptr, "Send"}, |
| 89 | {11, &BSD::SendTo, "SendTo"}, | 129 | {11, &BSD::SendTo, "SendTo"}, |
| 90 | {12, nullptr, "Accept"}, | 130 | {12, nullptr, "Accept"}, |
| 91 | {13, nullptr, "Bind"}, | 131 | {13, &BSD::Bind, "Bind"}, |
| 92 | {14, &BSD::Connect, "Connect"}, | 132 | {14, &BSD::Connect, "Connect"}, |
| 93 | {15, nullptr, "GetPeerName"}, | 133 | {15, nullptr, "GetPeerName"}, |
| 94 | {16, nullptr, "GetSockName"}, | 134 | {16, nullptr, "GetSockName"}, |
| 95 | {17, nullptr, "GetSockOpt"}, | 135 | {17, nullptr, "GetSockOpt"}, |
| 96 | {18, nullptr, "Listen"}, | 136 | {18, &BSD::Listen, "Listen"}, |
| 97 | {19, nullptr, "Ioctl"}, | 137 | {19, nullptr, "Ioctl"}, |
| 98 | {20, nullptr, "Fcntl"}, | 138 | {20, nullptr, "Fcntl"}, |
| 99 | {21, nullptr, "SetSockOpt"}, | 139 | {21, &BSD::SetSockOpt, "SetSockOpt"}, |
| 100 | {22, nullptr, "Shutdown"}, | 140 | {22, nullptr, "Shutdown"}, |
| 101 | {23, nullptr, "ShutdownAllSockets"}, | 141 | {23, nullptr, "ShutdownAllSockets"}, |
| 102 | {24, nullptr, "Write"}, | 142 | {24, nullptr, "Write"}, |
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 0fe0e65c6..3098e3baf 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h | |||
| @@ -18,7 +18,11 @@ private: | |||
| 18 | void RegisterClient(Kernel::HLERequestContext& ctx); | 18 | void RegisterClient(Kernel::HLERequestContext& ctx); |
| 19 | void StartMonitoring(Kernel::HLERequestContext& ctx); | 19 | void StartMonitoring(Kernel::HLERequestContext& ctx); |
| 20 | void Socket(Kernel::HLERequestContext& ctx); | 20 | void Socket(Kernel::HLERequestContext& ctx); |
| 21 | void Select(Kernel::HLERequestContext& ctx); | ||
| 22 | void Bind(Kernel::HLERequestContext& ctx); | ||
| 21 | void Connect(Kernel::HLERequestContext& ctx); | 23 | void Connect(Kernel::HLERequestContext& ctx); |
| 24 | void Listen(Kernel::HLERequestContext& ctx); | ||
| 25 | void SetSockOpt(Kernel::HLERequestContext& ctx); | ||
| 22 | void SendTo(Kernel::HLERequestContext& ctx); | 26 | void SendTo(Kernel::HLERequestContext& ctx); |
| 23 | void Close(Kernel::HLERequestContext& ctx); | 27 | void Close(Kernel::HLERequestContext& ctx); |
| 24 | 28 | ||
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp index ca1a783fc..1575f0b49 100644 --- a/src/core/hle/service/time/standard_steady_clock_core.cpp +++ b/src/core/hle/service/time/standard_steady_clock_core.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| 6 | #include "core/core_timing.h" | 6 | #include "core/core_timing.h" |
| 7 | #include "core/core_timing_util.h" | 7 | #include "core/core_timing_util.h" |
| 8 | #include "core/hardware_properties.h" | ||
| 8 | #include "core/hle/service/time/standard_steady_clock_core.h" | 9 | #include "core/hle/service/time/standard_steady_clock_core.h" |
| 9 | 10 | ||
| 10 | namespace Service::Time::Clock { | 11 | namespace Service::Time::Clock { |
| @@ -12,7 +13,7 @@ namespace Service::Time::Clock { | |||
| 12 | TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { | 13 | TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { |
| 13 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( | 14 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( |
| 14 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 15 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), |
| 15 | Core::Timing::CNTFREQ)}; | 16 | Core::Hardware::CNTFREQ)}; |
| 16 | TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; | 17 | TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; |
| 17 | 18 | ||
| 18 | if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { | 19 | if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { |
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp index c77b98189..44d5bc651 100644 --- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp +++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| 6 | #include "core/core_timing.h" | 6 | #include "core/core_timing.h" |
| 7 | #include "core/core_timing_util.h" | 7 | #include "core/core_timing_util.h" |
| 8 | #include "core/hardware_properties.h" | ||
| 8 | #include "core/hle/service/time/tick_based_steady_clock_core.h" | 9 | #include "core/hle/service/time/tick_based_steady_clock_core.h" |
| 9 | 10 | ||
| 10 | namespace Service::Time::Clock { | 11 | namespace Service::Time::Clock { |
| @@ -12,7 +13,7 @@ namespace Service::Time::Clock { | |||
| 12 | SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { | 13 | SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { |
| 13 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( | 14 | const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( |
| 14 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 15 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), |
| 15 | Core::Timing::CNTFREQ)}; | 16 | Core::Hardware::CNTFREQ)}; |
| 16 | 17 | ||
| 17 | return {ticks_time_span.ToSeconds(), GetClockSourceId()}; | 18 | return {ticks_time_span.ToSeconds(), GetClockSourceId()}; |
| 18 | } | 19 | } |
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 8ef4efcef..749b7be70 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/core_timing_util.h" | 8 | #include "core/core_timing_util.h" |
| 9 | #include "core/hardware_properties.h" | ||
| 9 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 10 | #include "core/hle/kernel/client_port.h" | 11 | #include "core/hle/kernel/client_port.h" |
| 11 | #include "core/hle/kernel/client_session.h" | 12 | #include "core/hle/kernel/client_session.h" |
| @@ -233,7 +234,7 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe | |||
| 233 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { | 234 | if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { |
| 234 | const auto ticks{Clock::TimeSpanType::FromTicks( | 235 | const auto ticks{Clock::TimeSpanType::FromTicks( |
| 235 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 236 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), |
| 236 | Core::Timing::CNTFREQ)}; | 237 | Core::Hardware::CNTFREQ)}; |
| 237 | const s64 base_time_point{context.offset + current_time_point.time_point - | 238 | const s64 base_time_point{context.offset + current_time_point.time_point - |
| 238 | ticks.ToSeconds()}; | 239 | ticks.ToSeconds()}; |
| 239 | IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; | 240 | IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; |
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index 9b03191bf..fdaef233f 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "core/core.h" | 5 | #include "core/core.h" |
| 6 | #include "core/core_timing.h" | 6 | #include "core/core_timing.h" |
| 7 | #include "core/core_timing_util.h" | 7 | #include "core/core_timing_util.h" |
| 8 | #include "core/hardware_properties.h" | ||
| 8 | #include "core/hle/service/time/clock_types.h" | 9 | #include "core/hle/service/time/clock_types.h" |
| 9 | #include "core/hle/service/time/steady_clock_core.h" | 10 | #include "core/hle/service/time/steady_clock_core.h" |
| 10 | #include "core/hle/service/time/time_sharedmemory.h" | 11 | #include "core/hle/service/time/time_sharedmemory.h" |
| @@ -31,7 +32,7 @@ void SharedMemory::SetupStandardSteadyClock(Core::System& system, | |||
| 31 | Clock::TimeSpanType current_time_point) { | 32 | Clock::TimeSpanType current_time_point) { |
| 32 | const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( | 33 | const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( |
| 33 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), | 34 | Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), |
| 34 | Core::Timing::CNTFREQ)}; | 35 | Core::Hardware::CNTFREQ)}; |
| 35 | const Clock::SteadyClockContext context{ | 36 | const Clock::SteadyClockContext context{ |
| 36 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), | 37 | static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), |
| 37 | clock_source_id}; | 38 | clock_source_id}; |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 515c5accb..044067a5b 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -97,7 +97,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 97 | if (nso_header.IsSegmentCompressed(i)) { | 97 | if (nso_header.IsSegmentCompressed(i)) { |
| 98 | data = DecompressSegment(data, nso_header.segments[i]); | 98 | data = DecompressSegment(data, nso_header.segments[i]); |
| 99 | } | 99 | } |
| 100 | program_image.resize(nso_header.segments[i].location + data.size()); | 100 | program_image.resize(nso_header.segments[i].location + |
| 101 | PageAlignSize(static_cast<u32>(data.size()))); | ||
| 101 | std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(), | 102 | std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(), |
| 102 | data.size()); | 103 | data.size()); |
| 103 | codeset.segments[i].addr = nso_header.segments[i].location; | 104 | codeset.segments[i].addr = nso_header.segments[i].location; |
| @@ -105,8 +106,12 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 105 | codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size())); | 106 | codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size())); |
| 106 | } | 107 | } |
| 107 | 108 | ||
| 108 | if (should_pass_arguments && !Settings::values.program_args.empty()) { | 109 | if (should_pass_arguments) { |
| 109 | const auto arg_data = Settings::values.program_args; | 110 | std::vector<u8> arg_data{Settings::values.program_args.begin(), |
| 111 | Settings::values.program_args.end()}; | ||
| 112 | if (arg_data.empty()) { | ||
| 113 | arg_data.resize(NSO_ARGUMENT_DEFAULT_SIZE); | ||
| 114 | } | ||
| 110 | codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; | 115 | codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE; |
| 111 | NSOArgumentHeader args_header{ | 116 | NSOArgumentHeader args_header{ |
| 112 | NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; | 117 | NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; |
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 58cbe162d..d2d600cd9 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -56,6 +56,8 @@ static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size."); | |||
| 56 | static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable."); | 56 | static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable."); |
| 57 | 57 | ||
| 58 | constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; | 58 | constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; |
| 59 | // NOTE: Official software default argument state is unverified. | ||
| 60 | constexpr u64 NSO_ARGUMENT_DEFAULT_SIZE = 1; | ||
| 59 | 61 | ||
| 60 | struct NSOArgumentHeader { | 62 | struct NSOArgumentHeader { |
| 61 | u32_le allocated_size; | 63 | u32_le allocated_size; |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index d1e6bed93..4472500d2 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/core_timing.h" | 10 | #include "core/core_timing.h" |
| 11 | #include "core/core_timing_util.h" | 11 | #include "core/core_timing_util.h" |
| 12 | #include "core/hardware_properties.h" | ||
| 12 | #include "core/hle/kernel/process.h" | 13 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/service/hid/controllers/npad.h" | 14 | #include "core/hle/service/hid/controllers/npad.h" |
| 14 | #include "core/hle/service/hid/hid.h" | 15 | #include "core/hle/service/hid/hid.h" |
| @@ -17,7 +18,7 @@ | |||
| 17 | 18 | ||
| 18 | namespace Memory { | 19 | namespace Memory { |
| 19 | 20 | ||
| 20 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 12); | 21 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 12); |
| 21 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | 22 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; |
| 22 | 23 | ||
| 23 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) | 24 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) |
diff --git a/src/core/settings.h b/src/core/settings.h index 9c98a9287..f837d3fbc 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -371,6 +371,11 @@ enum class SDMCSize : u64 { | |||
| 371 | S1TB = 0x10000000000ULL, | 371 | S1TB = 0x10000000000ULL, |
| 372 | }; | 372 | }; |
| 373 | 373 | ||
| 374 | enum class RendererBackend { | ||
| 375 | OpenGL = 0, | ||
| 376 | Vulkan = 1, | ||
| 377 | }; | ||
| 378 | |||
| 374 | struct Values { | 379 | struct Values { |
| 375 | // System | 380 | // System |
| 376 | bool use_docked_mode; | 381 | bool use_docked_mode; |
| @@ -401,6 +406,9 @@ struct Values { | |||
| 401 | std::string motion_device; | 406 | std::string motion_device; |
| 402 | TouchscreenInput touchscreen; | 407 | TouchscreenInput touchscreen; |
| 403 | std::atomic_bool is_device_reload_pending{true}; | 408 | std::atomic_bool is_device_reload_pending{true}; |
| 409 | std::string udp_input_address; | ||
| 410 | u16 udp_input_port; | ||
| 411 | u8 udp_pad_index; | ||
| 404 | 412 | ||
| 405 | // Core | 413 | // Core |
| 406 | bool use_multi_core; | 414 | bool use_multi_core; |
| @@ -416,7 +424,12 @@ struct Values { | |||
| 416 | SDMCSize sdmc_size; | 424 | SDMCSize sdmc_size; |
| 417 | 425 | ||
| 418 | // Renderer | 426 | // Renderer |
| 427 | RendererBackend renderer_backend; | ||
| 428 | bool renderer_debug; | ||
| 429 | int vulkan_device; | ||
| 430 | |||
| 419 | float resolution_factor; | 431 | float resolution_factor; |
| 432 | int aspect_ratio; | ||
| 420 | bool use_frame_limit; | 433 | bool use_frame_limit; |
| 421 | u16 frame_limit; | 434 | u16 frame_limit; |
| 422 | bool use_disk_shader_cache; | 435 | bool use_disk_shader_cache; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 320e8ad73..0e72d31cd 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -46,6 +46,16 @@ static u64 GenerateTelemetryId() { | |||
| 46 | return telemetry_id; | 46 | return telemetry_id; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | static const char* TranslateRenderer(Settings::RendererBackend backend) { | ||
| 50 | switch (backend) { | ||
| 51 | case Settings::RendererBackend::OpenGL: | ||
| 52 | return "OpenGL"; | ||
| 53 | case Settings::RendererBackend::Vulkan: | ||
| 54 | return "Vulkan"; | ||
| 55 | } | ||
| 56 | return "Unknown"; | ||
| 57 | } | ||
| 58 | |||
| 49 | u64 GetTelemetryId() { | 59 | u64 GetTelemetryId() { |
| 50 | u64 telemetry_id{}; | 60 | u64 telemetry_id{}; |
| 51 | const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + | 61 | const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + |
| @@ -169,7 +179,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { | |||
| 169 | AddField(field_type, "Audio_SinkId", Settings::values.sink_id); | 179 | AddField(field_type, "Audio_SinkId", Settings::values.sink_id); |
| 170 | AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); | 180 | AddField(field_type, "Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); |
| 171 | AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core); | 181 | AddField(field_type, "Core_UseMultiCore", Settings::values.use_multi_core); |
| 172 | AddField(field_type, "Renderer_Backend", "OpenGL"); | 182 | AddField(field_type, "Renderer_Backend", TranslateRenderer(Settings::values.renderer_backend)); |
| 173 | AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor); | 183 | AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor); |
| 174 | AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); | 184 | AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit); |
| 175 | AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); | 185 | AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit); |
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 55e0dbc49..1e060f009 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp | |||
| @@ -7,13 +7,14 @@ | |||
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/core_timing_util.h" | 9 | #include "core/core_timing_util.h" |
| 10 | #include "core/hardware_properties.h" | ||
| 10 | #include "core/memory.h" | 11 | #include "core/memory.h" |
| 11 | #include "core/tools/freezer.h" | 12 | #include "core/tools/freezer.h" |
| 12 | 13 | ||
| 13 | namespace Tools { | 14 | namespace Tools { |
| 14 | namespace { | 15 | namespace { |
| 15 | 16 | ||
| 16 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); | 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); |
| 17 | 18 | ||
| 18 | u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { | 19 | u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { |
| 19 | switch (width) { | 20 | switch (width) { |
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 5b4e032bd..2520ba321 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -9,6 +9,12 @@ add_library(input_common STATIC | |||
| 9 | motion_emu.h | 9 | motion_emu.h |
| 10 | sdl/sdl.cpp | 10 | sdl/sdl.cpp |
| 11 | sdl/sdl.h | 11 | sdl/sdl.h |
| 12 | udp/client.cpp | ||
| 13 | udp/client.h | ||
| 14 | udp/protocol.cpp | ||
| 15 | udp/protocol.h | ||
| 16 | udp/udp.cpp | ||
| 17 | udp/udp.h | ||
| 12 | ) | 18 | ) |
| 13 | 19 | ||
| 14 | if(SDL2_FOUND) | 20 | if(SDL2_FOUND) |
| @@ -21,4 +27,4 @@ if(SDL2_FOUND) | |||
| 21 | endif() | 27 | endif() |
| 22 | 28 | ||
| 23 | create_target_directory_groups(input_common) | 29 | create_target_directory_groups(input_common) |
| 24 | target_link_libraries(input_common PUBLIC core PRIVATE common) | 30 | target_link_libraries(input_common PUBLIC core PRIVATE common ${Boost_LIBRARIES}) |
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index 8e66c1b15..c98c848cf 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "input_common/keyboard.h" | 9 | #include "input_common/keyboard.h" |
| 10 | #include "input_common/main.h" | 10 | #include "input_common/main.h" |
| 11 | #include "input_common/motion_emu.h" | 11 | #include "input_common/motion_emu.h" |
| 12 | #include "input_common/udp/udp.h" | ||
| 12 | #ifdef HAVE_SDL2 | 13 | #ifdef HAVE_SDL2 |
| 13 | #include "input_common/sdl/sdl.h" | 14 | #include "input_common/sdl/sdl.h" |
| 14 | #endif | 15 | #endif |
| @@ -18,6 +19,7 @@ namespace InputCommon { | |||
| 18 | static std::shared_ptr<Keyboard> keyboard; | 19 | static std::shared_ptr<Keyboard> keyboard; |
| 19 | static std::shared_ptr<MotionEmu> motion_emu; | 20 | static std::shared_ptr<MotionEmu> motion_emu; |
| 20 | static std::unique_ptr<SDL::State> sdl; | 21 | static std::unique_ptr<SDL::State> sdl; |
| 22 | static std::unique_ptr<CemuhookUDP::State> udp; | ||
| 21 | 23 | ||
| 22 | void Init() { | 24 | void Init() { |
| 23 | keyboard = std::make_shared<Keyboard>(); | 25 | keyboard = std::make_shared<Keyboard>(); |
| @@ -28,6 +30,8 @@ void Init() { | |||
| 28 | Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu); | 30 | Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu); |
| 29 | 31 | ||
| 30 | sdl = SDL::Init(); | 32 | sdl = SDL::Init(); |
| 33 | |||
| 34 | udp = CemuhookUDP::Init(); | ||
| 31 | } | 35 | } |
| 32 | 36 | ||
| 33 | void Shutdown() { | 37 | void Shutdown() { |
| @@ -37,6 +41,7 @@ void Shutdown() { | |||
| 37 | Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); | 41 | Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); |
| 38 | motion_emu.reset(); | 42 | motion_emu.reset(); |
| 39 | sdl.reset(); | 43 | sdl.reset(); |
| 44 | udp.reset(); | ||
| 40 | } | 45 | } |
| 41 | 46 | ||
| 42 | Keyboard* GetKeyboard() { | 47 | Keyboard* GetKeyboard() { |
| @@ -72,11 +77,13 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, | |||
| 72 | namespace Polling { | 77 | namespace Polling { |
| 73 | 78 | ||
| 74 | std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) { | 79 | std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) { |
| 80 | std::vector<std::unique_ptr<DevicePoller>> pollers; | ||
| 81 | |||
| 75 | #ifdef HAVE_SDL2 | 82 | #ifdef HAVE_SDL2 |
| 76 | return sdl->GetPollers(type); | 83 | pollers = sdl->GetPollers(type); |
| 77 | #else | ||
| 78 | return {}; | ||
| 79 | #endif | 84 | #endif |
| 85 | |||
| 86 | return pollers; | ||
| 80 | } | 87 | } |
| 81 | 88 | ||
| 82 | } // namespace Polling | 89 | } // namespace Polling |
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index d2e9d278f..a2e0c0bd2 100644 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp | |||
| @@ -342,6 +342,22 @@ public: | |||
| 342 | return std::make_tuple<float, float>(0.0f, 0.0f); | 342 | return std::make_tuple<float, float>(0.0f, 0.0f); |
| 343 | } | 343 | } |
| 344 | 344 | ||
| 345 | bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override { | ||
| 346 | const auto [x, y] = GetStatus(); | ||
| 347 | const float directional_deadzone = 0.4f; | ||
| 348 | switch (direction) { | ||
| 349 | case Input::AnalogDirection::RIGHT: | ||
| 350 | return x > directional_deadzone; | ||
| 351 | case Input::AnalogDirection::LEFT: | ||
| 352 | return x < -directional_deadzone; | ||
| 353 | case Input::AnalogDirection::UP: | ||
| 354 | return y > directional_deadzone; | ||
| 355 | case Input::AnalogDirection::DOWN: | ||
| 356 | return y < -directional_deadzone; | ||
| 357 | } | ||
| 358 | return false; | ||
| 359 | } | ||
| 360 | |||
| 345 | private: | 361 | private: |
| 346 | std::shared_ptr<SDLJoystick> joystick; | 362 | std::shared_ptr<SDLJoystick> joystick; |
| 347 | const int axis_x; | 363 | const int axis_x; |
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp new file mode 100644 index 000000000..2228571a6 --- /dev/null +++ b/src/input_common/udp/client.cpp | |||
| @@ -0,0 +1,286 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <chrono> | ||
| 8 | #include <cstring> | ||
| 9 | #include <functional> | ||
| 10 | #include <thread> | ||
| 11 | #include <boost/asio.hpp> | ||
| 12 | #include <boost/bind.hpp> | ||
| 13 | #include "common/logging/log.h" | ||
| 14 | #include "input_common/udp/client.h" | ||
| 15 | #include "input_common/udp/protocol.h" | ||
| 16 | |||
| 17 | using boost::asio::ip::udp; | ||
| 18 | |||
| 19 | namespace InputCommon::CemuhookUDP { | ||
| 20 | |||
| 21 | struct SocketCallback { | ||
| 22 | std::function<void(Response::Version)> version; | ||
| 23 | std::function<void(Response::PortInfo)> port_info; | ||
| 24 | std::function<void(Response::PadData)> pad_data; | ||
| 25 | }; | ||
| 26 | |||
| 27 | class Socket { | ||
| 28 | public: | ||
| 29 | using clock = std::chrono::system_clock; | ||
| 30 | |||
| 31 | explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, | ||
| 32 | SocketCallback callback) | ||
| 33 | : callback(std::move(callback)), timer(io_service), | ||
| 34 | socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id), | ||
| 35 | pad_index(pad_index), | ||
| 36 | send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {} | ||
| 37 | |||
| 38 | void Stop() { | ||
| 39 | io_service.stop(); | ||
| 40 | } | ||
| 41 | |||
| 42 | void Loop() { | ||
| 43 | io_service.run(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void StartSend(const clock::time_point& from) { | ||
| 47 | timer.expires_at(from + std::chrono::seconds(3)); | ||
| 48 | timer.async_wait([this](const boost::system::error_code& error) { HandleSend(error); }); | ||
| 49 | } | ||
| 50 | |||
| 51 | void StartReceive() { | ||
| 52 | socket.async_receive_from( | ||
| 53 | boost::asio::buffer(receive_buffer), receive_endpoint, | ||
| 54 | [this](const boost::system::error_code& error, std::size_t bytes_transferred) { | ||
| 55 | HandleReceive(error, bytes_transferred); | ||
| 56 | }); | ||
| 57 | } | ||
| 58 | |||
| 59 | private: | ||
| 60 | void HandleReceive(const boost::system::error_code& error, std::size_t bytes_transferred) { | ||
| 61 | if (auto type = Response::Validate(receive_buffer.data(), bytes_transferred)) { | ||
| 62 | switch (*type) { | ||
| 63 | case Type::Version: { | ||
| 64 | Response::Version version; | ||
| 65 | std::memcpy(&version, &receive_buffer[sizeof(Header)], sizeof(Response::Version)); | ||
| 66 | callback.version(std::move(version)); | ||
| 67 | break; | ||
| 68 | } | ||
| 69 | case Type::PortInfo: { | ||
| 70 | Response::PortInfo port_info; | ||
| 71 | std::memcpy(&port_info, &receive_buffer[sizeof(Header)], | ||
| 72 | sizeof(Response::PortInfo)); | ||
| 73 | callback.port_info(std::move(port_info)); | ||
| 74 | break; | ||
| 75 | } | ||
| 76 | case Type::PadData: { | ||
| 77 | Response::PadData pad_data; | ||
| 78 | std::memcpy(&pad_data, &receive_buffer[sizeof(Header)], sizeof(Response::PadData)); | ||
| 79 | callback.pad_data(std::move(pad_data)); | ||
| 80 | break; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | StartReceive(); | ||
| 85 | } | ||
| 86 | |||
| 87 | void HandleSend(const boost::system::error_code& error) { | ||
| 88 | // Send a request for getting port info for the pad | ||
| 89 | Request::PortInfo port_info{1, {pad_index, 0, 0, 0}}; | ||
| 90 | const auto port_message = Request::Create(port_info, client_id); | ||
| 91 | std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE); | ||
| 92 | socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint); | ||
| 93 | |||
| 94 | // Send a request for getting pad data for the pad | ||
| 95 | Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS}; | ||
| 96 | const auto pad_message = Request::Create(pad_data, client_id); | ||
| 97 | std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE); | ||
| 98 | socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint); | ||
| 99 | StartSend(timer.expiry()); | ||
| 100 | } | ||
| 101 | |||
| 102 | SocketCallback callback; | ||
| 103 | boost::asio::io_service io_service; | ||
| 104 | boost::asio::basic_waitable_timer<clock> timer; | ||
| 105 | udp::socket socket; | ||
| 106 | |||
| 107 | u32 client_id{}; | ||
| 108 | u8 pad_index{}; | ||
| 109 | |||
| 110 | static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>); | ||
| 111 | static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>); | ||
| 112 | std::array<u8, PORT_INFO_SIZE> send_buffer1; | ||
| 113 | std::array<u8, PAD_DATA_SIZE> send_buffer2; | ||
| 114 | udp::endpoint send_endpoint; | ||
| 115 | |||
| 116 | std::array<u8, MAX_PACKET_SIZE> receive_buffer; | ||
| 117 | udp::endpoint receive_endpoint; | ||
| 118 | }; | ||
| 119 | |||
| 120 | static void SocketLoop(Socket* socket) { | ||
| 121 | socket->StartReceive(); | ||
| 122 | socket->StartSend(Socket::clock::now()); | ||
| 123 | socket->Loop(); | ||
| 124 | } | ||
| 125 | |||
| 126 | Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, | ||
| 127 | u8 pad_index, u32 client_id) | ||
| 128 | : status(std::move(status)) { | ||
| 129 | StartCommunication(host, port, pad_index, client_id); | ||
| 130 | } | ||
| 131 | |||
| 132 | Client::~Client() { | ||
| 133 | socket->Stop(); | ||
| 134 | thread.join(); | ||
| 135 | } | ||
| 136 | |||
| 137 | void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) { | ||
| 138 | socket->Stop(); | ||
| 139 | thread.join(); | ||
| 140 | StartCommunication(host, port, pad_index, client_id); | ||
| 141 | } | ||
| 142 | |||
| 143 | void Client::OnVersion(Response::Version data) { | ||
| 144 | LOG_TRACE(Input, "Version packet received: {}", data.version); | ||
| 145 | } | ||
| 146 | |||
| 147 | void Client::OnPortInfo(Response::PortInfo data) { | ||
| 148 | LOG_TRACE(Input, "PortInfo packet received: {}", data.model); | ||
| 149 | } | ||
| 150 | |||
| 151 | void Client::OnPadData(Response::PadData data) { | ||
| 152 | LOG_TRACE(Input, "PadData packet received"); | ||
| 153 | if (data.packet_counter <= packet_sequence) { | ||
| 154 | LOG_WARNING( | ||
| 155 | Input, | ||
| 156 | "PadData packet dropped because its stale info. Current count: {} Packet count: {}", | ||
| 157 | packet_sequence, data.packet_counter); | ||
| 158 | return; | ||
| 159 | } | ||
| 160 | packet_sequence = data.packet_counter; | ||
| 161 | // TODO: Check how the Switch handles motions and how the CemuhookUDP motion | ||
| 162 | // directions correspond to the ones of the Switch | ||
| 163 | Common::Vec3f accel = Common::MakeVec<float>(data.accel.x, data.accel.y, data.accel.z); | ||
| 164 | Common::Vec3f gyro = Common::MakeVec<float>(data.gyro.pitch, data.gyro.yaw, data.gyro.roll); | ||
| 165 | { | ||
| 166 | std::lock_guard guard(status->update_mutex); | ||
| 167 | |||
| 168 | status->motion_status = {accel, gyro}; | ||
| 169 | |||
| 170 | // TODO: add a setting for "click" touch. Click touch refers to a device that differentiates | ||
| 171 | // between a simple "tap" and a hard press that causes the touch screen to click. | ||
| 172 | const bool is_active = data.touch_1.is_active != 0; | ||
| 173 | |||
| 174 | float x = 0; | ||
| 175 | float y = 0; | ||
| 176 | |||
| 177 | if (is_active && status->touch_calibration) { | ||
| 178 | const u16 min_x = status->touch_calibration->min_x; | ||
| 179 | const u16 max_x = status->touch_calibration->max_x; | ||
| 180 | const u16 min_y = status->touch_calibration->min_y; | ||
| 181 | const u16 max_y = status->touch_calibration->max_y; | ||
| 182 | |||
| 183 | x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) / | ||
| 184 | static_cast<float>(max_x - min_x); | ||
| 185 | y = (std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) - min_y) / | ||
| 186 | static_cast<float>(max_y - min_y); | ||
| 187 | } | ||
| 188 | |||
| 189 | status->touch_status = {x, y, is_active}; | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) { | ||
| 194 | SocketCallback callback{[this](Response::Version version) { OnVersion(version); }, | ||
| 195 | [this](Response::PortInfo info) { OnPortInfo(info); }, | ||
| 196 | [this](Response::PadData data) { OnPadData(data); }}; | ||
| 197 | LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port); | ||
| 198 | socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback); | ||
| 199 | thread = std::thread{SocketLoop, this->socket.get()}; | ||
| 200 | } | ||
| 201 | |||
| 202 | void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, | ||
| 203 | std::function<void()> success_callback, | ||
| 204 | std::function<void()> failure_callback) { | ||
| 205 | std::thread([=] { | ||
| 206 | Common::Event success_event; | ||
| 207 | SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, | ||
| 208 | [&](Response::PadData data) { success_event.Set(); }}; | ||
| 209 | Socket socket{host, port, pad_index, client_id, std::move(callback)}; | ||
| 210 | std::thread worker_thread{SocketLoop, &socket}; | ||
| 211 | bool result = success_event.WaitFor(std::chrono::seconds(8)); | ||
| 212 | socket.Stop(); | ||
| 213 | worker_thread.join(); | ||
| 214 | if (result) { | ||
| 215 | success_callback(); | ||
| 216 | } else { | ||
| 217 | failure_callback(); | ||
| 218 | } | ||
| 219 | }) | ||
| 220 | .detach(); | ||
| 221 | } | ||
| 222 | |||
| 223 | CalibrationConfigurationJob::CalibrationConfigurationJob( | ||
| 224 | const std::string& host, u16 port, u8 pad_index, u32 client_id, | ||
| 225 | std::function<void(Status)> status_callback, | ||
| 226 | std::function<void(u16, u16, u16, u16)> data_callback) { | ||
| 227 | |||
| 228 | std::thread([=] { | ||
| 229 | constexpr u16 CALIBRATION_THRESHOLD = 100; | ||
| 230 | |||
| 231 | u16 min_x{UINT16_MAX}; | ||
| 232 | u16 min_y{UINT16_MAX}; | ||
| 233 | u16 max_x{}; | ||
| 234 | u16 max_y{}; | ||
| 235 | |||
| 236 | Status current_status{Status::Initialized}; | ||
| 237 | SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, | ||
| 238 | [&](Response::PadData data) { | ||
| 239 | if (current_status == Status::Initialized) { | ||
| 240 | // Receiving data means the communication is ready now | ||
| 241 | current_status = Status::Ready; | ||
| 242 | status_callback(current_status); | ||
| 243 | } | ||
| 244 | if (!data.touch_1.is_active) { | ||
| 245 | return; | ||
| 246 | } | ||
| 247 | LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x, | ||
| 248 | data.touch_1.y); | ||
| 249 | min_x = std::min(min_x, static_cast<u16>(data.touch_1.x)); | ||
| 250 | min_y = std::min(min_y, static_cast<u16>(data.touch_1.y)); | ||
| 251 | if (current_status == Status::Ready) { | ||
| 252 | // First touch - min data (min_x/min_y) | ||
| 253 | current_status = Status::Stage1Completed; | ||
| 254 | status_callback(current_status); | ||
| 255 | } | ||
| 256 | if (data.touch_1.x - min_x > CALIBRATION_THRESHOLD && | ||
| 257 | data.touch_1.y - min_y > CALIBRATION_THRESHOLD) { | ||
| 258 | // Set the current position as max value and finishes | ||
| 259 | // configuration | ||
| 260 | max_x = data.touch_1.x; | ||
| 261 | max_y = data.touch_1.y; | ||
| 262 | current_status = Status::Completed; | ||
| 263 | data_callback(min_x, min_y, max_x, max_y); | ||
| 264 | status_callback(current_status); | ||
| 265 | |||
| 266 | complete_event.Set(); | ||
| 267 | } | ||
| 268 | }}; | ||
| 269 | Socket socket{host, port, pad_index, client_id, std::move(callback)}; | ||
| 270 | std::thread worker_thread{SocketLoop, &socket}; | ||
| 271 | complete_event.Wait(); | ||
| 272 | socket.Stop(); | ||
| 273 | worker_thread.join(); | ||
| 274 | }) | ||
| 275 | .detach(); | ||
| 276 | } | ||
| 277 | |||
| 278 | CalibrationConfigurationJob::~CalibrationConfigurationJob() { | ||
| 279 | Stop(); | ||
| 280 | } | ||
| 281 | |||
| 282 | void CalibrationConfigurationJob::Stop() { | ||
| 283 | complete_event.Set(); | ||
| 284 | } | ||
| 285 | |||
| 286 | } // namespace InputCommon::CemuhookUDP | ||
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h new file mode 100644 index 000000000..b8c654755 --- /dev/null +++ b/src/input_common/udp/client.h | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | #include <memory> | ||
| 9 | #include <mutex> | ||
| 10 | #include <optional> | ||
| 11 | #include <string> | ||
| 12 | #include <thread> | ||
| 13 | #include <tuple> | ||
| 14 | #include "common/common_types.h" | ||
| 15 | #include "common/thread.h" | ||
| 16 | #include "common/vector_math.h" | ||
| 17 | |||
| 18 | namespace InputCommon::CemuhookUDP { | ||
| 19 | |||
| 20 | constexpr u16 DEFAULT_PORT = 26760; | ||
| 21 | constexpr char DEFAULT_ADDR[] = "127.0.0.1"; | ||
| 22 | |||
| 23 | class Socket; | ||
| 24 | |||
| 25 | namespace Response { | ||
| 26 | struct PadData; | ||
| 27 | struct PortInfo; | ||
| 28 | struct Version; | ||
| 29 | } // namespace Response | ||
| 30 | |||
| 31 | struct DeviceStatus { | ||
| 32 | std::mutex update_mutex; | ||
| 33 | std::tuple<Common::Vec3<float>, Common::Vec3<float>> motion_status; | ||
| 34 | std::tuple<float, float, bool> touch_status; | ||
| 35 | |||
| 36 | // calibration data for scaling the device's touch area to 3ds | ||
| 37 | struct CalibrationData { | ||
| 38 | u16 min_x{}; | ||
| 39 | u16 min_y{}; | ||
| 40 | u16 max_x{}; | ||
| 41 | u16 max_y{}; | ||
| 42 | }; | ||
| 43 | std::optional<CalibrationData> touch_calibration; | ||
| 44 | }; | ||
| 45 | |||
| 46 | class Client { | ||
| 47 | public: | ||
| 48 | explicit Client(std::shared_ptr<DeviceStatus> status, const std::string& host = DEFAULT_ADDR, | ||
| 49 | u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872); | ||
| 50 | ~Client(); | ||
| 51 | void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0, | ||
| 52 | u32 client_id = 24872); | ||
| 53 | |||
| 54 | private: | ||
| 55 | void OnVersion(Response::Version); | ||
| 56 | void OnPortInfo(Response::PortInfo); | ||
| 57 | void OnPadData(Response::PadData); | ||
| 58 | void StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id); | ||
| 59 | |||
| 60 | std::unique_ptr<Socket> socket; | ||
| 61 | std::shared_ptr<DeviceStatus> status; | ||
| 62 | std::thread thread; | ||
| 63 | u64 packet_sequence = 0; | ||
| 64 | }; | ||
| 65 | |||
| 66 | /// An async job allowing configuration of the touchpad calibration. | ||
| 67 | class CalibrationConfigurationJob { | ||
| 68 | public: | ||
| 69 | enum class Status { | ||
| 70 | Initialized, | ||
| 71 | Ready, | ||
| 72 | Stage1Completed, | ||
| 73 | Completed, | ||
| 74 | }; | ||
| 75 | /** | ||
| 76 | * Constructs and starts the job with the specified parameter. | ||
| 77 | * | ||
| 78 | * @param status_callback Callback for job status updates | ||
| 79 | * @param data_callback Called when calibration data is ready | ||
| 80 | */ | ||
| 81 | explicit CalibrationConfigurationJob(const std::string& host, u16 port, u8 pad_index, | ||
| 82 | u32 client_id, std::function<void(Status)> status_callback, | ||
| 83 | std::function<void(u16, u16, u16, u16)> data_callback); | ||
| 84 | ~CalibrationConfigurationJob(); | ||
| 85 | void Stop(); | ||
| 86 | |||
| 87 | private: | ||
| 88 | Common::Event complete_event; | ||
| 89 | }; | ||
| 90 | |||
| 91 | void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id, | ||
| 92 | std::function<void()> success_callback, | ||
| 93 | std::function<void()> failure_callback); | ||
| 94 | |||
| 95 | } // namespace InputCommon::CemuhookUDP | ||
diff --git a/src/input_common/udp/protocol.cpp b/src/input_common/udp/protocol.cpp new file mode 100644 index 000000000..a982ac49d --- /dev/null +++ b/src/input_common/udp/protocol.cpp | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstddef> | ||
| 6 | #include <cstring> | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "input_common/udp/protocol.h" | ||
| 9 | |||
| 10 | namespace InputCommon::CemuhookUDP { | ||
| 11 | |||
| 12 | static constexpr std::size_t GetSizeOfResponseType(Type t) { | ||
| 13 | switch (t) { | ||
| 14 | case Type::Version: | ||
| 15 | return sizeof(Response::Version); | ||
| 16 | case Type::PortInfo: | ||
| 17 | return sizeof(Response::PortInfo); | ||
| 18 | case Type::PadData: | ||
| 19 | return sizeof(Response::PadData); | ||
| 20 | } | ||
| 21 | return 0; | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace Response { | ||
| 25 | |||
| 26 | /** | ||
| 27 | * Returns Type if the packet is valid, else none | ||
| 28 | * | ||
| 29 | * Note: Modifies the buffer to zero out the crc (since thats the easiest way to check without | ||
| 30 | * copying the buffer) | ||
| 31 | */ | ||
| 32 | std::optional<Type> Validate(u8* data, std::size_t size) { | ||
| 33 | if (size < sizeof(Header)) { | ||
| 34 | LOG_DEBUG(Input, "Invalid UDP packet received"); | ||
| 35 | return std::nullopt; | ||
| 36 | } | ||
| 37 | Header header{}; | ||
| 38 | std::memcpy(&header, data, sizeof(Header)); | ||
| 39 | if (header.magic != SERVER_MAGIC) { | ||
| 40 | LOG_ERROR(Input, "UDP Packet has an unexpected magic value"); | ||
| 41 | return std::nullopt; | ||
| 42 | } | ||
| 43 | if (header.protocol_version != PROTOCOL_VERSION) { | ||
| 44 | LOG_ERROR(Input, "UDP Packet protocol mismatch"); | ||
| 45 | return std::nullopt; | ||
| 46 | } | ||
| 47 | if (header.type < Type::Version || header.type > Type::PadData) { | ||
| 48 | LOG_ERROR(Input, "UDP Packet is an unknown type"); | ||
| 49 | return std::nullopt; | ||
| 50 | } | ||
| 51 | |||
| 52 | // Packet size must equal sizeof(Header) + sizeof(Data) | ||
| 53 | // and also verify that the packet info mentions the correct size. Since the spec includes the | ||
| 54 | // type of the packet as part of the data, we need to include it in size calculations here | ||
| 55 | // ie: payload_length == sizeof(T) + sizeof(Type) | ||
| 56 | const std::size_t data_len = GetSizeOfResponseType(header.type); | ||
| 57 | if (header.payload_length != data_len + sizeof(Type) || size < data_len + sizeof(Header)) { | ||
| 58 | LOG_ERROR( | ||
| 59 | Input, | ||
| 60 | "UDP Packet payload length doesn't match. Received: {} PayloadLength: {} Expected: {}", | ||
| 61 | size, header.payload_length, data_len + sizeof(Type)); | ||
| 62 | return std::nullopt; | ||
| 63 | } | ||
| 64 | |||
| 65 | const u32 crc32 = header.crc; | ||
| 66 | boost::crc_32_type result; | ||
| 67 | // zero out the crc in the buffer and then run the crc against it | ||
| 68 | std::memset(&data[offsetof(Header, crc)], 0, sizeof(u32_le)); | ||
| 69 | |||
| 70 | result.process_bytes(data, data_len + sizeof(Header)); | ||
| 71 | if (crc32 != result.checksum()) { | ||
| 72 | LOG_ERROR(Input, "UDP Packet CRC check failed. Offset: {}", offsetof(Header, crc)); | ||
| 73 | return std::nullopt; | ||
| 74 | } | ||
| 75 | return header.type; | ||
| 76 | } | ||
| 77 | } // namespace Response | ||
| 78 | |||
| 79 | } // namespace InputCommon::CemuhookUDP | ||
diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h new file mode 100644 index 000000000..3ba4d1fc8 --- /dev/null +++ b/src/input_common/udp/protocol.h | |||
| @@ -0,0 +1,255 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <optional> | ||
| 9 | #include <type_traits> | ||
| 10 | #include <boost/crc.hpp> | ||
| 11 | #include "common/bit_field.h" | ||
| 12 | #include "common/swap.h" | ||
| 13 | |||
| 14 | namespace InputCommon::CemuhookUDP { | ||
| 15 | |||
| 16 | constexpr std::size_t MAX_PACKET_SIZE = 100; | ||
| 17 | constexpr u16 PROTOCOL_VERSION = 1001; | ||
| 18 | constexpr u32 CLIENT_MAGIC = 0x43555344; // DSUC (but flipped for LE) | ||
| 19 | constexpr u32 SERVER_MAGIC = 0x53555344; // DSUS (but flipped for LE) | ||
| 20 | |||
| 21 | enum class Type : u32 { | ||
| 22 | Version = 0x00100000, | ||
| 23 | PortInfo = 0x00100001, | ||
| 24 | PadData = 0x00100002, | ||
| 25 | }; | ||
| 26 | |||
| 27 | struct Header { | ||
| 28 | u32_le magic{}; | ||
| 29 | u16_le protocol_version{}; | ||
| 30 | u16_le payload_length{}; | ||
| 31 | u32_le crc{}; | ||
| 32 | u32_le id{}; | ||
| 33 | ///> In the protocol, the type of the packet is not part of the header, but its convenient to | ||
| 34 | ///> include in the header so the callee doesn't have to duplicate the type twice when building | ||
| 35 | ///> the data | ||
| 36 | Type type{}; | ||
| 37 | }; | ||
| 38 | static_assert(sizeof(Header) == 20, "UDP Message Header struct has wrong size"); | ||
| 39 | static_assert(std::is_trivially_copyable_v<Header>, "UDP Message Header is not trivially copyable"); | ||
| 40 | |||
| 41 | using MacAddress = std::array<u8, 6>; | ||
| 42 | constexpr MacAddress EMPTY_MAC_ADDRESS = {0, 0, 0, 0, 0, 0}; | ||
| 43 | |||
| 44 | #pragma pack(push, 1) | ||
| 45 | template <typename T> | ||
| 46 | struct Message { | ||
| 47 | Header header{}; | ||
| 48 | T data; | ||
| 49 | }; | ||
| 50 | #pragma pack(pop) | ||
| 51 | |||
| 52 | template <typename T> | ||
| 53 | constexpr Type GetMessageType(); | ||
| 54 | |||
| 55 | namespace Request { | ||
| 56 | |||
| 57 | struct Version {}; | ||
| 58 | /** | ||
| 59 | * Requests the server to send information about what controllers are plugged into the ports | ||
| 60 | * In citra's case, we only have one controller, so for simplicity's sake, we can just send a | ||
| 61 | * request explicitly for the first controller port and leave it at that. In the future it would be | ||
| 62 | * nice to make this configurable | ||
| 63 | */ | ||
| 64 | constexpr u32 MAX_PORTS = 4; | ||
| 65 | struct PortInfo { | ||
| 66 | u32_le pad_count{}; ///> Number of ports to request data for | ||
| 67 | std::array<u8, MAX_PORTS> port; | ||
| 68 | }; | ||
| 69 | static_assert(std::is_trivially_copyable_v<PortInfo>, | ||
| 70 | "UDP Request PortInfo is not trivially copyable"); | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Request the latest pad information from the server. If the server hasn't received this message | ||
| 74 | * from the client in a reasonable time frame, the server will stop sending updates. The default | ||
| 75 | * timeout seems to be 5 seconds. | ||
| 76 | */ | ||
| 77 | struct PadData { | ||
| 78 | enum class Flags : u8 { | ||
| 79 | AllPorts, | ||
| 80 | Id, | ||
| 81 | Mac, | ||
| 82 | }; | ||
| 83 | /// Determines which method will be used as a look up for the controller | ||
| 84 | Flags flags{}; | ||
| 85 | /// Index of the port of the controller to retrieve data about | ||
| 86 | u8 port_id{}; | ||
| 87 | /// Mac address of the controller to retrieve data about | ||
| 88 | MacAddress mac; | ||
| 89 | }; | ||
| 90 | static_assert(sizeof(PadData) == 8, "UDP Request PadData struct has wrong size"); | ||
| 91 | static_assert(std::is_trivially_copyable_v<PadData>, | ||
| 92 | "UDP Request PadData is not trivially copyable"); | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Creates a message with the proper header data that can be sent to the server. | ||
| 96 | * @param T data Request body to send | ||
| 97 | * @param client_id ID of the udp client (usually not checked on the server) | ||
| 98 | */ | ||
| 99 | template <typename T> | ||
| 100 | Message<T> Create(const T data, const u32 client_id = 0) { | ||
| 101 | boost::crc_32_type crc; | ||
| 102 | Header header{ | ||
| 103 | CLIENT_MAGIC, PROTOCOL_VERSION, sizeof(T) + sizeof(Type), 0, client_id, GetMessageType<T>(), | ||
| 104 | }; | ||
| 105 | Message<T> message{header, data}; | ||
| 106 | crc.process_bytes(&message, sizeof(Message<T>)); | ||
| 107 | message.header.crc = crc.checksum(); | ||
| 108 | return message; | ||
| 109 | } | ||
| 110 | } // namespace Request | ||
| 111 | |||
| 112 | namespace Response { | ||
| 113 | |||
| 114 | struct Version { | ||
| 115 | u16_le version{}; | ||
| 116 | }; | ||
| 117 | static_assert(sizeof(Version) == 2, "UDP Response Version struct has wrong size"); | ||
| 118 | static_assert(std::is_trivially_copyable_v<Version>, | ||
| 119 | "UDP Response Version is not trivially copyable"); | ||
| 120 | |||
| 121 | struct PortInfo { | ||
| 122 | u8 id{}; | ||
| 123 | u8 state{}; | ||
| 124 | u8 model{}; | ||
| 125 | u8 connection_type{}; | ||
| 126 | MacAddress mac; | ||
| 127 | u8 battery{}; | ||
| 128 | u8 is_pad_active{}; | ||
| 129 | }; | ||
| 130 | static_assert(sizeof(PortInfo) == 12, "UDP Response PortInfo struct has wrong size"); | ||
| 131 | static_assert(std::is_trivially_copyable_v<PortInfo>, | ||
| 132 | "UDP Response PortInfo is not trivially copyable"); | ||
| 133 | |||
| 134 | #pragma pack(push, 1) | ||
| 135 | struct PadData { | ||
| 136 | PortInfo info{}; | ||
| 137 | u32_le packet_counter{}; | ||
| 138 | |||
| 139 | u16_le digital_button{}; | ||
| 140 | // The following union isn't trivially copyable but we don't use this input anyway. | ||
| 141 | // union DigitalButton { | ||
| 142 | // u16_le button; | ||
| 143 | // BitField<0, 1, u16> button_1; // Share | ||
| 144 | // BitField<1, 1, u16> button_2; // L3 | ||
| 145 | // BitField<2, 1, u16> button_3; // R3 | ||
| 146 | // BitField<3, 1, u16> button_4; // Options | ||
| 147 | // BitField<4, 1, u16> button_5; // Up | ||
| 148 | // BitField<5, 1, u16> button_6; // Right | ||
| 149 | // BitField<6, 1, u16> button_7; // Down | ||
| 150 | // BitField<7, 1, u16> button_8; // Left | ||
| 151 | // BitField<8, 1, u16> button_9; // L2 | ||
| 152 | // BitField<9, 1, u16> button_10; // R2 | ||
| 153 | // BitField<10, 1, u16> button_11; // L1 | ||
| 154 | // BitField<11, 1, u16> button_12; // R1 | ||
| 155 | // BitField<12, 1, u16> button_13; // Triangle | ||
| 156 | // BitField<13, 1, u16> button_14; // Circle | ||
| 157 | // BitField<14, 1, u16> button_15; // Cross | ||
| 158 | // BitField<15, 1, u16> button_16; // Square | ||
| 159 | // } digital_button; | ||
| 160 | |||
| 161 | u8 home; | ||
| 162 | /// If the device supports a "click" on the touchpad, this will change to 1 when a click happens | ||
| 163 | u8 touch_hard_press{}; | ||
| 164 | u8 left_stick_x{}; | ||
| 165 | u8 left_stick_y{}; | ||
| 166 | u8 right_stick_x{}; | ||
| 167 | u8 right_stick_y{}; | ||
| 168 | |||
| 169 | struct AnalogButton { | ||
| 170 | u8 button_8{}; | ||
| 171 | u8 button_7{}; | ||
| 172 | u8 button_6{}; | ||
| 173 | u8 button_5{}; | ||
| 174 | u8 button_12{}; | ||
| 175 | u8 button_11{}; | ||
| 176 | u8 button_10{}; | ||
| 177 | u8 button_9{}; | ||
| 178 | u8 button_16{}; | ||
| 179 | u8 button_15{}; | ||
| 180 | u8 button_14{}; | ||
| 181 | u8 button_13{}; | ||
| 182 | } analog_button; | ||
| 183 | |||
| 184 | struct TouchPad { | ||
| 185 | u8 is_active{}; | ||
| 186 | u8 id{}; | ||
| 187 | u16_le x{}; | ||
| 188 | u16_le y{}; | ||
| 189 | } touch_1, touch_2; | ||
| 190 | |||
| 191 | u64_le motion_timestamp; | ||
| 192 | |||
| 193 | struct Accelerometer { | ||
| 194 | float x{}; | ||
| 195 | float y{}; | ||
| 196 | float z{}; | ||
| 197 | } accel; | ||
| 198 | |||
| 199 | struct Gyroscope { | ||
| 200 | float pitch{}; | ||
| 201 | float yaw{}; | ||
| 202 | float roll{}; | ||
| 203 | } gyro; | ||
| 204 | }; | ||
| 205 | #pragma pack(pop) | ||
| 206 | |||
| 207 | static_assert(sizeof(PadData) == 80, "UDP Response PadData struct has wrong size "); | ||
| 208 | static_assert(std::is_trivially_copyable_v<PadData>, | ||
| 209 | "UDP Response PadData is not trivially copyable"); | ||
| 210 | |||
| 211 | static_assert(sizeof(Message<PadData>) == MAX_PACKET_SIZE, | ||
| 212 | "UDP MAX_PACKET_SIZE is no longer larger than Message<PadData>"); | ||
| 213 | |||
| 214 | static_assert(sizeof(PadData::AnalogButton) == 12, | ||
| 215 | "UDP Response AnalogButton struct has wrong size "); | ||
| 216 | static_assert(sizeof(PadData::TouchPad) == 6, "UDP Response TouchPad struct has wrong size "); | ||
| 217 | static_assert(sizeof(PadData::Accelerometer) == 12, | ||
| 218 | "UDP Response Accelerometer struct has wrong size "); | ||
| 219 | static_assert(sizeof(PadData::Gyroscope) == 12, "UDP Response Gyroscope struct has wrong size "); | ||
| 220 | |||
| 221 | /** | ||
| 222 | * Create a Response Message from the data | ||
| 223 | * @param data array of bytes sent from the server | ||
| 224 | * @return boost::none if it failed to parse or Type if it succeeded. The client can then safely | ||
| 225 | * copy the data into the appropriate struct for that Type | ||
| 226 | */ | ||
| 227 | std::optional<Type> Validate(u8* data, std::size_t size); | ||
| 228 | |||
| 229 | } // namespace Response | ||
| 230 | |||
| 231 | template <> | ||
| 232 | constexpr Type GetMessageType<Request::Version>() { | ||
| 233 | return Type::Version; | ||
| 234 | } | ||
| 235 | template <> | ||
| 236 | constexpr Type GetMessageType<Request::PortInfo>() { | ||
| 237 | return Type::PortInfo; | ||
| 238 | } | ||
| 239 | template <> | ||
| 240 | constexpr Type GetMessageType<Request::PadData>() { | ||
| 241 | return Type::PadData; | ||
| 242 | } | ||
| 243 | template <> | ||
| 244 | constexpr Type GetMessageType<Response::Version>() { | ||
| 245 | return Type::Version; | ||
| 246 | } | ||
| 247 | template <> | ||
| 248 | constexpr Type GetMessageType<Response::PortInfo>() { | ||
| 249 | return Type::PortInfo; | ||
| 250 | } | ||
| 251 | template <> | ||
| 252 | constexpr Type GetMessageType<Response::PadData>() { | ||
| 253 | return Type::PadData; | ||
| 254 | } | ||
| 255 | } // namespace InputCommon::CemuhookUDP | ||
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp new file mode 100644 index 000000000..ca99cc22f --- /dev/null +++ b/src/input_common/udp/udp.cpp | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <mutex> | ||
| 6 | #include <tuple> | ||
| 7 | |||
| 8 | #include "common/param_package.h" | ||
| 9 | #include "core/frontend/input.h" | ||
| 10 | #include "core/settings.h" | ||
| 11 | #include "input_common/udp/client.h" | ||
| 12 | #include "input_common/udp/udp.h" | ||
| 13 | |||
| 14 | namespace InputCommon::CemuhookUDP { | ||
| 15 | |||
| 16 | class UDPTouchDevice final : public Input::TouchDevice { | ||
| 17 | public: | ||
| 18 | explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | ||
| 19 | std::tuple<float, float, bool> GetStatus() const override { | ||
| 20 | std::lock_guard guard(status->update_mutex); | ||
| 21 | return status->touch_status; | ||
| 22 | } | ||
| 23 | |||
| 24 | private: | ||
| 25 | std::shared_ptr<DeviceStatus> status; | ||
| 26 | }; | ||
| 27 | |||
| 28 | class UDPMotionDevice final : public Input::MotionDevice { | ||
| 29 | public: | ||
| 30 | explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | ||
| 31 | std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { | ||
| 32 | std::lock_guard guard(status->update_mutex); | ||
| 33 | return status->motion_status; | ||
| 34 | } | ||
| 35 | |||
| 36 | private: | ||
| 37 | std::shared_ptr<DeviceStatus> status; | ||
| 38 | }; | ||
| 39 | |||
| 40 | class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> { | ||
| 41 | public: | ||
| 42 | explicit UDPTouchFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | ||
| 43 | |||
| 44 | std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override { | ||
| 45 | { | ||
| 46 | std::lock_guard guard(status->update_mutex); | ||
| 47 | status->touch_calibration.emplace(); | ||
| 48 | // These default values work well for DS4 but probably not other touch inputs | ||
| 49 | status->touch_calibration->min_x = params.Get("min_x", 100); | ||
| 50 | status->touch_calibration->min_y = params.Get("min_y", 50); | ||
| 51 | status->touch_calibration->max_x = params.Get("max_x", 1800); | ||
| 52 | status->touch_calibration->max_y = params.Get("max_y", 850); | ||
| 53 | } | ||
| 54 | return std::make_unique<UDPTouchDevice>(status); | ||
| 55 | } | ||
| 56 | |||
| 57 | private: | ||
| 58 | std::shared_ptr<DeviceStatus> status; | ||
| 59 | }; | ||
| 60 | |||
| 61 | class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> { | ||
| 62 | public: | ||
| 63 | explicit UDPMotionFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | ||
| 64 | |||
| 65 | std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override { | ||
| 66 | return std::make_unique<UDPMotionDevice>(status); | ||
| 67 | } | ||
| 68 | |||
| 69 | private: | ||
| 70 | std::shared_ptr<DeviceStatus> status; | ||
| 71 | }; | ||
| 72 | |||
| 73 | State::State() { | ||
| 74 | auto status = std::make_shared<DeviceStatus>(); | ||
| 75 | client = | ||
| 76 | std::make_unique<Client>(status, Settings::values.udp_input_address, | ||
| 77 | Settings::values.udp_input_port, Settings::values.udp_pad_index); | ||
| 78 | |||
| 79 | Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", | ||
| 80 | std::make_shared<UDPTouchFactory>(status)); | ||
| 81 | Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", | ||
| 82 | std::make_shared<UDPMotionFactory>(status)); | ||
| 83 | } | ||
| 84 | |||
| 85 | State::~State() { | ||
| 86 | Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp"); | ||
| 87 | Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp"); | ||
| 88 | } | ||
| 89 | |||
| 90 | void State::ReloadUDPClient() { | ||
| 91 | client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, | ||
| 92 | Settings::values.udp_pad_index); | ||
| 93 | } | ||
| 94 | |||
| 95 | std::unique_ptr<State> Init() { | ||
| 96 | return std::make_unique<State>(); | ||
| 97 | } | ||
| 98 | } // namespace InputCommon::CemuhookUDP | ||
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h new file mode 100644 index 000000000..4f83f0441 --- /dev/null +++ b/src/input_common/udp/udp.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // Copyright 2018 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | |||
| 9 | namespace InputCommon::CemuhookUDP { | ||
| 10 | |||
| 11 | class Client; | ||
| 12 | |||
| 13 | class State { | ||
| 14 | public: | ||
| 15 | State(); | ||
| 16 | ~State(); | ||
| 17 | void ReloadUDPClient(); | ||
| 18 | |||
| 19 | private: | ||
| 20 | std::unique_ptr<Client> client; | ||
| 21 | }; | ||
| 22 | |||
| 23 | std::unique_ptr<State> Init(); | ||
| 24 | |||
| 25 | } // namespace InputCommon::CemuhookUDP | ||
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index ccfed4f2e..4b0c6346f 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -29,12 +29,15 @@ add_library(video_core STATIC | |||
| 29 | gpu_synch.h | 29 | gpu_synch.h |
| 30 | gpu_thread.cpp | 30 | gpu_thread.cpp |
| 31 | gpu_thread.h | 31 | gpu_thread.h |
| 32 | guest_driver.cpp | ||
| 33 | guest_driver.h | ||
| 32 | macro_interpreter.cpp | 34 | macro_interpreter.cpp |
| 33 | macro_interpreter.h | 35 | macro_interpreter.h |
| 34 | memory_manager.cpp | 36 | memory_manager.cpp |
| 35 | memory_manager.h | 37 | memory_manager.h |
| 36 | morton.cpp | 38 | morton.cpp |
| 37 | morton.h | 39 | morton.h |
| 40 | query_cache.h | ||
| 38 | rasterizer_accelerated.cpp | 41 | rasterizer_accelerated.cpp |
| 39 | rasterizer_accelerated.h | 42 | rasterizer_accelerated.h |
| 40 | rasterizer_cache.cpp | 43 | rasterizer_cache.cpp |
| @@ -72,6 +75,8 @@ add_library(video_core STATIC | |||
| 72 | renderer_opengl/gl_stream_buffer.h | 75 | renderer_opengl/gl_stream_buffer.h |
| 73 | renderer_opengl/gl_texture_cache.cpp | 76 | renderer_opengl/gl_texture_cache.cpp |
| 74 | renderer_opengl/gl_texture_cache.h | 77 | renderer_opengl/gl_texture_cache.h |
| 78 | renderer_opengl/gl_query_cache.cpp | ||
| 79 | renderer_opengl/gl_query_cache.h | ||
| 75 | renderer_opengl/maxwell_to_gl.h | 80 | renderer_opengl/maxwell_to_gl.h |
| 76 | renderer_opengl/renderer_opengl.cpp | 81 | renderer_opengl/renderer_opengl.cpp |
| 77 | renderer_opengl/renderer_opengl.h | 82 | renderer_opengl/renderer_opengl.h |
| @@ -154,6 +159,7 @@ if (ENABLE_VULKAN) | |||
| 154 | renderer_vulkan/maxwell_to_vk.cpp | 159 | renderer_vulkan/maxwell_to_vk.cpp |
| 155 | renderer_vulkan/maxwell_to_vk.h | 160 | renderer_vulkan/maxwell_to_vk.h |
| 156 | renderer_vulkan/renderer_vulkan.h | 161 | renderer_vulkan/renderer_vulkan.h |
| 162 | renderer_vulkan/renderer_vulkan.cpp | ||
| 157 | renderer_vulkan/vk_blit_screen.cpp | 163 | renderer_vulkan/vk_blit_screen.cpp |
| 158 | renderer_vulkan/vk_blit_screen.h | 164 | renderer_vulkan/vk_blit_screen.h |
| 159 | renderer_vulkan/vk_buffer_cache.cpp | 165 | renderer_vulkan/vk_buffer_cache.cpp |
| @@ -174,6 +180,8 @@ if (ENABLE_VULKAN) | |||
| 174 | renderer_vulkan/vk_memory_manager.h | 180 | renderer_vulkan/vk_memory_manager.h |
| 175 | renderer_vulkan/vk_pipeline_cache.cpp | 181 | renderer_vulkan/vk_pipeline_cache.cpp |
| 176 | renderer_vulkan/vk_pipeline_cache.h | 182 | renderer_vulkan/vk_pipeline_cache.h |
| 183 | renderer_vulkan/vk_query_cache.cpp | ||
| 184 | renderer_vulkan/vk_query_cache.h | ||
| 177 | renderer_vulkan/vk_rasterizer.cpp | 185 | renderer_vulkan/vk_rasterizer.cpp |
| 178 | renderer_vulkan/vk_rasterizer.h | 186 | renderer_vulkan/vk_rasterizer.h |
| 179 | renderer_vulkan/vk_renderpass_cache.cpp | 187 | renderer_vulkan/vk_renderpass_cache.cpp |
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 0510ed777..186aca61d 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h | |||
| @@ -101,7 +101,10 @@ public: | |||
| 101 | void TickFrame() { | 101 | void TickFrame() { |
| 102 | ++epoch; | 102 | ++epoch; |
| 103 | while (!pending_destruction.empty()) { | 103 | while (!pending_destruction.empty()) { |
| 104 | if (pending_destruction.front()->GetEpoch() + 1 > epoch) { | 104 | // Delay at least 4 frames before destruction. |
| 105 | // This is due to triple buffering happening on some drivers. | ||
| 106 | static constexpr u64 epochs_to_destroy = 5; | ||
| 107 | if (pending_destruction.front()->GetEpoch() + epochs_to_destroy > epoch) { | ||
| 105 | break; | 108 | break; |
| 106 | } | 109 | } |
| 107 | pending_destruction.pop_front(); | 110 | pending_destruction.pop_front(); |
diff --git a/src/video_core/engines/const_buffer_engine_interface.h b/src/video_core/engines/const_buffer_engine_interface.h index 44b8b8d22..d56a47710 100644 --- a/src/video_core/engines/const_buffer_engine_interface.h +++ b/src/video_core/engines/const_buffer_engine_interface.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/engines/shader_bytecode.h" | 10 | #include "video_core/engines/shader_bytecode.h" |
| 11 | #include "video_core/engines/shader_type.h" | 11 | #include "video_core/engines/shader_type.h" |
| 12 | #include "video_core/guest_driver.h" | ||
| 12 | #include "video_core/textures/texture.h" | 13 | #include "video_core/textures/texture.h" |
| 13 | 14 | ||
| 14 | namespace Tegra::Engines { | 15 | namespace Tegra::Engines { |
| @@ -106,6 +107,9 @@ public: | |||
| 106 | virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, | 107 | virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer, |
| 107 | u64 offset) const = 0; | 108 | u64 offset) const = 0; |
| 108 | virtual u32 GetBoundBuffer() const = 0; | 109 | virtual u32 GetBoundBuffer() const = 0; |
| 110 | |||
| 111 | virtual VideoCore::GuestDriverProfile& AccessGuestDriverProfile() = 0; | ||
| 112 | virtual const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const = 0; | ||
| 109 | }; | 113 | }; |
| 110 | 114 | ||
| 111 | } // namespace Tegra::Engines | 115 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 110406f2f..4b824aa4e 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp | |||
| @@ -94,6 +94,14 @@ SamplerDescriptor KeplerCompute::AccessBindlessSampler(ShaderType stage, u64 con | |||
| 94 | return result; | 94 | return result; |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() { | ||
| 98 | return rasterizer.AccessGuestDriverProfile(); | ||
| 99 | } | ||
| 100 | |||
| 101 | const VideoCore::GuestDriverProfile& KeplerCompute::AccessGuestDriverProfile() const { | ||
| 102 | return rasterizer.AccessGuestDriverProfile(); | ||
| 103 | } | ||
| 104 | |||
| 97 | void KeplerCompute::ProcessLaunch() { | 105 | void KeplerCompute::ProcessLaunch() { |
| 98 | const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); | 106 | const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); |
| 99 | memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description, | 107 | memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description, |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 4ef3e0613..eeb79c56f 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -218,6 +218,10 @@ public: | |||
| 218 | return regs.tex_cb_index; | 218 | return regs.tex_cb_index; |
| 219 | } | 219 | } |
| 220 | 220 | ||
| 221 | VideoCore::GuestDriverProfile& AccessGuestDriverProfile() override; | ||
| 222 | |||
| 223 | const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; | ||
| 224 | |||
| 221 | private: | 225 | private: |
| 222 | Core::System& system; | 226 | Core::System& system; |
| 223 | VideoCore::RasterizerInterface& rasterizer; | 227 | VideoCore::RasterizerInterface& rasterizer; |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 58dfa8033..b28de1092 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -4,17 +4,21 @@ | |||
| 4 | 4 | ||
| 5 | #include <cinttypes> | 5 | #include <cinttypes> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <optional> | ||
| 7 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 8 | #include "core/core.h" | 9 | #include "core/core.h" |
| 9 | #include "core/core_timing.h" | 10 | #include "core/core_timing.h" |
| 10 | #include "video_core/engines/maxwell_3d.h" | 11 | #include "video_core/engines/maxwell_3d.h" |
| 11 | #include "video_core/engines/shader_type.h" | 12 | #include "video_core/engines/shader_type.h" |
| 13 | #include "video_core/gpu.h" | ||
| 12 | #include "video_core/memory_manager.h" | 14 | #include "video_core/memory_manager.h" |
| 13 | #include "video_core/rasterizer_interface.h" | 15 | #include "video_core/rasterizer_interface.h" |
| 14 | #include "video_core/textures/texture.h" | 16 | #include "video_core/textures/texture.h" |
| 15 | 17 | ||
| 16 | namespace Tegra::Engines { | 18 | namespace Tegra::Engines { |
| 17 | 19 | ||
| 20 | using VideoCore::QueryType; | ||
| 21 | |||
| 18 | /// First register id that is actually a Macro call. | 22 | /// First register id that is actually a Macro call. |
| 19 | constexpr u32 MacroRegistersStart = 0xE00; | 23 | constexpr u32 MacroRegistersStart = 0xE00; |
| 20 | 24 | ||
| @@ -399,6 +403,10 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { | |||
| 399 | ProcessQueryCondition(); | 403 | ProcessQueryCondition(); |
| 400 | break; | 404 | break; |
| 401 | } | 405 | } |
| 406 | case MAXWELL3D_REG_INDEX(counter_reset): { | ||
| 407 | ProcessCounterReset(); | ||
| 408 | break; | ||
| 409 | } | ||
| 402 | case MAXWELL3D_REG_INDEX(sync_info): { | 410 | case MAXWELL3D_REG_INDEX(sync_info): { |
| 403 | ProcessSyncPoint(); | 411 | ProcessSyncPoint(); |
| 404 | break; | 412 | break; |
| @@ -481,7 +489,7 @@ void Maxwell3D::FlushMMEInlineDraw() { | |||
| 481 | 489 | ||
| 482 | const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; | 490 | const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed; |
| 483 | if (ShouldExecute()) { | 491 | if (ShouldExecute()) { |
| 484 | rasterizer.DrawMultiBatch(is_indexed); | 492 | rasterizer.Draw(is_indexed, true); |
| 485 | } | 493 | } |
| 486 | 494 | ||
| 487 | // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if | 495 | // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if |
| @@ -519,61 +527,51 @@ void Maxwell3D::ProcessFirmwareCall4() { | |||
| 519 | regs.reg_array[0xd00] = 1; | 527 | regs.reg_array[0xd00] = 1; |
| 520 | } | 528 | } |
| 521 | 529 | ||
| 522 | void Maxwell3D::ProcessQueryGet() { | 530 | void Maxwell3D::StampQueryResult(u64 payload, bool long_query) { |
| 531 | struct LongQueryResult { | ||
| 532 | u64_le value; | ||
| 533 | u64_le timestamp; | ||
| 534 | }; | ||
| 535 | static_assert(sizeof(LongQueryResult) == 16, "LongQueryResult has wrong size"); | ||
| 523 | const GPUVAddr sequence_address{regs.query.QueryAddress()}; | 536 | const GPUVAddr sequence_address{regs.query.QueryAddress()}; |
| 524 | // Since the sequence address is given as a GPU VAddr, we have to convert it to an application | 537 | if (long_query) { |
| 525 | // VAddr before writing. | 538 | // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast |
| 539 | // GPU, this command may actually take a while to complete in real hardware due to GPU | ||
| 540 | // wait queues. | ||
| 541 | LongQueryResult query_result{payload, system.GPU().GetTicks()}; | ||
| 542 | memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result)); | ||
| 543 | } else { | ||
| 544 | memory_manager.Write<u32>(sequence_address, static_cast<u32>(payload)); | ||
| 545 | } | ||
| 546 | } | ||
| 526 | 547 | ||
| 548 | void Maxwell3D::ProcessQueryGet() { | ||
| 527 | // TODO(Subv): Support the other query units. | 549 | // TODO(Subv): Support the other query units. |
| 528 | ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, | 550 | ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, |
| 529 | "Units other than CROP are unimplemented"); | 551 | "Units other than CROP are unimplemented"); |
| 530 | 552 | ||
| 531 | u64 result = 0; | 553 | switch (regs.query.query_get.operation) { |
| 532 | 554 | case Regs::QueryOperation::Release: | |
| 533 | // TODO(Subv): Support the other query variables | 555 | StampQueryResult(regs.query.query_sequence, regs.query.query_get.short_query == 0); |
| 534 | switch (regs.query.query_get.select) { | ||
| 535 | case Regs::QuerySelect::Zero: | ||
| 536 | // This seems to actually write the query sequence to the query address. | ||
| 537 | result = regs.query.query_sequence; | ||
| 538 | break; | 556 | break; |
| 539 | default: | 557 | case Regs::QueryOperation::Acquire: |
| 540 | result = 1; | 558 | // TODO(Blinkhawk): Under this operation, the GPU waits for the CPU to write a value that |
| 541 | UNIMPLEMENTED_MSG("Unimplemented query select type {}", | 559 | // matches the current payload. |
| 542 | static_cast<u32>(regs.query.query_get.select.Value())); | 560 | UNIMPLEMENTED_MSG("Unimplemented query operation ACQUIRE"); |
| 543 | } | 561 | break; |
| 544 | 562 | case Regs::QueryOperation::Counter: | |
| 545 | // TODO(Subv): Research and implement how query sync conditions work. | 563 | if (const std::optional<u64> result = GetQueryResult()) { |
| 546 | 564 | // If the query returns an empty optional it means it's cached and deferred. | |
| 547 | struct LongQueryResult { | 565 | // In this case we have a non-empty result, so we stamp it immediately. |
| 548 | u64_le value; | 566 | StampQueryResult(*result, regs.query.query_get.short_query == 0); |
| 549 | u64_le timestamp; | ||
| 550 | }; | ||
| 551 | static_assert(sizeof(LongQueryResult) == 16, "LongQueryResult has wrong size"); | ||
| 552 | |||
| 553 | switch (regs.query.query_get.mode) { | ||
| 554 | case Regs::QueryMode::Write: | ||
| 555 | case Regs::QueryMode::Write2: { | ||
| 556 | u32 sequence = regs.query.query_sequence; | ||
| 557 | if (regs.query.query_get.short_query) { | ||
| 558 | // Write the current query sequence to the sequence address. | ||
| 559 | // TODO(Subv): Find out what happens if you use a long query type but mark it as a short | ||
| 560 | // query. | ||
| 561 | memory_manager.Write<u32>(sequence_address, sequence); | ||
| 562 | } else { | ||
| 563 | // Write the 128-bit result structure in long mode. Note: We emulate an infinitely fast | ||
| 564 | // GPU, this command may actually take a while to complete in real hardware due to GPU | ||
| 565 | // wait queues. | ||
| 566 | LongQueryResult query_result{}; | ||
| 567 | query_result.value = result; | ||
| 568 | // TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming | ||
| 569 | query_result.timestamp = system.CoreTiming().GetTicks(); | ||
| 570 | memory_manager.WriteBlock(sequence_address, &query_result, sizeof(query_result)); | ||
| 571 | } | 567 | } |
| 572 | break; | 568 | break; |
| 573 | } | 569 | case Regs::QueryOperation::Trap: |
| 570 | UNIMPLEMENTED_MSG("Unimplemented query operation TRAP"); | ||
| 571 | break; | ||
| 574 | default: | 572 | default: |
| 575 | UNIMPLEMENTED_MSG("Query mode {} not implemented", | 573 | UNIMPLEMENTED_MSG("Unknown query operation"); |
| 576 | static_cast<u32>(regs.query.query_get.mode.Value())); | 574 | break; |
| 577 | } | 575 | } |
| 578 | } | 576 | } |
| 579 | 577 | ||
| @@ -590,20 +588,20 @@ void Maxwell3D::ProcessQueryCondition() { | |||
| 590 | } | 588 | } |
| 591 | case Regs::ConditionMode::ResNonZero: { | 589 | case Regs::ConditionMode::ResNonZero: { |
| 592 | Regs::QueryCompare cmp; | 590 | Regs::QueryCompare cmp; |
| 593 | memory_manager.ReadBlockUnsafe(condition_address, &cmp, sizeof(cmp)); | 591 | memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); |
| 594 | execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U; | 592 | execute_on = cmp.initial_sequence != 0U && cmp.initial_mode != 0U; |
| 595 | break; | 593 | break; |
| 596 | } | 594 | } |
| 597 | case Regs::ConditionMode::Equal: { | 595 | case Regs::ConditionMode::Equal: { |
| 598 | Regs::QueryCompare cmp; | 596 | Regs::QueryCompare cmp; |
| 599 | memory_manager.ReadBlockUnsafe(condition_address, &cmp, sizeof(cmp)); | 597 | memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); |
| 600 | execute_on = | 598 | execute_on = |
| 601 | cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode; | 599 | cmp.initial_sequence == cmp.current_sequence && cmp.initial_mode == cmp.current_mode; |
| 602 | break; | 600 | break; |
| 603 | } | 601 | } |
| 604 | case Regs::ConditionMode::NotEqual: { | 602 | case Regs::ConditionMode::NotEqual: { |
| 605 | Regs::QueryCompare cmp; | 603 | Regs::QueryCompare cmp; |
| 606 | memory_manager.ReadBlockUnsafe(condition_address, &cmp, sizeof(cmp)); | 604 | memory_manager.ReadBlock(condition_address, &cmp, sizeof(cmp)); |
| 607 | execute_on = | 605 | execute_on = |
| 608 | cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode; | 606 | cmp.initial_sequence != cmp.current_sequence || cmp.initial_mode != cmp.current_mode; |
| 609 | break; | 607 | break; |
| @@ -616,6 +614,18 @@ void Maxwell3D::ProcessQueryCondition() { | |||
| 616 | } | 614 | } |
| 617 | } | 615 | } |
| 618 | 616 | ||
| 617 | void Maxwell3D::ProcessCounterReset() { | ||
| 618 | switch (regs.counter_reset) { | ||
| 619 | case Regs::CounterReset::SampleCnt: | ||
| 620 | rasterizer.ResetCounter(QueryType::SamplesPassed); | ||
| 621 | break; | ||
| 622 | default: | ||
| 623 | LOG_WARNING(Render_OpenGL, "Unimplemented counter reset={}", | ||
| 624 | static_cast<int>(regs.counter_reset)); | ||
| 625 | break; | ||
| 626 | } | ||
| 627 | } | ||
| 628 | |||
| 619 | void Maxwell3D::ProcessSyncPoint() { | 629 | void Maxwell3D::ProcessSyncPoint() { |
| 620 | const u32 sync_point = regs.sync_info.sync_point.Value(); | 630 | const u32 sync_point = regs.sync_info.sync_point.Value(); |
| 621 | const u32 increment = regs.sync_info.increment.Value(); | 631 | const u32 increment = regs.sync_info.increment.Value(); |
| @@ -644,7 +654,7 @@ void Maxwell3D::DrawArrays() { | |||
| 644 | 654 | ||
| 645 | const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; | 655 | const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; |
| 646 | if (ShouldExecute()) { | 656 | if (ShouldExecute()) { |
| 647 | rasterizer.DrawBatch(is_indexed); | 657 | rasterizer.Draw(is_indexed, false); |
| 648 | } | 658 | } |
| 649 | 659 | ||
| 650 | // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if | 660 | // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if |
| @@ -658,6 +668,22 @@ void Maxwell3D::DrawArrays() { | |||
| 658 | } | 668 | } |
| 659 | } | 669 | } |
| 660 | 670 | ||
| 671 | std::optional<u64> Maxwell3D::GetQueryResult() { | ||
| 672 | switch (regs.query.query_get.select) { | ||
| 673 | case Regs::QuerySelect::Zero: | ||
| 674 | return 0; | ||
| 675 | case Regs::QuerySelect::SamplesPassed: | ||
| 676 | // Deferred. | ||
| 677 | rasterizer.Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed, | ||
| 678 | system.GPU().GetTicks()); | ||
| 679 | return {}; | ||
| 680 | default: | ||
| 681 | UNIMPLEMENTED_MSG("Unimplemented query select type {}", | ||
| 682 | static_cast<u32>(regs.query.query_get.select.Value())); | ||
| 683 | return 1; | ||
| 684 | } | ||
| 685 | } | ||
| 686 | |||
| 661 | void Maxwell3D::ProcessCBBind(std::size_t stage_index) { | 687 | void Maxwell3D::ProcessCBBind(std::size_t stage_index) { |
| 662 | // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. | 688 | // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. |
| 663 | auto& shader = state.shader_stages[stage_index]; | 689 | auto& shader = state.shader_stages[stage_index]; |
| @@ -784,4 +810,12 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b | |||
| 784 | return result; | 810 | return result; |
| 785 | } | 811 | } |
| 786 | 812 | ||
| 813 | VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() { | ||
| 814 | return rasterizer.AccessGuestDriverProfile(); | ||
| 815 | } | ||
| 816 | |||
| 817 | const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const { | ||
| 818 | return rasterizer.AccessGuestDriverProfile(); | ||
| 819 | } | ||
| 820 | |||
| 787 | } // namespace Tegra::Engines | 821 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index ee79260fc..26939be3f 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <bitset> | 8 | #include <bitset> |
| 9 | #include <optional> | ||
| 9 | #include <type_traits> | 10 | #include <type_traits> |
| 10 | #include <unordered_map> | 11 | #include <unordered_map> |
| 11 | #include <vector> | 12 | #include <vector> |
| @@ -71,12 +72,11 @@ public: | |||
| 71 | static constexpr std::size_t MaxConstBuffers = 18; | 72 | static constexpr std::size_t MaxConstBuffers = 18; |
| 72 | static constexpr std::size_t MaxConstBufferSize = 0x10000; | 73 | static constexpr std::size_t MaxConstBufferSize = 0x10000; |
| 73 | 74 | ||
| 74 | enum class QueryMode : u32 { | 75 | enum class QueryOperation : u32 { |
| 75 | Write = 0, | 76 | Release = 0, |
| 76 | Sync = 1, | 77 | Acquire = 1, |
| 77 | // TODO(Subv): It is currently unknown what the difference between method 2 and method 0 | 78 | Counter = 2, |
| 78 | // is. | 79 | Trap = 3, |
| 79 | Write2 = 2, | ||
| 80 | }; | 80 | }; |
| 81 | 81 | ||
| 82 | enum class QueryUnit : u32 { | 82 | enum class QueryUnit : u32 { |
| @@ -410,6 +410,27 @@ public: | |||
| 410 | Linear = 1, | 410 | Linear = 1, |
| 411 | }; | 411 | }; |
| 412 | 412 | ||
| 413 | enum class CounterReset : u32 { | ||
| 414 | SampleCnt = 0x01, | ||
| 415 | Unk02 = 0x02, | ||
| 416 | Unk03 = 0x03, | ||
| 417 | Unk04 = 0x04, | ||
| 418 | EmittedPrimitives = 0x10, // Not tested | ||
| 419 | Unk11 = 0x11, | ||
| 420 | Unk12 = 0x12, | ||
| 421 | Unk13 = 0x13, | ||
| 422 | Unk15 = 0x15, | ||
| 423 | Unk16 = 0x16, | ||
| 424 | Unk17 = 0x17, | ||
| 425 | Unk18 = 0x18, | ||
| 426 | Unk1A = 0x1A, | ||
| 427 | Unk1B = 0x1B, | ||
| 428 | Unk1C = 0x1C, | ||
| 429 | Unk1D = 0x1D, | ||
| 430 | Unk1E = 0x1E, | ||
| 431 | GeneratedPrimitives = 0x1F, | ||
| 432 | }; | ||
| 433 | |||
| 413 | struct Cull { | 434 | struct Cull { |
| 414 | enum class FrontFace : u32 { | 435 | enum class FrontFace : u32 { |
| 415 | ClockWise = 0x0900, | 436 | ClockWise = 0x0900, |
| @@ -704,8 +725,8 @@ public: | |||
| 704 | INSERT_UNION_PADDING_WORDS(0x15); | 725 | INSERT_UNION_PADDING_WORDS(0x15); |
| 705 | 726 | ||
| 706 | s32 stencil_back_func_ref; | 727 | s32 stencil_back_func_ref; |
| 707 | u32 stencil_back_func_mask; | ||
| 708 | u32 stencil_back_mask; | 728 | u32 stencil_back_mask; |
| 729 | u32 stencil_back_func_mask; | ||
| 709 | 730 | ||
| 710 | INSERT_UNION_PADDING_WORDS(0xC); | 731 | INSERT_UNION_PADDING_WORDS(0xC); |
| 711 | 732 | ||
| @@ -858,11 +879,19 @@ public: | |||
| 858 | BitField<7, 1, u32> c7; | 879 | BitField<7, 1, u32> c7; |
| 859 | } clip_distance_enabled; | 880 | } clip_distance_enabled; |
| 860 | 881 | ||
| 861 | INSERT_UNION_PADDING_WORDS(0x1); | 882 | u32 samplecnt_enable; |
| 862 | 883 | ||
| 863 | float point_size; | 884 | float point_size; |
| 864 | 885 | ||
| 865 | INSERT_UNION_PADDING_WORDS(0x7); | 886 | INSERT_UNION_PADDING_WORDS(0x1); |
| 887 | |||
| 888 | u32 point_sprite_enable; | ||
| 889 | |||
| 890 | INSERT_UNION_PADDING_WORDS(0x3); | ||
| 891 | |||
| 892 | CounterReset counter_reset; | ||
| 893 | |||
| 894 | INSERT_UNION_PADDING_WORDS(0x1); | ||
| 866 | 895 | ||
| 867 | u32 zeta_enable; | 896 | u32 zeta_enable; |
| 868 | 897 | ||
| @@ -1077,7 +1106,7 @@ public: | |||
| 1077 | u32 query_sequence; | 1106 | u32 query_sequence; |
| 1078 | union { | 1107 | union { |
| 1079 | u32 raw; | 1108 | u32 raw; |
| 1080 | BitField<0, 2, QueryMode> mode; | 1109 | BitField<0, 2, QueryOperation> operation; |
| 1081 | BitField<4, 1, u32> fence; | 1110 | BitField<4, 1, u32> fence; |
| 1082 | BitField<12, 4, QueryUnit> unit; | 1111 | BitField<12, 4, QueryUnit> unit; |
| 1083 | BitField<16, 1, QuerySyncCondition> sync_cond; | 1112 | BitField<16, 1, QuerySyncCondition> sync_cond; |
| @@ -1306,6 +1335,10 @@ public: | |||
| 1306 | return regs.tex_cb_index; | 1335 | return regs.tex_cb_index; |
| 1307 | } | 1336 | } |
| 1308 | 1337 | ||
| 1338 | VideoCore::GuestDriverProfile& AccessGuestDriverProfile() override; | ||
| 1339 | |||
| 1340 | const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; | ||
| 1341 | |||
| 1309 | /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than | 1342 | /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than |
| 1310 | /// we've seen used. | 1343 | /// we've seen used. |
| 1311 | using MacroMemory = std::array<u32, 0x40000>; | 1344 | using MacroMemory = std::array<u32, 0x40000>; |
| @@ -1405,9 +1438,15 @@ private: | |||
| 1405 | /// Handles a write to the QUERY_GET register. | 1438 | /// Handles a write to the QUERY_GET register. |
| 1406 | void ProcessQueryGet(); | 1439 | void ProcessQueryGet(); |
| 1407 | 1440 | ||
| 1408 | // Handles Conditional Rendering | 1441 | /// Writes the query result accordingly. |
| 1442 | void StampQueryResult(u64 payload, bool long_query); | ||
| 1443 | |||
| 1444 | /// Handles conditional rendering. | ||
| 1409 | void ProcessQueryCondition(); | 1445 | void ProcessQueryCondition(); |
| 1410 | 1446 | ||
| 1447 | /// Handles counter resets. | ||
| 1448 | void ProcessCounterReset(); | ||
| 1449 | |||
| 1411 | /// Handles writes to syncing register. | 1450 | /// Handles writes to syncing register. |
| 1412 | void ProcessSyncPoint(); | 1451 | void ProcessSyncPoint(); |
| 1413 | 1452 | ||
| @@ -1424,6 +1463,9 @@ private: | |||
| 1424 | 1463 | ||
| 1425 | // Handles a instance drawcall from MME | 1464 | // Handles a instance drawcall from MME |
| 1426 | void StepInstance(MMEDrawMode expected_mode, u32 count); | 1465 | void StepInstance(MMEDrawMode expected_mode, u32 count); |
| 1466 | |||
| 1467 | /// Returns a query's value or an empty object if the value will be deferred through a cache. | ||
| 1468 | std::optional<u64> GetQueryResult(); | ||
| 1427 | }; | 1469 | }; |
| 1428 | 1470 | ||
| 1429 | #define ASSERT_REG_POSITION(field_name, position) \ | 1471 | #define ASSERT_REG_POSITION(field_name, position) \ |
| @@ -1454,8 +1496,8 @@ ASSERT_REG_POSITION(polygon_offset_fill_enable, 0x372); | |||
| 1454 | ASSERT_REG_POSITION(patch_vertices, 0x373); | 1496 | ASSERT_REG_POSITION(patch_vertices, 0x373); |
| 1455 | ASSERT_REG_POSITION(scissor_test, 0x380); | 1497 | ASSERT_REG_POSITION(scissor_test, 0x380); |
| 1456 | ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); | 1498 | ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5); |
| 1457 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D6); | 1499 | ASSERT_REG_POSITION(stencil_back_mask, 0x3D6); |
| 1458 | ASSERT_REG_POSITION(stencil_back_mask, 0x3D7); | 1500 | ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7); |
| 1459 | ASSERT_REG_POSITION(color_mask_common, 0x3E4); | 1501 | ASSERT_REG_POSITION(color_mask_common, 0x3E4); |
| 1460 | ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); | 1502 | ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB); |
| 1461 | ASSERT_REG_POSITION(depth_bounds, 0x3E7); | 1503 | ASSERT_REG_POSITION(depth_bounds, 0x3E7); |
| @@ -1489,7 +1531,10 @@ ASSERT_REG_POSITION(screen_y_control, 0x4EB); | |||
| 1489 | ASSERT_REG_POSITION(vb_element_base, 0x50D); | 1531 | ASSERT_REG_POSITION(vb_element_base, 0x50D); |
| 1490 | ASSERT_REG_POSITION(vb_base_instance, 0x50E); | 1532 | ASSERT_REG_POSITION(vb_base_instance, 0x50E); |
| 1491 | ASSERT_REG_POSITION(clip_distance_enabled, 0x544); | 1533 | ASSERT_REG_POSITION(clip_distance_enabled, 0x544); |
| 1534 | ASSERT_REG_POSITION(samplecnt_enable, 0x545); | ||
| 1492 | ASSERT_REG_POSITION(point_size, 0x546); | 1535 | ASSERT_REG_POSITION(point_size, 0x546); |
| 1536 | ASSERT_REG_POSITION(point_sprite_enable, 0x548); | ||
| 1537 | ASSERT_REG_POSITION(counter_reset, 0x54C); | ||
| 1493 | ASSERT_REG_POSITION(zeta_enable, 0x54E); | 1538 | ASSERT_REG_POSITION(zeta_enable, 0x54E); |
| 1494 | ASSERT_REG_POSITION(multisample_control, 0x54F); | 1539 | ASSERT_REG_POSITION(multisample_control, 0x54F); |
| 1495 | ASSERT_REG_POSITION(condition, 0x554); | 1540 | ASSERT_REG_POSITION(condition, 0x554); |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 6f98bd827..c9bc83cd7 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -227,6 +227,28 @@ enum class AtomicOp : u64 { | |||
| 227 | Exch = 8, | 227 | Exch = 8, |
| 228 | }; | 228 | }; |
| 229 | 229 | ||
| 230 | enum class GlobalAtomicOp : u64 { | ||
| 231 | Add = 0, | ||
| 232 | Min = 1, | ||
| 233 | Max = 2, | ||
| 234 | Inc = 3, | ||
| 235 | Dec = 4, | ||
| 236 | And = 5, | ||
| 237 | Or = 6, | ||
| 238 | Xor = 7, | ||
| 239 | Exch = 8, | ||
| 240 | SafeAdd = 10, | ||
| 241 | }; | ||
| 242 | |||
| 243 | enum class GlobalAtomicType : u64 { | ||
| 244 | U32 = 0, | ||
| 245 | S32 = 1, | ||
| 246 | U64 = 2, | ||
| 247 | F32_FTZ_RN = 3, | ||
| 248 | F16x2_FTZ_RN = 4, | ||
| 249 | S64 = 5, | ||
| 250 | }; | ||
| 251 | |||
| 230 | enum class UniformType : u64 { | 252 | enum class UniformType : u64 { |
| 231 | UnsignedByte = 0, | 253 | UnsignedByte = 0, |
| 232 | SignedByte = 1, | 254 | SignedByte = 1, |
| @@ -602,6 +624,19 @@ enum class ShuffleOperation : u64 { | |||
| 602 | Bfly = 3, // shuffleXorNV | 624 | Bfly = 3, // shuffleXorNV |
| 603 | }; | 625 | }; |
| 604 | 626 | ||
| 627 | enum class ShfType : u64 { | ||
| 628 | Bits32 = 0, | ||
| 629 | U64 = 2, | ||
| 630 | S64 = 3, | ||
| 631 | }; | ||
| 632 | |||
| 633 | enum class ShfXmode : u64 { | ||
| 634 | None = 0, | ||
| 635 | HI = 1, | ||
| 636 | X = 2, | ||
| 637 | XHI = 3, | ||
| 638 | }; | ||
| 639 | |||
| 605 | union Instruction { | 640 | union Instruction { |
| 606 | constexpr Instruction& operator=(const Instruction& instr) { | 641 | constexpr Instruction& operator=(const Instruction& instr) { |
| 607 | value = instr.value; | 642 | value = instr.value; |
| @@ -754,6 +789,13 @@ union Instruction { | |||
| 754 | } shr; | 789 | } shr; |
| 755 | 790 | ||
| 756 | union { | 791 | union { |
| 792 | BitField<37, 2, ShfType> type; | ||
| 793 | BitField<48, 2, ShfXmode> xmode; | ||
| 794 | BitField<50, 1, u64> wrap; | ||
| 795 | BitField<20, 6, u64> immediate; | ||
| 796 | } shf; | ||
| 797 | |||
| 798 | union { | ||
| 757 | BitField<39, 5, u64> shift_amount; | 799 | BitField<39, 5, u64> shift_amount; |
| 758 | BitField<48, 1, u64> negate_b; | 800 | BitField<48, 1, u64> negate_b; |
| 759 | BitField<49, 1, u64> negate_a; | 801 | BitField<49, 1, u64> negate_a; |
| @@ -958,6 +1000,12 @@ union Instruction { | |||
| 958 | } stg; | 1000 | } stg; |
| 959 | 1001 | ||
| 960 | union { | 1002 | union { |
| 1003 | BitField<52, 4, GlobalAtomicOp> operation; | ||
| 1004 | BitField<49, 3, GlobalAtomicType> type; | ||
| 1005 | BitField<28, 20, s64> offset; | ||
| 1006 | } atom; | ||
| 1007 | |||
| 1008 | union { | ||
| 961 | BitField<52, 4, AtomicOp> operation; | 1009 | BitField<52, 4, AtomicOp> operation; |
| 962 | BitField<28, 2, AtomicType> type; | 1010 | BitField<28, 2, AtomicType> type; |
| 963 | BitField<30, 22, s64> offset; | 1011 | BitField<30, 22, s64> offset; |
| @@ -1096,6 +1144,11 @@ union Instruction { | |||
| 1096 | } fset; | 1144 | } fset; |
| 1097 | 1145 | ||
| 1098 | union { | 1146 | union { |
| 1147 | BitField<47, 1, u64> ftz; | ||
| 1148 | BitField<48, 4, PredCondition> cond; | ||
| 1149 | } fcmp; | ||
| 1150 | |||
| 1151 | union { | ||
| 1099 | BitField<49, 1, u64> bf; | 1152 | BitField<49, 1, u64> bf; |
| 1100 | BitField<35, 3, PredCondition> cond; | 1153 | BitField<35, 3, PredCondition> cond; |
| 1101 | BitField<50, 1, u64> ftz; | 1154 | BitField<50, 1, u64> ftz; |
| @@ -1624,11 +1677,11 @@ union Instruction { | |||
| 1624 | } xmad; | 1677 | } xmad; |
| 1625 | 1678 | ||
| 1626 | union { | 1679 | union { |
| 1627 | BitField<20, 14, u64> offset; | 1680 | BitField<20, 14, u64> shifted_offset; |
| 1628 | BitField<34, 5, u64> index; | 1681 | BitField<34, 5, u64> index; |
| 1629 | 1682 | ||
| 1630 | u64 GetOffset() const { | 1683 | u64 GetOffset() const { |
| 1631 | return offset * 4; | 1684 | return shifted_offset * 4; |
| 1632 | } | 1685 | } |
| 1633 | } cbuf34; | 1686 | } cbuf34; |
| 1634 | 1687 | ||
| @@ -1675,6 +1728,7 @@ public: | |||
| 1675 | BFE_C, | 1728 | BFE_C, |
| 1676 | BFE_R, | 1729 | BFE_R, |
| 1677 | BFE_IMM, | 1730 | BFE_IMM, |
| 1731 | BFI_RC, | ||
| 1678 | BFI_IMM_R, | 1732 | BFI_IMM_R, |
| 1679 | BRA, | 1733 | BRA, |
| 1680 | BRX, | 1734 | BRX, |
| @@ -1690,6 +1744,7 @@ public: | |||
| 1690 | ST_S, | 1744 | ST_S, |
| 1691 | ST, // Store in generic memory | 1745 | ST, // Store in generic memory |
| 1692 | STG, // Store in global memory | 1746 | STG, // Store in global memory |
| 1747 | ATOM, // Atomic operation on global memory | ||
| 1693 | ATOMS, // Atomic operation on shared memory | 1748 | ATOMS, // Atomic operation on shared memory |
| 1694 | AL2P, // Transforms attribute memory into physical memory | 1749 | AL2P, // Transforms attribute memory into physical memory |
| 1695 | TEX, | 1750 | TEX, |
| @@ -1771,6 +1826,7 @@ public: | |||
| 1771 | ICMP_R, | 1826 | ICMP_R, |
| 1772 | ICMP_CR, | 1827 | ICMP_CR, |
| 1773 | ICMP_IMM, | 1828 | ICMP_IMM, |
| 1829 | FCMP_R, | ||
| 1774 | MUFU, // Multi-Function Operator | 1830 | MUFU, // Multi-Function Operator |
| 1775 | RRO_C, // Range Reduction Operator | 1831 | RRO_C, // Range Reduction Operator |
| 1776 | RRO_R, | 1832 | RRO_R, |
| @@ -1994,6 +2050,7 @@ private: | |||
| 1994 | INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), | 2050 | INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), |
| 1995 | INST("101-------------", Id::ST, Type::Memory, "ST"), | 2051 | INST("101-------------", Id::ST, Type::Memory, "ST"), |
| 1996 | INST("1110111011011---", Id::STG, Type::Memory, "STG"), | 2052 | INST("1110111011011---", Id::STG, Type::Memory, "STG"), |
| 2053 | INST("11101101--------", Id::ATOM, Type::Memory, "ATOM"), | ||
| 1997 | INST("11101100--------", Id::ATOMS, Type::Memory, "ATOMS"), | 2054 | INST("11101100--------", Id::ATOMS, Type::Memory, "ATOMS"), |
| 1998 | INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"), | 2055 | INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"), |
| 1999 | INST("110000----111---", Id::TEX, Type::Texture, "TEX"), | 2056 | INST("110000----111---", Id::TEX, Type::Texture, "TEX"), |
| @@ -2074,6 +2131,7 @@ private: | |||
| 2074 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), | 2131 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), |
| 2075 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), | 2132 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), |
| 2076 | INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), | 2133 | INST("0101110100011---", Id::HSET2_R, Type::HalfSet, "HSET2_R"), |
| 2134 | INST("010110111010----", Id::FCMP_R, Type::Arithmetic, "FCMP_R"), | ||
| 2077 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), | 2135 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), |
| 2078 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), | 2136 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), |
| 2079 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), | 2137 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), |
| @@ -2098,6 +2156,7 @@ private: | |||
| 2098 | INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), | 2156 | INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), |
| 2099 | INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), | 2157 | INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), |
| 2100 | INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), | 2158 | INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), |
| 2159 | INST("0101001111110---", Id::BFI_RC, Type::Bfi, "BFI_RC"), | ||
| 2101 | INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"), | 2160 | INST("0011011-11110---", Id::BFI_IMM_R, Type::Bfi, "BFI_IMM_R"), |
| 2102 | INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), | 2161 | INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), |
| 2103 | INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), | 2162 | INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"), |
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index b9c5c41a2..7d7137109 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/core_timing.h" | 8 | #include "core/core_timing.h" |
| 9 | #include "core/core_timing_util.h" | ||
| 9 | #include "core/memory.h" | 10 | #include "core/memory.h" |
| 10 | #include "video_core/engines/fermi_2d.h" | 11 | #include "video_core/engines/fermi_2d.h" |
| 11 | #include "video_core/engines/kepler_compute.h" | 12 | #include "video_core/engines/kepler_compute.h" |
| @@ -122,6 +123,19 @@ bool GPU::CancelSyncptInterrupt(const u32 syncpoint_id, const u32 value) { | |||
| 122 | return true; | 123 | return true; |
| 123 | } | 124 | } |
| 124 | 125 | ||
| 126 | u64 GPU::GetTicks() const { | ||
| 127 | // This values were reversed engineered by fincs from NVN | ||
| 128 | // The gpu clock is reported in units of 385/625 nanoseconds | ||
| 129 | constexpr u64 gpu_ticks_num = 384; | ||
| 130 | constexpr u64 gpu_ticks_den = 625; | ||
| 131 | |||
| 132 | const u64 cpu_ticks = system.CoreTiming().GetTicks(); | ||
| 133 | const u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count(); | ||
| 134 | const u64 nanoseconds_num = nanoseconds / gpu_ticks_den; | ||
| 135 | const u64 nanoseconds_rem = nanoseconds % gpu_ticks_den; | ||
| 136 | return nanoseconds_num * gpu_ticks_num + (nanoseconds_rem * gpu_ticks_num) / gpu_ticks_den; | ||
| 137 | } | ||
| 138 | |||
| 125 | void GPU::FlushCommands() { | 139 | void GPU::FlushCommands() { |
| 126 | renderer.Rasterizer().FlushCommands(); | 140 | renderer.Rasterizer().FlushCommands(); |
| 127 | } | 141 | } |
| @@ -340,7 +354,7 @@ void GPU::ProcessSemaphoreTriggerMethod() { | |||
| 340 | block.sequence = regs.semaphore_sequence; | 354 | block.sequence = regs.semaphore_sequence; |
| 341 | // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of | 355 | // TODO(Kmather73): Generate a real GPU timestamp and write it here instead of |
| 342 | // CoreTiming | 356 | // CoreTiming |
| 343 | block.timestamp = system.CoreTiming().GetTicks(); | 357 | block.timestamp = GetTicks(); |
| 344 | memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, | 358 | memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block, |
| 345 | sizeof(block)); | 359 | sizeof(block)); |
| 346 | } else { | 360 | } else { |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index b648317bb..07727210c 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -192,6 +192,8 @@ public: | |||
| 192 | 192 | ||
| 193 | bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); | 193 | bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value); |
| 194 | 194 | ||
| 195 | u64 GetTicks() const; | ||
| 196 | |||
| 195 | std::unique_lock<std::mutex> LockSync() { | 197 | std::unique_lock<std::mutex> LockSync() { |
| 196 | return std::unique_lock{sync_mutex}; | 198 | return std::unique_lock{sync_mutex}; |
| 197 | } | 199 | } |
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 08dc96bb3..882e2d9c7 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h | |||
| @@ -86,7 +86,7 @@ struct CommandDataContainer { | |||
| 86 | struct SynchState final { | 86 | struct SynchState final { |
| 87 | std::atomic_bool is_running{true}; | 87 | std::atomic_bool is_running{true}; |
| 88 | 88 | ||
| 89 | using CommandQueue = Common::SPSCQueue<CommandDataContainer>; | 89 | using CommandQueue = Common::MPSCQueue<CommandDataContainer>; |
| 90 | CommandQueue queue; | 90 | CommandQueue queue; |
| 91 | u64 last_fence{}; | 91 | u64 last_fence{}; |
| 92 | std::atomic<u64> signaled_fence{}; | 92 | std::atomic<u64> signaled_fence{}; |
diff --git a/src/video_core/guest_driver.cpp b/src/video_core/guest_driver.cpp new file mode 100644 index 000000000..6adef459e --- /dev/null +++ b/src/video_core/guest_driver.cpp | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <limits> | ||
| 7 | |||
| 8 | #include "video_core/guest_driver.h" | ||
| 9 | |||
| 10 | namespace VideoCore { | ||
| 11 | |||
| 12 | void GuestDriverProfile::DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets) { | ||
| 13 | if (texture_handler_size_deduced) { | ||
| 14 | return; | ||
| 15 | } | ||
| 16 | const std::size_t size = bound_offsets.size(); | ||
| 17 | if (size < 2) { | ||
| 18 | return; | ||
| 19 | } | ||
| 20 | std::sort(bound_offsets.begin(), bound_offsets.end(), std::less{}); | ||
| 21 | u32 min_val = std::numeric_limits<u32>::max(); | ||
| 22 | for (std::size_t i = 1; i < size; ++i) { | ||
| 23 | if (bound_offsets[i] == bound_offsets[i - 1]) { | ||
| 24 | continue; | ||
| 25 | } | ||
| 26 | const u32 new_min = bound_offsets[i] - bound_offsets[i - 1]; | ||
| 27 | min_val = std::min(min_val, new_min); | ||
| 28 | } | ||
| 29 | if (min_val > 2) { | ||
| 30 | return; | ||
| 31 | } | ||
| 32 | texture_handler_size_deduced = true; | ||
| 33 | texture_handler_size = min_texture_handler_size * min_val; | ||
| 34 | } | ||
| 35 | |||
| 36 | } // namespace VideoCore | ||
diff --git a/src/video_core/guest_driver.h b/src/video_core/guest_driver.h new file mode 100644 index 000000000..fc1917347 --- /dev/null +++ b/src/video_core/guest_driver.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace VideoCore { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * The GuestDriverProfile class is used to learn about the GPU drivers behavior and collect | ||
| 15 | * information necessary for impossible to avoid HLE methods like shader tracks as they are | ||
| 16 | * Entscheidungsproblems. | ||
| 17 | */ | ||
| 18 | class GuestDriverProfile { | ||
| 19 | public: | ||
| 20 | void DeduceTextureHandlerSize(std::vector<u32>&& bound_offsets); | ||
| 21 | |||
| 22 | u32 GetTextureHandlerSize() const { | ||
| 23 | return texture_handler_size; | ||
| 24 | } | ||
| 25 | |||
| 26 | bool TextureHandlerSizeKnown() const { | ||
| 27 | return texture_handler_size_deduced; | ||
| 28 | } | ||
| 29 | |||
| 30 | private: | ||
| 31 | // Minimum size of texture handler any driver can use. | ||
| 32 | static constexpr u32 min_texture_handler_size = 4; | ||
| 33 | // This goes with Vulkan and OpenGL standards but Nvidia GPUs can easily | ||
| 34 | // use 4 bytes instead. Thus, certain drivers may squish the size. | ||
| 35 | static constexpr u32 default_texture_handler_size = 8; | ||
| 36 | |||
| 37 | u32 texture_handler_size = default_texture_handler_size; | ||
| 38 | bool texture_handler_size_deduced = false; | ||
| 39 | }; | ||
| 40 | |||
| 41 | } // namespace VideoCore | ||
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 11848fbce..f5d33f27a 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/hle/kernel/process.h" | 9 | #include "core/hle/kernel/process.h" |
| 10 | #include "core/hle/kernel/vm_manager.h" | 10 | #include "core/hle/kernel/vm_manager.h" |
| 11 | #include "core/memory.h" | 11 | #include "core/memory.h" |
| 12 | #include "video_core/gpu.h" | ||
| 12 | #include "video_core/memory_manager.h" | 13 | #include "video_core/memory_manager.h" |
| 13 | #include "video_core/rasterizer_interface.h" | 14 | #include "video_core/rasterizer_interface.h" |
| 14 | 15 | ||
| @@ -84,7 +85,9 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { | |||
| 84 | const auto cpu_addr = GpuToCpuAddress(gpu_addr); | 85 | const auto cpu_addr = GpuToCpuAddress(gpu_addr); |
| 85 | ASSERT(cpu_addr); | 86 | ASSERT(cpu_addr); |
| 86 | 87 | ||
| 87 | rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size); | 88 | // Flush and invalidate through the GPU interface, to be asynchronous if possible. |
| 89 | system.GPU().FlushAndInvalidateRegion(cache_addr, aligned_size); | ||
| 90 | |||
| 88 | UnmapRange(gpu_addr, aligned_size); | 91 | UnmapRange(gpu_addr, aligned_size); |
| 89 | ASSERT(system.CurrentProcess() | 92 | ASSERT(system.CurrentProcess() |
| 90 | ->VMManager() | 93 | ->VMManager() |
| @@ -242,6 +245,8 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s | |||
| 242 | switch (page_table.attributes[page_index]) { | 245 | switch (page_table.attributes[page_index]) { |
| 243 | case Common::PageType::Memory: { | 246 | case Common::PageType::Memory: { |
| 244 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; | 247 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; |
| 248 | // Flush must happen on the rasterizer interface, such that memory is always synchronous | ||
| 249 | // when it is read (even when in asynchronous GPU mode). Fixes Dead Cells title menu. | ||
| 245 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); | 250 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); |
| 246 | std::memcpy(dest_buffer, src_ptr, copy_amount); | 251 | std::memcpy(dest_buffer, src_ptr, copy_amount); |
| 247 | break; | 252 | break; |
| @@ -292,6 +297,8 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const | |||
| 292 | switch (page_table.attributes[page_index]) { | 297 | switch (page_table.attributes[page_index]) { |
| 293 | case Common::PageType::Memory: { | 298 | case Common::PageType::Memory: { |
| 294 | u8* dest_ptr{page_table.pointers[page_index] + page_offset}; | 299 | u8* dest_ptr{page_table.pointers[page_index] + page_offset}; |
| 300 | // Invalidate must happen on the rasterizer interface, such that memory is always | ||
| 301 | // synchronous when it is written (even when in asynchronous GPU mode). | ||
| 295 | rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount); | 302 | rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount); |
| 296 | std::memcpy(dest_ptr, src_buffer, copy_amount); | 303 | std::memcpy(dest_ptr, src_buffer, copy_amount); |
| 297 | break; | 304 | break; |
| @@ -339,6 +346,8 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std:: | |||
| 339 | 346 | ||
| 340 | switch (page_table.attributes[page_index]) { | 347 | switch (page_table.attributes[page_index]) { |
| 341 | case Common::PageType::Memory: { | 348 | case Common::PageType::Memory: { |
| 349 | // Flush must happen on the rasterizer interface, such that memory is always synchronous | ||
| 350 | // when it is copied (even when in asynchronous GPU mode). | ||
| 342 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; | 351 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; |
| 343 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); | 352 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); |
| 344 | WriteBlock(dest_addr, src_ptr, copy_amount); | 353 | WriteBlock(dest_addr, src_ptr, copy_amount); |
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h new file mode 100644 index 000000000..e66054ed0 --- /dev/null +++ b/src/video_core/query_cache.h | |||
| @@ -0,0 +1,359 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <array> | ||
| 9 | #include <cstring> | ||
| 10 | #include <iterator> | ||
| 11 | #include <memory> | ||
| 12 | #include <mutex> | ||
| 13 | #include <optional> | ||
| 14 | #include <unordered_map> | ||
| 15 | #include <vector> | ||
| 16 | |||
| 17 | #include "common/assert.h" | ||
| 18 | #include "core/core.h" | ||
| 19 | #include "video_core/engines/maxwell_3d.h" | ||
| 20 | #include "video_core/gpu.h" | ||
| 21 | #include "video_core/memory_manager.h" | ||
| 22 | #include "video_core/rasterizer_interface.h" | ||
| 23 | |||
| 24 | namespace VideoCommon { | ||
| 25 | |||
| 26 | template <class QueryCache, class HostCounter> | ||
| 27 | class CounterStreamBase { | ||
| 28 | public: | ||
| 29 | explicit CounterStreamBase(QueryCache& cache, VideoCore::QueryType type) | ||
| 30 | : cache{cache}, type{type} {} | ||
| 31 | |||
| 32 | /// Updates the state of the stream, enabling or disabling as needed. | ||
| 33 | void Update(bool enabled) { | ||
| 34 | if (enabled) { | ||
| 35 | Enable(); | ||
| 36 | } else { | ||
| 37 | Disable(); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | /// Resets the stream to zero. It doesn't disable the query after resetting. | ||
| 42 | void Reset() { | ||
| 43 | if (current) { | ||
| 44 | current->EndQuery(); | ||
| 45 | |||
| 46 | // Immediately start a new query to avoid disabling its state. | ||
| 47 | current = cache.Counter(nullptr, type); | ||
| 48 | } | ||
| 49 | last = nullptr; | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Returns the current counter slicing as needed. | ||
| 53 | std::shared_ptr<HostCounter> Current() { | ||
| 54 | if (!current) { | ||
| 55 | return nullptr; | ||
| 56 | } | ||
| 57 | current->EndQuery(); | ||
| 58 | last = std::move(current); | ||
| 59 | current = cache.Counter(last, type); | ||
| 60 | return last; | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Returns true when the counter stream is enabled. | ||
| 64 | bool IsEnabled() const { | ||
| 65 | return current != nullptr; | ||
| 66 | } | ||
| 67 | |||
| 68 | private: | ||
| 69 | /// Enables the stream. | ||
| 70 | void Enable() { | ||
| 71 | if (current) { | ||
| 72 | return; | ||
| 73 | } | ||
| 74 | current = cache.Counter(last, type); | ||
| 75 | } | ||
| 76 | |||
| 77 | // Disables the stream. | ||
| 78 | void Disable() { | ||
| 79 | if (current) { | ||
| 80 | current->EndQuery(); | ||
| 81 | } | ||
| 82 | last = std::exchange(current, nullptr); | ||
| 83 | } | ||
| 84 | |||
| 85 | QueryCache& cache; | ||
| 86 | const VideoCore::QueryType type; | ||
| 87 | |||
| 88 | std::shared_ptr<HostCounter> current; | ||
| 89 | std::shared_ptr<HostCounter> last; | ||
| 90 | }; | ||
| 91 | |||
| 92 | template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter, | ||
| 93 | class QueryPool> | ||
| 94 | class QueryCacheBase { | ||
| 95 | public: | ||
| 96 | explicit QueryCacheBase(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | ||
| 97 | : system{system}, rasterizer{rasterizer}, streams{{CounterStream{ | ||
| 98 | static_cast<QueryCache&>(*this), | ||
| 99 | VideoCore::QueryType::SamplesPassed}}} {} | ||
| 100 | |||
| 101 | void InvalidateRegion(CacheAddr addr, std::size_t size) { | ||
| 102 | std::unique_lock lock{mutex}; | ||
| 103 | FlushAndRemoveRegion(addr, size); | ||
| 104 | } | ||
| 105 | |||
| 106 | void FlushRegion(CacheAddr addr, std::size_t size) { | ||
| 107 | std::unique_lock lock{mutex}; | ||
| 108 | FlushAndRemoveRegion(addr, size); | ||
| 109 | } | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Records a query in GPU mapped memory, potentially marked with a timestamp. | ||
| 113 | * @param gpu_addr GPU address to flush to when the mapped memory is read. | ||
| 114 | * @param type Query type, e.g. SamplesPassed. | ||
| 115 | * @param timestamp Timestamp, when empty the flushed query is assumed to be short. | ||
| 116 | */ | ||
| 117 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) { | ||
| 118 | std::unique_lock lock{mutex}; | ||
| 119 | auto& memory_manager = system.GPU().MemoryManager(); | ||
| 120 | const auto host_ptr = memory_manager.GetPointer(gpu_addr); | ||
| 121 | |||
| 122 | CachedQuery* query = TryGet(ToCacheAddr(host_ptr)); | ||
| 123 | if (!query) { | ||
| 124 | const auto cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr); | ||
| 125 | ASSERT_OR_EXECUTE(cpu_addr, return;); | ||
| 126 | |||
| 127 | query = Register(type, *cpu_addr, host_ptr, timestamp.has_value()); | ||
| 128 | } | ||
| 129 | |||
| 130 | query->BindCounter(Stream(type).Current(), timestamp); | ||
| 131 | } | ||
| 132 | |||
| 133 | /// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch. | ||
| 134 | void UpdateCounters() { | ||
| 135 | std::unique_lock lock{mutex}; | ||
| 136 | const auto& regs = system.GPU().Maxwell3D().regs; | ||
| 137 | Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable); | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Resets a counter to zero. It doesn't disable the query after resetting. | ||
| 141 | void ResetCounter(VideoCore::QueryType type) { | ||
| 142 | std::unique_lock lock{mutex}; | ||
| 143 | Stream(type).Reset(); | ||
| 144 | } | ||
| 145 | |||
| 146 | /// Disable all active streams. Expected to be called at the end of a command buffer. | ||
| 147 | void DisableStreams() { | ||
| 148 | std::unique_lock lock{mutex}; | ||
| 149 | for (auto& stream : streams) { | ||
| 150 | stream.Update(false); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | /// Returns a new host counter. | ||
| 155 | std::shared_ptr<HostCounter> Counter(std::shared_ptr<HostCounter> dependency, | ||
| 156 | VideoCore::QueryType type) { | ||
| 157 | return std::make_shared<HostCounter>(static_cast<QueryCache&>(*this), std::move(dependency), | ||
| 158 | type); | ||
| 159 | } | ||
| 160 | |||
| 161 | /// Returns the counter stream of the specified type. | ||
| 162 | CounterStream& Stream(VideoCore::QueryType type) { | ||
| 163 | return streams[static_cast<std::size_t>(type)]; | ||
| 164 | } | ||
| 165 | |||
| 166 | /// Returns the counter stream of the specified type. | ||
| 167 | const CounterStream& Stream(VideoCore::QueryType type) const { | ||
| 168 | return streams[static_cast<std::size_t>(type)]; | ||
| 169 | } | ||
| 170 | |||
| 171 | protected: | ||
| 172 | std::array<QueryPool, VideoCore::NumQueryTypes> query_pools; | ||
| 173 | |||
| 174 | private: | ||
| 175 | /// Flushes a memory range to guest memory and removes it from the cache. | ||
| 176 | void FlushAndRemoveRegion(CacheAddr addr, std::size_t size) { | ||
| 177 | const u64 addr_begin = static_cast<u64>(addr); | ||
| 178 | const u64 addr_end = addr_begin + static_cast<u64>(size); | ||
| 179 | const auto in_range = [addr_begin, addr_end](CachedQuery& query) { | ||
| 180 | const u64 cache_begin = query.GetCacheAddr(); | ||
| 181 | const u64 cache_end = cache_begin + query.SizeInBytes(); | ||
| 182 | return cache_begin < addr_end && addr_begin < cache_end; | ||
| 183 | }; | ||
| 184 | |||
| 185 | const u64 page_end = addr_end >> PAGE_SHIFT; | ||
| 186 | for (u64 page = addr_begin >> PAGE_SHIFT; page <= page_end; ++page) { | ||
| 187 | const auto& it = cached_queries.find(page); | ||
| 188 | if (it == std::end(cached_queries)) { | ||
| 189 | continue; | ||
| 190 | } | ||
| 191 | auto& contents = it->second; | ||
| 192 | for (auto& query : contents) { | ||
| 193 | if (!in_range(query)) { | ||
| 194 | continue; | ||
| 195 | } | ||
| 196 | rasterizer.UpdatePagesCachedCount(query.CpuAddr(), query.SizeInBytes(), -1); | ||
| 197 | query.Flush(); | ||
| 198 | } | ||
| 199 | contents.erase(std::remove_if(std::begin(contents), std::end(contents), in_range), | ||
| 200 | std::end(contents)); | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | /// Registers the passed parameters as cached and returns a pointer to the stored cached query. | ||
| 205 | CachedQuery* Register(VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr, bool timestamp) { | ||
| 206 | rasterizer.UpdatePagesCachedCount(cpu_addr, CachedQuery::SizeInBytes(timestamp), 1); | ||
| 207 | const u64 page = static_cast<u64>(ToCacheAddr(host_ptr)) >> PAGE_SHIFT; | ||
| 208 | return &cached_queries[page].emplace_back(static_cast<QueryCache&>(*this), type, cpu_addr, | ||
| 209 | host_ptr); | ||
| 210 | } | ||
| 211 | |||
| 212 | /// Tries to a get a cached query. Returns nullptr on failure. | ||
| 213 | CachedQuery* TryGet(CacheAddr addr) { | ||
| 214 | const u64 page = static_cast<u64>(addr) >> PAGE_SHIFT; | ||
| 215 | const auto it = cached_queries.find(page); | ||
| 216 | if (it == std::end(cached_queries)) { | ||
| 217 | return nullptr; | ||
| 218 | } | ||
| 219 | auto& contents = it->second; | ||
| 220 | const auto found = | ||
| 221 | std::find_if(std::begin(contents), std::end(contents), | ||
| 222 | [addr](auto& query) { return query.GetCacheAddr() == addr; }); | ||
| 223 | return found != std::end(contents) ? &*found : nullptr; | ||
| 224 | } | ||
| 225 | |||
| 226 | static constexpr std::uintptr_t PAGE_SIZE = 4096; | ||
| 227 | static constexpr unsigned PAGE_SHIFT = 12; | ||
| 228 | |||
| 229 | Core::System& system; | ||
| 230 | VideoCore::RasterizerInterface& rasterizer; | ||
| 231 | |||
| 232 | std::recursive_mutex mutex; | ||
| 233 | |||
| 234 | std::unordered_map<u64, std::vector<CachedQuery>> cached_queries; | ||
| 235 | |||
| 236 | std::array<CounterStream, VideoCore::NumQueryTypes> streams; | ||
| 237 | }; | ||
| 238 | |||
| 239 | template <class QueryCache, class HostCounter> | ||
| 240 | class HostCounterBase { | ||
| 241 | public: | ||
| 242 | explicit HostCounterBase(std::shared_ptr<HostCounter> dependency_) | ||
| 243 | : dependency{std::move(dependency_)}, depth{dependency ? (dependency->Depth() + 1) : 0} { | ||
| 244 | // Avoid nesting too many dependencies to avoid a stack overflow when these are deleted. | ||
| 245 | constexpr u64 depth_threshold = 96; | ||
| 246 | if (depth > depth_threshold) { | ||
| 247 | depth = 0; | ||
| 248 | base_result = dependency->Query(); | ||
| 249 | dependency = nullptr; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | virtual ~HostCounterBase() = default; | ||
| 253 | |||
| 254 | /// Returns the current value of the query. | ||
| 255 | u64 Query() { | ||
| 256 | if (result) { | ||
| 257 | return *result; | ||
| 258 | } | ||
| 259 | |||
| 260 | u64 value = BlockingQuery() + base_result; | ||
| 261 | if (dependency) { | ||
| 262 | value += dependency->Query(); | ||
| 263 | dependency = nullptr; | ||
| 264 | } | ||
| 265 | |||
| 266 | result = value; | ||
| 267 | return *result; | ||
| 268 | } | ||
| 269 | |||
| 270 | /// Returns true when flushing this query will potentially wait. | ||
| 271 | bool WaitPending() const noexcept { | ||
| 272 | return result.has_value(); | ||
| 273 | } | ||
| 274 | |||
| 275 | u64 Depth() const noexcept { | ||
| 276 | return depth; | ||
| 277 | } | ||
| 278 | |||
| 279 | protected: | ||
| 280 | /// Returns the value of query from the backend API blocking as needed. | ||
| 281 | virtual u64 BlockingQuery() const = 0; | ||
| 282 | |||
| 283 | private: | ||
| 284 | std::shared_ptr<HostCounter> dependency; ///< Counter to add to this value. | ||
| 285 | std::optional<u64> result; ///< Filled with the already returned value. | ||
| 286 | u64 depth; ///< Number of nested dependencies. | ||
| 287 | u64 base_result = 0; ///< Equivalent to nested dependencies value. | ||
| 288 | }; | ||
| 289 | |||
| 290 | template <class HostCounter> | ||
| 291 | class CachedQueryBase { | ||
| 292 | public: | ||
| 293 | explicit CachedQueryBase(VAddr cpu_addr, u8* host_ptr) | ||
| 294 | : cpu_addr{cpu_addr}, host_ptr{host_ptr} {} | ||
| 295 | virtual ~CachedQueryBase() = default; | ||
| 296 | |||
| 297 | CachedQueryBase(CachedQueryBase&&) noexcept = default; | ||
| 298 | CachedQueryBase(const CachedQueryBase&) = delete; | ||
| 299 | |||
| 300 | CachedQueryBase& operator=(CachedQueryBase&&) noexcept = default; | ||
| 301 | CachedQueryBase& operator=(const CachedQueryBase&) = delete; | ||
| 302 | |||
| 303 | /// Flushes the query to guest memory. | ||
| 304 | virtual void Flush() { | ||
| 305 | // When counter is nullptr it means that it's just been reseted. We are supposed to write a | ||
| 306 | // zero in these cases. | ||
| 307 | const u64 value = counter ? counter->Query() : 0; | ||
| 308 | std::memcpy(host_ptr, &value, sizeof(u64)); | ||
| 309 | |||
| 310 | if (timestamp) { | ||
| 311 | std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64)); | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Binds a counter to this query. | ||
| 316 | void BindCounter(std::shared_ptr<HostCounter> counter_, std::optional<u64> timestamp_) { | ||
| 317 | if (counter) { | ||
| 318 | // If there's an old counter set it means the query is being rewritten by the game. | ||
| 319 | // To avoid losing the data forever, flush here. | ||
| 320 | Flush(); | ||
| 321 | } | ||
| 322 | counter = std::move(counter_); | ||
| 323 | timestamp = timestamp_; | ||
| 324 | } | ||
| 325 | |||
| 326 | VAddr CpuAddr() const noexcept { | ||
| 327 | return cpu_addr; | ||
| 328 | } | ||
| 329 | |||
| 330 | CacheAddr GetCacheAddr() const noexcept { | ||
| 331 | return ToCacheAddr(host_ptr); | ||
| 332 | } | ||
| 333 | |||
| 334 | u64 SizeInBytes() const noexcept { | ||
| 335 | return SizeInBytes(timestamp.has_value()); | ||
| 336 | } | ||
| 337 | |||
| 338 | static constexpr u64 SizeInBytes(bool with_timestamp) noexcept { | ||
| 339 | return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE; | ||
| 340 | } | ||
| 341 | |||
| 342 | protected: | ||
| 343 | /// Returns true when querying the counter may potentially block. | ||
| 344 | bool WaitPending() const noexcept { | ||
| 345 | return counter && counter->WaitPending(); | ||
| 346 | } | ||
| 347 | |||
| 348 | private: | ||
| 349 | static constexpr std::size_t SMALL_QUERY_SIZE = 8; // Query size without timestamp. | ||
| 350 | static constexpr std::size_t LARGE_QUERY_SIZE = 16; // Query size with timestamp. | ||
| 351 | static constexpr std::intptr_t TIMESTAMP_OFFSET = 8; // Timestamp offset in a large query. | ||
| 352 | |||
| 353 | VAddr cpu_addr; ///< Guest CPU address. | ||
| 354 | u8* host_ptr; ///< Writable host pointer. | ||
| 355 | std::shared_ptr<HostCounter> counter; ///< Host counter to query, owns the dependency tree. | ||
| 356 | std::optional<u64> timestamp; ///< Timestamp to flush to guest memory. | ||
| 357 | }; | ||
| 358 | |||
| 359 | } // namespace VideoCommon | ||
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 5b0eca9e2..f18eaf4bc 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -6,9 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <functional> | 8 | #include <functional> |
| 9 | #include <optional> | ||
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "video_core/engines/fermi_2d.h" | 11 | #include "video_core/engines/fermi_2d.h" |
| 11 | #include "video_core/gpu.h" | 12 | #include "video_core/gpu.h" |
| 13 | #include "video_core/guest_driver.h" | ||
| 12 | 14 | ||
| 13 | namespace Tegra { | 15 | namespace Tegra { |
| 14 | class MemoryManager; | 16 | class MemoryManager; |
| @@ -16,6 +18,11 @@ class MemoryManager; | |||
| 16 | 18 | ||
| 17 | namespace VideoCore { | 19 | namespace VideoCore { |
| 18 | 20 | ||
| 21 | enum class QueryType { | ||
| 22 | SamplesPassed, | ||
| 23 | }; | ||
| 24 | constexpr std::size_t NumQueryTypes = 1; | ||
| 25 | |||
| 19 | enum class LoadCallbackStage { | 26 | enum class LoadCallbackStage { |
| 20 | Prepare, | 27 | Prepare, |
| 21 | Decompile, | 28 | Decompile, |
| @@ -28,11 +35,8 @@ class RasterizerInterface { | |||
| 28 | public: | 35 | public: |
| 29 | virtual ~RasterizerInterface() {} | 36 | virtual ~RasterizerInterface() {} |
| 30 | 37 | ||
| 31 | /// Draw the current batch of vertex arrays | 38 | /// Dispatches a draw invocation |
| 32 | virtual bool DrawBatch(bool is_indexed) = 0; | 39 | virtual void Draw(bool is_indexed, bool is_instanced) = 0; |
| 33 | |||
| 34 | /// Draw the current batch of multiple instances of vertex arrays | ||
| 35 | virtual bool DrawMultiBatch(bool is_indexed) = 0; | ||
| 36 | 40 | ||
| 37 | /// Clear the current framebuffer | 41 | /// Clear the current framebuffer |
| 38 | virtual void Clear() = 0; | 42 | virtual void Clear() = 0; |
| @@ -40,6 +44,12 @@ public: | |||
| 40 | /// Dispatches a compute shader invocation | 44 | /// Dispatches a compute shader invocation |
| 41 | virtual void DispatchCompute(GPUVAddr code_addr) = 0; | 45 | virtual void DispatchCompute(GPUVAddr code_addr) = 0; |
| 42 | 46 | ||
| 47 | /// Resets the counter of a query | ||
| 48 | virtual void ResetCounter(QueryType type) = 0; | ||
| 49 | |||
| 50 | /// Records a GPU query and caches it | ||
| 51 | virtual void Query(GPUVAddr gpu_addr, QueryType type, std::optional<u64> timestamp) = 0; | ||
| 52 | |||
| 43 | /// Notify rasterizer that all caches should be flushed to Switch memory | 53 | /// Notify rasterizer that all caches should be flushed to Switch memory |
| 44 | virtual void FlushAll() = 0; | 54 | virtual void FlushAll() = 0; |
| 45 | 55 | ||
| @@ -78,5 +88,18 @@ public: | |||
| 78 | /// Initialize disk cached resources for the game being emulated | 88 | /// Initialize disk cached resources for the game being emulated |
| 79 | virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, | 89 | virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, |
| 80 | const DiskResourceLoadCallback& callback = {}) {} | 90 | const DiskResourceLoadCallback& callback = {}) {} |
| 91 | |||
| 92 | /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. | ||
| 93 | GuestDriverProfile& AccessGuestDriverProfile() { | ||
| 94 | return guest_driver_profile; | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. | ||
| 98 | const GuestDriverProfile& AccessGuestDriverProfile() const { | ||
| 99 | return guest_driver_profile; | ||
| 100 | } | ||
| 101 | |||
| 102 | private: | ||
| 103 | GuestDriverProfile guest_driver_profile{}; | ||
| 81 | }; | 104 | }; |
| 82 | } // namespace VideoCore | 105 | } // namespace VideoCore |
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp new file mode 100644 index 000000000..f12e9f55f --- /dev/null +++ b/src/video_core/renderer_opengl/gl_query_cache.cpp | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstring> | ||
| 7 | #include <memory> | ||
| 8 | #include <unordered_map> | ||
| 9 | #include <utility> | ||
| 10 | #include <vector> | ||
| 11 | |||
| 12 | #include <glad/glad.h> | ||
| 13 | |||
| 14 | #include "common/assert.h" | ||
| 15 | #include "core/core.h" | ||
| 16 | #include "video_core/engines/maxwell_3d.h" | ||
| 17 | #include "video_core/memory_manager.h" | ||
| 18 | #include "video_core/renderer_opengl/gl_query_cache.h" | ||
| 19 | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||
| 20 | |||
| 21 | namespace OpenGL { | ||
| 22 | |||
| 23 | namespace { | ||
| 24 | |||
| 25 | constexpr std::array<GLenum, VideoCore::NumQueryTypes> QueryTargets = {GL_SAMPLES_PASSED}; | ||
| 26 | |||
| 27 | constexpr GLenum GetTarget(VideoCore::QueryType type) { | ||
| 28 | return QueryTargets[static_cast<std::size_t>(type)]; | ||
| 29 | } | ||
| 30 | |||
| 31 | } // Anonymous namespace | ||
| 32 | |||
| 33 | QueryCache::QueryCache(Core::System& system, RasterizerOpenGL& gl_rasterizer) | ||
| 34 | : VideoCommon::QueryCacheBase< | ||
| 35 | QueryCache, CachedQuery, CounterStream, HostCounter, | ||
| 36 | std::vector<OGLQuery>>{system, | ||
| 37 | static_cast<VideoCore::RasterizerInterface&>(gl_rasterizer)}, | ||
| 38 | gl_rasterizer{gl_rasterizer} {} | ||
| 39 | |||
| 40 | QueryCache::~QueryCache() = default; | ||
| 41 | |||
| 42 | OGLQuery QueryCache::AllocateQuery(VideoCore::QueryType type) { | ||
| 43 | auto& reserve = query_pools[static_cast<std::size_t>(type)]; | ||
| 44 | OGLQuery query; | ||
| 45 | if (reserve.empty()) { | ||
| 46 | query.Create(GetTarget(type)); | ||
| 47 | return query; | ||
| 48 | } | ||
| 49 | |||
| 50 | query = std::move(reserve.back()); | ||
| 51 | reserve.pop_back(); | ||
| 52 | return query; | ||
| 53 | } | ||
| 54 | |||
| 55 | void QueryCache::Reserve(VideoCore::QueryType type, OGLQuery&& query) { | ||
| 56 | query_pools[static_cast<std::size_t>(type)].push_back(std::move(query)); | ||
| 57 | } | ||
| 58 | |||
| 59 | bool QueryCache::AnyCommandQueued() const noexcept { | ||
| 60 | return gl_rasterizer.AnyCommandQueued(); | ||
| 61 | } | ||
| 62 | |||
| 63 | HostCounter::HostCounter(QueryCache& cache, std::shared_ptr<HostCounter> dependency, | ||
| 64 | VideoCore::QueryType type) | ||
| 65 | : VideoCommon::HostCounterBase<QueryCache, HostCounter>{std::move(dependency)}, cache{cache}, | ||
| 66 | type{type}, query{cache.AllocateQuery(type)} { | ||
| 67 | glBeginQuery(GetTarget(type), query.handle); | ||
| 68 | } | ||
| 69 | |||
| 70 | HostCounter::~HostCounter() { | ||
| 71 | cache.Reserve(type, std::move(query)); | ||
| 72 | } | ||
| 73 | |||
| 74 | void HostCounter::EndQuery() { | ||
| 75 | if (!cache.AnyCommandQueued()) { | ||
| 76 | // There are chances a query waited on without commands (glDraw, glClear, glDispatch). Not | ||
| 77 | // having any of these causes a lock. glFlush is considered a command, so we can safely wait | ||
| 78 | // for this. Insert to the OpenGL command stream a flush. | ||
| 79 | glFlush(); | ||
| 80 | } | ||
| 81 | glEndQuery(GetTarget(type)); | ||
| 82 | } | ||
| 83 | |||
| 84 | u64 HostCounter::BlockingQuery() const { | ||
| 85 | GLint64 value; | ||
| 86 | glGetQueryObjecti64v(query.handle, GL_QUERY_RESULT, &value); | ||
| 87 | return static_cast<u64>(value); | ||
| 88 | } | ||
| 89 | |||
| 90 | CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr) | ||
| 91 | : VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {} | ||
| 92 | |||
| 93 | CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept | ||
| 94 | : VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {} | ||
| 95 | |||
| 96 | CachedQuery& CachedQuery::operator=(CachedQuery&& rhs) noexcept { | ||
| 97 | VideoCommon::CachedQueryBase<HostCounter>::operator=(std::move(rhs)); | ||
| 98 | cache = rhs.cache; | ||
| 99 | type = rhs.type; | ||
| 100 | return *this; | ||
| 101 | } | ||
| 102 | |||
| 103 | void CachedQuery::Flush() { | ||
| 104 | // Waiting for a query while another query of the same target is enabled locks Nvidia's driver. | ||
| 105 | // To avoid this disable and re-enable keeping the dependency stream. | ||
| 106 | // But we only have to do this if we have pending waits to be done. | ||
| 107 | auto& stream = cache->Stream(type); | ||
| 108 | const bool slice_counter = WaitPending() && stream.IsEnabled(); | ||
| 109 | if (slice_counter) { | ||
| 110 | stream.Update(false); | ||
| 111 | } | ||
| 112 | |||
| 113 | VideoCommon::CachedQueryBase<HostCounter>::Flush(); | ||
| 114 | |||
| 115 | if (slice_counter) { | ||
| 116 | stream.Update(true); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_query_cache.h b/src/video_core/renderer_opengl/gl_query_cache.h new file mode 100644 index 000000000..d8e7052a1 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_query_cache.h | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <memory> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "video_core/query_cache.h" | ||
| 13 | #include "video_core/rasterizer_interface.h" | ||
| 14 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 15 | |||
| 16 | namespace Core { | ||
| 17 | class System; | ||
| 18 | } | ||
| 19 | |||
| 20 | namespace OpenGL { | ||
| 21 | |||
| 22 | class CachedQuery; | ||
| 23 | class HostCounter; | ||
| 24 | class QueryCache; | ||
| 25 | class RasterizerOpenGL; | ||
| 26 | |||
| 27 | using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>; | ||
| 28 | |||
| 29 | class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, | ||
| 30 | HostCounter, std::vector<OGLQuery>> { | ||
| 31 | public: | ||
| 32 | explicit QueryCache(Core::System& system, RasterizerOpenGL& rasterizer); | ||
| 33 | ~QueryCache(); | ||
| 34 | |||
| 35 | OGLQuery AllocateQuery(VideoCore::QueryType type); | ||
| 36 | |||
| 37 | void Reserve(VideoCore::QueryType type, OGLQuery&& query); | ||
| 38 | |||
| 39 | bool AnyCommandQueued() const noexcept; | ||
| 40 | |||
| 41 | private: | ||
| 42 | RasterizerOpenGL& gl_rasterizer; | ||
| 43 | }; | ||
| 44 | |||
| 45 | class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> { | ||
| 46 | public: | ||
| 47 | explicit HostCounter(QueryCache& cache, std::shared_ptr<HostCounter> dependency, | ||
| 48 | VideoCore::QueryType type); | ||
| 49 | ~HostCounter(); | ||
| 50 | |||
| 51 | void EndQuery(); | ||
| 52 | |||
| 53 | private: | ||
| 54 | u64 BlockingQuery() const override; | ||
| 55 | |||
| 56 | QueryCache& cache; | ||
| 57 | const VideoCore::QueryType type; | ||
| 58 | OGLQuery query; | ||
| 59 | }; | ||
| 60 | |||
| 61 | class CachedQuery final : public VideoCommon::CachedQueryBase<HostCounter> { | ||
| 62 | public: | ||
| 63 | explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, | ||
| 64 | u8* host_ptr); | ||
| 65 | CachedQuery(CachedQuery&& rhs) noexcept; | ||
| 66 | CachedQuery(const CachedQuery&) = delete; | ||
| 67 | |||
| 68 | CachedQuery& operator=(CachedQuery&& rhs) noexcept; | ||
| 69 | CachedQuery& operator=(const CachedQuery&) = delete; | ||
| 70 | |||
| 71 | void Flush() override; | ||
| 72 | |||
| 73 | private: | ||
| 74 | QueryCache* cache; | ||
| 75 | VideoCore::QueryType type; | ||
| 76 | }; | ||
| 77 | |||
| 78 | } // namespace OpenGL | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index c428f06e4..e1965fb21 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include "video_core/engines/maxwell_3d.h" | 25 | #include "video_core/engines/maxwell_3d.h" |
| 26 | #include "video_core/engines/shader_type.h" | 26 | #include "video_core/engines/shader_type.h" |
| 27 | #include "video_core/memory_manager.h" | 27 | #include "video_core/memory_manager.h" |
| 28 | #include "video_core/renderer_opengl/gl_query_cache.h" | ||
| 28 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 29 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 29 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 30 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| 30 | #include "video_core/renderer_opengl/gl_shader_gen.h" | 31 | #include "video_core/renderer_opengl/gl_shader_gen.h" |
| @@ -55,16 +56,20 @@ namespace { | |||
| 55 | 56 | ||
| 56 | template <typename Engine, typename Entry> | 57 | template <typename Engine, typename Entry> |
| 57 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, | 58 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, |
| 58 | Tegra::Engines::ShaderType shader_type) { | 59 | Tegra::Engines::ShaderType shader_type, |
| 60 | std::size_t index = 0) { | ||
| 59 | if (entry.IsBindless()) { | 61 | if (entry.IsBindless()) { |
| 60 | const Tegra::Texture::TextureHandle tex_handle = | 62 | const Tegra::Texture::TextureHandle tex_handle = |
| 61 | engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset()); | 63 | engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset()); |
| 62 | return engine.GetTextureInfo(tex_handle); | 64 | return engine.GetTextureInfo(tex_handle); |
| 63 | } | 65 | } |
| 66 | const auto& gpu_profile = engine.AccessGuestDriverProfile(); | ||
| 67 | const u32 offset = | ||
| 68 | entry.GetOffset() + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize()); | ||
| 64 | if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { | 69 | if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { |
| 65 | return engine.GetStageTexture(shader_type, entry.GetOffset()); | 70 | return engine.GetStageTexture(shader_type, offset); |
| 66 | } else { | 71 | } else { |
| 67 | return engine.GetTexture(entry.GetOffset()); | 72 | return engine.GetTexture(offset); |
| 68 | } | 73 | } |
| 69 | } | 74 | } |
| 70 | 75 | ||
| @@ -88,8 +93,8 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer, | |||
| 88 | RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, | 93 | RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window, |
| 89 | ScreenInfo& info) | 94 | ScreenInfo& info) |
| 90 | : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device}, | 95 | : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device}, |
| 91 | shader_cache{*this, system, emu_window, device}, system{system}, screen_info{info}, | 96 | shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system}, |
| 92 | buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} { | 97 | screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} { |
| 93 | shader_program_manager = std::make_unique<GLShader::ProgramManager>(); | 98 | shader_program_manager = std::make_unique<GLShader::ProgramManager>(); |
| 94 | state.draw.shader_program = 0; | 99 | state.draw.shader_program = 0; |
| 95 | state.Apply(); | 100 | state.Apply(); |
| @@ -244,9 +249,6 @@ void RasterizerOpenGL::SetupVertexInstances(GLuint vao) { | |||
| 244 | } | 249 | } |
| 245 | 250 | ||
| 246 | GLintptr RasterizerOpenGL::SetupIndexBuffer() { | 251 | GLintptr RasterizerOpenGL::SetupIndexBuffer() { |
| 247 | if (accelerate_draw != AccelDraw::Indexed) { | ||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | MICROPROFILE_SCOPE(OpenGL_Index); | 252 | MICROPROFILE_SCOPE(OpenGL_Index); |
| 251 | const auto& regs = system.GPU().Maxwell3D().regs; | 253 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 252 | const std::size_t size = CalculateIndexBufferSize(); | 254 | const std::size_t size = CalculateIndexBufferSize(); |
| @@ -540,10 +542,16 @@ void RasterizerOpenGL::Clear() { | |||
| 540 | } else if (use_stencil) { | 542 | } else if (use_stencil) { |
| 541 | glClearBufferiv(GL_STENCIL, 0, ®s.clear_stencil); | 543 | glClearBufferiv(GL_STENCIL, 0, ®s.clear_stencil); |
| 542 | } | 544 | } |
| 545 | |||
| 546 | ++num_queued_commands; | ||
| 543 | } | 547 | } |
| 544 | 548 | ||
| 545 | void RasterizerOpenGL::DrawPrelude() { | 549 | void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
| 550 | MICROPROFILE_SCOPE(OpenGL_Drawing); | ||
| 546 | auto& gpu = system.GPU().Maxwell3D(); | 551 | auto& gpu = system.GPU().Maxwell3D(); |
| 552 | const auto& regs = gpu.regs; | ||
| 553 | |||
| 554 | query_cache.UpdateCounters(); | ||
| 547 | 555 | ||
| 548 | SyncRasterizeEnable(state); | 556 | SyncRasterizeEnable(state); |
| 549 | SyncColorMask(); | 557 | SyncColorMask(); |
| @@ -563,9 +571,6 @@ void RasterizerOpenGL::DrawPrelude() { | |||
| 563 | 571 | ||
| 564 | buffer_cache.Acquire(); | 572 | buffer_cache.Acquire(); |
| 565 | 573 | ||
| 566 | // Draw the vertex batch | ||
| 567 | const bool is_indexed = accelerate_draw == AccelDraw::Indexed; | ||
| 568 | |||
| 569 | std::size_t buffer_size = CalculateVertexArraysSize(); | 574 | std::size_t buffer_size = CalculateVertexArraysSize(); |
| 570 | 575 | ||
| 571 | // Add space for index buffer | 576 | // Add space for index buffer |
| @@ -592,7 +597,11 @@ void RasterizerOpenGL::DrawPrelude() { | |||
| 592 | // Upload vertex and index data. | 597 | // Upload vertex and index data. |
| 593 | SetupVertexBuffer(vao); | 598 | SetupVertexBuffer(vao); |
| 594 | SetupVertexInstances(vao); | 599 | SetupVertexInstances(vao); |
| 595 | index_buffer_offset = SetupIndexBuffer(); | 600 | |
| 601 | GLintptr index_buffer_offset; | ||
| 602 | if (is_indexed) { | ||
| 603 | index_buffer_offset = SetupIndexBuffer(); | ||
| 604 | } | ||
| 596 | 605 | ||
| 597 | // Prepare packed bindings. | 606 | // Prepare packed bindings. |
| 598 | bind_ubo_pushbuffer.Setup(); | 607 | bind_ubo_pushbuffer.Setup(); |
| @@ -608,7 +617,7 @@ void RasterizerOpenGL::DrawPrelude() { | |||
| 608 | 617 | ||
| 609 | // Setup shaders and their used resources. | 618 | // Setup shaders and their used resources. |
| 610 | texture_cache.GuardSamplers(true); | 619 | texture_cache.GuardSamplers(true); |
| 611 | const auto primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology); | 620 | const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology); |
| 612 | SetupShaders(primitive_mode); | 621 | SetupShaders(primitive_mode); |
| 613 | texture_cache.GuardSamplers(false); | 622 | texture_cache.GuardSamplers(false); |
| 614 | 623 | ||
| @@ -626,6 +635,7 @@ void RasterizerOpenGL::DrawPrelude() { | |||
| 626 | // As all cached buffers are invalidated, we need to recheck their state. | 635 | // As all cached buffers are invalidated, we need to recheck their state. |
| 627 | gpu.dirty.ResetVertexArrays(); | 636 | gpu.dirty.ResetVertexArrays(); |
| 628 | } | 637 | } |
| 638 | gpu.dirty.memory_general = false; | ||
| 629 | 639 | ||
| 630 | shader_program_manager->ApplyTo(state); | 640 | shader_program_manager->ApplyTo(state); |
| 631 | state.Apply(); | 641 | state.Apply(); |
| @@ -633,107 +643,46 @@ void RasterizerOpenGL::DrawPrelude() { | |||
| 633 | if (texture_cache.TextureBarrier()) { | 643 | if (texture_cache.TextureBarrier()) { |
| 634 | glTextureBarrier(); | 644 | glTextureBarrier(); |
| 635 | } | 645 | } |
| 636 | } | ||
| 637 | 646 | ||
| 638 | struct DrawParams { | 647 | ++num_queued_commands; |
| 639 | bool is_indexed{}; | 648 | |
| 640 | bool is_instanced{}; | 649 | const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance); |
| 641 | GLenum primitive_mode{}; | 650 | const GLsizei num_instances = |
| 642 | GLint count{}; | 651 | static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1); |
| 643 | GLint base_vertex{}; | 652 | if (is_indexed) { |
| 644 | 653 | const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base); | |
| 645 | // Indexed settings | 654 | const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count); |
| 646 | GLenum index_format{}; | 655 | const GLvoid* offset = reinterpret_cast<const GLvoid*>(index_buffer_offset); |
| 647 | GLintptr index_buffer_offset{}; | 656 | const GLenum format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format); |
| 648 | 657 | if (num_instances == 1 && base_instance == 0 && base_vertex == 0) { | |
| 649 | // Instanced setting | 658 | glDrawElements(primitive_mode, num_vertices, format, offset); |
| 650 | GLint num_instances{}; | 659 | } else if (num_instances == 1 && base_instance == 0) { |
| 651 | GLint base_instance{}; | 660 | glDrawElementsBaseVertex(primitive_mode, num_vertices, format, offset, base_vertex); |
| 652 | 661 | } else if (base_vertex == 0 && base_instance == 0) { | |
| 653 | void DispatchDraw() { | 662 | glDrawElementsInstanced(primitive_mode, num_vertices, format, offset, num_instances); |
| 654 | if (is_indexed) { | 663 | } else if (base_vertex == 0) { |
| 655 | const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); | 664 | glDrawElementsInstancedBaseInstance(primitive_mode, num_vertices, format, offset, |
| 656 | if (is_instanced) { | 665 | num_instances, base_instance); |
| 657 | glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, | 666 | } else if (base_instance == 0) { |
| 658 | index_buffer_ptr, num_instances, | 667 | glDrawElementsInstancedBaseVertex(primitive_mode, num_vertices, format, offset, |
| 659 | base_vertex, base_instance); | 668 | num_instances, base_vertex); |
| 660 | } else { | ||
| 661 | glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, | ||
| 662 | base_vertex); | ||
| 663 | } | ||
| 664 | } else { | 669 | } else { |
| 665 | if (is_instanced) { | 670 | glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, num_vertices, format, |
| 666 | glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances, | 671 | offset, num_instances, base_vertex, |
| 667 | base_instance); | 672 | base_instance); |
| 668 | } else { | ||
| 669 | glDrawArrays(primitive_mode, base_vertex, count); | ||
| 670 | } | ||
| 671 | } | 673 | } |
| 672 | } | ||
| 673 | }; | ||
| 674 | |||
| 675 | bool RasterizerOpenGL::DrawBatch(bool is_indexed) { | ||
| 676 | accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; | ||
| 677 | |||
| 678 | MICROPROFILE_SCOPE(OpenGL_Drawing); | ||
| 679 | |||
| 680 | DrawPrelude(); | ||
| 681 | |||
| 682 | auto& maxwell3d = system.GPU().Maxwell3D(); | ||
| 683 | const auto& regs = maxwell3d.regs; | ||
| 684 | const auto current_instance = maxwell3d.state.current_instance; | ||
| 685 | DrawParams draw_call{}; | ||
| 686 | draw_call.is_indexed = is_indexed; | ||
| 687 | draw_call.num_instances = static_cast<GLint>(1); | ||
| 688 | draw_call.base_instance = static_cast<GLint>(current_instance); | ||
| 689 | draw_call.is_instanced = current_instance > 0; | ||
| 690 | draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); | ||
| 691 | if (draw_call.is_indexed) { | ||
| 692 | draw_call.count = static_cast<GLint>(regs.index_array.count); | ||
| 693 | draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); | ||
| 694 | draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); | ||
| 695 | draw_call.index_buffer_offset = index_buffer_offset; | ||
| 696 | } else { | 674 | } else { |
| 697 | draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); | 675 | const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first); |
| 698 | draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); | 676 | const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count); |
| 699 | } | 677 | if (num_instances == 1 && base_instance == 0) { |
| 700 | draw_call.DispatchDraw(); | 678 | glDrawArrays(primitive_mode, base_vertex, num_vertices); |
| 701 | 679 | } else if (base_instance == 0) { | |
| 702 | maxwell3d.dirty.memory_general = false; | 680 | glDrawArraysInstanced(primitive_mode, base_vertex, num_vertices, num_instances); |
| 703 | accelerate_draw = AccelDraw::Disabled; | 681 | } else { |
| 704 | return true; | 682 | glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, |
| 705 | } | 683 | num_instances, base_instance); |
| 706 | 684 | } | |
| 707 | bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) { | ||
| 708 | accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; | ||
| 709 | |||
| 710 | MICROPROFILE_SCOPE(OpenGL_Drawing); | ||
| 711 | |||
| 712 | DrawPrelude(); | ||
| 713 | |||
| 714 | auto& maxwell3d = system.GPU().Maxwell3D(); | ||
| 715 | const auto& regs = maxwell3d.regs; | ||
| 716 | const auto& draw_setup = maxwell3d.mme_draw; | ||
| 717 | DrawParams draw_call{}; | ||
| 718 | draw_call.is_indexed = is_indexed; | ||
| 719 | draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count); | ||
| 720 | draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance); | ||
| 721 | draw_call.is_instanced = draw_setup.instance_count > 1; | ||
| 722 | draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); | ||
| 723 | if (draw_call.is_indexed) { | ||
| 724 | draw_call.count = static_cast<GLint>(regs.index_array.count); | ||
| 725 | draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); | ||
| 726 | draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); | ||
| 727 | draw_call.index_buffer_offset = index_buffer_offset; | ||
| 728 | } else { | ||
| 729 | draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); | ||
| 730 | draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); | ||
| 731 | } | 685 | } |
| 732 | draw_call.DispatchDraw(); | ||
| 733 | |||
| 734 | maxwell3d.dirty.memory_general = false; | ||
| 735 | accelerate_draw = AccelDraw::Disabled; | ||
| 736 | return true; | ||
| 737 | } | 686 | } |
| 738 | 687 | ||
| 739 | void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { | 688 | void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { |
| @@ -776,6 +725,16 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { | |||
| 776 | state.ApplyProgramPipeline(); | 725 | state.ApplyProgramPipeline(); |
| 777 | 726 | ||
| 778 | glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z); | 727 | glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z); |
| 728 | ++num_queued_commands; | ||
| 729 | } | ||
| 730 | |||
| 731 | void RasterizerOpenGL::ResetCounter(VideoCore::QueryType type) { | ||
| 732 | query_cache.ResetCounter(type); | ||
| 733 | } | ||
| 734 | |||
| 735 | void RasterizerOpenGL::Query(GPUVAddr gpu_addr, VideoCore::QueryType type, | ||
| 736 | std::optional<u64> timestamp) { | ||
| 737 | query_cache.Query(gpu_addr, type, timestamp); | ||
| 779 | } | 738 | } |
| 780 | 739 | ||
| 781 | void RasterizerOpenGL::FlushAll() {} | 740 | void RasterizerOpenGL::FlushAll() {} |
| @@ -787,6 +746,7 @@ void RasterizerOpenGL::FlushRegion(CacheAddr addr, u64 size) { | |||
| 787 | } | 746 | } |
| 788 | texture_cache.FlushRegion(addr, size); | 747 | texture_cache.FlushRegion(addr, size); |
| 789 | buffer_cache.FlushRegion(addr, size); | 748 | buffer_cache.FlushRegion(addr, size); |
| 749 | query_cache.FlushRegion(addr, size); | ||
| 790 | } | 750 | } |
| 791 | 751 | ||
| 792 | void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { | 752 | void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { |
| @@ -797,6 +757,7 @@ void RasterizerOpenGL::InvalidateRegion(CacheAddr addr, u64 size) { | |||
| 797 | texture_cache.InvalidateRegion(addr, size); | 757 | texture_cache.InvalidateRegion(addr, size); |
| 798 | shader_cache.InvalidateRegion(addr, size); | 758 | shader_cache.InvalidateRegion(addr, size); |
| 799 | buffer_cache.InvalidateRegion(addr, size); | 759 | buffer_cache.InvalidateRegion(addr, size); |
| 760 | query_cache.InvalidateRegion(addr, size); | ||
| 800 | } | 761 | } |
| 801 | 762 | ||
| 802 | void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | 763 | void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { |
| @@ -807,10 +768,18 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | |||
| 807 | } | 768 | } |
| 808 | 769 | ||
| 809 | void RasterizerOpenGL::FlushCommands() { | 770 | void RasterizerOpenGL::FlushCommands() { |
| 771 | // Only flush when we have commands queued to OpenGL. | ||
| 772 | if (num_queued_commands == 0) { | ||
| 773 | return; | ||
| 774 | } | ||
| 775 | num_queued_commands = 0; | ||
| 810 | glFlush(); | 776 | glFlush(); |
| 811 | } | 777 | } |
| 812 | 778 | ||
| 813 | void RasterizerOpenGL::TickFrame() { | 779 | void RasterizerOpenGL::TickFrame() { |
| 780 | // Ticking a frame means that buffers will be swapped, calling glFlush implicitly. | ||
| 781 | num_queued_commands = 0; | ||
| 782 | |||
| 814 | buffer_cache.TickFrame(); | 783 | buffer_cache.TickFrame(); |
| 815 | } | 784 | } |
| 816 | 785 | ||
| @@ -942,8 +911,15 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader& | |||
| 942 | u32 binding = device.GetBaseBindings(stage_index).sampler; | 911 | u32 binding = device.GetBaseBindings(stage_index).sampler; |
| 943 | for (const auto& entry : shader->GetShaderEntries().samplers) { | 912 | for (const auto& entry : shader->GetShaderEntries().samplers) { |
| 944 | const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); | 913 | const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); |
| 945 | const auto texture = GetTextureInfo(maxwell3d, entry, shader_type); | 914 | if (!entry.IsIndexed()) { |
| 946 | SetupTexture(binding++, texture, entry); | 915 | const auto texture = GetTextureInfo(maxwell3d, entry, shader_type); |
| 916 | SetupTexture(binding++, texture, entry); | ||
| 917 | } else { | ||
| 918 | for (std::size_t i = 0; i < entry.Size(); ++i) { | ||
| 919 | const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i); | ||
| 920 | SetupTexture(binding++, texture, entry); | ||
| 921 | } | ||
| 922 | } | ||
| 947 | } | 923 | } |
| 948 | } | 924 | } |
| 949 | 925 | ||
| @@ -952,8 +928,17 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) { | |||
| 952 | const auto& compute = system.GPU().KeplerCompute(); | 928 | const auto& compute = system.GPU().KeplerCompute(); |
| 953 | u32 binding = 0; | 929 | u32 binding = 0; |
| 954 | for (const auto& entry : kernel->GetShaderEntries().samplers) { | 930 | for (const auto& entry : kernel->GetShaderEntries().samplers) { |
| 955 | const auto texture = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute); | 931 | if (!entry.IsIndexed()) { |
| 956 | SetupTexture(binding++, texture, entry); | 932 | const auto texture = |
| 933 | GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute); | ||
| 934 | SetupTexture(binding++, texture, entry); | ||
| 935 | } else { | ||
| 936 | for (std::size_t i = 0; i < entry.Size(); ++i) { | ||
| 937 | const auto texture = | ||
| 938 | GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute, i); | ||
| 939 | SetupTexture(binding++, texture, entry); | ||
| 940 | } | ||
| 941 | } | ||
| 957 | } | 942 | } |
| 958 | } | 943 | } |
| 959 | 944 | ||
| @@ -1273,6 +1258,7 @@ void RasterizerOpenGL::SyncPointState() { | |||
| 1273 | // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid | 1258 | // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid |
| 1274 | // in OpenGL). | 1259 | // in OpenGL). |
| 1275 | state.point.program_control = regs.vp_point_size.enable != 0; | 1260 | state.point.program_control = regs.vp_point_size.enable != 0; |
| 1261 | state.point.sprite = regs.point_sprite_enable != 0; | ||
| 1276 | state.point.size = std::max(1.0f, regs.point_size); | 1262 | state.point.size = std::max(1.0f, regs.point_size); |
| 1277 | } | 1263 | } |
| 1278 | 1264 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 6a27cf497..68abe9a21 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 24 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 25 | #include "video_core/renderer_opengl/gl_device.h" | 25 | #include "video_core/renderer_opengl/gl_device.h" |
| 26 | #include "video_core/renderer_opengl/gl_framebuffer_cache.h" | 26 | #include "video_core/renderer_opengl/gl_framebuffer_cache.h" |
| 27 | #include "video_core/renderer_opengl/gl_query_cache.h" | ||
| 27 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 28 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 28 | #include "video_core/renderer_opengl/gl_sampler_cache.h" | 29 | #include "video_core/renderer_opengl/gl_sampler_cache.h" |
| 29 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 30 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| @@ -57,10 +58,11 @@ public: | |||
| 57 | ScreenInfo& info); | 58 | ScreenInfo& info); |
| 58 | ~RasterizerOpenGL() override; | 59 | ~RasterizerOpenGL() override; |
| 59 | 60 | ||
| 60 | bool DrawBatch(bool is_indexed) override; | 61 | void Draw(bool is_indexed, bool is_instanced) override; |
| 61 | bool DrawMultiBatch(bool is_indexed) override; | ||
| 62 | void Clear() override; | 62 | void Clear() override; |
| 63 | void DispatchCompute(GPUVAddr code_addr) override; | 63 | void DispatchCompute(GPUVAddr code_addr) override; |
| 64 | void ResetCounter(VideoCore::QueryType type) override; | ||
| 65 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; | ||
| 64 | void FlushAll() override; | 66 | void FlushAll() override; |
| 65 | void FlushRegion(CacheAddr addr, u64 size) override; | 67 | void FlushRegion(CacheAddr addr, u64 size) override; |
| 66 | void InvalidateRegion(CacheAddr addr, u64 size) override; | 68 | void InvalidateRegion(CacheAddr addr, u64 size) override; |
| @@ -75,6 +77,11 @@ public: | |||
| 75 | void LoadDiskResources(const std::atomic_bool& stop_loading, | 77 | void LoadDiskResources(const std::atomic_bool& stop_loading, |
| 76 | const VideoCore::DiskResourceLoadCallback& callback) override; | 78 | const VideoCore::DiskResourceLoadCallback& callback) override; |
| 77 | 79 | ||
| 80 | /// Returns true when there are commands queued to the OpenGL server. | ||
| 81 | bool AnyCommandQueued() const { | ||
| 82 | return num_queued_commands > 0; | ||
| 83 | } | ||
| 84 | |||
| 78 | private: | 85 | private: |
| 79 | /// Configures the color and depth framebuffer states. | 86 | /// Configures the color and depth framebuffer states. |
| 80 | void ConfigureFramebuffers(); | 87 | void ConfigureFramebuffers(); |
| @@ -102,9 +109,6 @@ private: | |||
| 102 | void SetupGlobalMemory(u32 binding, const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr, | 109 | void SetupGlobalMemory(u32 binding, const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr, |
| 103 | std::size_t size); | 110 | std::size_t size); |
| 104 | 111 | ||
| 105 | /// Syncs all the state, shaders, render targets and textures setting before a draw call. | ||
| 106 | void DrawPrelude(); | ||
| 107 | |||
| 108 | /// Configures the current textures to use for the draw command. | 112 | /// Configures the current textures to use for the draw command. |
| 109 | void SetupDrawTextures(std::size_t stage_index, const Shader& shader); | 113 | void SetupDrawTextures(std::size_t stage_index, const Shader& shader); |
| 110 | 114 | ||
| @@ -180,10 +184,23 @@ private: | |||
| 180 | /// Syncs the alpha test state to match the guest state | 184 | /// Syncs the alpha test state to match the guest state |
| 181 | void SyncAlphaTest(); | 185 | void SyncAlphaTest(); |
| 182 | 186 | ||
| 183 | /// Check for extension that are not strictly required | 187 | /// Check for extension that are not strictly required but are needed for correct emulation |
| 184 | /// but are needed for correct emulation | ||
| 185 | void CheckExtensions(); | 188 | void CheckExtensions(); |
| 186 | 189 | ||
| 190 | std::size_t CalculateVertexArraysSize() const; | ||
| 191 | |||
| 192 | std::size_t CalculateIndexBufferSize() const; | ||
| 193 | |||
| 194 | /// Updates and returns a vertex array object representing current vertex format | ||
| 195 | GLuint SetupVertexFormat(); | ||
| 196 | |||
| 197 | void SetupVertexBuffer(GLuint vao); | ||
| 198 | void SetupVertexInstances(GLuint vao); | ||
| 199 | |||
| 200 | GLintptr SetupIndexBuffer(); | ||
| 201 | |||
| 202 | void SetupShaders(GLenum primitive_mode); | ||
| 203 | |||
| 187 | const Device device; | 204 | const Device device; |
| 188 | OpenGLState state; | 205 | OpenGLState state; |
| 189 | 206 | ||
| @@ -191,6 +208,7 @@ private: | |||
| 191 | ShaderCacheOpenGL shader_cache; | 208 | ShaderCacheOpenGL shader_cache; |
| 192 | SamplerCacheOpenGL sampler_cache; | 209 | SamplerCacheOpenGL sampler_cache; |
| 193 | FramebufferCacheOpenGL framebuffer_cache; | 210 | FramebufferCacheOpenGL framebuffer_cache; |
| 211 | QueryCache query_cache; | ||
| 194 | 212 | ||
| 195 | Core::System& system; | 213 | Core::System& system; |
| 196 | ScreenInfo& screen_info; | 214 | ScreenInfo& screen_info; |
| @@ -208,24 +226,8 @@ private: | |||
| 208 | BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; | 226 | BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; |
| 209 | BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; | 227 | BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; |
| 210 | 228 | ||
| 211 | std::size_t CalculateVertexArraysSize() const; | 229 | /// Number of commands queued to the OpenGL driver. Reseted on flush. |
| 212 | 230 | std::size_t num_queued_commands = 0; | |
| 213 | std::size_t CalculateIndexBufferSize() const; | ||
| 214 | |||
| 215 | /// Updates and returns a vertex array object representing current vertex format | ||
| 216 | GLuint SetupVertexFormat(); | ||
| 217 | |||
| 218 | void SetupVertexBuffer(GLuint vao); | ||
| 219 | void SetupVertexInstances(GLuint vao); | ||
| 220 | |||
| 221 | GLintptr SetupIndexBuffer(); | ||
| 222 | |||
| 223 | GLintptr index_buffer_offset; | ||
| 224 | |||
| 225 | void SetupShaders(GLenum primitive_mode); | ||
| 226 | |||
| 227 | enum class AccelDraw { Disabled, Arrays, Indexed }; | ||
| 228 | AccelDraw accelerate_draw = AccelDraw::Disabled; | ||
| 229 | }; | 231 | }; |
| 230 | 232 | ||
| 231 | } // namespace OpenGL | 233 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp index 5c96c1d46..f0ddfb276 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp | |||
| @@ -207,4 +207,21 @@ void OGLFramebuffer::Release() { | |||
| 207 | handle = 0; | 207 | handle = 0; |
| 208 | } | 208 | } |
| 209 | 209 | ||
| 210 | void OGLQuery::Create(GLenum target) { | ||
| 211 | if (handle != 0) | ||
| 212 | return; | ||
| 213 | |||
| 214 | MICROPROFILE_SCOPE(OpenGL_ResourceCreation); | ||
| 215 | glCreateQueries(target, 1, &handle); | ||
| 216 | } | ||
| 217 | |||
| 218 | void OGLQuery::Release() { | ||
| 219 | if (handle == 0) | ||
| 220 | return; | ||
| 221 | |||
| 222 | MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); | ||
| 223 | glDeleteQueries(1, &handle); | ||
| 224 | handle = 0; | ||
| 225 | } | ||
| 226 | |||
| 210 | } // namespace OpenGL | 227 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 3a85a1d4c..514d1d165 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h | |||
| @@ -266,4 +266,29 @@ public: | |||
| 266 | GLuint handle = 0; | 266 | GLuint handle = 0; |
| 267 | }; | 267 | }; |
| 268 | 268 | ||
| 269 | class OGLQuery : private NonCopyable { | ||
| 270 | public: | ||
| 271 | OGLQuery() = default; | ||
| 272 | |||
| 273 | OGLQuery(OGLQuery&& o) noexcept : handle(std::exchange(o.handle, 0)) {} | ||
| 274 | |||
| 275 | ~OGLQuery() { | ||
| 276 | Release(); | ||
| 277 | } | ||
| 278 | |||
| 279 | OGLQuery& operator=(OGLQuery&& o) noexcept { | ||
| 280 | Release(); | ||
| 281 | handle = std::exchange(o.handle, 0); | ||
| 282 | return *this; | ||
| 283 | } | ||
| 284 | |||
| 285 | /// Creates a new internal OpenGL resource and stores the handle | ||
| 286 | void Create(GLenum target); | ||
| 287 | |||
| 288 | /// Deletes the internal OpenGL resource | ||
| 289 | void Release(); | ||
| 290 | |||
| 291 | GLuint handle = 0; | ||
| 292 | }; | ||
| 293 | |||
| 269 | } // namespace OpenGL | 294 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 3c5bdd377..489eb143c 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -214,6 +214,7 @@ std::unique_ptr<ConstBufferLocker> MakeLocker(Core::System& system, ShaderType s | |||
| 214 | } | 214 | } |
| 215 | 215 | ||
| 216 | void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) { | 216 | void FillLocker(ConstBufferLocker& locker, const ShaderDiskCacheUsage& usage) { |
| 217 | locker.SetBoundBuffer(usage.bound_buffer); | ||
| 217 | for (const auto& key : usage.keys) { | 218 | for (const auto& key : usage.keys) { |
| 218 | const auto [buffer, offset] = key.first; | 219 | const auto [buffer, offset] = key.first; |
| 219 | locker.InsertKey(buffer, offset, key.second); | 220 | locker.InsertKey(buffer, offset, key.second); |
| @@ -418,7 +419,8 @@ bool CachedShader::EnsureValidLockerVariant() { | |||
| 418 | 419 | ||
| 419 | ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant, | 420 | ShaderDiskCacheUsage CachedShader::GetUsage(const ProgramVariant& variant, |
| 420 | const ConstBufferLocker& locker) const { | 421 | const ConstBufferLocker& locker) const { |
| 421 | return ShaderDiskCacheUsage{unique_identifier, variant, locker.GetKeys(), | 422 | return ShaderDiskCacheUsage{unique_identifier, variant, |
| 423 | locker.GetBoundBuffer(), locker.GetKeys(), | ||
| 422 | locker.GetBoundSamplers(), locker.GetBindlessSamplers()}; | 424 | locker.GetBoundSamplers(), locker.GetBindlessSamplers()}; |
| 423 | } | 425 | } |
| 424 | 426 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 2996aaf08..4735000b5 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -391,6 +391,7 @@ public: | |||
| 391 | DeclareVertex(); | 391 | DeclareVertex(); |
| 392 | DeclareGeometry(); | 392 | DeclareGeometry(); |
| 393 | DeclareRegisters(); | 393 | DeclareRegisters(); |
| 394 | DeclareCustomVariables(); | ||
| 394 | DeclarePredicates(); | 395 | DeclarePredicates(); |
| 395 | DeclareLocalMemory(); | 396 | DeclareLocalMemory(); |
| 396 | DeclareInternalFlags(); | 397 | DeclareInternalFlags(); |
| @@ -503,6 +504,16 @@ private: | |||
| 503 | } | 504 | } |
| 504 | } | 505 | } |
| 505 | 506 | ||
| 507 | void DeclareCustomVariables() { | ||
| 508 | const u32 num_custom_variables = ir.GetNumCustomVariables(); | ||
| 509 | for (u32 i = 0; i < num_custom_variables; ++i) { | ||
| 510 | code.AddLine("float {} = 0.0f;", GetCustomVariable(i)); | ||
| 511 | } | ||
| 512 | if (num_custom_variables > 0) { | ||
| 513 | code.AddNewLine(); | ||
| 514 | } | ||
| 515 | } | ||
| 516 | |||
| 506 | void DeclarePredicates() { | 517 | void DeclarePredicates() { |
| 507 | const auto& predicates = ir.GetPredicates(); | 518 | const auto& predicates = ir.GetPredicates(); |
| 508 | for (const auto pred : predicates) { | 519 | for (const auto pred : predicates) { |
| @@ -655,7 +666,8 @@ private: | |||
| 655 | u32 binding = device.GetBaseBindings(stage).sampler; | 666 | u32 binding = device.GetBaseBindings(stage).sampler; |
| 656 | for (const auto& sampler : ir.GetSamplers()) { | 667 | for (const auto& sampler : ir.GetSamplers()) { |
| 657 | const std::string name = GetSampler(sampler); | 668 | const std::string name = GetSampler(sampler); |
| 658 | const std::string description = fmt::format("layout (binding = {}) uniform", binding++); | 669 | const std::string description = fmt::format("layout (binding = {}) uniform", binding); |
| 670 | binding += sampler.IsIndexed() ? sampler.Size() : 1; | ||
| 659 | 671 | ||
| 660 | std::string sampler_type = [&]() { | 672 | std::string sampler_type = [&]() { |
| 661 | if (sampler.IsBuffer()) { | 673 | if (sampler.IsBuffer()) { |
| @@ -682,7 +694,11 @@ private: | |||
| 682 | sampler_type += "Shadow"; | 694 | sampler_type += "Shadow"; |
| 683 | } | 695 | } |
| 684 | 696 | ||
| 685 | code.AddLine("{} {} {};", description, sampler_type, name); | 697 | if (!sampler.IsIndexed()) { |
| 698 | code.AddLine("{} {} {};", description, sampler_type, name); | ||
| 699 | } else { | ||
| 700 | code.AddLine("{} {} {}[{}];", description, sampler_type, name, sampler.Size()); | ||
| 701 | } | ||
| 686 | } | 702 | } |
| 687 | if (!ir.GetSamplers().empty()) { | 703 | if (!ir.GetSamplers().empty()) { |
| 688 | code.AddNewLine(); | 704 | code.AddNewLine(); |
| @@ -775,6 +791,11 @@ private: | |||
| 775 | return {GetRegister(index), Type::Float}; | 791 | return {GetRegister(index), Type::Float}; |
| 776 | } | 792 | } |
| 777 | 793 | ||
| 794 | if (const auto cv = std::get_if<CustomVarNode>(&*node)) { | ||
| 795 | const u32 index = cv->GetIndex(); | ||
| 796 | return {GetCustomVariable(index), Type::Float}; | ||
| 797 | } | ||
| 798 | |||
| 778 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { | 799 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { |
| 779 | const u32 value = immediate->GetValue(); | 800 | const u32 value = immediate->GetValue(); |
| 780 | if (value < 10) { | 801 | if (value < 10) { |
| @@ -1019,7 +1040,6 @@ private: | |||
| 1019 | } | 1040 | } |
| 1020 | return {{"gl_ViewportIndex", Type::Int}}; | 1041 | return {{"gl_ViewportIndex", Type::Int}}; |
| 1021 | case 3: | 1042 | case 3: |
| 1022 | UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader"); | ||
| 1023 | return {{"gl_PointSize", Type::Float}}; | 1043 | return {{"gl_PointSize", Type::Float}}; |
| 1024 | } | 1044 | } |
| 1025 | return {}; | 1045 | return {}; |
| @@ -1099,7 +1119,11 @@ private: | |||
| 1099 | } else if (!meta->ptp.empty()) { | 1119 | } else if (!meta->ptp.empty()) { |
| 1100 | expr += "Offsets"; | 1120 | expr += "Offsets"; |
| 1101 | } | 1121 | } |
| 1102 | expr += '(' + GetSampler(meta->sampler) + ", "; | 1122 | if (!meta->sampler.IsIndexed()) { |
| 1123 | expr += '(' + GetSampler(meta->sampler) + ", "; | ||
| 1124 | } else { | ||
| 1125 | expr += '(' + GetSampler(meta->sampler) + '[' + Visit(meta->index).AsUint() + "], "; | ||
| 1126 | } | ||
| 1103 | expr += coord_constructors.at(count + (has_array ? 1 : 0) + | 1127 | expr += coord_constructors.at(count + (has_array ? 1 : 0) + |
| 1104 | (has_shadow && !separate_dc ? 1 : 0) - 1); | 1128 | (has_shadow && !separate_dc ? 1 : 0) - 1); |
| 1105 | expr += '('; | 1129 | expr += '('; |
| @@ -1311,6 +1335,8 @@ private: | |||
| 1311 | const std::string final_offset = fmt::format("({} - {}) >> 2", real, base); | 1335 | const std::string final_offset = fmt::format("({} - {}) >> 2", real, base); |
| 1312 | target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset), | 1336 | target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset), |
| 1313 | Type::Uint}; | 1337 | Type::Uint}; |
| 1338 | } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) { | ||
| 1339 | target = {GetCustomVariable(cv->GetIndex()), Type::Float}; | ||
| 1314 | } else { | 1340 | } else { |
| 1315 | UNREACHABLE_MSG("Assign called without a proper target"); | 1341 | UNREACHABLE_MSG("Assign called without a proper target"); |
| 1316 | } | 1342 | } |
| @@ -1858,10 +1884,7 @@ private: | |||
| 1858 | 1884 | ||
| 1859 | template <const std::string_view& opname, Type type> | 1885 | template <const std::string_view& opname, Type type> |
| 1860 | Expression Atomic(Operation operation) { | 1886 | Expression Atomic(Operation operation) { |
| 1861 | ASSERT(stage == ShaderType::Compute); | 1887 | return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(), |
| 1862 | auto& smem = std::get<SmemNode>(*operation[0]); | ||
| 1863 | |||
| 1864 | return {fmt::format("atomic{}(smem[{} >> 2], {})", opname, Visit(smem.GetAddress()).AsInt(), | ||
| 1865 | Visit(operation[1]).As(type)), | 1888 | Visit(operation[1]).As(type)), |
| 1866 | type}; | 1889 | type}; |
| 1867 | } | 1890 | } |
| @@ -2241,6 +2264,10 @@ private: | |||
| 2241 | return GetDeclarationWithSuffix(index, "gpr"); | 2264 | return GetDeclarationWithSuffix(index, "gpr"); |
| 2242 | } | 2265 | } |
| 2243 | 2266 | ||
| 2267 | std::string GetCustomVariable(u32 index) const { | ||
| 2268 | return GetDeclarationWithSuffix(index, "custom_var"); | ||
| 2269 | } | ||
| 2270 | |||
| 2244 | std::string GetPredicate(Tegra::Shader::Pred pred) const { | 2271 | std::string GetPredicate(Tegra::Shader::Pred pred) const { |
| 2245 | return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); | 2272 | return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); |
| 2246 | } | 2273 | } |
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index cf874a09a..1fc204f6f 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | |||
| @@ -53,7 +53,7 @@ struct BindlessSamplerKey { | |||
| 53 | Tegra::Engines::SamplerDescriptor sampler{}; | 53 | Tegra::Engines::SamplerDescriptor sampler{}; |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | constexpr u32 NativeVersion = 11; | 56 | constexpr u32 NativeVersion = 12; |
| 57 | 57 | ||
| 58 | // Making sure sizes doesn't change by accident | 58 | // Making sure sizes doesn't change by accident |
| 59 | static_assert(sizeof(ProgramVariant) == 20); | 59 | static_assert(sizeof(ProgramVariant) == 20); |
| @@ -186,7 +186,8 @@ ShaderDiskCacheOpenGL::LoadTransferable() { | |||
| 186 | u32 num_bound_samplers{}; | 186 | u32 num_bound_samplers{}; |
| 187 | u32 num_bindless_samplers{}; | 187 | u32 num_bindless_samplers{}; |
| 188 | if (file.ReadArray(&usage.unique_identifier, 1) != 1 || | 188 | if (file.ReadArray(&usage.unique_identifier, 1) != 1 || |
| 189 | file.ReadArray(&usage.variant, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 || | 189 | file.ReadArray(&usage.variant, 1) != 1 || |
| 190 | file.ReadArray(&usage.bound_buffer, 1) != 1 || file.ReadArray(&num_keys, 1) != 1 || | ||
| 190 | file.ReadArray(&num_bound_samplers, 1) != 1 || | 191 | file.ReadArray(&num_bound_samplers, 1) != 1 || |
| 191 | file.ReadArray(&num_bindless_samplers, 1) != 1) { | 192 | file.ReadArray(&num_bindless_samplers, 1) != 1) { |
| 192 | LOG_ERROR(Render_OpenGL, error_loading); | 193 | LOG_ERROR(Render_OpenGL, error_loading); |
| @@ -281,7 +282,9 @@ ShaderDiskCacheOpenGL::LoadPrecompiledFile(FileUtil::IOFile& file) { | |||
| 281 | u32 num_bindless_samplers{}; | 282 | u32 num_bindless_samplers{}; |
| 282 | ShaderDiskCacheUsage usage; | 283 | ShaderDiskCacheUsage usage; |
| 283 | if (!LoadObjectFromPrecompiled(usage.unique_identifier) || | 284 | if (!LoadObjectFromPrecompiled(usage.unique_identifier) || |
| 284 | !LoadObjectFromPrecompiled(usage.variant) || !LoadObjectFromPrecompiled(num_keys) || | 285 | !LoadObjectFromPrecompiled(usage.variant) || |
| 286 | !LoadObjectFromPrecompiled(usage.bound_buffer) || | ||
| 287 | !LoadObjectFromPrecompiled(num_keys) || | ||
| 285 | !LoadObjectFromPrecompiled(num_bound_samplers) || | 288 | !LoadObjectFromPrecompiled(num_bound_samplers) || |
| 286 | !LoadObjectFromPrecompiled(num_bindless_samplers)) { | 289 | !LoadObjectFromPrecompiled(num_bindless_samplers)) { |
| 287 | return {}; | 290 | return {}; |
| @@ -393,6 +396,7 @@ void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) { | |||
| 393 | 396 | ||
| 394 | if (file.WriteObject(TransferableEntryKind::Usage) != 1 || | 397 | if (file.WriteObject(TransferableEntryKind::Usage) != 1 || |
| 395 | file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 || | 398 | file.WriteObject(usage.unique_identifier) != 1 || file.WriteObject(usage.variant) != 1 || |
| 399 | file.WriteObject(usage.bound_buffer) != 1 || | ||
| 396 | file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 || | 400 | file.WriteObject(static_cast<u32>(usage.keys.size())) != 1 || |
| 397 | file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 || | 401 | file.WriteObject(static_cast<u32>(usage.bound_samplers.size())) != 1 || |
| 398 | file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) { | 402 | file.WriteObject(static_cast<u32>(usage.bindless_samplers.size())) != 1) { |
| @@ -447,7 +451,7 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p | |||
| 447 | }; | 451 | }; |
| 448 | 452 | ||
| 449 | if (!SaveObjectToPrecompiled(usage.unique_identifier) || | 453 | if (!SaveObjectToPrecompiled(usage.unique_identifier) || |
| 450 | !SaveObjectToPrecompiled(usage.variant) || | 454 | !SaveObjectToPrecompiled(usage.variant) || !SaveObjectToPrecompiled(usage.bound_buffer) || |
| 451 | !SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) || | 455 | !SaveObjectToPrecompiled(static_cast<u32>(usage.keys.size())) || |
| 452 | !SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) || | 456 | !SaveObjectToPrecompiled(static_cast<u32>(usage.bound_samplers.size())) || |
| 453 | !SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) { | 457 | !SaveObjectToPrecompiled(static_cast<u32>(usage.bindless_samplers.size()))) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h index 69a2fbdda..ef2371f6d 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h | |||
| @@ -79,6 +79,7 @@ static_assert(std::is_trivially_copyable_v<ProgramVariant>); | |||
| 79 | struct ShaderDiskCacheUsage { | 79 | struct ShaderDiskCacheUsage { |
| 80 | u64 unique_identifier{}; | 80 | u64 unique_identifier{}; |
| 81 | ProgramVariant variant; | 81 | ProgramVariant variant; |
| 82 | u32 bound_buffer{}; | ||
| 82 | VideoCommon::Shader::KeyMap keys; | 83 | VideoCommon::Shader::KeyMap keys; |
| 83 | VideoCommon::Shader::BoundSamplerMap bound_samplers; | 84 | VideoCommon::Shader::BoundSamplerMap bound_samplers; |
| 84 | VideoCommon::Shader::BindlessSamplerMap bindless_samplers; | 85 | VideoCommon::Shader::BindlessSamplerMap bindless_samplers; |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index cc185e9e1..ab1f7983c 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -128,6 +128,7 @@ void OpenGLState::ApplyClipDistances() { | |||
| 128 | 128 | ||
| 129 | void OpenGLState::ApplyPointSize() { | 129 | void OpenGLState::ApplyPointSize() { |
| 130 | Enable(GL_PROGRAM_POINT_SIZE, cur_state.point.program_control, point.program_control); | 130 | Enable(GL_PROGRAM_POINT_SIZE, cur_state.point.program_control, point.program_control); |
| 131 | Enable(GL_POINT_SPRITE, cur_state.point.sprite, point.sprite); | ||
| 131 | if (UpdateValue(cur_state.point.size, point.size)) { | 132 | if (UpdateValue(cur_state.point.size, point.size)) { |
| 132 | glPointSize(point.size); | 133 | glPointSize(point.size); |
| 133 | } | 134 | } |
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 678e5cd89..4953eeda2 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -132,6 +132,7 @@ public: | |||
| 132 | 132 | ||
| 133 | struct { | 133 | struct { |
| 134 | bool program_control = false; // GL_PROGRAM_POINT_SIZE | 134 | bool program_control = false; // GL_PROGRAM_POINT_SIZE |
| 135 | bool sprite = false; // GL_POINT_SPRITE | ||
| 135 | GLfloat size = 1.0f; // GL_POINT_SIZE | 136 | GLfloat size = 1.0f; // GL_POINT_SIZE |
| 136 | } point; | 137 | } point; |
| 137 | 138 | ||
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index e95eb069e..d4b81cd87 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -176,6 +176,19 @@ GLint GetSwizzleSource(SwizzleSource source) { | |||
| 176 | return GL_NONE; | 176 | return GL_NONE; |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | GLenum GetComponent(PixelFormat format, bool is_first) { | ||
| 180 | switch (format) { | ||
| 181 | case PixelFormat::Z24S8: | ||
| 182 | case PixelFormat::Z32FS8: | ||
| 183 | return is_first ? GL_DEPTH_COMPONENT : GL_STENCIL_INDEX; | ||
| 184 | case PixelFormat::S8Z24: | ||
| 185 | return is_first ? GL_STENCIL_INDEX : GL_DEPTH_COMPONENT; | ||
| 186 | default: | ||
| 187 | UNREACHABLE(); | ||
| 188 | return GL_DEPTH_COMPONENT; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 179 | void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { | 192 | void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { |
| 180 | if (params.IsBuffer()) { | 193 | if (params.IsBuffer()) { |
| 181 | return; | 194 | return; |
| @@ -184,7 +197,7 @@ void ApplyTextureDefaults(const SurfaceParams& params, GLuint texture) { | |||
| 184 | glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | 197 | glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 185 | glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 198 | glTextureParameteri(texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 186 | glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 199 | glTextureParameteri(texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 187 | glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, params.num_levels - 1); | 200 | glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, static_cast<GLint>(params.num_levels - 1)); |
| 188 | if (params.num_levels == 1) { | 201 | if (params.num_levels == 1) { |
| 189 | glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f); | 202 | glTextureParameterf(texture, GL_TEXTURE_LOD_BIAS, 1000.0f); |
| 190 | } | 203 | } |
| @@ -416,11 +429,21 @@ void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_sou | |||
| 416 | if (new_swizzle == swizzle) | 429 | if (new_swizzle == swizzle) |
| 417 | return; | 430 | return; |
| 418 | swizzle = new_swizzle; | 431 | swizzle = new_swizzle; |
| 419 | const std::array<GLint, 4> gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), | 432 | const std::array gl_swizzle = {GetSwizzleSource(x_source), GetSwizzleSource(y_source), |
| 420 | GetSwizzleSource(z_source), | 433 | GetSwizzleSource(z_source), GetSwizzleSource(w_source)}; |
| 421 | GetSwizzleSource(w_source)}; | ||
| 422 | const GLuint handle = GetTexture(); | 434 | const GLuint handle = GetTexture(); |
| 423 | glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data()); | 435 | const PixelFormat format = surface.GetSurfaceParams().pixel_format; |
| 436 | switch (format) { | ||
| 437 | case PixelFormat::Z24S8: | ||
| 438 | case PixelFormat::Z32FS8: | ||
| 439 | case PixelFormat::S8Z24: | ||
| 440 | glTextureParameteri(handle, GL_DEPTH_STENCIL_TEXTURE_MODE, | ||
| 441 | GetComponent(format, x_source == SwizzleSource::R)); | ||
| 442 | break; | ||
| 443 | default: | ||
| 444 | glTextureParameteriv(handle, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle.data()); | ||
| 445 | break; | ||
| 446 | } | ||
| 424 | } | 447 | } |
| 425 | 448 | ||
| 426 | OGLTextureView CachedSurfaceView::CreateTextureView() const { | 449 | OGLTextureView CachedSurfaceView::CreateTextureView() const { |
| @@ -529,8 +552,11 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view, | |||
| 529 | const Common::Rectangle<u32>& dst_rect = copy_config.dst_rect; | 552 | const Common::Rectangle<u32>& dst_rect = copy_config.dst_rect; |
| 530 | const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear; | 553 | const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear; |
| 531 | 554 | ||
| 532 | glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, | 555 | glBlitFramebuffer(static_cast<GLint>(src_rect.left), static_cast<GLint>(src_rect.top), |
| 533 | dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, | 556 | static_cast<GLint>(src_rect.right), static_cast<GLint>(src_rect.bottom), |
| 557 | static_cast<GLint>(dst_rect.left), static_cast<GLint>(dst_rect.top), | ||
| 558 | static_cast<GLint>(dst_rect.right), static_cast<GLint>(dst_rect.bottom), | ||
| 559 | buffers, | ||
| 534 | is_linear && (buffers == GL_COLOR_BUFFER_BIT) ? GL_LINEAR : GL_NEAREST); | 560 | is_linear && (buffers == GL_COLOR_BUFFER_BIT) ? GL_LINEAR : GL_NEAREST); |
| 535 | } | 561 | } |
| 536 | 562 | ||
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h index ea4f35663..7ed505628 100644 --- a/src/video_core/renderer_opengl/maxwell_to_gl.h +++ b/src/video_core/renderer_opengl/maxwell_to_gl.h | |||
| @@ -47,8 +47,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 47 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 47 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 48 | return GL_UNSIGNED_INT_2_10_10_10_REV; | 48 | return GL_UNSIGNED_INT_2_10_10_10_REV; |
| 49 | default: | 49 | default: |
| 50 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | 50 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); |
| 51 | UNREACHABLE(); | ||
| 52 | return {}; | 51 | return {}; |
| 53 | } | 52 | } |
| 54 | case Maxwell::VertexAttribute::Type::SignedInt: | 53 | case Maxwell::VertexAttribute::Type::SignedInt: |
| @@ -72,8 +71,7 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 72 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: | 71 | case Maxwell::VertexAttribute::Size::Size_10_10_10_2: |
| 73 | return GL_INT_2_10_10_10_REV; | 72 | return GL_INT_2_10_10_10_REV; |
| 74 | default: | 73 | default: |
| 75 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | 74 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); |
| 76 | UNREACHABLE(); | ||
| 77 | return {}; | 75 | return {}; |
| 78 | } | 76 | } |
| 79 | case Maxwell::VertexAttribute::Type::Float: | 77 | case Maxwell::VertexAttribute::Type::Float: |
| @@ -89,13 +87,19 @@ inline GLenum VertexType(Maxwell::VertexAttribute attrib) { | |||
| 89 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | 87 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: |
| 90 | return GL_FLOAT; | 88 | return GL_FLOAT; |
| 91 | default: | 89 | default: |
| 92 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | 90 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); |
| 93 | UNREACHABLE(); | 91 | return {}; |
| 92 | } | ||
| 93 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | ||
| 94 | switch (attrib.size) { | ||
| 95 | case Maxwell::VertexAttribute::Size::Size_8_8: | ||
| 96 | return GL_UNSIGNED_BYTE; | ||
| 97 | default: | ||
| 98 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex size={}", attrib.SizeString()); | ||
| 94 | return {}; | 99 | return {}; |
| 95 | } | 100 | } |
| 96 | default: | 101 | default: |
| 97 | LOG_CRITICAL(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); | 102 | LOG_ERROR(Render_OpenGL, "Unimplemented vertex type={}", attrib.TypeString()); |
| 98 | UNREACHABLE(); | ||
| 99 | return {}; | 103 | return {}; |
| 100 | } | 104 | } |
| 101 | } | 105 | } |
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index 331808113..5403c3ab7 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp | |||
| @@ -164,7 +164,7 @@ struct FormatTuple { | |||
| 164 | {vk::Format::eUndefined, {}}, // ASTC_2D_5X4 | 164 | {vk::Format::eUndefined, {}}, // ASTC_2D_5X4 |
| 165 | {vk::Format::eUndefined, {}}, // BGRA8_SRGB | 165 | {vk::Format::eUndefined, {}}, // BGRA8_SRGB |
| 166 | {vk::Format::eBc1RgbaSrgbBlock, {}}, // DXT1_SRGB | 166 | {vk::Format::eBc1RgbaSrgbBlock, {}}, // DXT1_SRGB |
| 167 | {vk::Format::eUndefined, {}}, // DXT23_SRGB | 167 | {vk::Format::eBc2SrgbBlock, {}}, // DXT23_SRGB |
| 168 | {vk::Format::eBc3SrgbBlock, {}}, // DXT45_SRGB | 168 | {vk::Format::eBc3SrgbBlock, {}}, // DXT45_SRGB |
| 169 | {vk::Format::eBc7SrgbBlock, {}}, // BC7U_SRGB | 169 | {vk::Format::eBc7SrgbBlock, {}}, // BC7U_SRGB |
| 170 | {vk::Format::eR4G4B4A4UnormPack16, Attachable}, // R4G4B4A4U | 170 | {vk::Format::eR4G4B4A4UnormPack16, Attachable}, // R4G4B4A4U |
| @@ -363,6 +363,8 @@ vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttr | |||
| 363 | return vk::Format::eR8G8B8A8Uint; | 363 | return vk::Format::eR8G8B8A8Uint; |
| 364 | case Maxwell::VertexAttribute::Size::Size_32: | 364 | case Maxwell::VertexAttribute::Size::Size_32: |
| 365 | return vk::Format::eR32Uint; | 365 | return vk::Format::eR32Uint; |
| 366 | case Maxwell::VertexAttribute::Size::Size_32_32_32_32: | ||
| 367 | return vk::Format::eR32G32B32A32Uint; | ||
| 366 | default: | 368 | default: |
| 367 | break; | 369 | break; |
| 368 | } | 370 | } |
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp new file mode 100644 index 000000000..d5032b432 --- /dev/null +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp | |||
| @@ -0,0 +1,265 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include <optional> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 11 | #include "common/assert.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 13 | #include "common/telemetry.h" | ||
| 14 | #include "core/core.h" | ||
| 15 | #include "core/core_timing.h" | ||
| 16 | #include "core/frontend/emu_window.h" | ||
| 17 | #include "core/memory.h" | ||
| 18 | #include "core/perf_stats.h" | ||
| 19 | #include "core/settings.h" | ||
| 20 | #include "core/telemetry_session.h" | ||
| 21 | #include "video_core/gpu.h" | ||
| 22 | #include "video_core/renderer_vulkan/declarations.h" | ||
| 23 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||
| 24 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | ||
| 25 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 26 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | ||
| 27 | #include "video_core/renderer_vulkan/vk_rasterizer.h" | ||
| 28 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||
| 29 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 30 | #include "video_core/renderer_vulkan/vk_swapchain.h" | ||
| 31 | |||
| 32 | namespace Vulkan { | ||
| 33 | |||
| 34 | namespace { | ||
| 35 | |||
| 36 | VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_, | ||
| 37 | VkDebugUtilsMessageTypeFlagsEXT type, | ||
| 38 | const VkDebugUtilsMessengerCallbackDataEXT* data, | ||
| 39 | [[maybe_unused]] void* user_data) { | ||
| 40 | const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_}; | ||
| 41 | const char* message{data->pMessage}; | ||
| 42 | |||
| 43 | if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) { | ||
| 44 | LOG_CRITICAL(Render_Vulkan, "{}", message); | ||
| 45 | } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) { | ||
| 46 | LOG_WARNING(Render_Vulkan, "{}", message); | ||
| 47 | } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo) { | ||
| 48 | LOG_INFO(Render_Vulkan, "{}", message); | ||
| 49 | } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose) { | ||
| 50 | LOG_DEBUG(Render_Vulkan, "{}", message); | ||
| 51 | } | ||
| 52 | return VK_FALSE; | ||
| 53 | } | ||
| 54 | |||
| 55 | std::string GetReadableVersion(u32 version) { | ||
| 56 | return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), | ||
| 57 | VK_VERSION_PATCH(version)); | ||
| 58 | } | ||
| 59 | |||
| 60 | std::string GetDriverVersion(const VKDevice& device) { | ||
| 61 | // Extracted from | ||
| 62 | // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314 | ||
| 63 | const u32 version = device.GetDriverVersion(); | ||
| 64 | |||
| 65 | if (device.GetDriverID() == vk::DriverIdKHR::eNvidiaProprietary) { | ||
| 66 | const u32 major = (version >> 22) & 0x3ff; | ||
| 67 | const u32 minor = (version >> 14) & 0x0ff; | ||
| 68 | const u32 secondary = (version >> 6) & 0x0ff; | ||
| 69 | const u32 tertiary = version & 0x003f; | ||
| 70 | return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary); | ||
| 71 | } | ||
| 72 | if (device.GetDriverID() == vk::DriverIdKHR::eIntelProprietaryWindows) { | ||
| 73 | const u32 major = version >> 14; | ||
| 74 | const u32 minor = version & 0x3fff; | ||
| 75 | return fmt::format("{}.{}", major, minor); | ||
| 76 | } | ||
| 77 | |||
| 78 | return GetReadableVersion(version); | ||
| 79 | } | ||
| 80 | |||
| 81 | std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) { | ||
| 82 | std::sort(std::begin(available_extensions), std::end(available_extensions)); | ||
| 83 | |||
| 84 | static constexpr std::size_t AverageExtensionSize = 64; | ||
| 85 | std::string separated_extensions; | ||
| 86 | separated_extensions.reserve(available_extensions.size() * AverageExtensionSize); | ||
| 87 | |||
| 88 | const auto end = std::end(available_extensions); | ||
| 89 | for (auto extension = std::begin(available_extensions); extension != end; ++extension) { | ||
| 90 | if (const bool is_last = extension + 1 == end; is_last) { | ||
| 91 | separated_extensions += *extension; | ||
| 92 | } else { | ||
| 93 | separated_extensions += fmt::format("{},", *extension); | ||
| 94 | } | ||
| 95 | } | ||
| 96 | return separated_extensions; | ||
| 97 | } | ||
| 98 | |||
| 99 | } // Anonymous namespace | ||
| 100 | |||
| 101 | RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system) | ||
| 102 | : RendererBase(window), system{system} {} | ||
| 103 | |||
| 104 | RendererVulkan::~RendererVulkan() { | ||
| 105 | ShutDown(); | ||
| 106 | } | ||
| 107 | |||
| 108 | void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 109 | const auto& layout = render_window.GetFramebufferLayout(); | ||
| 110 | if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) { | ||
| 111 | const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset; | ||
| 112 | const bool use_accelerated = | ||
| 113 | rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); | ||
| 114 | const bool is_srgb = use_accelerated && screen_info.is_srgb; | ||
| 115 | if (swapchain->HasFramebufferChanged(layout) || swapchain->GetSrgbState() != is_srgb) { | ||
| 116 | swapchain->Create(layout.width, layout.height, is_srgb); | ||
| 117 | blit_screen->Recreate(); | ||
| 118 | } | ||
| 119 | |||
| 120 | scheduler->WaitWorker(); | ||
| 121 | |||
| 122 | swapchain->AcquireNextImage(); | ||
| 123 | const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated); | ||
| 124 | |||
| 125 | scheduler->Flush(false, render_semaphore); | ||
| 126 | |||
| 127 | if (swapchain->Present(render_semaphore, fence)) { | ||
| 128 | blit_screen->Recreate(); | ||
| 129 | } | ||
| 130 | |||
| 131 | render_window.SwapBuffers(); | ||
| 132 | rasterizer->TickFrame(); | ||
| 133 | } | ||
| 134 | |||
| 135 | render_window.PollEvents(); | ||
| 136 | } | ||
| 137 | |||
| 138 | bool RendererVulkan::Init() { | ||
| 139 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; | ||
| 140 | render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface); | ||
| 141 | const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr); | ||
| 142 | |||
| 143 | std::optional<vk::DebugUtilsMessengerEXT> callback; | ||
| 144 | if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) { | ||
| 145 | callback = CreateDebugCallback(dldi); | ||
| 146 | if (!callback) { | ||
| 147 | return false; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | if (!PickDevices(dldi)) { | ||
| 152 | if (callback) { | ||
| 153 | instance.destroy(*callback, nullptr, dldi); | ||
| 154 | } | ||
| 155 | return false; | ||
| 156 | } | ||
| 157 | debug_callback = UniqueDebugUtilsMessengerEXT( | ||
| 158 | *callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>( | ||
| 159 | instance, nullptr, device->GetDispatchLoader())); | ||
| 160 | |||
| 161 | Report(); | ||
| 162 | |||
| 163 | memory_manager = std::make_unique<VKMemoryManager>(*device); | ||
| 164 | |||
| 165 | resource_manager = std::make_unique<VKResourceManager>(*device); | ||
| 166 | |||
| 167 | const auto& framebuffer = render_window.GetFramebufferLayout(); | ||
| 168 | swapchain = std::make_unique<VKSwapchain>(surface, *device); | ||
| 169 | swapchain->Create(framebuffer.width, framebuffer.height, false); | ||
| 170 | |||
| 171 | scheduler = std::make_unique<VKScheduler>(*device, *resource_manager); | ||
| 172 | |||
| 173 | rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device, | ||
| 174 | *resource_manager, *memory_manager, *scheduler); | ||
| 175 | |||
| 176 | blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device, | ||
| 177 | *resource_manager, *memory_manager, *swapchain, | ||
| 178 | *scheduler, screen_info); | ||
| 179 | |||
| 180 | return true; | ||
| 181 | } | ||
| 182 | |||
| 183 | void RendererVulkan::ShutDown() { | ||
| 184 | if (!device) { | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | const auto dev = device->GetLogical(); | ||
| 188 | const auto& dld = device->GetDispatchLoader(); | ||
| 189 | if (dev && dld.vkDeviceWaitIdle) { | ||
| 190 | dev.waitIdle(dld); | ||
| 191 | } | ||
| 192 | |||
| 193 | rasterizer.reset(); | ||
| 194 | blit_screen.reset(); | ||
| 195 | scheduler.reset(); | ||
| 196 | swapchain.reset(); | ||
| 197 | memory_manager.reset(); | ||
| 198 | resource_manager.reset(); | ||
| 199 | device.reset(); | ||
| 200 | } | ||
| 201 | |||
| 202 | std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback( | ||
| 203 | const vk::DispatchLoaderDynamic& dldi) { | ||
| 204 | const vk::DebugUtilsMessengerCreateInfoEXT callback_ci( | ||
| 205 | {}, | ||
| 206 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | | ||
| 207 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | | ||
| 208 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo | | ||
| 209 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose, | ||
| 210 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | | ||
| 211 | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | | ||
| 212 | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, | ||
| 213 | &DebugCallback, nullptr); | ||
| 214 | vk::DebugUtilsMessengerEXT callback; | ||
| 215 | if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) != | ||
| 216 | vk::Result::eSuccess) { | ||
| 217 | LOG_ERROR(Render_Vulkan, "Failed to create debug callback"); | ||
| 218 | return {}; | ||
| 219 | } | ||
| 220 | return callback; | ||
| 221 | } | ||
| 222 | |||
| 223 | bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) { | ||
| 224 | const auto devices = instance.enumeratePhysicalDevices(dldi); | ||
| 225 | |||
| 226 | // TODO(Rodrigo): Choose device from config file | ||
| 227 | const s32 device_index = Settings::values.vulkan_device; | ||
| 228 | if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) { | ||
| 229 | LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index); | ||
| 230 | return false; | ||
| 231 | } | ||
| 232 | const vk::PhysicalDevice physical_device = devices[device_index]; | ||
| 233 | |||
| 234 | if (!VKDevice::IsSuitable(dldi, physical_device, surface)) { | ||
| 235 | return false; | ||
| 236 | } | ||
| 237 | |||
| 238 | device = std::make_unique<VKDevice>(dldi, physical_device, surface); | ||
| 239 | return device->Create(dldi, instance); | ||
| 240 | } | ||
| 241 | |||
| 242 | void RendererVulkan::Report() const { | ||
| 243 | const std::string vendor_name{device->GetVendorName()}; | ||
| 244 | const std::string model_name{device->GetModelName()}; | ||
| 245 | const std::string driver_version = GetDriverVersion(*device); | ||
| 246 | const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version); | ||
| 247 | |||
| 248 | const std::string api_version = GetReadableVersion(device->GetApiVersion()); | ||
| 249 | |||
| 250 | const std::string extensions = BuildCommaSeparatedExtensions(device->GetAvailableExtensions()); | ||
| 251 | |||
| 252 | LOG_INFO(Render_Vulkan, "Driver: {}", driver_name); | ||
| 253 | LOG_INFO(Render_Vulkan, "Device: {}", model_name); | ||
| 254 | LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version); | ||
| 255 | |||
| 256 | auto& telemetry_session = system.TelemetrySession(); | ||
| 257 | constexpr auto field = Telemetry::FieldType::UserSystem; | ||
| 258 | telemetry_session.AddField(field, "GPU_Vendor", vendor_name); | ||
| 259 | telemetry_session.AddField(field, "GPU_Model", model_name); | ||
| 260 | telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name); | ||
| 261 | telemetry_session.AddField(field, "GPU_Vulkan_Version", api_version); | ||
| 262 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | ||
| 263 | } | ||
| 264 | |||
| 265 | } // namespace Vulkan \ No newline at end of file | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 939eebe83..d1da4f9d3 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -104,8 +104,11 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan | |||
| 104 | features.depthBiasClamp = true; | 104 | features.depthBiasClamp = true; |
| 105 | features.geometryShader = true; | 105 | features.geometryShader = true; |
| 106 | features.tessellationShader = true; | 106 | features.tessellationShader = true; |
| 107 | features.occlusionQueryPrecise = true; | ||
| 107 | features.fragmentStoresAndAtomics = true; | 108 | features.fragmentStoresAndAtomics = true; |
| 108 | features.shaderImageGatherExtended = true; | 109 | features.shaderImageGatherExtended = true; |
| 110 | features.shaderStorageImageReadWithoutFormat = | ||
| 111 | is_shader_storage_img_read_without_format_supported; | ||
| 109 | features.shaderStorageImageWriteWithoutFormat = true; | 112 | features.shaderStorageImageWriteWithoutFormat = true; |
| 110 | features.textureCompressionASTC_LDR = is_optimal_astc_supported; | 113 | features.textureCompressionASTC_LDR = is_optimal_astc_supported; |
| 111 | 114 | ||
| @@ -117,6 +120,10 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan | |||
| 117 | bit8_storage.uniformAndStorageBuffer8BitAccess = true; | 120 | bit8_storage.uniformAndStorageBuffer8BitAccess = true; |
| 118 | SetNext(next, bit8_storage); | 121 | SetNext(next, bit8_storage); |
| 119 | 122 | ||
| 123 | vk::PhysicalDeviceHostQueryResetFeaturesEXT host_query_reset; | ||
| 124 | host_query_reset.hostQueryReset = true; | ||
| 125 | SetNext(next, host_query_reset); | ||
| 126 | |||
| 120 | vk::PhysicalDeviceFloat16Int8FeaturesKHR float16_int8; | 127 | vk::PhysicalDeviceFloat16Int8FeaturesKHR float16_int8; |
| 121 | if (is_float16_supported) { | 128 | if (is_float16_supported) { |
| 122 | float16_int8.shaderFloat16 = true; | 129 | float16_int8.shaderFloat16 = true; |
| @@ -273,6 +280,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev | |||
| 273 | VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, | 280 | VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, |
| 274 | VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, | 281 | VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, |
| 275 | VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, | 282 | VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, |
| 283 | VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, | ||
| 276 | }; | 284 | }; |
| 277 | std::bitset<required_extensions.size()> available_extensions{}; | 285 | std::bitset<required_extensions.size()> available_extensions{}; |
| 278 | 286 | ||
| @@ -340,6 +348,7 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev | |||
| 340 | std::make_pair(features.depthBiasClamp, "depthBiasClamp"), | 348 | std::make_pair(features.depthBiasClamp, "depthBiasClamp"), |
| 341 | std::make_pair(features.geometryShader, "geometryShader"), | 349 | std::make_pair(features.geometryShader, "geometryShader"), |
| 342 | std::make_pair(features.tessellationShader, "tessellationShader"), | 350 | std::make_pair(features.tessellationShader, "tessellationShader"), |
| 351 | std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), | ||
| 343 | std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), | 352 | std::make_pair(features.fragmentStoresAndAtomics, "fragmentStoresAndAtomics"), |
| 344 | std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), | 353 | std::make_pair(features.shaderImageGatherExtended, "shaderImageGatherExtended"), |
| 345 | std::make_pair(features.shaderStorageImageWriteWithoutFormat, | 354 | std::make_pair(features.shaderStorageImageWriteWithoutFormat, |
| @@ -376,7 +385,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 376 | } | 385 | } |
| 377 | }; | 386 | }; |
| 378 | 387 | ||
| 379 | extensions.reserve(13); | 388 | extensions.reserve(14); |
| 380 | extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); | 389 | extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); |
| 381 | extensions.push_back(VK_KHR_16BIT_STORAGE_EXTENSION_NAME); | 390 | extensions.push_back(VK_KHR_16BIT_STORAGE_EXTENSION_NAME); |
| 382 | extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME); | 391 | extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME); |
| @@ -384,6 +393,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 384 | extensions.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME); | 393 | extensions.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME); |
| 385 | extensions.push_back(VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME); | 394 | extensions.push_back(VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME); |
| 386 | extensions.push_back(VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME); | 395 | extensions.push_back(VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME); |
| 396 | extensions.push_back(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME); | ||
| 387 | 397 | ||
| 388 | [[maybe_unused]] const bool nsight = | 398 | [[maybe_unused]] const bool nsight = |
| 389 | std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED"); | 399 | std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED"); |
| @@ -400,8 +410,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 400 | VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); | 410 | VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); |
| 401 | Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, | 411 | Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, |
| 402 | false); | 412 | false); |
| 403 | Test(extension, nv_device_diagnostic_checkpoints, | 413 | if (Settings::values.renderer_debug) { |
| 404 | VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); | 414 | Test(extension, nv_device_diagnostic_checkpoints, |
| 415 | VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); | ||
| 416 | } | ||
| 405 | } | 417 | } |
| 406 | 418 | ||
| 407 | if (khr_shader_float16_int8) { | 419 | if (khr_shader_float16_int8) { |
| @@ -455,6 +467,8 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK | |||
| 455 | 467 | ||
| 456 | void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) { | 468 | void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) { |
| 457 | const auto supported_features{physical.getFeatures(dldi)}; | 469 | const auto supported_features{physical.getFeatures(dldi)}; |
| 470 | is_shader_storage_img_read_without_format_supported = | ||
| 471 | supported_features.shaderStorageImageReadWithoutFormat; | ||
| 458 | is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi); | 472 | is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi); |
| 459 | } | 473 | } |
| 460 | 474 | ||
| @@ -528,6 +542,7 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti | |||
| 528 | vk::Format::eBc6HUfloatBlock, | 542 | vk::Format::eBc6HUfloatBlock, |
| 529 | vk::Format::eBc6HSfloatBlock, | 543 | vk::Format::eBc6HSfloatBlock, |
| 530 | vk::Format::eBc1RgbaSrgbBlock, | 544 | vk::Format::eBc1RgbaSrgbBlock, |
| 545 | vk::Format::eBc2SrgbBlock, | ||
| 531 | vk::Format::eBc3SrgbBlock, | 546 | vk::Format::eBc3SrgbBlock, |
| 532 | vk::Format::eBc7SrgbBlock, | 547 | vk::Format::eBc7SrgbBlock, |
| 533 | vk::Format::eAstc4x4SrgbBlock, | 548 | vk::Format::eAstc4x4SrgbBlock, |
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h index 72603f9f6..2c27ad730 100644 --- a/src/video_core/renderer_vulkan/vk_device.h +++ b/src/video_core/renderer_vulkan/vk_device.h | |||
| @@ -122,6 +122,11 @@ public: | |||
| 122 | return properties.limits.maxPushConstantsSize; | 122 | return properties.limits.maxPushConstantsSize; |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | /// Returns true if Shader storage Image Read Without Format supported. | ||
| 126 | bool IsShaderStorageImageReadWithoutFormatSupported() const { | ||
| 127 | return is_shader_storage_img_read_without_format_supported; | ||
| 128 | } | ||
| 129 | |||
| 125 | /// Returns true if ASTC is natively supported. | 130 | /// Returns true if ASTC is natively supported. |
| 126 | bool IsOptimalAstcSupported() const { | 131 | bool IsOptimalAstcSupported() const { |
| 127 | return is_optimal_astc_supported; | 132 | return is_optimal_astc_supported; |
| @@ -227,6 +232,8 @@ private: | |||
| 227 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. | 232 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. |
| 228 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. | 233 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. |
| 229 | bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints. | 234 | bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints. |
| 235 | bool is_shader_storage_img_read_without_format_supported{}; ///< Support for shader storage | ||
| 236 | ///< image read without format | ||
| 230 | 237 | ||
| 231 | // Telemetry parameters | 238 | // Telemetry parameters |
| 232 | std::string vendor_name; ///< Device's driver name. | 239 | std::string vendor_name; ///< Device's driver name. |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 48e23d4cd..7ddf7d3ee 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -325,9 +325,6 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 325 | specialization.tessellation.primitive = fixed_state.tessellation.primitive; | 325 | specialization.tessellation.primitive = fixed_state.tessellation.primitive; |
| 326 | specialization.tessellation.spacing = fixed_state.tessellation.spacing; | 326 | specialization.tessellation.spacing = fixed_state.tessellation.spacing; |
| 327 | specialization.tessellation.clockwise = fixed_state.tessellation.clockwise; | 327 | specialization.tessellation.clockwise = fixed_state.tessellation.clockwise; |
| 328 | for (const auto& rt : key.renderpass_params.color_attachments) { | ||
| 329 | specialization.enabled_rendertargets.set(rt.index); | ||
| 330 | } | ||
| 331 | 328 | ||
| 332 | SPIRVProgram program; | 329 | SPIRVProgram program; |
| 333 | std::vector<vk::DescriptorSetLayoutBinding> bindings; | 330 | std::vector<vk::DescriptorSetLayoutBinding> bindings; |
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp new file mode 100644 index 000000000..ffbf60dda --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstddef> | ||
| 7 | #include <cstdint> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "video_core/renderer_vulkan/declarations.h" | ||
| 12 | #include "video_core/renderer_vulkan/vk_device.h" | ||
| 13 | #include "video_core/renderer_vulkan/vk_query_cache.h" | ||
| 14 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 16 | |||
| 17 | namespace Vulkan { | ||
| 18 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | constexpr std::array QUERY_TARGETS = {vk::QueryType::eOcclusion}; | ||
| 22 | |||
| 23 | constexpr vk::QueryType GetTarget(VideoCore::QueryType type) { | ||
| 24 | return QUERY_TARGETS[static_cast<std::size_t>(type)]; | ||
| 25 | } | ||
| 26 | |||
| 27 | } // Anonymous namespace | ||
| 28 | |||
| 29 | QueryPool::QueryPool() : VKFencedPool{GROW_STEP} {} | ||
| 30 | |||
| 31 | QueryPool::~QueryPool() = default; | ||
| 32 | |||
| 33 | void QueryPool::Initialize(const VKDevice& device_, VideoCore::QueryType type_) { | ||
| 34 | device = &device_; | ||
| 35 | type = type_; | ||
| 36 | } | ||
| 37 | |||
| 38 | std::pair<vk::QueryPool, std::uint32_t> QueryPool::Commit(VKFence& fence) { | ||
| 39 | std::size_t index; | ||
| 40 | do { | ||
| 41 | index = CommitResource(fence); | ||
| 42 | } while (usage[index]); | ||
| 43 | usage[index] = true; | ||
| 44 | |||
| 45 | return {*pools[index / GROW_STEP], static_cast<std::uint32_t>(index % GROW_STEP)}; | ||
| 46 | } | ||
| 47 | |||
| 48 | void QueryPool::Allocate(std::size_t begin, std::size_t end) { | ||
| 49 | usage.resize(end); | ||
| 50 | |||
| 51 | const auto dev = device->GetLogical(); | ||
| 52 | const u32 size = static_cast<u32>(end - begin); | ||
| 53 | const vk::QueryPoolCreateInfo query_pool_ci({}, GetTarget(type), size, {}); | ||
| 54 | pools.push_back(dev.createQueryPoolUnique(query_pool_ci, nullptr, device->GetDispatchLoader())); | ||
| 55 | } | ||
| 56 | |||
| 57 | void QueryPool::Reserve(std::pair<vk::QueryPool, std::uint32_t> query) { | ||
| 58 | const auto it = | ||
| 59 | std::find_if(std::begin(pools), std::end(pools), | ||
| 60 | [query_pool = query.first](auto& pool) { return query_pool == *pool; }); | ||
| 61 | ASSERT(it != std::end(pools)); | ||
| 62 | |||
| 63 | const std::ptrdiff_t pool_index = std::distance(std::begin(pools), it); | ||
| 64 | usage[pool_index * GROW_STEP + static_cast<std::ptrdiff_t>(query.second)] = false; | ||
| 65 | } | ||
| 66 | |||
| 67 | VKQueryCache::VKQueryCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | ||
| 68 | const VKDevice& device, VKScheduler& scheduler) | ||
| 69 | : VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter, | ||
| 70 | QueryPool>{system, rasterizer}, | ||
| 71 | device{device}, scheduler{scheduler} { | ||
| 72 | for (std::size_t i = 0; i < static_cast<std::size_t>(VideoCore::NumQueryTypes); ++i) { | ||
| 73 | query_pools[i].Initialize(device, static_cast<VideoCore::QueryType>(i)); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | VKQueryCache::~VKQueryCache() = default; | ||
| 78 | |||
| 79 | std::pair<vk::QueryPool, std::uint32_t> VKQueryCache::AllocateQuery(VideoCore::QueryType type) { | ||
| 80 | return query_pools[static_cast<std::size_t>(type)].Commit(scheduler.GetFence()); | ||
| 81 | } | ||
| 82 | |||
| 83 | void VKQueryCache::Reserve(VideoCore::QueryType type, | ||
| 84 | std::pair<vk::QueryPool, std::uint32_t> query) { | ||
| 85 | query_pools[static_cast<std::size_t>(type)].Reserve(query); | ||
| 86 | } | ||
| 87 | |||
| 88 | HostCounter::HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency, | ||
| 89 | VideoCore::QueryType type) | ||
| 90 | : VideoCommon::HostCounterBase<VKQueryCache, HostCounter>{std::move(dependency)}, cache{cache}, | ||
| 91 | type{type}, query{cache.AllocateQuery(type)}, ticks{cache.Scheduler().Ticks()} { | ||
| 92 | const auto dev = cache.Device().GetLogical(); | ||
| 93 | cache.Scheduler().Record([dev, query = query](vk::CommandBuffer cmdbuf, auto& dld) { | ||
| 94 | dev.resetQueryPoolEXT(query.first, query.second, 1, dld); | ||
| 95 | cmdbuf.beginQuery(query.first, query.second, vk::QueryControlFlagBits::ePrecise, dld); | ||
| 96 | }); | ||
| 97 | } | ||
| 98 | |||
| 99 | HostCounter::~HostCounter() { | ||
| 100 | cache.Reserve(type, query); | ||
| 101 | } | ||
| 102 | |||
| 103 | void HostCounter::EndQuery() { | ||
| 104 | cache.Scheduler().Record([query = query](auto cmdbuf, auto& dld) { | ||
| 105 | cmdbuf.endQuery(query.first, query.second, dld); | ||
| 106 | }); | ||
| 107 | } | ||
| 108 | |||
| 109 | u64 HostCounter::BlockingQuery() const { | ||
| 110 | if (ticks >= cache.Scheduler().Ticks()) { | ||
| 111 | cache.Scheduler().Flush(); | ||
| 112 | } | ||
| 113 | |||
| 114 | const auto dev = cache.Device().GetLogical(); | ||
| 115 | const auto& dld = cache.Device().GetDispatchLoader(); | ||
| 116 | u64 value; | ||
| 117 | dev.getQueryPoolResults(query.first, query.second, 1, sizeof(value), &value, sizeof(value), | ||
| 118 | vk::QueryResultFlagBits::e64 | vk::QueryResultFlagBits::eWait, dld); | ||
| 119 | return value; | ||
| 120 | } | ||
| 121 | |||
| 122 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h new file mode 100644 index 000000000..c3092ee96 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_query_cache.h | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <cstdint> | ||
| 9 | #include <memory> | ||
| 10 | #include <utility> | ||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | #include "common/common_types.h" | ||
| 14 | #include "video_core/query_cache.h" | ||
| 15 | #include "video_core/renderer_vulkan/declarations.h" | ||
| 16 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | ||
| 17 | |||
| 18 | namespace VideoCore { | ||
| 19 | class RasterizerInterface; | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace Vulkan { | ||
| 23 | |||
| 24 | class CachedQuery; | ||
| 25 | class HostCounter; | ||
| 26 | class VKDevice; | ||
| 27 | class VKQueryCache; | ||
| 28 | class VKScheduler; | ||
| 29 | |||
| 30 | using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>; | ||
| 31 | |||
| 32 | class QueryPool final : public VKFencedPool { | ||
| 33 | public: | ||
| 34 | explicit QueryPool(); | ||
| 35 | ~QueryPool() override; | ||
| 36 | |||
| 37 | void Initialize(const VKDevice& device, VideoCore::QueryType type); | ||
| 38 | |||
| 39 | std::pair<vk::QueryPool, std::uint32_t> Commit(VKFence& fence); | ||
| 40 | |||
| 41 | void Reserve(std::pair<vk::QueryPool, std::uint32_t> query); | ||
| 42 | |||
| 43 | protected: | ||
| 44 | void Allocate(std::size_t begin, std::size_t end) override; | ||
| 45 | |||
| 46 | private: | ||
| 47 | static constexpr std::size_t GROW_STEP = 512; | ||
| 48 | |||
| 49 | const VKDevice* device = nullptr; | ||
| 50 | VideoCore::QueryType type = {}; | ||
| 51 | |||
| 52 | std::vector<UniqueQueryPool> pools; | ||
| 53 | std::vector<bool> usage; | ||
| 54 | }; | ||
| 55 | |||
| 56 | class VKQueryCache final | ||
| 57 | : public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter, | ||
| 58 | QueryPool> { | ||
| 59 | public: | ||
| 60 | explicit VKQueryCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, | ||
| 61 | const VKDevice& device, VKScheduler& scheduler); | ||
| 62 | ~VKQueryCache(); | ||
| 63 | |||
| 64 | std::pair<vk::QueryPool, std::uint32_t> AllocateQuery(VideoCore::QueryType type); | ||
| 65 | |||
| 66 | void Reserve(VideoCore::QueryType type, std::pair<vk::QueryPool, std::uint32_t> query); | ||
| 67 | |||
| 68 | const VKDevice& Device() const noexcept { | ||
| 69 | return device; | ||
| 70 | } | ||
| 71 | |||
| 72 | VKScheduler& Scheduler() const noexcept { | ||
| 73 | return scheduler; | ||
| 74 | } | ||
| 75 | |||
| 76 | private: | ||
| 77 | const VKDevice& device; | ||
| 78 | VKScheduler& scheduler; | ||
| 79 | }; | ||
| 80 | |||
| 81 | class HostCounter final : public VideoCommon::HostCounterBase<VKQueryCache, HostCounter> { | ||
| 82 | public: | ||
| 83 | explicit HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency, | ||
| 84 | VideoCore::QueryType type); | ||
| 85 | ~HostCounter(); | ||
| 86 | |||
| 87 | void EndQuery(); | ||
| 88 | |||
| 89 | private: | ||
| 90 | u64 BlockingQuery() const override; | ||
| 91 | |||
| 92 | VKQueryCache& cache; | ||
| 93 | const VideoCore::QueryType type; | ||
| 94 | const std::pair<vk::QueryPool, std::uint32_t> query; | ||
| 95 | const u64 ticks; | ||
| 96 | }; | ||
| 97 | |||
| 98 | class CachedQuery : public VideoCommon::CachedQueryBase<HostCounter> { | ||
| 99 | public: | ||
| 100 | explicit CachedQuery(VKQueryCache&, VideoCore::QueryType, VAddr cpu_addr, u8* host_ptr) | ||
| 101 | : VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr} {} | ||
| 102 | }; | ||
| 103 | |||
| 104 | } // namespace Vulkan | ||
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d2c6b1189..31c078f6a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -289,25 +289,19 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind | |||
| 289 | staging_pool), | 289 | staging_pool), |
| 290 | pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue), | 290 | pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue), |
| 291 | buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool), | 291 | buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool), |
| 292 | sampler_cache(device) {} | 292 | sampler_cache(device), query_cache(system, *this, device, scheduler) { |
| 293 | 293 | scheduler.SetQueryCache(query_cache); | |
| 294 | RasterizerVulkan::~RasterizerVulkan() = default; | ||
| 295 | |||
| 296 | bool RasterizerVulkan::DrawBatch(bool is_indexed) { | ||
| 297 | Draw(is_indexed, false); | ||
| 298 | return true; | ||
| 299 | } | 294 | } |
| 300 | 295 | ||
| 301 | bool RasterizerVulkan::DrawMultiBatch(bool is_indexed) { | 296 | RasterizerVulkan::~RasterizerVulkan() = default; |
| 302 | Draw(is_indexed, true); | ||
| 303 | return true; | ||
| 304 | } | ||
| 305 | 297 | ||
| 306 | void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | 298 | void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { |
| 307 | MICROPROFILE_SCOPE(Vulkan_Drawing); | 299 | MICROPROFILE_SCOPE(Vulkan_Drawing); |
| 308 | 300 | ||
| 309 | FlushWork(); | 301 | FlushWork(); |
| 310 | 302 | ||
| 303 | query_cache.UpdateCounters(); | ||
| 304 | |||
| 311 | const auto& gpu = system.GPU().Maxwell3D(); | 305 | const auto& gpu = system.GPU().Maxwell3D(); |
| 312 | GraphicsPipelineCacheKey key{GetFixedPipelineState(gpu.regs)}; | 306 | GraphicsPipelineCacheKey key{GetFixedPipelineState(gpu.regs)}; |
| 313 | 307 | ||
| @@ -362,6 +356,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 362 | void RasterizerVulkan::Clear() { | 356 | void RasterizerVulkan::Clear() { |
| 363 | MICROPROFILE_SCOPE(Vulkan_Clearing); | 357 | MICROPROFILE_SCOPE(Vulkan_Clearing); |
| 364 | 358 | ||
| 359 | query_cache.UpdateCounters(); | ||
| 360 | |||
| 365 | const auto& gpu = system.GPU().Maxwell3D(); | 361 | const auto& gpu = system.GPU().Maxwell3D(); |
| 366 | if (!system.GPU().Maxwell3D().ShouldExecute()) { | 362 | if (!system.GPU().Maxwell3D().ShouldExecute()) { |
| 367 | return; | 363 | return; |
| @@ -429,6 +425,8 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) { | |||
| 429 | sampled_views.clear(); | 425 | sampled_views.clear(); |
| 430 | image_views.clear(); | 426 | image_views.clear(); |
| 431 | 427 | ||
| 428 | query_cache.UpdateCounters(); | ||
| 429 | |||
| 432 | const auto& launch_desc = system.GPU().KeplerCompute().launch_description; | 430 | const auto& launch_desc = system.GPU().KeplerCompute().launch_description; |
| 433 | const ComputePipelineCacheKey key{ | 431 | const ComputePipelineCacheKey key{ |
| 434 | code_addr, | 432 | code_addr, |
| @@ -471,17 +469,28 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) { | |||
| 471 | }); | 469 | }); |
| 472 | } | 470 | } |
| 473 | 471 | ||
| 472 | void RasterizerVulkan::ResetCounter(VideoCore::QueryType type) { | ||
| 473 | query_cache.ResetCounter(type); | ||
| 474 | } | ||
| 475 | |||
| 476 | void RasterizerVulkan::Query(GPUVAddr gpu_addr, VideoCore::QueryType type, | ||
| 477 | std::optional<u64> timestamp) { | ||
| 478 | query_cache.Query(gpu_addr, type, timestamp); | ||
| 479 | } | ||
| 480 | |||
| 474 | void RasterizerVulkan::FlushAll() {} | 481 | void RasterizerVulkan::FlushAll() {} |
| 475 | 482 | ||
| 476 | void RasterizerVulkan::FlushRegion(CacheAddr addr, u64 size) { | 483 | void RasterizerVulkan::FlushRegion(CacheAddr addr, u64 size) { |
| 477 | texture_cache.FlushRegion(addr, size); | 484 | texture_cache.FlushRegion(addr, size); |
| 478 | buffer_cache.FlushRegion(addr, size); | 485 | buffer_cache.FlushRegion(addr, size); |
| 486 | query_cache.FlushRegion(addr, size); | ||
| 479 | } | 487 | } |
| 480 | 488 | ||
| 481 | void RasterizerVulkan::InvalidateRegion(CacheAddr addr, u64 size) { | 489 | void RasterizerVulkan::InvalidateRegion(CacheAddr addr, u64 size) { |
| 482 | texture_cache.InvalidateRegion(addr, size); | 490 | texture_cache.InvalidateRegion(addr, size); |
| 483 | pipeline_cache.InvalidateRegion(addr, size); | 491 | pipeline_cache.InvalidateRegion(addr, size); |
| 484 | buffer_cache.InvalidateRegion(addr, size); | 492 | buffer_cache.InvalidateRegion(addr, size); |
| 493 | query_cache.InvalidateRegion(addr, size); | ||
| 485 | } | 494 | } |
| 486 | 495 | ||
| 487 | void RasterizerVulkan::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { | 496 | void RasterizerVulkan::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { |
| @@ -571,7 +580,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | |||
| 571 | color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); | 580 | color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); |
| 572 | } | 581 | } |
| 573 | if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { | 582 | if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { |
| 574 | texceptions.set(rt); | 583 | texceptions[rt] = true; |
| 575 | } | 584 | } |
| 576 | } | 585 | } |
| 577 | 586 | ||
| @@ -579,7 +588,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | |||
| 579 | zeta_attachment = texture_cache.GetDepthBufferSurface(true); | 588 | zeta_attachment = texture_cache.GetDepthBufferSurface(true); |
| 580 | } | 589 | } |
| 581 | if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { | 590 | if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { |
| 582 | texceptions.set(ZETA_TEXCEPTION_INDEX); | 591 | texceptions[ZETA_TEXCEPTION_INDEX] = true; |
| 583 | } | 592 | } |
| 584 | 593 | ||
| 585 | texture_cache.GuardRenderTargets(false); | 594 | texture_cache.GuardRenderTargets(false); |
| @@ -1122,11 +1131,12 @@ RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions) | |||
| 1122 | 1131 | ||
| 1123 | for (std::size_t rt = 0; rt < static_cast<std::size_t>(regs.rt_control.count); ++rt) { | 1132 | for (std::size_t rt = 0; rt < static_cast<std::size_t>(regs.rt_control.count); ++rt) { |
| 1124 | const auto& rendertarget = regs.rt[rt]; | 1133 | const auto& rendertarget = regs.rt[rt]; |
| 1125 | if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) | 1134 | if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) { |
| 1126 | continue; | 1135 | continue; |
| 1136 | } | ||
| 1127 | renderpass_params.color_attachments.push_back(RenderPassParams::ColorAttachment{ | 1137 | renderpass_params.color_attachments.push_back(RenderPassParams::ColorAttachment{ |
| 1128 | static_cast<u32>(rt), PixelFormatFromRenderTargetFormat(rendertarget.format), | 1138 | static_cast<u32>(rt), PixelFormatFromRenderTargetFormat(rendertarget.format), |
| 1129 | texceptions.test(rt)}); | 1139 | texceptions[rt]}); |
| 1130 | } | 1140 | } |
| 1131 | 1141 | ||
| 1132 | renderpass_params.has_zeta = regs.zeta_enable; | 1142 | renderpass_params.has_zeta = regs.zeta_enable; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 7be71e734..138903d60 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" | 24 | #include "video_core/renderer_vulkan/vk_descriptor_pool.h" |
| 25 | #include "video_core/renderer_vulkan/vk_memory_manager.h" | 25 | #include "video_core/renderer_vulkan/vk_memory_manager.h" |
| 26 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" | 26 | #include "video_core/renderer_vulkan/vk_pipeline_cache.h" |
| 27 | #include "video_core/renderer_vulkan/vk_query_cache.h" | ||
| 27 | #include "video_core/renderer_vulkan/vk_renderpass_cache.h" | 28 | #include "video_core/renderer_vulkan/vk_renderpass_cache.h" |
| 28 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | 29 | #include "video_core/renderer_vulkan/vk_resource_manager.h" |
| 29 | #include "video_core/renderer_vulkan/vk_sampler_cache.h" | 30 | #include "video_core/renderer_vulkan/vk_sampler_cache.h" |
| @@ -96,7 +97,7 @@ struct ImageView { | |||
| 96 | vk::ImageLayout* layout = nullptr; | 97 | vk::ImageLayout* layout = nullptr; |
| 97 | }; | 98 | }; |
| 98 | 99 | ||
| 99 | class RasterizerVulkan : public VideoCore::RasterizerAccelerated { | 100 | class RasterizerVulkan final : public VideoCore::RasterizerAccelerated { |
| 100 | public: | 101 | public: |
| 101 | explicit RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& render_window, | 102 | explicit RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& render_window, |
| 102 | VKScreenInfo& screen_info, const VKDevice& device, | 103 | VKScreenInfo& screen_info, const VKDevice& device, |
| @@ -104,10 +105,11 @@ public: | |||
| 104 | VKScheduler& scheduler); | 105 | VKScheduler& scheduler); |
| 105 | ~RasterizerVulkan() override; | 106 | ~RasterizerVulkan() override; |
| 106 | 107 | ||
| 107 | bool DrawBatch(bool is_indexed) override; | 108 | void Draw(bool is_indexed, bool is_instanced) override; |
| 108 | bool DrawMultiBatch(bool is_indexed) override; | ||
| 109 | void Clear() override; | 109 | void Clear() override; |
| 110 | void DispatchCompute(GPUVAddr code_addr) override; | 110 | void DispatchCompute(GPUVAddr code_addr) override; |
| 111 | void ResetCounter(VideoCore::QueryType type) override; | ||
| 112 | void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) override; | ||
| 111 | void FlushAll() override; | 113 | void FlushAll() override; |
| 112 | void FlushRegion(CacheAddr addr, u64 size) override; | 114 | void FlushRegion(CacheAddr addr, u64 size) override; |
| 113 | void InvalidateRegion(CacheAddr addr, u64 size) override; | 115 | void InvalidateRegion(CacheAddr addr, u64 size) override; |
| @@ -140,8 +142,6 @@ private: | |||
| 140 | 142 | ||
| 141 | static constexpr std::size_t ZETA_TEXCEPTION_INDEX = 8; | 143 | static constexpr std::size_t ZETA_TEXCEPTION_INDEX = 8; |
| 142 | 144 | ||
| 143 | void Draw(bool is_indexed, bool is_instanced); | ||
| 144 | |||
| 145 | void FlushWork(); | 145 | void FlushWork(); |
| 146 | 146 | ||
| 147 | Texceptions UpdateAttachments(); | 147 | Texceptions UpdateAttachments(); |
| @@ -247,6 +247,7 @@ private: | |||
| 247 | VKPipelineCache pipeline_cache; | 247 | VKPipelineCache pipeline_cache; |
| 248 | VKBufferCache buffer_cache; | 248 | VKBufferCache buffer_cache; |
| 249 | VKSamplerCache sampler_cache; | 249 | VKSamplerCache sampler_cache; |
| 250 | VKQueryCache query_cache; | ||
| 250 | 251 | ||
| 251 | std::array<View, Maxwell::NumRenderTargets> color_attachments; | 252 | std::array<View, Maxwell::NumRenderTargets> color_attachments; |
| 252 | View zeta_attachment; | 253 | View zeta_attachment; |
diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp index 0a8ec8398..204b7c39c 100644 --- a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp | |||
| @@ -23,7 +23,14 @@ static std::optional<vk::BorderColor> TryConvertBorderColor(std::array<float, 4> | |||
| 23 | } else if (color == std::array<float, 4>{1, 1, 1, 1}) { | 23 | } else if (color == std::array<float, 4>{1, 1, 1, 1}) { |
| 24 | return vk::BorderColor::eFloatOpaqueWhite; | 24 | return vk::BorderColor::eFloatOpaqueWhite; |
| 25 | } else { | 25 | } else { |
| 26 | return {}; | 26 | if (color[0] + color[1] + color[2] > 1.35f) { |
| 27 | // If color elements are brighter than roughly 0.5 average, use white border | ||
| 28 | return vk::BorderColor::eFloatOpaqueWhite; | ||
| 29 | } | ||
| 30 | if (color[3] > 0.5f) { | ||
| 31 | return vk::BorderColor::eFloatOpaqueBlack; | ||
| 32 | } | ||
| 33 | return vk::BorderColor::eFloatTransparentBlack; | ||
| 27 | } | 34 | } |
| 28 | } | 35 | } |
| 29 | 36 | ||
| @@ -37,8 +44,6 @@ UniqueSampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) | |||
| 37 | 44 | ||
| 38 | const auto border_color{tsc.GetBorderColor()}; | 45 | const auto border_color{tsc.GetBorderColor()}; |
| 39 | const auto vk_border_color{TryConvertBorderColor(border_color)}; | 46 | const auto vk_border_color{TryConvertBorderColor(border_color)}; |
| 40 | UNIMPLEMENTED_IF_MSG(!vk_border_color, "Unimplemented border color {} {} {} {}", | ||
| 41 | border_color[0], border_color[1], border_color[2], border_color[3]); | ||
| 42 | 47 | ||
| 43 | constexpr bool unnormalized_coords{false}; | 48 | constexpr bool unnormalized_coords{false}; |
| 44 | 49 | ||
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index d66133ad1..92bd6c344 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "common/microprofile.h" | 6 | #include "common/microprofile.h" |
| 7 | #include "video_core/renderer_vulkan/declarations.h" | 7 | #include "video_core/renderer_vulkan/declarations.h" |
| 8 | #include "video_core/renderer_vulkan/vk_device.h" | 8 | #include "video_core/renderer_vulkan/vk_device.h" |
| 9 | #include "video_core/renderer_vulkan/vk_query_cache.h" | ||
| 9 | #include "video_core/renderer_vulkan/vk_resource_manager.h" | 10 | #include "video_core/renderer_vulkan/vk_resource_manager.h" |
| 10 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 11 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 11 | 12 | ||
| @@ -139,6 +140,8 @@ void VKScheduler::SubmitExecution(vk::Semaphore semaphore) { | |||
| 139 | } | 140 | } |
| 140 | 141 | ||
| 141 | void VKScheduler::AllocateNewContext() { | 142 | void VKScheduler::AllocateNewContext() { |
| 143 | ++ticks; | ||
| 144 | |||
| 142 | std::unique_lock lock{mutex}; | 145 | std::unique_lock lock{mutex}; |
| 143 | current_fence = next_fence; | 146 | current_fence = next_fence; |
| 144 | next_fence = &resource_manager.CommitFence(); | 147 | next_fence = &resource_manager.CommitFence(); |
| @@ -146,6 +149,10 @@ void VKScheduler::AllocateNewContext() { | |||
| 146 | current_cmdbuf = resource_manager.CommitCommandBuffer(*current_fence); | 149 | current_cmdbuf = resource_manager.CommitCommandBuffer(*current_fence); |
| 147 | current_cmdbuf.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit}, | 150 | current_cmdbuf.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit}, |
| 148 | device.GetDispatchLoader()); | 151 | device.GetDispatchLoader()); |
| 152 | // Enable counters once again. These are disabled when a command buffer is finished. | ||
| 153 | if (query_cache) { | ||
| 154 | query_cache->UpdateCounters(); | ||
| 155 | } | ||
| 149 | } | 156 | } |
| 150 | 157 | ||
| 151 | void VKScheduler::InvalidateState() { | 158 | void VKScheduler::InvalidateState() { |
| @@ -159,6 +166,7 @@ void VKScheduler::InvalidateState() { | |||
| 159 | } | 166 | } |
| 160 | 167 | ||
| 161 | void VKScheduler::EndPendingOperations() { | 168 | void VKScheduler::EndPendingOperations() { |
| 169 | query_cache->DisableStreams(); | ||
| 162 | EndRenderPass(); | 170 | EndRenderPass(); |
| 163 | } | 171 | } |
| 164 | 172 | ||
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index bcdffbba0..62fd7858b 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <condition_variable> | 8 | #include <condition_variable> |
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <optional> | 10 | #include <optional> |
| @@ -18,6 +19,7 @@ namespace Vulkan { | |||
| 18 | 19 | ||
| 19 | class VKDevice; | 20 | class VKDevice; |
| 20 | class VKFence; | 21 | class VKFence; |
| 22 | class VKQueryCache; | ||
| 21 | class VKResourceManager; | 23 | class VKResourceManager; |
| 22 | 24 | ||
| 23 | class VKFenceView { | 25 | class VKFenceView { |
| @@ -67,6 +69,11 @@ public: | |||
| 67 | /// Binds a pipeline to the current execution context. | 69 | /// Binds a pipeline to the current execution context. |
| 68 | void BindGraphicsPipeline(vk::Pipeline pipeline); | 70 | void BindGraphicsPipeline(vk::Pipeline pipeline); |
| 69 | 71 | ||
| 72 | /// Assigns the query cache. | ||
| 73 | void SetQueryCache(VKQueryCache& query_cache_) { | ||
| 74 | query_cache = &query_cache_; | ||
| 75 | } | ||
| 76 | |||
| 70 | /// Returns true when viewports have been set in the current command buffer. | 77 | /// Returns true when viewports have been set in the current command buffer. |
| 71 | bool TouchViewports() { | 78 | bool TouchViewports() { |
| 72 | return std::exchange(state.viewports, true); | 79 | return std::exchange(state.viewports, true); |
| @@ -112,6 +119,11 @@ public: | |||
| 112 | return current_fence; | 119 | return current_fence; |
| 113 | } | 120 | } |
| 114 | 121 | ||
| 122 | /// Returns the current command buffer tick. | ||
| 123 | u64 Ticks() const { | ||
| 124 | return ticks; | ||
| 125 | } | ||
| 126 | |||
| 115 | private: | 127 | private: |
| 116 | class Command { | 128 | class Command { |
| 117 | public: | 129 | public: |
| @@ -205,6 +217,8 @@ private: | |||
| 205 | 217 | ||
| 206 | const VKDevice& device; | 218 | const VKDevice& device; |
| 207 | VKResourceManager& resource_manager; | 219 | VKResourceManager& resource_manager; |
| 220 | VKQueryCache* query_cache = nullptr; | ||
| 221 | |||
| 208 | vk::CommandBuffer current_cmdbuf; | 222 | vk::CommandBuffer current_cmdbuf; |
| 209 | VKFence* current_fence = nullptr; | 223 | VKFence* current_fence = nullptr; |
| 210 | VKFence* next_fence = nullptr; | 224 | VKFence* next_fence = nullptr; |
| @@ -227,6 +241,7 @@ private: | |||
| 227 | Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve; | 241 | Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve; |
| 228 | std::mutex mutex; | 242 | std::mutex mutex; |
| 229 | std::condition_variable cv; | 243 | std::condition_variable cv; |
| 244 | std::atomic<u64> ticks = 0; | ||
| 230 | bool quit = false; | 245 | bool quit = false; |
| 231 | }; | 246 | }; |
| 232 | 247 | ||
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index dd6d2ef03..6d0bf6aa1 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -275,12 +275,14 @@ public: | |||
| 275 | AddCapability(spv::Capability::ImageGatherExtended); | 275 | AddCapability(spv::Capability::ImageGatherExtended); |
| 276 | AddCapability(spv::Capability::SampledBuffer); | 276 | AddCapability(spv::Capability::SampledBuffer); |
| 277 | AddCapability(spv::Capability::StorageImageWriteWithoutFormat); | 277 | AddCapability(spv::Capability::StorageImageWriteWithoutFormat); |
| 278 | AddCapability(spv::Capability::DrawParameters); | ||
| 278 | AddCapability(spv::Capability::SubgroupBallotKHR); | 279 | AddCapability(spv::Capability::SubgroupBallotKHR); |
| 279 | AddCapability(spv::Capability::SubgroupVoteKHR); | 280 | AddCapability(spv::Capability::SubgroupVoteKHR); |
| 280 | AddExtension("SPV_KHR_shader_ballot"); | 281 | AddExtension("SPV_KHR_shader_ballot"); |
| 281 | AddExtension("SPV_KHR_subgroup_vote"); | 282 | AddExtension("SPV_KHR_subgroup_vote"); |
| 282 | AddExtension("SPV_KHR_storage_buffer_storage_class"); | 283 | AddExtension("SPV_KHR_storage_buffer_storage_class"); |
| 283 | AddExtension("SPV_KHR_variable_pointers"); | 284 | AddExtension("SPV_KHR_variable_pointers"); |
| 285 | AddExtension("SPV_KHR_shader_draw_parameters"); | ||
| 284 | 286 | ||
| 285 | if (ir.UsesViewportIndex()) { | 287 | if (ir.UsesViewportIndex()) { |
| 286 | AddCapability(spv::Capability::MultiViewport); | 288 | AddCapability(spv::Capability::MultiViewport); |
| @@ -290,6 +292,10 @@ public: | |||
| 290 | } | 292 | } |
| 291 | } | 293 | } |
| 292 | 294 | ||
| 295 | if (device.IsShaderStorageImageReadWithoutFormatSupported()) { | ||
| 296 | AddCapability(spv::Capability::StorageImageReadWithoutFormat); | ||
| 297 | } | ||
| 298 | |||
| 293 | if (device.IsFloat16Supported()) { | 299 | if (device.IsFloat16Supported()) { |
| 294 | AddCapability(spv::Capability::Float16); | 300 | AddCapability(spv::Capability::Float16); |
| 295 | } | 301 | } |
| @@ -353,6 +359,7 @@ private: | |||
| 353 | DeclareFragment(); | 359 | DeclareFragment(); |
| 354 | DeclareCompute(); | 360 | DeclareCompute(); |
| 355 | DeclareRegisters(); | 361 | DeclareRegisters(); |
| 362 | DeclareCustomVariables(); | ||
| 356 | DeclarePredicates(); | 363 | DeclarePredicates(); |
| 357 | DeclareLocalMemory(); | 364 | DeclareLocalMemory(); |
| 358 | DeclareSharedMemory(); | 365 | DeclareSharedMemory(); |
| @@ -491,9 +498,11 @@ private: | |||
| 491 | interfaces.push_back(AddGlobalVariable(Name(out_vertex, "out_vertex"))); | 498 | interfaces.push_back(AddGlobalVariable(Name(out_vertex, "out_vertex"))); |
| 492 | 499 | ||
| 493 | // Declare input attributes | 500 | // Declare input attributes |
| 494 | vertex_index = DeclareInputBuiltIn(spv::BuiltIn::VertexIndex, t_in_uint, "vertex_index"); | 501 | vertex_index = DeclareInputBuiltIn(spv::BuiltIn::VertexIndex, t_in_int, "vertex_index"); |
| 495 | instance_index = | 502 | instance_index = |
| 496 | DeclareInputBuiltIn(spv::BuiltIn::InstanceIndex, t_in_uint, "instance_index"); | 503 | DeclareInputBuiltIn(spv::BuiltIn::InstanceIndex, t_in_int, "instance_index"); |
| 504 | base_vertex = DeclareInputBuiltIn(spv::BuiltIn::BaseVertex, t_in_int, "base_vertex"); | ||
| 505 | base_instance = DeclareInputBuiltIn(spv::BuiltIn::BaseInstance, t_in_int, "base_instance"); | ||
| 497 | } | 506 | } |
| 498 | 507 | ||
| 499 | void DeclareTessControl() { | 508 | void DeclareTessControl() { |
| @@ -542,11 +551,10 @@ private: | |||
| 542 | return; | 551 | return; |
| 543 | } | 552 | } |
| 544 | 553 | ||
| 545 | for (u32 rt = 0; rt < static_cast<u32>(frag_colors.size()); ++rt) { | 554 | for (u32 rt = 0; rt < static_cast<u32>(std::size(frag_colors)); ++rt) { |
| 546 | if (!specialization.enabled_rendertargets[rt]) { | 555 | if (!IsRenderTargetEnabled(rt)) { |
| 547 | continue; | 556 | continue; |
| 548 | } | 557 | } |
| 549 | |||
| 550 | const Id id = AddGlobalVariable(OpVariable(t_out_float4, spv::StorageClass::Output)); | 558 | const Id id = AddGlobalVariable(OpVariable(t_out_float4, spv::StorageClass::Output)); |
| 551 | Name(id, fmt::format("frag_color{}", rt)); | 559 | Name(id, fmt::format("frag_color{}", rt)); |
| 552 | Decorate(id, spv::Decoration::Location, rt); | 560 | Decorate(id, spv::Decoration::Location, rt); |
| @@ -587,6 +595,15 @@ private: | |||
| 587 | } | 595 | } |
| 588 | } | 596 | } |
| 589 | 597 | ||
| 598 | void DeclareCustomVariables() { | ||
| 599 | const u32 num_custom_variables = ir.GetNumCustomVariables(); | ||
| 600 | for (u32 i = 0; i < num_custom_variables; ++i) { | ||
| 601 | const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero); | ||
| 602 | Name(id, fmt::format("custom_var_{}", i)); | ||
| 603 | custom_variables.emplace(i, AddGlobalVariable(id)); | ||
| 604 | } | ||
| 605 | } | ||
| 606 | |||
| 590 | void DeclarePredicates() { | 607 | void DeclarePredicates() { |
| 591 | for (const auto pred : ir.GetPredicates()) { | 608 | for (const auto pred : ir.GetPredicates()) { |
| 592 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | 609 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); |
| @@ -852,6 +869,15 @@ private: | |||
| 852 | return binding; | 869 | return binding; |
| 853 | } | 870 | } |
| 854 | 871 | ||
| 872 | bool IsRenderTargetEnabled(u32 rt) const { | ||
| 873 | for (u32 component = 0; component < 4; ++component) { | ||
| 874 | if (header.ps.IsColorComponentOutputEnabled(rt, component)) { | ||
| 875 | return true; | ||
| 876 | } | ||
| 877 | } | ||
| 878 | return false; | ||
| 879 | } | ||
| 880 | |||
| 855 | bool IsInputAttributeArray() const { | 881 | bool IsInputAttributeArray() const { |
| 856 | return stage == ShaderType::TesselationControl || stage == ShaderType::TesselationEval || | 882 | return stage == ShaderType::TesselationControl || stage == ShaderType::TesselationEval || |
| 857 | stage == ShaderType::Geometry; | 883 | stage == ShaderType::Geometry; |
| @@ -974,6 +1000,11 @@ private: | |||
| 974 | return {OpLoad(t_float, registers.at(index)), Type::Float}; | 1000 | return {OpLoad(t_float, registers.at(index)), Type::Float}; |
| 975 | } | 1001 | } |
| 976 | 1002 | ||
| 1003 | if (const auto cv = std::get_if<CustomVarNode>(&*node)) { | ||
| 1004 | const u32 index = cv->GetIndex(); | ||
| 1005 | return {OpLoad(t_float, custom_variables.at(index)), Type::Float}; | ||
| 1006 | } | ||
| 1007 | |||
| 977 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { | 1008 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { |
| 978 | return {Constant(t_uint, immediate->GetValue()), Type::Uint}; | 1009 | return {Constant(t_uint, immediate->GetValue()), Type::Uint}; |
| 979 | } | 1010 | } |
| @@ -1045,9 +1076,12 @@ private: | |||
| 1045 | return {OpLoad(t_float, AccessElement(t_in_float, tess_coord, element)), | 1076 | return {OpLoad(t_float, AccessElement(t_in_float, tess_coord, element)), |
| 1046 | Type::Float}; | 1077 | Type::Float}; |
| 1047 | case 2: | 1078 | case 2: |
| 1048 | return {OpLoad(t_uint, instance_index), Type::Uint}; | 1079 | return { |
| 1080 | OpISub(t_int, OpLoad(t_int, instance_index), OpLoad(t_int, base_instance)), | ||
| 1081 | Type::Int}; | ||
| 1049 | case 3: | 1082 | case 3: |
| 1050 | return {OpLoad(t_uint, vertex_index), Type::Uint}; | 1083 | return {OpISub(t_int, OpLoad(t_int, vertex_index), OpLoad(t_int, base_vertex)), |
| 1084 | Type::Int}; | ||
| 1051 | } | 1085 | } |
| 1052 | UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); | 1086 | UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element); |
| 1053 | return {Constant(t_uint, 0U), Type::Uint}; | 1087 | return {Constant(t_uint, 0U), Type::Uint}; |
| @@ -1115,15 +1149,7 @@ private: | |||
| 1115 | } | 1149 | } |
| 1116 | 1150 | ||
| 1117 | if (const auto gmem = std::get_if<GmemNode>(&*node)) { | 1151 | if (const auto gmem = std::get_if<GmemNode>(&*node)) { |
| 1118 | const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); | 1152 | return {OpLoad(t_uint, GetGlobalMemoryPointer(*gmem)), Type::Uint}; |
| 1119 | const Id real = AsUint(Visit(gmem->GetRealAddress())); | ||
| 1120 | const Id base = AsUint(Visit(gmem->GetBaseAddress())); | ||
| 1121 | |||
| 1122 | Id offset = OpISub(t_uint, real, base); | ||
| 1123 | offset = OpUDiv(t_uint, offset, Constant(t_uint, 4U)); | ||
| 1124 | return {OpLoad(t_float, | ||
| 1125 | OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0U), offset)), | ||
| 1126 | Type::Float}; | ||
| 1127 | } | 1153 | } |
| 1128 | 1154 | ||
| 1129 | if (const auto lmem = std::get_if<LmemNode>(&*node)) { | 1155 | if (const auto lmem = std::get_if<LmemNode>(&*node)) { |
| @@ -1134,10 +1160,7 @@ private: | |||
| 1134 | } | 1160 | } |
| 1135 | 1161 | ||
| 1136 | if (const auto smem = std::get_if<SmemNode>(&*node)) { | 1162 | if (const auto smem = std::get_if<SmemNode>(&*node)) { |
| 1137 | Id address = AsUint(Visit(smem->GetAddress())); | 1163 | return {OpLoad(t_uint, GetSharedMemoryPointer(*smem)), Type::Uint}; |
| 1138 | address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); | ||
| 1139 | const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address); | ||
| 1140 | return {OpLoad(t_uint, pointer), Type::Uint}; | ||
| 1141 | } | 1164 | } |
| 1142 | 1165 | ||
| 1143 | if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) { | 1166 | if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) { |
| @@ -1331,20 +1354,13 @@ private: | |||
| 1331 | target = {OpAccessChain(t_prv_float, local_memory, address), Type::Float}; | 1354 | target = {OpAccessChain(t_prv_float, local_memory, address), Type::Float}; |
| 1332 | 1355 | ||
| 1333 | } else if (const auto smem = std::get_if<SmemNode>(&*dest)) { | 1356 | } else if (const auto smem = std::get_if<SmemNode>(&*dest)) { |
| 1334 | ASSERT(stage == ShaderType::Compute); | 1357 | target = {GetSharedMemoryPointer(*smem), Type::Uint}; |
| 1335 | Id address = AsUint(Visit(smem->GetAddress())); | ||
| 1336 | address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); | ||
| 1337 | target = {OpAccessChain(t_smem_uint, shared_memory, address), Type::Uint}; | ||
| 1338 | 1358 | ||
| 1339 | } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { | 1359 | } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { |
| 1340 | const Id real = AsUint(Visit(gmem->GetRealAddress())); | 1360 | target = {GetGlobalMemoryPointer(*gmem), Type::Uint}; |
| 1341 | const Id base = AsUint(Visit(gmem->GetBaseAddress())); | ||
| 1342 | const Id diff = OpISub(t_uint, real, base); | ||
| 1343 | const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2)); | ||
| 1344 | 1361 | ||
| 1345 | const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor()); | 1362 | } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) { |
| 1346 | target = {OpAccessChain(t_gmem_float, gmem_buffer, Constant(t_uint, 0), offset), | 1363 | target = {custom_variables.at(cv->GetIndex()), Type::Float}; |
| 1347 | Type::Float}; | ||
| 1348 | 1364 | ||
| 1349 | } else { | 1365 | } else { |
| 1350 | UNIMPLEMENTED(); | 1366 | UNIMPLEMENTED(); |
| @@ -1743,8 +1759,16 @@ private: | |||
| 1743 | } | 1759 | } |
| 1744 | 1760 | ||
| 1745 | Expression ImageLoad(Operation operation) { | 1761 | Expression ImageLoad(Operation operation) { |
| 1746 | UNIMPLEMENTED(); | 1762 | if (!device.IsShaderStorageImageReadWithoutFormatSupported()) { |
| 1747 | return {}; | 1763 | return {v_float_zero, Type::Float}; |
| 1764 | } | ||
| 1765 | |||
| 1766 | const auto& meta{std::get<MetaImage>(operation.GetMeta())}; | ||
| 1767 | |||
| 1768 | const Id coords = GetCoordinates(operation, Type::Int); | ||
| 1769 | const Id texel = OpImageRead(t_uint4, GetImage(operation), coords); | ||
| 1770 | |||
| 1771 | return {OpCompositeExtract(t_uint, texel, meta.element), Type::Uint}; | ||
| 1748 | } | 1772 | } |
| 1749 | 1773 | ||
| 1750 | Expression ImageStore(Operation operation) { | 1774 | Expression ImageStore(Operation operation) { |
| @@ -1796,11 +1820,16 @@ private: | |||
| 1796 | return {}; | 1820 | return {}; |
| 1797 | } | 1821 | } |
| 1798 | 1822 | ||
| 1799 | Expression UAtomicAdd(Operation operation) { | 1823 | Expression AtomicAdd(Operation operation) { |
| 1800 | const auto& smem = std::get<SmemNode>(*operation[0]); | 1824 | Id pointer; |
| 1801 | Id address = AsUint(Visit(smem.GetAddress())); | 1825 | if (const auto smem = std::get_if<SmemNode>(&*operation[0])) { |
| 1802 | address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); | 1826 | pointer = GetSharedMemoryPointer(*smem); |
| 1803 | const Id pointer = OpAccessChain(t_smem_uint, shared_memory, address); | 1827 | } else if (const auto gmem = std::get_if<GmemNode>(&*operation[0])) { |
| 1828 | pointer = GetGlobalMemoryPointer(*gmem); | ||
| 1829 | } else { | ||
| 1830 | UNREACHABLE(); | ||
| 1831 | return {Constant(t_uint, 0), Type::Uint}; | ||
| 1832 | } | ||
| 1804 | 1833 | ||
| 1805 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); | 1834 | const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); |
| 1806 | const Id semantics = Constant(t_uint, 0U); | 1835 | const Id semantics = Constant(t_uint, 0U); |
| @@ -1889,19 +1918,14 @@ private: | |||
| 1889 | // rendertargets/components are skipped in the register assignment. | 1918 | // rendertargets/components are skipped in the register assignment. |
| 1890 | u32 current_reg = 0; | 1919 | u32 current_reg = 0; |
| 1891 | for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { | 1920 | for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) { |
| 1892 | if (!specialization.enabled_rendertargets[rt]) { | ||
| 1893 | // Skip rendertargets that are not enabled | ||
| 1894 | continue; | ||
| 1895 | } | ||
| 1896 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. | 1921 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. |
| 1897 | for (u32 component = 0; component < 4; ++component) { | 1922 | for (u32 component = 0; component < 4; ++component) { |
| 1898 | const Id pointer = AccessElement(t_out_float, frag_colors.at(rt), component); | 1923 | if (!header.ps.IsColorComponentOutputEnabled(rt, component)) { |
| 1899 | if (header.ps.IsColorComponentOutputEnabled(rt, component)) { | 1924 | continue; |
| 1900 | OpStore(pointer, SafeGetRegister(current_reg)); | ||
| 1901 | ++current_reg; | ||
| 1902 | } else { | ||
| 1903 | OpStore(pointer, component == 3 ? v_float_one : v_float_zero); | ||
| 1904 | } | 1925 | } |
| 1926 | const Id pointer = AccessElement(t_out_float, frag_colors[rt], component); | ||
| 1927 | OpStore(pointer, SafeGetRegister(current_reg)); | ||
| 1928 | ++current_reg; | ||
| 1905 | } | 1929 | } |
| 1906 | } | 1930 | } |
| 1907 | if (header.ps.omap.depth) { | 1931 | if (header.ps.omap.depth) { |
| @@ -2240,6 +2264,22 @@ private: | |||
| 2240 | return {}; | 2264 | return {}; |
| 2241 | } | 2265 | } |
| 2242 | 2266 | ||
| 2267 | Id GetGlobalMemoryPointer(const GmemNode& gmem) { | ||
| 2268 | const Id real = AsUint(Visit(gmem.GetRealAddress())); | ||
| 2269 | const Id base = AsUint(Visit(gmem.GetBaseAddress())); | ||
| 2270 | const Id diff = OpISub(t_uint, real, base); | ||
| 2271 | const Id offset = OpShiftRightLogical(t_uint, diff, Constant(t_uint, 2)); | ||
| 2272 | const Id buffer = global_buffers.at(gmem.GetDescriptor()); | ||
| 2273 | return OpAccessChain(t_gmem_uint, buffer, Constant(t_uint, 0), offset); | ||
| 2274 | } | ||
| 2275 | |||
| 2276 | Id GetSharedMemoryPointer(const SmemNode& smem) { | ||
| 2277 | ASSERT(stage == ShaderType::Compute); | ||
| 2278 | Id address = AsUint(Visit(smem.GetAddress())); | ||
| 2279 | address = OpShiftRightLogical(t_uint, address, Constant(t_uint, 2U)); | ||
| 2280 | return OpAccessChain(t_smem_uint, shared_memory, address); | ||
| 2281 | } | ||
| 2282 | |||
| 2243 | static constexpr std::array operation_decompilers = { | 2283 | static constexpr std::array operation_decompilers = { |
| 2244 | &SPIRVDecompiler::Assign, | 2284 | &SPIRVDecompiler::Assign, |
| 2245 | 2285 | ||
| @@ -2386,7 +2426,7 @@ private: | |||
| 2386 | &SPIRVDecompiler::AtomicImageXor, | 2426 | &SPIRVDecompiler::AtomicImageXor, |
| 2387 | &SPIRVDecompiler::AtomicImageExchange, | 2427 | &SPIRVDecompiler::AtomicImageExchange, |
| 2388 | 2428 | ||
| 2389 | &SPIRVDecompiler::UAtomicAdd, | 2429 | &SPIRVDecompiler::AtomicAdd, |
| 2390 | 2430 | ||
| 2391 | &SPIRVDecompiler::Branch, | 2431 | &SPIRVDecompiler::Branch, |
| 2392 | &SPIRVDecompiler::BranchIndirect, | 2432 | &SPIRVDecompiler::BranchIndirect, |
| @@ -2482,9 +2522,9 @@ private: | |||
| 2482 | 2522 | ||
| 2483 | Id t_smem_uint{}; | 2523 | Id t_smem_uint{}; |
| 2484 | 2524 | ||
| 2485 | const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float); | 2525 | const Id t_gmem_uint = TypePointer(spv::StorageClass::StorageBuffer, t_uint); |
| 2486 | const Id t_gmem_array = | 2526 | const Id t_gmem_array = |
| 2487 | Name(Decorate(TypeRuntimeArray(t_float), spv::Decoration::ArrayStride, 4U), "GmemArray"); | 2527 | Name(Decorate(TypeRuntimeArray(t_uint), spv::Decoration::ArrayStride, 4U), "GmemArray"); |
| 2488 | const Id t_gmem_struct = MemberDecorate( | 2528 | const Id t_gmem_struct = MemberDecorate( |
| 2489 | Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); | 2529 | Decorate(TypeStruct(t_gmem_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0); |
| 2490 | const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); | 2530 | const Id t_gmem_ssbo = TypePointer(spv::StorageClass::StorageBuffer, t_gmem_struct); |
| @@ -2505,6 +2545,7 @@ private: | |||
| 2505 | Id out_vertex{}; | 2545 | Id out_vertex{}; |
| 2506 | Id in_vertex{}; | 2546 | Id in_vertex{}; |
| 2507 | std::map<u32, Id> registers; | 2547 | std::map<u32, Id> registers; |
| 2548 | std::map<u32, Id> custom_variables; | ||
| 2508 | std::map<Tegra::Shader::Pred, Id> predicates; | 2549 | std::map<Tegra::Shader::Pred, Id> predicates; |
| 2509 | std::map<u32, Id> flow_variables; | 2550 | std::map<u32, Id> flow_variables; |
| 2510 | Id local_memory{}; | 2551 | Id local_memory{}; |
| @@ -2520,6 +2561,8 @@ private: | |||
| 2520 | 2561 | ||
| 2521 | Id instance_index{}; | 2562 | Id instance_index{}; |
| 2522 | Id vertex_index{}; | 2563 | Id vertex_index{}; |
| 2564 | Id base_instance{}; | ||
| 2565 | Id base_vertex{}; | ||
| 2523 | std::array<Id, Maxwell::NumRenderTargets> frag_colors{}; | 2566 | std::array<Id, Maxwell::NumRenderTargets> frag_colors{}; |
| 2524 | Id frag_depth{}; | 2567 | Id frag_depth{}; |
| 2525 | Id frag_coord{}; | 2568 | Id frag_coord{}; |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h index 10794be1c..f5dc14d9e 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h | |||
| @@ -102,9 +102,6 @@ struct Specialization final { | |||
| 102 | Maxwell::TessellationSpacing spacing{}; | 102 | Maxwell::TessellationSpacing spacing{}; |
| 103 | bool clockwise{}; | 103 | bool clockwise{}; |
| 104 | } tessellation; | 104 | } tessellation; |
| 105 | |||
| 106 | // Fragment specific | ||
| 107 | std::bitset<8> enabled_rendertargets; | ||
| 108 | }; | 105 | }; |
| 109 | // Old gcc versions don't consider this trivially copyable. | 106 | // Old gcc versions don't consider this trivially copyable. |
| 110 | // static_assert(std::is_trivially_copyable_v<Specialization>); | 107 | // static_assert(std::is_trivially_copyable_v<Specialization>); |
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h index a2f0044ba..cca13bcde 100644 --- a/src/video_core/shader/ast.h +++ b/src/video_core/shader/ast.h | |||
| @@ -65,8 +65,8 @@ public: | |||
| 65 | void DetachSegment(ASTNode start, ASTNode end); | 65 | void DetachSegment(ASTNode start, ASTNode end); |
| 66 | void Remove(ASTNode node); | 66 | void Remove(ASTNode node); |
| 67 | 67 | ||
| 68 | ASTNode first{}; | 68 | ASTNode first; |
| 69 | ASTNode last{}; | 69 | ASTNode last; |
| 70 | }; | 70 | }; |
| 71 | 71 | ||
| 72 | class ASTProgram { | 72 | class ASTProgram { |
| @@ -299,9 +299,9 @@ private: | |||
| 299 | friend class ASTZipper; | 299 | friend class ASTZipper; |
| 300 | 300 | ||
| 301 | ASTData data; | 301 | ASTData data; |
| 302 | ASTNode parent{}; | 302 | ASTNode parent; |
| 303 | ASTNode next{}; | 303 | ASTNode next; |
| 304 | ASTNode previous{}; | 304 | ASTNode previous; |
| 305 | ASTZipper* manager{}; | 305 | ASTZipper* manager{}; |
| 306 | }; | 306 | }; |
| 307 | 307 | ||
diff --git a/src/video_core/shader/const_buffer_locker.cpp b/src/video_core/shader/const_buffer_locker.cpp index a4a0319eb..0638be8cb 100644 --- a/src/video_core/shader/const_buffer_locker.cpp +++ b/src/video_core/shader/const_buffer_locker.cpp | |||
| @@ -66,6 +66,18 @@ std::optional<Tegra::Engines::SamplerDescriptor> ConstBufferLocker::ObtainBindle | |||
| 66 | return value; | 66 | return value; |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | std::optional<u32> ConstBufferLocker::ObtainBoundBuffer() { | ||
| 70 | if (bound_buffer_saved) { | ||
| 71 | return bound_buffer; | ||
| 72 | } | ||
| 73 | if (!engine) { | ||
| 74 | return std::nullopt; | ||
| 75 | } | ||
| 76 | bound_buffer_saved = true; | ||
| 77 | bound_buffer = engine->GetBoundBuffer(); | ||
| 78 | return bound_buffer; | ||
| 79 | } | ||
| 80 | |||
| 69 | void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) { | 81 | void ConstBufferLocker::InsertKey(u32 buffer, u32 offset, u32 value) { |
| 70 | keys.insert_or_assign({buffer, offset}, value); | 82 | keys.insert_or_assign({buffer, offset}, value); |
| 71 | } | 83 | } |
| @@ -78,6 +90,11 @@ void ConstBufferLocker::InsertBindlessSampler(u32 buffer, u32 offset, SamplerDes | |||
| 78 | bindless_samplers.insert_or_assign({buffer, offset}, sampler); | 90 | bindless_samplers.insert_or_assign({buffer, offset}, sampler); |
| 79 | } | 91 | } |
| 80 | 92 | ||
| 93 | void ConstBufferLocker::SetBoundBuffer(u32 buffer) { | ||
| 94 | bound_buffer_saved = true; | ||
| 95 | bound_buffer = buffer; | ||
| 96 | } | ||
| 97 | |||
| 81 | bool ConstBufferLocker::IsConsistent() const { | 98 | bool ConstBufferLocker::IsConsistent() const { |
| 82 | if (!engine) { | 99 | if (!engine) { |
| 83 | return false; | 100 | return false; |
diff --git a/src/video_core/shader/const_buffer_locker.h b/src/video_core/shader/const_buffer_locker.h index d32e2d657..d3ea11087 100644 --- a/src/video_core/shader/const_buffer_locker.h +++ b/src/video_core/shader/const_buffer_locker.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/hash.h" | 10 | #include "common/hash.h" |
| 11 | #include "video_core/engines/const_buffer_engine_interface.h" | 11 | #include "video_core/engines/const_buffer_engine_interface.h" |
| 12 | #include "video_core/engines/shader_type.h" | 12 | #include "video_core/engines/shader_type.h" |
| 13 | #include "video_core/guest_driver.h" | ||
| 13 | 14 | ||
| 14 | namespace VideoCommon::Shader { | 15 | namespace VideoCommon::Shader { |
| 15 | 16 | ||
| @@ -40,6 +41,8 @@ public: | |||
| 40 | 41 | ||
| 41 | std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); | 42 | std::optional<Tegra::Engines::SamplerDescriptor> ObtainBindlessSampler(u32 buffer, u32 offset); |
| 42 | 43 | ||
| 44 | std::optional<u32> ObtainBoundBuffer(); | ||
| 45 | |||
| 43 | /// Inserts a key. | 46 | /// Inserts a key. |
| 44 | void InsertKey(u32 buffer, u32 offset, u32 value); | 47 | void InsertKey(u32 buffer, u32 offset, u32 value); |
| 45 | 48 | ||
| @@ -49,6 +52,9 @@ public: | |||
| 49 | /// Inserts a bindless sampler key. | 52 | /// Inserts a bindless sampler key. |
| 50 | void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler); | 53 | void InsertBindlessSampler(u32 buffer, u32 offset, Tegra::Engines::SamplerDescriptor sampler); |
| 51 | 54 | ||
| 55 | /// Set the bound buffer for this locker. | ||
| 56 | void SetBoundBuffer(u32 buffer); | ||
| 57 | |||
| 52 | /// Checks keys and samplers against engine's current const buffers. Returns true if they are | 58 | /// Checks keys and samplers against engine's current const buffers. Returns true if they are |
| 53 | /// the same value, false otherwise; | 59 | /// the same value, false otherwise; |
| 54 | bool IsConsistent() const; | 60 | bool IsConsistent() const; |
| @@ -71,12 +77,27 @@ public: | |||
| 71 | return bindless_samplers; | 77 | return bindless_samplers; |
| 72 | } | 78 | } |
| 73 | 79 | ||
| 80 | /// Gets bound buffer used on this shader | ||
| 81 | u32 GetBoundBuffer() const { | ||
| 82 | return bound_buffer; | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Obtains access to the guest driver's profile. | ||
| 86 | VideoCore::GuestDriverProfile* AccessGuestDriverProfile() const { | ||
| 87 | if (engine) { | ||
| 88 | return &engine->AccessGuestDriverProfile(); | ||
| 89 | } | ||
| 90 | return nullptr; | ||
| 91 | } | ||
| 92 | |||
| 74 | private: | 93 | private: |
| 75 | const Tegra::Engines::ShaderType stage; | 94 | const Tegra::Engines::ShaderType stage; |
| 76 | Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; | 95 | Tegra::Engines::ConstBufferEngineInterface* engine = nullptr; |
| 77 | KeyMap keys; | 96 | KeyMap keys; |
| 78 | BoundSamplerMap bound_samplers; | 97 | BoundSamplerMap bound_samplers; |
| 79 | BindlessSamplerMap bindless_samplers; | 98 | BindlessSamplerMap bindless_samplers; |
| 99 | bool bound_buffer_saved{}; | ||
| 100 | u32 bound_buffer{}; | ||
| 80 | }; | 101 | }; |
| 81 | 102 | ||
| 82 | } // namespace VideoCommon::Shader | 103 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode.cpp b/src/video_core/shader/decode.cpp index 22c3e5120..6b697ed5d 100644 --- a/src/video_core/shader/decode.cpp +++ b/src/video_core/shader/decode.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include <limits> | ||
| 6 | #include <set> | 7 | #include <set> |
| 7 | 8 | ||
| 8 | #include <fmt/format.h> | 9 | #include <fmt/format.h> |
| @@ -33,6 +34,52 @@ constexpr bool IsSchedInstruction(u32 offset, u32 main_offset) { | |||
| 33 | return (absolute_offset % SchedPeriod) == 0; | 34 | return (absolute_offset % SchedPeriod) == 0; |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 37 | void DeduceTextureHandlerSize(VideoCore::GuestDriverProfile* gpu_driver, | ||
| 38 | const std::list<Sampler>& used_samplers) { | ||
| 39 | if (gpu_driver == nullptr) { | ||
| 40 | LOG_CRITICAL(HW_GPU, "GPU driver profile has not been created yet"); | ||
| 41 | return; | ||
| 42 | } | ||
| 43 | if (gpu_driver->TextureHandlerSizeKnown() || used_samplers.size() <= 1) { | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | u32 count{}; | ||
| 47 | std::vector<u32> bound_offsets; | ||
| 48 | for (const auto& sampler : used_samplers) { | ||
| 49 | if (sampler.IsBindless()) { | ||
| 50 | continue; | ||
| 51 | } | ||
| 52 | ++count; | ||
| 53 | bound_offsets.emplace_back(sampler.GetOffset()); | ||
| 54 | } | ||
| 55 | if (count > 1) { | ||
| 56 | gpu_driver->DeduceTextureHandlerSize(std::move(bound_offsets)); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | std::optional<u32> TryDeduceSamplerSize(const Sampler& sampler_to_deduce, | ||
| 61 | VideoCore::GuestDriverProfile* gpu_driver, | ||
| 62 | const std::list<Sampler>& used_samplers) { | ||
| 63 | if (gpu_driver == nullptr) { | ||
| 64 | LOG_CRITICAL(HW_GPU, "GPU Driver profile has not been created yet"); | ||
| 65 | return std::nullopt; | ||
| 66 | } | ||
| 67 | const u32 base_offset = sampler_to_deduce.GetOffset(); | ||
| 68 | u32 max_offset{std::numeric_limits<u32>::max()}; | ||
| 69 | for (const auto& sampler : used_samplers) { | ||
| 70 | if (sampler.IsBindless()) { | ||
| 71 | continue; | ||
| 72 | } | ||
| 73 | if (sampler.GetOffset() > base_offset) { | ||
| 74 | max_offset = std::min(sampler.GetOffset(), max_offset); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | if (max_offset == std::numeric_limits<u32>::max()) { | ||
| 78 | return std::nullopt; | ||
| 79 | } | ||
| 80 | return ((max_offset - base_offset) * 4) / gpu_driver->GetTextureHandlerSize(); | ||
| 81 | } | ||
| 82 | |||
| 36 | } // Anonymous namespace | 83 | } // Anonymous namespace |
| 37 | 84 | ||
| 38 | class ASTDecoder { | 85 | class ASTDecoder { |
| @@ -315,4 +362,25 @@ u32 ShaderIR::DecodeInstr(NodeBlock& bb, u32 pc) { | |||
| 315 | return pc + 1; | 362 | return pc + 1; |
| 316 | } | 363 | } |
| 317 | 364 | ||
| 365 | void ShaderIR::PostDecode() { | ||
| 366 | // Deduce texture handler size if needed | ||
| 367 | auto gpu_driver = locker.AccessGuestDriverProfile(); | ||
| 368 | DeduceTextureHandlerSize(gpu_driver, used_samplers); | ||
| 369 | // Deduce Indexed Samplers | ||
| 370 | if (!uses_indexed_samplers) { | ||
| 371 | return; | ||
| 372 | } | ||
| 373 | for (auto& sampler : used_samplers) { | ||
| 374 | if (!sampler.IsIndexed()) { | ||
| 375 | continue; | ||
| 376 | } | ||
| 377 | if (const auto size = TryDeduceSamplerSize(sampler, gpu_driver, used_samplers)) { | ||
| 378 | sampler.SetSize(*size); | ||
| 379 | } else { | ||
| 380 | LOG_CRITICAL(HW_GPU, "Failed to deduce size of indexed sampler"); | ||
| 381 | sampler.SetSize(1); | ||
| 382 | } | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 318 | } // namespace VideoCommon::Shader | 386 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp index fcedd2af6..90240c765 100644 --- a/src/video_core/shader/decode/arithmetic.cpp +++ b/src/video_core/shader/decode/arithmetic.cpp | |||
| @@ -21,7 +21,7 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { | |||
| 21 | 21 | ||
| 22 | Node op_a = GetRegister(instr.gpr8); | 22 | Node op_a = GetRegister(instr.gpr8); |
| 23 | 23 | ||
| 24 | Node op_b = [&]() -> Node { | 24 | Node op_b = [&] { |
| 25 | if (instr.is_b_imm) { | 25 | if (instr.is_b_imm) { |
| 26 | return GetImmediate19(instr); | 26 | return GetImmediate19(instr); |
| 27 | } else if (instr.is_b_gpr) { | 27 | } else if (instr.is_b_gpr) { |
| @@ -141,6 +141,15 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) { | |||
| 141 | SetRegister(bb, instr.gpr0, value); | 141 | SetRegister(bb, instr.gpr0, value); |
| 142 | break; | 142 | break; |
| 143 | } | 143 | } |
| 144 | case OpCode::Id::FCMP_R: { | ||
| 145 | UNIMPLEMENTED_IF(instr.fcmp.ftz == 0); | ||
| 146 | Node op_c = GetRegister(instr.gpr39); | ||
| 147 | Node comp = GetPredicateComparisonFloat(instr.fcmp.cond, std::move(op_c), Immediate(0.0f)); | ||
| 148 | SetRegister( | ||
| 149 | bb, instr.gpr0, | ||
| 150 | Operation(OperationCode::Select, std::move(comp), std::move(op_a), std::move(op_b))); | ||
| 151 | break; | ||
| 152 | } | ||
| 144 | case OpCode::Id::RRO_C: | 153 | case OpCode::Id::RRO_C: |
| 145 | case OpCode::Id::RRO_R: | 154 | case OpCode::Id::RRO_R: |
| 146 | case OpCode::Id::RRO_IMM: { | 155 | case OpCode::Id::RRO_IMM: { |
diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp index 371fae127..21366869d 100644 --- a/src/video_core/shader/decode/arithmetic_integer.cpp +++ b/src/video_core/shader/decode/arithmetic_integer.cpp | |||
| @@ -166,13 +166,13 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) { | |||
| 166 | const auto [op_rhs, test] = [&]() -> std::pair<Node, Node> { | 166 | const auto [op_rhs, test] = [&]() -> std::pair<Node, Node> { |
| 167 | switch (opcode->get().GetId()) { | 167 | switch (opcode->get().GetId()) { |
| 168 | case OpCode::Id::ICMP_CR: | 168 | case OpCode::Id::ICMP_CR: |
| 169 | return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset), | 169 | return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()), |
| 170 | GetRegister(instr.gpr39)}; | 170 | GetRegister(instr.gpr39)}; |
| 171 | case OpCode::Id::ICMP_R: | 171 | case OpCode::Id::ICMP_R: |
| 172 | return {GetRegister(instr.gpr20), GetRegister(instr.gpr39)}; | 172 | return {GetRegister(instr.gpr20), GetRegister(instr.gpr39)}; |
| 173 | case OpCode::Id::ICMP_RC: | 173 | case OpCode::Id::ICMP_RC: |
| 174 | return {GetRegister(instr.gpr39), | 174 | return {GetRegister(instr.gpr39), |
| 175 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)}; | 175 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; |
| 176 | case OpCode::Id::ICMP_IMM: | 176 | case OpCode::Id::ICMP_IMM: |
| 177 | return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)}; | 177 | return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)}; |
| 178 | default: | 178 | default: |
| @@ -297,7 +297,7 @@ void ShaderIR::WriteLop3Instruction(NodeBlock& bb, Register dest, Node op_a, Nod | |||
| 297 | const Node one = Immediate(1); | 297 | const Node one = Immediate(1); |
| 298 | const Node two = Immediate(2); | 298 | const Node two = Immediate(2); |
| 299 | 299 | ||
| 300 | Node value{}; | 300 | Node value; |
| 301 | for (u32 i = 0; i < lop_iterations; ++i) { | 301 | for (u32 i = 0; i < lop_iterations; ++i) { |
| 302 | const Node shift_amount = Immediate(i); | 302 | const Node shift_amount = Immediate(i); |
| 303 | 303 | ||
diff --git a/src/video_core/shader/decode/bfi.cpp b/src/video_core/shader/decode/bfi.cpp index 8be1119df..70d1c055b 100644 --- a/src/video_core/shader/decode/bfi.cpp +++ b/src/video_core/shader/decode/bfi.cpp | |||
| @@ -17,10 +17,13 @@ u32 ShaderIR::DecodeBfi(NodeBlock& bb, u32 pc) { | |||
| 17 | const Instruction instr = {program_code[pc]}; | 17 | const Instruction instr = {program_code[pc]}; |
| 18 | const auto opcode = OpCode::Decode(instr); | 18 | const auto opcode = OpCode::Decode(instr); |
| 19 | 19 | ||
| 20 | const auto [base, packed_shift] = [&]() -> std::tuple<Node, Node> { | 20 | const auto [packed_shift, base] = [&]() -> std::pair<Node, Node> { |
| 21 | switch (opcode->get().GetId()) { | 21 | switch (opcode->get().GetId()) { |
| 22 | case OpCode::Id::BFI_RC: | ||
| 23 | return {GetRegister(instr.gpr39), | ||
| 24 | GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset())}; | ||
| 22 | case OpCode::Id::BFI_IMM_R: | 25 | case OpCode::Id::BFI_IMM_R: |
| 23 | return {GetRegister(instr.gpr39), Immediate(instr.alu.GetSignedImm20_20())}; | 26 | return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)}; |
| 24 | default: | 27 | default: |
| 25 | UNREACHABLE(); | 28 | UNREACHABLE(); |
| 26 | return {Immediate(0), Immediate(0)}; | 29 | return {Immediate(0), Immediate(0)}; |
diff --git a/src/video_core/shader/decode/conversion.cpp b/src/video_core/shader/decode/conversion.cpp index 0eeb75559..6ead42070 100644 --- a/src/video_core/shader/decode/conversion.cpp +++ b/src/video_core/shader/decode/conversion.cpp | |||
| @@ -83,14 +83,14 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | |||
| 83 | 83 | ||
| 84 | const bool input_signed = instr.conversion.is_input_signed; | 84 | const bool input_signed = instr.conversion.is_input_signed; |
| 85 | 85 | ||
| 86 | if (instr.conversion.src_size == Register::Size::Byte) { | 86 | if (const u32 offset = static_cast<u32>(instr.conversion.int_src.selector); offset > 0) { |
| 87 | const u32 offset = static_cast<u32>(instr.conversion.int_src.selector) * 8; | 87 | ASSERT(instr.conversion.src_size == Register::Size::Byte || |
| 88 | if (offset > 0) { | 88 | instr.conversion.src_size == Register::Size::Short); |
| 89 | value = SignedOperation(OperationCode::ILogicalShiftRight, input_signed, | 89 | if (instr.conversion.src_size == Register::Size::Short) { |
| 90 | std::move(value), Immediate(offset)); | 90 | ASSERT(offset == 0 || offset == 2); |
| 91 | } | 91 | } |
| 92 | } else { | 92 | value = SignedOperation(OperationCode::ILogicalShiftRight, input_signed, |
| 93 | UNIMPLEMENTED_IF(instr.conversion.int_src.selector != 0); | 93 | std::move(value), Immediate(offset * 8)); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed); | 96 | value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed); |
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index 7591a715f..b5fbc4d58 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp | |||
| @@ -19,9 +19,12 @@ namespace VideoCommon::Shader { | |||
| 19 | using Tegra::Shader::AtomicOp; | 19 | using Tegra::Shader::AtomicOp; |
| 20 | using Tegra::Shader::AtomicType; | 20 | using Tegra::Shader::AtomicType; |
| 21 | using Tegra::Shader::Attribute; | 21 | using Tegra::Shader::Attribute; |
| 22 | using Tegra::Shader::GlobalAtomicOp; | ||
| 23 | using Tegra::Shader::GlobalAtomicType; | ||
| 22 | using Tegra::Shader::Instruction; | 24 | using Tegra::Shader::Instruction; |
| 23 | using Tegra::Shader::OpCode; | 25 | using Tegra::Shader::OpCode; |
| 24 | using Tegra::Shader::Register; | 26 | using Tegra::Shader::Register; |
| 27 | using Tegra::Shader::StoreType; | ||
| 25 | 28 | ||
| 26 | namespace { | 29 | namespace { |
| 27 | 30 | ||
| @@ -61,6 +64,27 @@ u32 GetMemorySize(Tegra::Shader::UniformType uniform_type) { | |||
| 61 | } | 64 | } |
| 62 | } | 65 | } |
| 63 | 66 | ||
| 67 | Node ExtractUnaligned(Node value, Node address, u32 mask, u32 size) { | ||
| 68 | Node offset = Operation(OperationCode::UBitwiseAnd, address, Immediate(mask)); | ||
| 69 | offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3)); | ||
| 70 | return Operation(OperationCode::UBitfieldExtract, std::move(value), std::move(offset), | ||
| 71 | Immediate(size)); | ||
| 72 | } | ||
| 73 | |||
| 74 | Node InsertUnaligned(Node dest, Node value, Node address, u32 mask, u32 size) { | ||
| 75 | Node offset = Operation(OperationCode::UBitwiseAnd, std::move(address), Immediate(mask)); | ||
| 76 | offset = Operation(OperationCode::ULogicalShiftLeft, std::move(offset), Immediate(3)); | ||
| 77 | return Operation(OperationCode::UBitfieldInsert, std::move(dest), std::move(value), | ||
| 78 | std::move(offset), Immediate(size)); | ||
| 79 | } | ||
| 80 | |||
| 81 | Node Sign16Extend(Node value) { | ||
| 82 | Node sign = Operation(OperationCode::UBitwiseAnd, value, Immediate(1U << 15)); | ||
| 83 | Node is_sign = Operation(OperationCode::LogicalUEqual, std::move(sign), Immediate(1U << 15)); | ||
| 84 | Node extend = Operation(OperationCode::Select, is_sign, Immediate(0xFFFF0000), Immediate(0)); | ||
| 85 | return Operation(OperationCode::UBitwiseOr, std::move(value), std::move(extend)); | ||
| 86 | } | ||
| 87 | |||
| 64 | } // Anonymous namespace | 88 | } // Anonymous namespace |
| 65 | 89 | ||
| 66 | u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | 90 | u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { |
| @@ -136,26 +160,31 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 136 | LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown)); | 160 | LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown)); |
| 137 | [[fallthrough]]; | 161 | [[fallthrough]]; |
| 138 | case OpCode::Id::LD_S: { | 162 | case OpCode::Id::LD_S: { |
| 139 | const auto GetMemory = [&](s32 offset) { | 163 | const auto GetAddress = [&](s32 offset) { |
| 140 | ASSERT(offset % 4 == 0); | 164 | ASSERT(offset % 4 == 0); |
| 141 | const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset); | 165 | const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset); |
| 142 | const Node address = Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), | 166 | return Operation(OperationCode::IAdd, GetRegister(instr.gpr8), immediate_offset); |
| 143 | immediate_offset); | 167 | }; |
| 144 | return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(address) | 168 | const auto GetMemory = [&](s32 offset) { |
| 145 | : GetLocalMemory(address); | 169 | return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(GetAddress(offset)) |
| 170 | : GetLocalMemory(GetAddress(offset)); | ||
| 146 | }; | 171 | }; |
| 147 | 172 | ||
| 148 | switch (instr.ldst_sl.type.Value()) { | 173 | switch (instr.ldst_sl.type.Value()) { |
| 149 | case Tegra::Shader::StoreType::Bits32: | 174 | case StoreType::Signed16: |
| 150 | case Tegra::Shader::StoreType::Bits64: | 175 | SetRegister(bb, instr.gpr0, |
| 151 | case Tegra::Shader::StoreType::Bits128: { | 176 | Sign16Extend(ExtractUnaligned(GetMemory(0), GetAddress(0), 0b10, 16))); |
| 152 | const u32 count = [&]() { | 177 | break; |
| 178 | case StoreType::Bits32: | ||
| 179 | case StoreType::Bits64: | ||
| 180 | case StoreType::Bits128: { | ||
| 181 | const u32 count = [&] { | ||
| 153 | switch (instr.ldst_sl.type.Value()) { | 182 | switch (instr.ldst_sl.type.Value()) { |
| 154 | case Tegra::Shader::StoreType::Bits32: | 183 | case StoreType::Bits32: |
| 155 | return 1; | 184 | return 1; |
| 156 | case Tegra::Shader::StoreType::Bits64: | 185 | case StoreType::Bits64: |
| 157 | return 2; | 186 | return 2; |
| 158 | case Tegra::Shader::StoreType::Bits128: | 187 | case StoreType::Bits128: |
| 159 | return 4; | 188 | return 4; |
| 160 | default: | 189 | default: |
| 161 | UNREACHABLE(); | 190 | UNREACHABLE(); |
| @@ -212,12 +241,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 212 | // To handle unaligned loads get the bytes used to dereference global memory and extract | 241 | // To handle unaligned loads get the bytes used to dereference global memory and extract |
| 213 | // those bytes from the loaded u32. | 242 | // those bytes from the loaded u32. |
| 214 | if (IsUnaligned(type)) { | 243 | if (IsUnaligned(type)) { |
| 215 | Node mask = Immediate(GetUnalignedMask(type)); | 244 | gmem = ExtractUnaligned(gmem, real_address, GetUnalignedMask(type), size); |
| 216 | Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask)); | ||
| 217 | offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3)); | ||
| 218 | |||
| 219 | gmem = Operation(OperationCode::UBitfieldExtract, std::move(gmem), | ||
| 220 | std::move(offset), Immediate(size)); | ||
| 221 | } | 245 | } |
| 222 | 246 | ||
| 223 | SetTemporary(bb, i, gmem); | 247 | SetTemporary(bb, i, gmem); |
| @@ -269,21 +293,28 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 269 | return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate); | 293 | return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate); |
| 270 | }; | 294 | }; |
| 271 | 295 | ||
| 272 | const auto set_memory = opcode->get().GetId() == OpCode::Id::ST_L | 296 | const bool is_local = opcode->get().GetId() == OpCode::Id::ST_L; |
| 273 | ? &ShaderIR::SetLocalMemory | 297 | const auto set_memory = is_local ? &ShaderIR::SetLocalMemory : &ShaderIR::SetSharedMemory; |
| 274 | : &ShaderIR::SetSharedMemory; | 298 | const auto get_memory = is_local ? &ShaderIR::GetLocalMemory : &ShaderIR::GetSharedMemory; |
| 275 | 299 | ||
| 276 | switch (instr.ldst_sl.type.Value()) { | 300 | switch (instr.ldst_sl.type.Value()) { |
| 277 | case Tegra::Shader::StoreType::Bits128: | 301 | case StoreType::Bits128: |
| 278 | (this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3)); | 302 | (this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3)); |
| 279 | (this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2)); | 303 | (this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2)); |
| 280 | [[fallthrough]]; | 304 | [[fallthrough]]; |
| 281 | case Tegra::Shader::StoreType::Bits64: | 305 | case StoreType::Bits64: |
| 282 | (this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1)); | 306 | (this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1)); |
| 283 | [[fallthrough]]; | 307 | [[fallthrough]]; |
| 284 | case Tegra::Shader::StoreType::Bits32: | 308 | case StoreType::Bits32: |
| 285 | (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0)); | 309 | (this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0)); |
| 286 | break; | 310 | break; |
| 311 | case StoreType::Signed16: { | ||
| 312 | Node address = GetAddress(0); | ||
| 313 | Node memory = (this->*get_memory)(address); | ||
| 314 | (this->*set_memory)( | ||
| 315 | bb, address, InsertUnaligned(memory, GetRegister(instr.gpr0), address, 0b10, 16)); | ||
| 316 | break; | ||
| 317 | } | ||
| 287 | default: | 318 | default: |
| 288 | UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(), | 319 | UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(), |
| 289 | static_cast<u32>(instr.ldst_sl.type.Value())); | 320 | static_cast<u32>(instr.ldst_sl.type.Value())); |
| @@ -323,18 +354,32 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 323 | Node value = GetRegister(instr.gpr0.Value() + i); | 354 | Node value = GetRegister(instr.gpr0.Value() + i); |
| 324 | 355 | ||
| 325 | if (IsUnaligned(type)) { | 356 | if (IsUnaligned(type)) { |
| 326 | Node mask = Immediate(GetUnalignedMask(type)); | 357 | const u32 mask = GetUnalignedMask(type); |
| 327 | Node offset = Operation(OperationCode::UBitwiseAnd, real_address, std::move(mask)); | 358 | value = InsertUnaligned(gmem, std::move(value), real_address, mask, size); |
| 328 | offset = Operation(OperationCode::ULogicalShiftLeft, offset, Immediate(3)); | ||
| 329 | |||
| 330 | value = Operation(OperationCode::UBitfieldInsert, gmem, std::move(value), offset, | ||
| 331 | Immediate(size)); | ||
| 332 | } | 359 | } |
| 333 | 360 | ||
| 334 | bb.push_back(Operation(OperationCode::Assign, gmem, value)); | 361 | bb.push_back(Operation(OperationCode::Assign, gmem, value)); |
| 335 | } | 362 | } |
| 336 | break; | 363 | break; |
| 337 | } | 364 | } |
| 365 | case OpCode::Id::ATOM: { | ||
| 366 | UNIMPLEMENTED_IF_MSG(instr.atom.operation != GlobalAtomicOp::Add, "operation={}", | ||
| 367 | static_cast<int>(instr.atom.operation.Value())); | ||
| 368 | UNIMPLEMENTED_IF_MSG(instr.atom.type != GlobalAtomicType::S32, "type={}", | ||
| 369 | static_cast<int>(instr.atom.type.Value())); | ||
| 370 | |||
| 371 | const auto [real_address, base_address, descriptor] = | ||
| 372 | TrackGlobalMemory(bb, instr, true, true); | ||
| 373 | if (!real_address || !base_address) { | ||
| 374 | // Tracking failed, skip atomic. | ||
| 375 | break; | ||
| 376 | } | ||
| 377 | |||
| 378 | Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor); | ||
| 379 | Node value = Operation(OperationCode::AtomicAdd, std::move(gmem), GetRegister(instr.gpr20)); | ||
| 380 | SetRegister(bb, instr.gpr0, std::move(value)); | ||
| 381 | break; | ||
| 382 | } | ||
| 338 | case OpCode::Id::ATOMS: { | 383 | case OpCode::Id::ATOMS: { |
| 339 | UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}", | 384 | UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}", |
| 340 | static_cast<int>(instr.atoms.operation.Value())); | 385 | static_cast<int>(instr.atoms.operation.Value())); |
| @@ -348,7 +393,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { | |||
| 348 | Node memory = GetSharedMemory(std::move(address)); | 393 | Node memory = GetSharedMemory(std::move(address)); |
| 349 | Node data = GetRegister(instr.gpr20); | 394 | Node data = GetRegister(instr.gpr20); |
| 350 | 395 | ||
| 351 | Node value = Operation(OperationCode::UAtomicAdd, std::move(memory), std::move(data)); | 396 | Node value = Operation(OperationCode::AtomicAdd, std::move(memory), std::move(data)); |
| 352 | SetRegister(bb, instr.gpr0, std::move(value)); | 397 | SetRegister(bb, instr.gpr0, std::move(value)); |
| 353 | break; | 398 | break; |
| 354 | } | 399 | } |
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp index 7321698b2..4944e9d69 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp | |||
| @@ -69,13 +69,16 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 69 | case OpCode::Id::MOV_SYS: { | 69 | case OpCode::Id::MOV_SYS: { |
| 70 | const Node value = [this, instr] { | 70 | const Node value = [this, instr] { |
| 71 | switch (instr.sys20) { | 71 | switch (instr.sys20) { |
| 72 | case SystemVariable::LaneId: | ||
| 73 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with LaneId is incomplete"); | ||
| 74 | return Immediate(0U); | ||
| 72 | case SystemVariable::InvocationId: | 75 | case SystemVariable::InvocationId: |
| 73 | return Operation(OperationCode::InvocationId); | 76 | return Operation(OperationCode::InvocationId); |
| 74 | case SystemVariable::Ydirection: | 77 | case SystemVariable::Ydirection: |
| 75 | return Operation(OperationCode::YNegate); | 78 | return Operation(OperationCode::YNegate); |
| 76 | case SystemVariable::InvocationInfo: | 79 | case SystemVariable::InvocationInfo: |
| 77 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); | 80 | LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete"); |
| 78 | return Immediate(0u); | 81 | return Immediate(0U); |
| 79 | case SystemVariable::Tid: { | 82 | case SystemVariable::Tid: { |
| 80 | Node value = Immediate(0); | 83 | Node value = Immediate(0); |
| 81 | value = BitfieldInsert(value, Operation(OperationCode::LocalInvocationIdX), 0, 9); | 84 | value = BitfieldInsert(value, Operation(OperationCode::LocalInvocationIdX), 0, 9); |
| @@ -188,7 +191,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 188 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}", | 191 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "SYNC condition code used: {}", |
| 189 | static_cast<u32>(cc)); | 192 | static_cast<u32>(cc)); |
| 190 | 193 | ||
| 191 | if (disable_flow_stack) { | 194 | if (decompiled) { |
| 192 | break; | 195 | break; |
| 193 | } | 196 | } |
| 194 | 197 | ||
| @@ -200,7 +203,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { | |||
| 200 | const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; | 203 | const Tegra::Shader::ConditionCode cc = instr.flow_condition_code; |
| 201 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}", | 204 | UNIMPLEMENTED_IF_MSG(cc != Tegra::Shader::ConditionCode::T, "BRK condition code used: {}", |
| 202 | static_cast<u32>(cc)); | 205 | static_cast<u32>(cc)); |
| 203 | if (disable_flow_stack) { | 206 | if (decompiled) { |
| 204 | break; | 207 | break; |
| 205 | } | 208 | } |
| 206 | 209 | ||
diff --git a/src/video_core/shader/decode/shift.cpp b/src/video_core/shader/decode/shift.cpp index d419e9c45..3b391d3e6 100644 --- a/src/video_core/shader/decode/shift.cpp +++ b/src/video_core/shader/decode/shift.cpp | |||
| @@ -10,8 +10,80 @@ | |||
| 10 | 10 | ||
| 11 | namespace VideoCommon::Shader { | 11 | namespace VideoCommon::Shader { |
| 12 | 12 | ||
| 13 | using std::move; | ||
| 13 | using Tegra::Shader::Instruction; | 14 | using Tegra::Shader::Instruction; |
| 14 | using Tegra::Shader::OpCode; | 15 | using Tegra::Shader::OpCode; |
| 16 | using Tegra::Shader::ShfType; | ||
| 17 | using Tegra::Shader::ShfXmode; | ||
| 18 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | Node IsFull(Node shift) { | ||
| 22 | return Operation(OperationCode::LogicalIEqual, move(shift), Immediate(32)); | ||
| 23 | } | ||
| 24 | |||
| 25 | Node Shift(OperationCode opcode, Node value, Node shift) { | ||
| 26 | Node is_full = Operation(OperationCode::LogicalIEqual, shift, Immediate(32)); | ||
| 27 | Node shifted = Operation(opcode, move(value), shift); | ||
| 28 | return Operation(OperationCode::Select, IsFull(move(shift)), Immediate(0), move(shifted)); | ||
| 29 | } | ||
| 30 | |||
| 31 | Node ClampShift(Node shift, s32 size = 32) { | ||
| 32 | shift = Operation(OperationCode::IMax, move(shift), Immediate(0)); | ||
| 33 | return Operation(OperationCode::IMin, move(shift), Immediate(size)); | ||
| 34 | } | ||
| 35 | |||
| 36 | Node WrapShift(Node shift, s32 size = 32) { | ||
| 37 | return Operation(OperationCode::UBitwiseAnd, move(shift), Immediate(size - 1)); | ||
| 38 | } | ||
| 39 | |||
| 40 | Node ShiftRight(Node low, Node high, Node shift, Node low_shift, ShfType type) { | ||
| 41 | // These values are used when the shift value is less than 32 | ||
| 42 | Node less_low = Shift(OperationCode::ILogicalShiftRight, low, shift); | ||
| 43 | Node less_high = Shift(OperationCode::ILogicalShiftLeft, high, low_shift); | ||
| 44 | Node less = Operation(OperationCode::IBitwiseOr, move(less_high), move(less_low)); | ||
| 45 | |||
| 46 | if (type == ShfType::Bits32) { | ||
| 47 | // On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits | ||
| 48 | return Operation(OperationCode::Select, IsFull(move(shift)), move(high), move(less)); | ||
| 49 | } | ||
| 50 | |||
| 51 | // And these when it's larger than or 32 | ||
| 52 | const bool is_signed = type == ShfType::S64; | ||
| 53 | const auto opcode = SignedToUnsignedCode(OperationCode::IArithmeticShiftRight, is_signed); | ||
| 54 | Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32)); | ||
| 55 | Node greater = Shift(opcode, high, move(reduced)); | ||
| 56 | |||
| 57 | Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32)); | ||
| 58 | Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0)); | ||
| 59 | |||
| 60 | Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater)); | ||
| 61 | return Operation(OperationCode::Select, move(is_zero), move(high), move(value)); | ||
| 62 | } | ||
| 63 | |||
| 64 | Node ShiftLeft(Node low, Node high, Node shift, Node low_shift, ShfType type) { | ||
| 65 | // These values are used when the shift value is less than 32 | ||
| 66 | Node less_low = Operation(OperationCode::ILogicalShiftRight, low, low_shift); | ||
| 67 | Node less_high = Operation(OperationCode::ILogicalShiftLeft, high, shift); | ||
| 68 | Node less = Operation(OperationCode::IBitwiseOr, move(less_low), move(less_high)); | ||
| 69 | |||
| 70 | if (type == ShfType::Bits32) { | ||
| 71 | // On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits | ||
| 72 | return Operation(OperationCode::Select, IsFull(move(shift)), move(low), move(less)); | ||
| 73 | } | ||
| 74 | |||
| 75 | // And these when it's larger than or 32 | ||
| 76 | Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32)); | ||
| 77 | Node greater = Shift(OperationCode::ILogicalShiftLeft, move(low), move(reduced)); | ||
| 78 | |||
| 79 | Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32)); | ||
| 80 | Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0)); | ||
| 81 | |||
| 82 | Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater)); | ||
| 83 | return Operation(OperationCode::Select, move(is_zero), move(high), move(value)); | ||
| 84 | } | ||
| 85 | |||
| 86 | } // Anonymous namespace | ||
| 15 | 87 | ||
| 16 | u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) { | 88 | u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) { |
| 17 | const Instruction instr = {program_code[pc]}; | 89 | const Instruction instr = {program_code[pc]}; |
| @@ -28,29 +100,48 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) { | |||
| 28 | } | 100 | } |
| 29 | }(); | 101 | }(); |
| 30 | 102 | ||
| 31 | switch (opcode->get().GetId()) { | 103 | switch (const auto opid = opcode->get().GetId(); opid) { |
| 32 | case OpCode::Id::SHR_C: | 104 | case OpCode::Id::SHR_C: |
| 33 | case OpCode::Id::SHR_R: | 105 | case OpCode::Id::SHR_R: |
| 34 | case OpCode::Id::SHR_IMM: { | 106 | case OpCode::Id::SHR_IMM: { |
| 35 | if (instr.shr.wrap) { | 107 | op_b = instr.shr.wrap ? WrapShift(move(op_b)) : ClampShift(move(op_b)); |
| 36 | op_b = Operation(OperationCode::UBitwiseAnd, std::move(op_b), Immediate(0x1f)); | ||
| 37 | } else { | ||
| 38 | op_b = Operation(OperationCode::IMax, std::move(op_b), Immediate(0)); | ||
| 39 | op_b = Operation(OperationCode::IMin, std::move(op_b), Immediate(31)); | ||
| 40 | } | ||
| 41 | 108 | ||
| 42 | Node value = SignedOperation(OperationCode::IArithmeticShiftRight, instr.shift.is_signed, | 109 | Node value = SignedOperation(OperationCode::IArithmeticShiftRight, instr.shift.is_signed, |
| 43 | std::move(op_a), std::move(op_b)); | 110 | move(op_a), move(op_b)); |
| 44 | SetInternalFlagsFromInteger(bb, value, instr.generates_cc); | 111 | SetInternalFlagsFromInteger(bb, value, instr.generates_cc); |
| 45 | SetRegister(bb, instr.gpr0, std::move(value)); | 112 | SetRegister(bb, instr.gpr0, move(value)); |
| 46 | break; | 113 | break; |
| 47 | } | 114 | } |
| 48 | case OpCode::Id::SHL_C: | 115 | case OpCode::Id::SHL_C: |
| 49 | case OpCode::Id::SHL_R: | 116 | case OpCode::Id::SHL_R: |
| 50 | case OpCode::Id::SHL_IMM: { | 117 | case OpCode::Id::SHL_IMM: { |
| 51 | const Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b); | 118 | Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b); |
| 52 | SetInternalFlagsFromInteger(bb, value, instr.generates_cc); | 119 | SetInternalFlagsFromInteger(bb, value, instr.generates_cc); |
| 53 | SetRegister(bb, instr.gpr0, value); | 120 | SetRegister(bb, instr.gpr0, move(value)); |
| 121 | break; | ||
| 122 | } | ||
| 123 | case OpCode::Id::SHF_RIGHT_R: | ||
| 124 | case OpCode::Id::SHF_RIGHT_IMM: | ||
| 125 | case OpCode::Id::SHF_LEFT_R: | ||
| 126 | case OpCode::Id::SHF_LEFT_IMM: { | ||
| 127 | UNIMPLEMENTED_IF(instr.generates_cc); | ||
| 128 | UNIMPLEMENTED_IF_MSG(instr.shf.xmode != ShfXmode::None, "xmode={}", | ||
| 129 | static_cast<int>(instr.shf.xmode.Value())); | ||
| 130 | |||
| 131 | if (instr.is_b_imm) { | ||
| 132 | op_b = Immediate(static_cast<u32>(instr.shf.immediate)); | ||
| 133 | } | ||
| 134 | const s32 size = instr.shf.type == ShfType::Bits32 ? 32 : 64; | ||
| 135 | Node shift = instr.shf.wrap ? WrapShift(move(op_b), size) : ClampShift(move(op_b), size); | ||
| 136 | |||
| 137 | Node negated_shift = Operation(OperationCode::INegate, shift); | ||
| 138 | Node low_shift = Operation(OperationCode::IAdd, move(negated_shift), Immediate(32)); | ||
| 139 | |||
| 140 | const bool is_right = opid == OpCode::Id::SHF_RIGHT_R || opid == OpCode::Id::SHF_RIGHT_IMM; | ||
| 141 | Node value = (is_right ? ShiftRight : ShiftLeft)( | ||
| 142 | move(op_a), GetRegister(instr.gpr39), move(shift), move(low_shift), instr.shf.type); | ||
| 143 | |||
| 144 | SetRegister(bb, instr.gpr0, move(value)); | ||
| 54 | break; | 145 | break; |
| 55 | } | 146 | } |
| 56 | default: | 147 | default: |
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp index cd984f763..bee7d8cad 100644 --- a/src/video_core/shader/decode/texture.cpp +++ b/src/video_core/shader/decode/texture.cpp | |||
| @@ -144,7 +144,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 144 | Node4 values; | 144 | Node4 values; |
| 145 | for (u32 element = 0; element < values.size(); ++element) { | 145 | for (u32 element = 0; element < values.size(); ++element) { |
| 146 | auto coords_copy = coords; | 146 | auto coords_copy = coords; |
| 147 | MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {}, {}, {}, component, element}; | 147 | MetaTexture meta{sampler, {}, depth_compare, aoffi, {}, {}, |
| 148 | {}, {}, component, element, {}}; | ||
| 148 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); | 149 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); |
| 149 | } | 150 | } |
| 150 | 151 | ||
| @@ -161,16 +162,16 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 161 | case OpCode::Id::TXD: { | 162 | case OpCode::Id::TXD: { |
| 162 | UNIMPLEMENTED_IF_MSG(instr.txd.UsesMiscMode(TextureMiscMode::AOFFI), | 163 | UNIMPLEMENTED_IF_MSG(instr.txd.UsesMiscMode(TextureMiscMode::AOFFI), |
| 163 | "AOFFI is not implemented"); | 164 | "AOFFI is not implemented"); |
| 164 | UNIMPLEMENTED_IF_MSG(instr.txd.is_array != 0, "TXD Array is not implemented"); | ||
| 165 | 165 | ||
| 166 | const bool is_array = instr.txd.is_array != 0; | ||
| 166 | u64 base_reg = instr.gpr8.Value(); | 167 | u64 base_reg = instr.gpr8.Value(); |
| 167 | const auto derivate_reg = instr.gpr20.Value(); | 168 | const auto derivate_reg = instr.gpr20.Value(); |
| 168 | const auto texture_type = instr.txd.texture_type.Value(); | 169 | const auto texture_type = instr.txd.texture_type.Value(); |
| 169 | const auto coord_count = GetCoordCount(texture_type); | 170 | const auto coord_count = GetCoordCount(texture_type); |
| 170 | 171 | Node index_var{}; | |
| 171 | const Sampler* sampler = is_bindless | 172 | const Sampler* sampler = |
| 172 | ? GetBindlessSampler(base_reg, {{texture_type, false, false}}) | 173 | is_bindless ? GetBindlessSampler(base_reg, index_var, {{texture_type, is_array, false}}) |
| 173 | : GetSampler(instr.sampler, {{texture_type, false, false}}); | 174 | : GetSampler(instr.sampler, {{texture_type, is_array, false}}); |
| 174 | Node4 values; | 175 | Node4 values; |
| 175 | if (sampler == nullptr) { | 176 | if (sampler == nullptr) { |
| 176 | for (u32 element = 0; element < values.size(); ++element) { | 177 | for (u32 element = 0; element < values.size(); ++element) { |
| @@ -179,6 +180,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 179 | WriteTexInstructionFloat(bb, instr, values); | 180 | WriteTexInstructionFloat(bb, instr, values); |
| 180 | break; | 181 | break; |
| 181 | } | 182 | } |
| 183 | |||
| 182 | if (is_bindless) { | 184 | if (is_bindless) { |
| 183 | base_reg++; | 185 | base_reg++; |
| 184 | } | 186 | } |
| @@ -192,8 +194,15 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 192 | derivates.push_back(GetRegister(derivate_reg + derivate + 1)); | 194 | derivates.push_back(GetRegister(derivate_reg + derivate + 1)); |
| 193 | } | 195 | } |
| 194 | 196 | ||
| 197 | Node array_node = {}; | ||
| 198 | if (is_array) { | ||
| 199 | const Node info_reg = GetRegister(base_reg + coord_count); | ||
| 200 | array_node = BitfieldExtract(info_reg, 0, 16); | ||
| 201 | } | ||
| 202 | |||
| 195 | for (u32 element = 0; element < values.size(); ++element) { | 203 | for (u32 element = 0; element < values.size(); ++element) { |
| 196 | MetaTexture meta{*sampler, {}, {}, {}, {}, derivates, {}, {}, {}, element}; | 204 | MetaTexture meta{*sampler, array_node, {}, {}, {}, derivates, |
| 205 | {}, {}, {}, element, index_var}; | ||
| 197 | values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords); | 206 | values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords); |
| 198 | } | 207 | } |
| 199 | 208 | ||
| @@ -208,8 +217,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 208 | // TODO: The new commits on the texture refactor, change the way samplers work. | 217 | // TODO: The new commits on the texture refactor, change the way samplers work. |
| 209 | // Sadly, not all texture instructions specify the type of texture their sampler | 218 | // Sadly, not all texture instructions specify the type of texture their sampler |
| 210 | // uses. This must be fixed at a later instance. | 219 | // uses. This must be fixed at a later instance. |
| 220 | Node index_var{}; | ||
| 211 | const Sampler* sampler = | 221 | const Sampler* sampler = |
| 212 | is_bindless ? GetBindlessSampler(instr.gpr8) : GetSampler(instr.sampler); | 222 | is_bindless ? GetBindlessSampler(instr.gpr8, index_var) : GetSampler(instr.sampler); |
| 213 | 223 | ||
| 214 | if (sampler == nullptr) { | 224 | if (sampler == nullptr) { |
| 215 | u32 indexer = 0; | 225 | u32 indexer = 0; |
| @@ -233,7 +243,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 233 | if (!instr.txq.IsComponentEnabled(element)) { | 243 | if (!instr.txq.IsComponentEnabled(element)) { |
| 234 | continue; | 244 | continue; |
| 235 | } | 245 | } |
| 236 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element}; | 246 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var}; |
| 237 | const Node value = | 247 | const Node value = |
| 238 | Operation(OperationCode::TextureQueryDimensions, meta, | 248 | Operation(OperationCode::TextureQueryDimensions, meta, |
| 239 | GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0))); | 249 | GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0))); |
| @@ -259,8 +269,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 259 | 269 | ||
| 260 | auto texture_type = instr.tmml.texture_type.Value(); | 270 | auto texture_type = instr.tmml.texture_type.Value(); |
| 261 | const bool is_array = instr.tmml.array != 0; | 271 | const bool is_array = instr.tmml.array != 0; |
| 272 | Node index_var{}; | ||
| 262 | const Sampler* sampler = | 273 | const Sampler* sampler = |
| 263 | is_bindless ? GetBindlessSampler(instr.gpr20) : GetSampler(instr.sampler); | 274 | is_bindless ? GetBindlessSampler(instr.gpr20, index_var) : GetSampler(instr.sampler); |
| 264 | 275 | ||
| 265 | if (sampler == nullptr) { | 276 | if (sampler == nullptr) { |
| 266 | u32 indexer = 0; | 277 | u32 indexer = 0; |
| @@ -302,7 +313,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 302 | continue; | 313 | continue; |
| 303 | } | 314 | } |
| 304 | auto params = coords; | 315 | auto params = coords; |
| 305 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element}; | 316 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var}; |
| 306 | const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); | 317 | const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); |
| 307 | SetTemporary(bb, indexer++, value); | 318 | SetTemporary(bb, indexer++, value); |
| 308 | } | 319 | } |
| @@ -376,37 +387,65 @@ const Sampler* ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, | |||
| 376 | // Otherwise create a new mapping for this sampler | 387 | // Otherwise create a new mapping for this sampler |
| 377 | const auto next_index = static_cast<u32>(used_samplers.size()); | 388 | const auto next_index = static_cast<u32>(used_samplers.size()); |
| 378 | return &used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow, | 389 | return &used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow, |
| 379 | info.is_buffer); | 390 | info.is_buffer, false); |
| 380 | } | 391 | } |
| 381 | 392 | ||
| 382 | const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, | 393 | const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var, |
| 383 | std::optional<SamplerInfo> sampler_info) { | 394 | std::optional<SamplerInfo> sampler_info) { |
| 384 | const Node sampler_register = GetRegister(reg); | 395 | const Node sampler_register = GetRegister(reg); |
| 385 | const auto [base_sampler, buffer, offset] = | 396 | const auto [base_node, tracked_sampler_info] = |
| 386 | TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size())); | 397 | TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size())); |
| 387 | ASSERT(base_sampler != nullptr); | 398 | ASSERT(base_node != nullptr); |
| 388 | if (base_sampler == nullptr) { | 399 | if (base_node == nullptr) { |
| 389 | return nullptr; | 400 | return nullptr; |
| 390 | } | 401 | } |
| 391 | 402 | ||
| 392 | const auto info = GetSamplerInfo(sampler_info, offset, buffer); | 403 | if (const auto bindless_sampler_info = |
| 404 | std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) { | ||
| 405 | const u32 buffer = bindless_sampler_info->GetIndex(); | ||
| 406 | const u32 offset = bindless_sampler_info->GetOffset(); | ||
| 407 | const auto info = GetSamplerInfo(sampler_info, offset, buffer); | ||
| 408 | |||
| 409 | // If this sampler has already been used, return the existing mapping. | ||
| 410 | const auto it = | ||
| 411 | std::find_if(used_samplers.begin(), used_samplers.end(), | ||
| 412 | [buffer = buffer, offset = offset](const Sampler& entry) { | ||
| 413 | return entry.GetBuffer() == buffer && entry.GetOffset() == offset; | ||
| 414 | }); | ||
| 415 | if (it != used_samplers.end()) { | ||
| 416 | ASSERT(it->IsBindless() && it->GetType() == info.type && | ||
| 417 | it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow); | ||
| 418 | return &*it; | ||
| 419 | } | ||
| 393 | 420 | ||
| 394 | // If this sampler has already been used, return the existing mapping. | 421 | // Otherwise create a new mapping for this sampler |
| 395 | const auto it = | 422 | const auto next_index = static_cast<u32>(used_samplers.size()); |
| 396 | std::find_if(used_samplers.begin(), used_samplers.end(), | 423 | return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array, |
| 397 | [buffer = buffer, offset = offset](const Sampler& entry) { | 424 | info.is_shadow, info.is_buffer, false); |
| 398 | return entry.GetBuffer() == buffer && entry.GetOffset() == offset; | 425 | } else if (const auto array_sampler_info = |
| 399 | }); | 426 | std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) { |
| 400 | if (it != used_samplers.end()) { | 427 | const u32 base_offset = array_sampler_info->GetBaseOffset() / 4; |
| 401 | ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array && | 428 | index_var = GetCustomVariable(array_sampler_info->GetIndexVar()); |
| 402 | it->IsShadow() == info.is_shadow); | 429 | const auto info = GetSamplerInfo(sampler_info, base_offset); |
| 403 | return &*it; | 430 | |
| 404 | } | 431 | // If this sampler has already been used, return the existing mapping. |
| 432 | const auto it = std::find_if( | ||
| 433 | used_samplers.begin(), used_samplers.end(), | ||
| 434 | [base_offset](const Sampler& entry) { return entry.GetOffset() == base_offset; }); | ||
| 435 | if (it != used_samplers.end()) { | ||
| 436 | ASSERT(!it->IsBindless() && it->GetType() == info.type && | ||
| 437 | it->IsArray() == info.is_array && it->IsShadow() == info.is_shadow && | ||
| 438 | it->IsBuffer() == info.is_buffer && it->IsIndexed()); | ||
| 439 | return &*it; | ||
| 440 | } | ||
| 405 | 441 | ||
| 406 | // Otherwise create a new mapping for this sampler | 442 | uses_indexed_samplers = true; |
| 407 | const auto next_index = static_cast<u32>(used_samplers.size()); | 443 | // Otherwise create a new mapping for this sampler |
| 408 | return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array, | 444 | const auto next_index = static_cast<u32>(used_samplers.size()); |
| 409 | info.is_shadow, info.is_buffer); | 445 | return &used_samplers.emplace_back(next_index, base_offset, info.type, info.is_array, |
| 446 | info.is_shadow, info.is_buffer, true); | ||
| 447 | } | ||
| 448 | return nullptr; | ||
| 410 | } | 449 | } |
| 411 | 450 | ||
| 412 | void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { | 451 | void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { |
| @@ -483,66 +522,53 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, | |||
| 483 | Node array, Node depth_compare, u32 bias_offset, | 522 | Node array, Node depth_compare, u32 bias_offset, |
| 484 | std::vector<Node> aoffi, | 523 | std::vector<Node> aoffi, |
| 485 | std::optional<Tegra::Shader::Register> bindless_reg) { | 524 | std::optional<Tegra::Shader::Register> bindless_reg) { |
| 486 | const auto is_array = static_cast<bool>(array); | 525 | const bool is_array = array != nullptr; |
| 487 | const auto is_shadow = static_cast<bool>(depth_compare); | 526 | const bool is_shadow = depth_compare != nullptr; |
| 488 | const bool is_bindless = bindless_reg.has_value(); | 527 | const bool is_bindless = bindless_reg.has_value(); |
| 489 | 528 | ||
| 490 | UNIMPLEMENTED_IF_MSG((texture_type == TextureType::Texture3D && (is_array || is_shadow)) || | 529 | UNIMPLEMENTED_IF(texture_type == TextureType::TextureCube && is_array && is_shadow); |
| 491 | (texture_type == TextureType::TextureCube && is_array && is_shadow), | 530 | ASSERT_MSG(texture_type != TextureType::Texture3D || !is_array || !is_shadow, |
| 492 | "This method is not supported."); | 531 | "Illegal texture type"); |
| 493 | 532 | ||
| 494 | const SamplerInfo info{texture_type, is_array, is_shadow, false}; | 533 | const SamplerInfo info{texture_type, is_array, is_shadow, false}; |
| 495 | const Sampler* sampler = | 534 | Node index_var; |
| 496 | is_bindless ? GetBindlessSampler(*bindless_reg, info) : GetSampler(instr.sampler, info); | 535 | const Sampler* sampler = is_bindless ? GetBindlessSampler(*bindless_reg, index_var, info) |
| 497 | Node4 values; | 536 | : GetSampler(instr.sampler, info); |
| 498 | if (sampler == nullptr) { | 537 | if (!sampler) { |
| 499 | for (u32 element = 0; element < values.size(); ++element) { | 538 | return {Immediate(0), Immediate(0), Immediate(0), Immediate(0)}; |
| 500 | values[element] = Immediate(0); | ||
| 501 | } | ||
| 502 | return values; | ||
| 503 | } | 539 | } |
| 504 | 540 | ||
| 505 | const bool lod_needed = process_mode == TextureProcessMode::LZ || | 541 | const bool lod_needed = process_mode == TextureProcessMode::LZ || |
| 506 | process_mode == TextureProcessMode::LL || | 542 | process_mode == TextureProcessMode::LL || |
| 507 | process_mode == TextureProcessMode::LLA; | 543 | process_mode == TextureProcessMode::LLA; |
| 508 | 544 | const OperationCode opcode = lod_needed ? OperationCode::TextureLod : OperationCode::Texture; | |
| 509 | // LOD selection (either via bias or explicit textureLod) not supported in GL for | ||
| 510 | // sampler2DArrayShadow and samplerCubeArrayShadow. | ||
| 511 | const bool gl_lod_supported = | ||
| 512 | !((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) || | ||
| 513 | (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow)); | ||
| 514 | |||
| 515 | const OperationCode read_method = | ||
| 516 | (lod_needed && gl_lod_supported) ? OperationCode::TextureLod : OperationCode::Texture; | ||
| 517 | |||
| 518 | UNIMPLEMENTED_IF(process_mode != TextureProcessMode::None && !gl_lod_supported); | ||
| 519 | 545 | ||
| 520 | Node bias; | 546 | Node bias; |
| 521 | Node lod; | 547 | Node lod; |
| 522 | if (process_mode != TextureProcessMode::None && gl_lod_supported) { | 548 | switch (process_mode) { |
| 523 | switch (process_mode) { | 549 | case TextureProcessMode::None: |
| 524 | case TextureProcessMode::LZ: | 550 | break; |
| 525 | lod = Immediate(0.0f); | 551 | case TextureProcessMode::LZ: |
| 526 | break; | 552 | lod = Immediate(0.0f); |
| 527 | case TextureProcessMode::LB: | 553 | break; |
| 528 | // If present, lod or bias are always stored in the register | 554 | case TextureProcessMode::LB: |
| 529 | // indexed by the gpr20 field with an offset depending on the | 555 | // If present, lod or bias are always stored in the register indexed by the gpr20 field with |
| 530 | // usage of the other registers | 556 | // an offset depending on the usage of the other registers. |
| 531 | bias = GetRegister(instr.gpr20.Value() + bias_offset); | 557 | bias = GetRegister(instr.gpr20.Value() + bias_offset); |
| 532 | break; | 558 | break; |
| 533 | case TextureProcessMode::LL: | 559 | case TextureProcessMode::LL: |
| 534 | lod = GetRegister(instr.gpr20.Value() + bias_offset); | 560 | lod = GetRegister(instr.gpr20.Value() + bias_offset); |
| 535 | break; | 561 | break; |
| 536 | default: | 562 | default: |
| 537 | UNIMPLEMENTED_MSG("Unimplemented process mode={}", static_cast<u32>(process_mode)); | 563 | UNIMPLEMENTED_MSG("Unimplemented process mode={}", static_cast<u32>(process_mode)); |
| 538 | break; | 564 | break; |
| 539 | } | ||
| 540 | } | 565 | } |
| 541 | 566 | ||
| 567 | Node4 values; | ||
| 542 | for (u32 element = 0; element < values.size(); ++element) { | 568 | for (u32 element = 0; element < values.size(); ++element) { |
| 543 | auto copy_coords = coords; | 569 | MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, |
| 544 | MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, lod, {}, element}; | 570 | lod, {}, element, index_var}; |
| 545 | values[element] = Operation(read_method, meta, std::move(copy_coords)); | 571 | values[element] = Operation(opcode, meta, coords); |
| 546 | } | 572 | } |
| 547 | 573 | ||
| 548 | return values; | 574 | return values; |
| @@ -589,7 +615,7 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type, | |||
| 589 | aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false); | 615 | aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false); |
| 590 | } | 616 | } |
| 591 | 617 | ||
| 592 | Node dc{}; | 618 | Node dc; |
| 593 | if (depth_compare) { | 619 | if (depth_compare) { |
| 594 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod | 620 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod |
| 595 | // or bias are used | 621 | // or bias are used |
| @@ -625,7 +651,7 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, | |||
| 625 | 651 | ||
| 626 | const Node array = is_array ? GetRegister(array_register) : nullptr; | 652 | const Node array = is_array ? GetRegister(array_register) : nullptr; |
| 627 | 653 | ||
| 628 | Node dc{}; | 654 | Node dc; |
| 629 | if (depth_compare) { | 655 | if (depth_compare) { |
| 630 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod | 656 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod |
| 631 | // or bias are used | 657 | // or bias are used |
| @@ -656,7 +682,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de | |||
| 656 | u64 parameter_register = instr.gpr20.Value(); | 682 | u64 parameter_register = instr.gpr20.Value(); |
| 657 | 683 | ||
| 658 | const SamplerInfo info{texture_type, is_array, depth_compare, false}; | 684 | const SamplerInfo info{texture_type, is_array, depth_compare, false}; |
| 659 | const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, info) | 685 | Node index_var{}; |
| 686 | const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, index_var, info) | ||
| 660 | : GetSampler(instr.sampler, info); | 687 | : GetSampler(instr.sampler, info); |
| 661 | Node4 values; | 688 | Node4 values; |
| 662 | if (sampler == nullptr) { | 689 | if (sampler == nullptr) { |
| @@ -685,7 +712,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de | |||
| 685 | for (u32 element = 0; element < values.size(); ++element) { | 712 | for (u32 element = 0; element < values.size(); ++element) { |
| 686 | auto coords_copy = coords; | 713 | auto coords_copy = coords; |
| 687 | MetaTexture meta{ | 714 | MetaTexture meta{ |
| 688 | *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element}; | 715 | *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element, |
| 716 | index_var}; | ||
| 689 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); | 717 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); |
| 690 | } | 718 | } |
| 691 | 719 | ||
| @@ -718,7 +746,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) { | |||
| 718 | Node4 values; | 746 | Node4 values; |
| 719 | for (u32 element = 0; element < values.size(); ++element) { | 747 | for (u32 element = 0; element < values.size(); ++element) { |
| 720 | auto coords_copy = coords; | 748 | auto coords_copy = coords; |
| 721 | MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element}; | 749 | MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element, {}}; |
| 722 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); | 750 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); |
| 723 | } | 751 | } |
| 724 | 752 | ||
| @@ -768,7 +796,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is | |||
| 768 | Node4 values; | 796 | Node4 values; |
| 769 | for (u32 element = 0; element < values.size(); ++element) { | 797 | for (u32 element = 0; element < values.size(); ++element) { |
| 770 | auto coords_copy = coords; | 798 | auto coords_copy = coords; |
| 771 | MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element}; | 799 | MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element, {}}; |
| 772 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); | 800 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); |
| 773 | } | 801 | } |
| 774 | return values; | 802 | return values; |
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index 075c7d07c..a0a7b9111 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h | |||
| @@ -162,7 +162,7 @@ enum class OperationCode { | |||
| 162 | AtomicImageXor, /// (MetaImage, int[N] coords) -> void | 162 | AtomicImageXor, /// (MetaImage, int[N] coords) -> void |
| 163 | AtomicImageExchange, /// (MetaImage, int[N] coords) -> void | 163 | AtomicImageExchange, /// (MetaImage, int[N] coords) -> void |
| 164 | 164 | ||
| 165 | UAtomicAdd, /// (smem, uint) -> uint | 165 | AtomicAdd, /// (memory, {u}int) -> {u}int |
| 166 | 166 | ||
| 167 | Branch, /// (uint branch_target) -> void | 167 | Branch, /// (uint branch_target) -> void |
| 168 | BranchIndirect, /// (uint branch_target) -> void | 168 | BranchIndirect, /// (uint branch_target) -> void |
| @@ -212,6 +212,7 @@ enum class MetaStackClass { | |||
| 212 | class OperationNode; | 212 | class OperationNode; |
| 213 | class ConditionalNode; | 213 | class ConditionalNode; |
| 214 | class GprNode; | 214 | class GprNode; |
| 215 | class CustomVarNode; | ||
| 215 | class ImmediateNode; | 216 | class ImmediateNode; |
| 216 | class InternalFlagNode; | 217 | class InternalFlagNode; |
| 217 | class PredicateNode; | 218 | class PredicateNode; |
| @@ -223,26 +224,32 @@ class SmemNode; | |||
| 223 | class GmemNode; | 224 | class GmemNode; |
| 224 | class CommentNode; | 225 | class CommentNode; |
| 225 | 226 | ||
| 226 | using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, | 227 | using NodeData = std::variant<OperationNode, ConditionalNode, GprNode, CustomVarNode, ImmediateNode, |
| 227 | InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode, | 228 | InternalFlagNode, PredicateNode, AbufNode, PatchNode, CbufNode, |
| 228 | LmemNode, SmemNode, GmemNode, CommentNode>; | 229 | LmemNode, SmemNode, GmemNode, CommentNode>; |
| 229 | using Node = std::shared_ptr<NodeData>; | 230 | using Node = std::shared_ptr<NodeData>; |
| 230 | using Node4 = std::array<Node, 4>; | 231 | using Node4 = std::array<Node, 4>; |
| 231 | using NodeBlock = std::vector<Node>; | 232 | using NodeBlock = std::vector<Node>; |
| 232 | 233 | ||
| 234 | class BindlessSamplerNode; | ||
| 235 | class ArraySamplerNode; | ||
| 236 | |||
| 237 | using TrackSamplerData = std::variant<BindlessSamplerNode, ArraySamplerNode>; | ||
| 238 | using TrackSampler = std::shared_ptr<TrackSamplerData>; | ||
| 239 | |||
| 233 | class Sampler { | 240 | class Sampler { |
| 234 | public: | 241 | public: |
| 235 | /// This constructor is for bound samplers | 242 | /// This constructor is for bound samplers |
| 236 | constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type, | 243 | constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type, |
| 237 | bool is_array, bool is_shadow, bool is_buffer) | 244 | bool is_array, bool is_shadow, bool is_buffer, bool is_indexed) |
| 238 | : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow}, | 245 | : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow}, |
| 239 | is_buffer{is_buffer} {} | 246 | is_buffer{is_buffer}, is_indexed{is_indexed} {} |
| 240 | 247 | ||
| 241 | /// This constructor is for bindless samplers | 248 | /// This constructor is for bindless samplers |
| 242 | constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type, | 249 | constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type, |
| 243 | bool is_array, bool is_shadow, bool is_buffer) | 250 | bool is_array, bool is_shadow, bool is_buffer, bool is_indexed) |
| 244 | : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array}, | 251 | : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array}, |
| 245 | is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true} {} | 252 | is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true}, is_indexed{is_indexed} {} |
| 246 | 253 | ||
| 247 | constexpr u32 GetIndex() const { | 254 | constexpr u32 GetIndex() const { |
| 248 | return index; | 255 | return index; |
| @@ -276,16 +283,72 @@ public: | |||
| 276 | return is_bindless; | 283 | return is_bindless; |
| 277 | } | 284 | } |
| 278 | 285 | ||
| 286 | constexpr bool IsIndexed() const { | ||
| 287 | return is_indexed; | ||
| 288 | } | ||
| 289 | |||
| 290 | constexpr u32 Size() const { | ||
| 291 | return size; | ||
| 292 | } | ||
| 293 | |||
| 294 | constexpr void SetSize(u32 new_size) { | ||
| 295 | size = new_size; | ||
| 296 | } | ||
| 297 | |||
| 279 | private: | 298 | private: |
| 280 | u32 index{}; ///< Emulated index given for the this sampler. | 299 | u32 index{}; ///< Emulated index given for the this sampler. |
| 281 | u32 offset{}; ///< Offset in the const buffer from where the sampler is being read. | 300 | u32 offset{}; ///< Offset in the const buffer from where the sampler is being read. |
| 282 | u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers). | 301 | u32 buffer{}; ///< Buffer where the bindless sampler is being read (unused on bound samplers). |
| 302 | u32 size{}; ///< Size of the sampler if indexed. | ||
| 283 | 303 | ||
| 284 | Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) | 304 | Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) |
| 285 | bool is_array{}; ///< Whether the texture is being sampled as an array texture or not. | 305 | bool is_array{}; ///< Whether the texture is being sampled as an array texture or not. |
| 286 | bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not. | 306 | bool is_shadow{}; ///< Whether the texture is being sampled as a depth texture or not. |
| 287 | bool is_buffer{}; ///< Whether the texture is a texture buffer without sampler. | 307 | bool is_buffer{}; ///< Whether the texture is a texture buffer without sampler. |
| 288 | bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not. | 308 | bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not. |
| 309 | bool is_indexed{}; ///< Whether this sampler is an indexed array of textures. | ||
| 310 | }; | ||
| 311 | |||
| 312 | /// Represents a tracked bindless sampler into a direct const buffer | ||
| 313 | class ArraySamplerNode final { | ||
| 314 | public: | ||
| 315 | explicit ArraySamplerNode(u32 index, u32 base_offset, u32 bindless_var) | ||
| 316 | : index{index}, base_offset{base_offset}, bindless_var{bindless_var} {} | ||
| 317 | |||
| 318 | constexpr u32 GetIndex() const { | ||
| 319 | return index; | ||
| 320 | } | ||
| 321 | |||
| 322 | constexpr u32 GetBaseOffset() const { | ||
| 323 | return base_offset; | ||
| 324 | } | ||
| 325 | |||
| 326 | constexpr u32 GetIndexVar() const { | ||
| 327 | return bindless_var; | ||
| 328 | } | ||
| 329 | |||
| 330 | private: | ||
| 331 | u32 index; | ||
| 332 | u32 base_offset; | ||
| 333 | u32 bindless_var; | ||
| 334 | }; | ||
| 335 | |||
| 336 | /// Represents a tracked bindless sampler into a direct const buffer | ||
| 337 | class BindlessSamplerNode final { | ||
| 338 | public: | ||
| 339 | explicit BindlessSamplerNode(u32 index, u32 offset) : index{index}, offset{offset} {} | ||
| 340 | |||
| 341 | constexpr u32 GetIndex() const { | ||
| 342 | return index; | ||
| 343 | } | ||
| 344 | |||
| 345 | constexpr u32 GetOffset() const { | ||
| 346 | return offset; | ||
| 347 | } | ||
| 348 | |||
| 349 | private: | ||
| 350 | u32 index; | ||
| 351 | u32 offset; | ||
| 289 | }; | 352 | }; |
| 290 | 353 | ||
| 291 | class Image final { | 354 | class Image final { |
| @@ -380,8 +443,9 @@ struct MetaTexture { | |||
| 380 | std::vector<Node> derivates; | 443 | std::vector<Node> derivates; |
| 381 | Node bias; | 444 | Node bias; |
| 382 | Node lod; | 445 | Node lod; |
| 383 | Node component{}; | 446 | Node component; |
| 384 | u32 element{}; | 447 | u32 element{}; |
| 448 | Node index; | ||
| 385 | }; | 449 | }; |
| 386 | 450 | ||
| 387 | struct MetaImage { | 451 | struct MetaImage { |
| @@ -488,6 +552,19 @@ private: | |||
| 488 | Tegra::Shader::Register index{}; | 552 | Tegra::Shader::Register index{}; |
| 489 | }; | 553 | }; |
| 490 | 554 | ||
| 555 | /// A custom variable | ||
| 556 | class CustomVarNode final { | ||
| 557 | public: | ||
| 558 | explicit constexpr CustomVarNode(u32 index) : index{index} {} | ||
| 559 | |||
| 560 | constexpr u32 GetIndex() const { | ||
| 561 | return index; | ||
| 562 | } | ||
| 563 | |||
| 564 | private: | ||
| 565 | u32 index{}; | ||
| 566 | }; | ||
| 567 | |||
| 491 | /// A 32-bits value that represents an immediate value | 568 | /// A 32-bits value that represents an immediate value |
| 492 | class ImmediateNode final { | 569 | class ImmediateNode final { |
| 493 | public: | 570 | public: |
diff --git a/src/video_core/shader/node_helper.h b/src/video_core/shader/node_helper.h index 0c2aa749b..11231bbea 100644 --- a/src/video_core/shader/node_helper.h +++ b/src/video_core/shader/node_helper.h | |||
| @@ -45,6 +45,12 @@ Node MakeNode(Args&&... args) { | |||
| 45 | return std::make_shared<NodeData>(T(std::forward<Args>(args)...)); | 45 | return std::make_shared<NodeData>(T(std::forward<Args>(args)...)); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | template <typename T, typename... Args> | ||
| 49 | TrackSampler MakeTrackSampler(Args&&... args) { | ||
| 50 | static_assert(std::is_convertible_v<T, TrackSamplerData>); | ||
| 51 | return std::make_shared<TrackSamplerData>(T(std::forward<Args>(args)...)); | ||
| 52 | } | ||
| 53 | |||
| 48 | template <typename... Args> | 54 | template <typename... Args> |
| 49 | Node Operation(OperationCode code, Args&&... args) { | 55 | Node Operation(OperationCode code, Args&&... args) { |
| 50 | if constexpr (sizeof...(args) == 0) { | 56 | if constexpr (sizeof...(args) == 0) { |
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp index 31eecb3f4..3a5d280a9 100644 --- a/src/video_core/shader/shader_ir.cpp +++ b/src/video_core/shader/shader_ir.cpp | |||
| @@ -27,6 +27,7 @@ ShaderIR::ShaderIR(const ProgramCode& program_code, u32 main_offset, CompilerSet | |||
| 27 | ConstBufferLocker& locker) | 27 | ConstBufferLocker& locker) |
| 28 | : program_code{program_code}, main_offset{main_offset}, settings{settings}, locker{locker} { | 28 | : program_code{program_code}, main_offset{main_offset}, settings{settings}, locker{locker} { |
| 29 | Decode(); | 29 | Decode(); |
| 30 | PostDecode(); | ||
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | ShaderIR::~ShaderIR() = default; | 33 | ShaderIR::~ShaderIR() = default; |
| @@ -38,6 +39,10 @@ Node ShaderIR::GetRegister(Register reg) { | |||
| 38 | return MakeNode<GprNode>(reg); | 39 | return MakeNode<GprNode>(reg); |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 42 | Node ShaderIR::GetCustomVariable(u32 id) { | ||
| 43 | return MakeNode<CustomVarNode>(id); | ||
| 44 | } | ||
| 45 | |||
| 41 | Node ShaderIR::GetImmediate19(Instruction instr) { | 46 | Node ShaderIR::GetImmediate19(Instruction instr) { |
| 42 | return Immediate(instr.alu.GetImm20_19()); | 47 | return Immediate(instr.alu.GetImm20_19()); |
| 43 | } | 48 | } |
| @@ -452,4 +457,8 @@ std::size_t ShaderIR::DeclareAmend(Node new_amend) { | |||
| 452 | return id; | 457 | return id; |
| 453 | } | 458 | } |
| 454 | 459 | ||
| 460 | u32 ShaderIR::NewCustomVariable() { | ||
| 461 | return num_custom_variables++; | ||
| 462 | } | ||
| 463 | |||
| 455 | } // namespace VideoCommon::Shader | 464 | } // namespace VideoCommon::Shader |
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index ba1db4c11..b0851c3be 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h | |||
| @@ -180,6 +180,10 @@ public: | |||
| 180 | return amend_code[index]; | 180 | return amend_code[index]; |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | u32 GetNumCustomVariables() const { | ||
| 184 | return num_custom_variables; | ||
| 185 | } | ||
| 186 | |||
| 183 | private: | 187 | private: |
| 184 | friend class ASTDecoder; | 188 | friend class ASTDecoder; |
| 185 | 189 | ||
| @@ -191,6 +195,7 @@ private: | |||
| 191 | }; | 195 | }; |
| 192 | 196 | ||
| 193 | void Decode(); | 197 | void Decode(); |
| 198 | void PostDecode(); | ||
| 194 | 199 | ||
| 195 | NodeBlock DecodeRange(u32 begin, u32 end); | 200 | NodeBlock DecodeRange(u32 begin, u32 end); |
| 196 | void DecodeRangeInner(NodeBlock& bb, u32 begin, u32 end); | 201 | void DecodeRangeInner(NodeBlock& bb, u32 begin, u32 end); |
| @@ -235,6 +240,8 @@ private: | |||
| 235 | 240 | ||
| 236 | /// Generates a node for a passed register. | 241 | /// Generates a node for a passed register. |
| 237 | Node GetRegister(Tegra::Shader::Register reg); | 242 | Node GetRegister(Tegra::Shader::Register reg); |
| 243 | /// Generates a node for a custom variable | ||
| 244 | Node GetCustomVariable(u32 id); | ||
| 238 | /// Generates a node representing a 19-bit immediate value | 245 | /// Generates a node representing a 19-bit immediate value |
| 239 | Node GetImmediate19(Tegra::Shader::Instruction instr); | 246 | Node GetImmediate19(Tegra::Shader::Instruction instr); |
| 240 | /// Generates a node representing a 32-bit immediate value | 247 | /// Generates a node representing a 32-bit immediate value |
| @@ -321,7 +328,7 @@ private: | |||
| 321 | std::optional<SamplerInfo> sampler_info = std::nullopt); | 328 | std::optional<SamplerInfo> sampler_info = std::nullopt); |
| 322 | 329 | ||
| 323 | /// Accesses a texture sampler for a bindless texture. | 330 | /// Accesses a texture sampler for a bindless texture. |
| 324 | const Sampler* GetBindlessSampler(Tegra::Shader::Register reg, | 331 | const Sampler* GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var, |
| 325 | std::optional<SamplerInfo> sampler_info = std::nullopt); | 332 | std::optional<SamplerInfo> sampler_info = std::nullopt); |
| 326 | 333 | ||
| 327 | /// Accesses an image. | 334 | /// Accesses an image. |
| @@ -387,6 +394,9 @@ private: | |||
| 387 | 394 | ||
| 388 | std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const; | 395 | std::tuple<Node, u32, u32> TrackCbuf(Node tracked, const NodeBlock& code, s64 cursor) const; |
| 389 | 396 | ||
| 397 | std::tuple<Node, TrackSampler> TrackBindlessSampler(Node tracked, const NodeBlock& code, | ||
| 398 | s64 cursor); | ||
| 399 | |||
| 390 | std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const; | 400 | std::optional<u32> TrackImmediate(Node tracked, const NodeBlock& code, s64 cursor) const; |
| 391 | 401 | ||
| 392 | std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, | 402 | std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code, |
| @@ -399,6 +409,8 @@ private: | |||
| 399 | /// Register new amending code and obtain the reference id. | 409 | /// Register new amending code and obtain the reference id. |
| 400 | std::size_t DeclareAmend(Node new_amend); | 410 | std::size_t DeclareAmend(Node new_amend); |
| 401 | 411 | ||
| 412 | u32 NewCustomVariable(); | ||
| 413 | |||
| 402 | const ProgramCode& program_code; | 414 | const ProgramCode& program_code; |
| 403 | const u32 main_offset; | 415 | const u32 main_offset; |
| 404 | const CompilerSettings settings; | 416 | const CompilerSettings settings; |
| @@ -414,6 +426,7 @@ private: | |||
| 414 | NodeBlock global_code; | 426 | NodeBlock global_code; |
| 415 | ASTManager program_manager{true, true}; | 427 | ASTManager program_manager{true, true}; |
| 416 | std::vector<Node> amend_code; | 428 | std::vector<Node> amend_code; |
| 429 | u32 num_custom_variables{}; | ||
| 417 | 430 | ||
| 418 | std::set<u32> used_registers; | 431 | std::set<u32> used_registers; |
| 419 | std::set<Tegra::Shader::Pred> used_predicates; | 432 | std::set<Tegra::Shader::Pred> used_predicates; |
| @@ -431,6 +444,7 @@ private: | |||
| 431 | bool uses_instance_id{}; | 444 | bool uses_instance_id{}; |
| 432 | bool uses_vertex_id{}; | 445 | bool uses_vertex_id{}; |
| 433 | bool uses_warps{}; | 446 | bool uses_warps{}; |
| 447 | bool uses_indexed_samplers{}; | ||
| 434 | 448 | ||
| 435 | Tegra::Shader::Header header; | 449 | Tegra::Shader::Header header; |
| 436 | }; | 450 | }; |
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp index 165c79330..face8c943 100644 --- a/src/video_core/shader/track.cpp +++ b/src/video_core/shader/track.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/shader/node.h" | 10 | #include "video_core/shader/node.h" |
| 11 | #include "video_core/shader/node_helper.h" | ||
| 11 | #include "video_core/shader/shader_ir.h" | 12 | #include "video_core/shader/shader_ir.h" |
| 12 | 13 | ||
| 13 | namespace VideoCommon::Shader { | 14 | namespace VideoCommon::Shader { |
| @@ -35,8 +36,113 @@ std::pair<Node, s64> FindOperation(const NodeBlock& code, s64 cursor, | |||
| 35 | } | 36 | } |
| 36 | return {}; | 37 | return {}; |
| 37 | } | 38 | } |
| 39 | |||
| 40 | std::optional<std::pair<Node, Node>> DecoupleIndirectRead(const OperationNode& operation) { | ||
| 41 | if (operation.GetCode() != OperationCode::UAdd) { | ||
| 42 | return std::nullopt; | ||
| 43 | } | ||
| 44 | Node gpr; | ||
| 45 | Node offset; | ||
| 46 | ASSERT(operation.GetOperandsCount() == 2); | ||
| 47 | for (std::size_t i = 0; i < operation.GetOperandsCount(); i++) { | ||
| 48 | Node operand = operation[i]; | ||
| 49 | if (std::holds_alternative<ImmediateNode>(*operand)) { | ||
| 50 | offset = operation[i]; | ||
| 51 | } else if (std::holds_alternative<GprNode>(*operand)) { | ||
| 52 | gpr = operation[i]; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | if (offset && gpr) { | ||
| 56 | return std::make_pair(gpr, offset); | ||
| 57 | } | ||
| 58 | return std::nullopt; | ||
| 59 | } | ||
| 60 | |||
| 61 | bool AmendNodeCv(std::size_t amend_index, Node node) { | ||
| 62 | if (const auto operation = std::get_if<OperationNode>(&*node)) { | ||
| 63 | operation->SetAmendIndex(amend_index); | ||
| 64 | return true; | ||
| 65 | } else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) { | ||
| 66 | conditional->SetAmendIndex(amend_index); | ||
| 67 | return true; | ||
| 68 | } | ||
| 69 | return false; | ||
| 70 | } | ||
| 71 | |||
| 38 | } // Anonymous namespace | 72 | } // Anonymous namespace |
| 39 | 73 | ||
| 74 | std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, const NodeBlock& code, | ||
| 75 | s64 cursor) { | ||
| 76 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { | ||
| 77 | // Constant buffer found, test if it's an immediate | ||
| 78 | const auto offset = cbuf->GetOffset(); | ||
| 79 | if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { | ||
| 80 | auto track = | ||
| 81 | MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue()); | ||
| 82 | return {tracked, track}; | ||
| 83 | } else if (const auto operation = std::get_if<OperationNode>(&*offset)) { | ||
| 84 | auto bound_buffer = locker.ObtainBoundBuffer(); | ||
| 85 | if (!bound_buffer) { | ||
| 86 | return {}; | ||
| 87 | } | ||
| 88 | if (*bound_buffer != cbuf->GetIndex()) { | ||
| 89 | return {}; | ||
| 90 | } | ||
| 91 | auto pair = DecoupleIndirectRead(*operation); | ||
| 92 | if (!pair) { | ||
| 93 | return {}; | ||
| 94 | } | ||
| 95 | auto [gpr, base_offset] = *pair; | ||
| 96 | const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset); | ||
| 97 | auto gpu_driver = locker.AccessGuestDriverProfile(); | ||
| 98 | if (gpu_driver == nullptr) { | ||
| 99 | return {}; | ||
| 100 | } | ||
| 101 | const u32 bindless_cv = NewCustomVariable(); | ||
| 102 | const Node op = Operation(OperationCode::UDiv, NO_PRECISE, gpr, | ||
| 103 | Immediate(gpu_driver->GetTextureHandlerSize())); | ||
| 104 | |||
| 105 | const Node cv_node = GetCustomVariable(bindless_cv); | ||
| 106 | Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op)); | ||
| 107 | const std::size_t amend_index = DeclareAmend(amend_op); | ||
| 108 | AmendNodeCv(amend_index, code[cursor]); | ||
| 109 | // TODO Implement Bindless Index custom variable | ||
| 110 | auto track = MakeTrackSampler<ArraySamplerNode>(cbuf->GetIndex(), | ||
| 111 | offset_inm->GetValue(), bindless_cv); | ||
| 112 | return {tracked, track}; | ||
| 113 | } | ||
| 114 | return {}; | ||
| 115 | } | ||
| 116 | if (const auto gpr = std::get_if<GprNode>(&*tracked)) { | ||
| 117 | if (gpr->GetIndex() == Tegra::Shader::Register::ZeroIndex) { | ||
| 118 | return {}; | ||
| 119 | } | ||
| 120 | // Reduce the cursor in one to avoid infinite loops when the instruction sets the same | ||
| 121 | // register that it uses as operand | ||
| 122 | const auto [source, new_cursor] = TrackRegister(gpr, code, cursor - 1); | ||
| 123 | if (!source) { | ||
| 124 | return {}; | ||
| 125 | } | ||
| 126 | return TrackBindlessSampler(source, code, new_cursor); | ||
| 127 | } | ||
| 128 | if (const auto operation = std::get_if<OperationNode>(&*tracked)) { | ||
| 129 | for (std::size_t i = operation->GetOperandsCount(); i > 0; --i) { | ||
| 130 | if (auto found = TrackBindlessSampler((*operation)[i - 1], code, cursor); | ||
| 131 | std::get<0>(found)) { | ||
| 132 | // Cbuf found in operand. | ||
| 133 | return found; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | return {}; | ||
| 137 | } | ||
| 138 | if (const auto conditional = std::get_if<ConditionalNode>(&*tracked)) { | ||
| 139 | const auto& conditional_code = conditional->GetCode(); | ||
| 140 | return TrackBindlessSampler(tracked, conditional_code, | ||
| 141 | static_cast<s64>(conditional_code.size())); | ||
| 142 | } | ||
| 143 | return {}; | ||
| 144 | } | ||
| 145 | |||
| 40 | std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, | 146 | std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& code, |
| 41 | s64 cursor) const { | 147 | s64 cursor) const { |
| 42 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { | 148 | if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { |
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 829268b4c..84469b7ba 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp | |||
| @@ -135,7 +135,7 @@ std::vector<CopyParams> SurfaceBaseImpl::BreakDownLayered(const SurfaceParams& i | |||
| 135 | for (u32 level = 0; level < mipmaps; level++) { | 135 | for (u32 level = 0; level < mipmaps; level++) { |
| 136 | const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level); | 136 | const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level); |
| 137 | const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level); | 137 | const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level); |
| 138 | result.emplace_back(width, height, layer, level); | 138 | result.emplace_back(0, 0, layer, 0, 0, layer, level, level, width, height, 1); |
| 139 | } | 139 | } |
| 140 | } | 140 | } |
| 141 | return result; | 141 | return result; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index f4c015635..0d105d386 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -721,7 +721,6 @@ private: | |||
| 721 | std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const CacheAddr cache_addr, | 721 | std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const CacheAddr cache_addr, |
| 722 | const SurfaceParams& params, bool preserve_contents, | 722 | const SurfaceParams& params, bool preserve_contents, |
| 723 | bool is_render) { | 723 | bool is_render) { |
| 724 | |||
| 725 | // Step 1 | 724 | // Step 1 |
| 726 | // Check Level 1 Cache for a fast structural match. If candidate surface | 725 | // Check Level 1 Cache for a fast structural match. If candidate surface |
| 727 | // matches at certain level we are pretty much done. | 726 | // matches at certain level we are pretty much done. |
| @@ -733,14 +732,18 @@ private: | |||
| 733 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, | 732 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, |
| 734 | topological_result); | 733 | topological_result); |
| 735 | } | 734 | } |
| 735 | |||
| 736 | const auto struct_result = current_surface->MatchesStructure(params); | 736 | const auto struct_result = current_surface->MatchesStructure(params); |
| 737 | if (struct_result != MatchStructureResult::None && | 737 | if (struct_result != MatchStructureResult::None) { |
| 738 | (params.target != SurfaceTarget::Texture3D || | 738 | const auto& old_params = current_surface->GetSurfaceParams(); |
| 739 | current_surface->MatchTarget(params.target))) { | 739 | const bool not_3d = params.target != SurfaceTarget::Texture3D && |
| 740 | if (struct_result == MatchStructureResult::FullMatch) { | 740 | old_params.target != SurfaceTarget::Texture3D; |
| 741 | return ManageStructuralMatch(current_surface, params, is_render); | 741 | if (not_3d || current_surface->MatchTarget(params.target)) { |
| 742 | } else { | 742 | if (struct_result == MatchStructureResult::FullMatch) { |
| 743 | return RebuildSurface(current_surface, params, is_render); | 743 | return ManageStructuralMatch(current_surface, params, is_render); |
| 744 | } else { | ||
| 745 | return RebuildSurface(current_surface, params, is_render); | ||
| 746 | } | ||
| 744 | } | 747 | } |
| 745 | } | 748 | } |
| 746 | } | 749 | } |
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 8e947394c..a5f81a8a0 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp | |||
| @@ -3,19 +3,32 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include "common/logging/log.h" | ||
| 6 | #include "core/core.h" | 7 | #include "core/core.h" |
| 7 | #include "core/settings.h" | 8 | #include "core/settings.h" |
| 8 | #include "video_core/gpu_asynch.h" | 9 | #include "video_core/gpu_asynch.h" |
| 9 | #include "video_core/gpu_synch.h" | 10 | #include "video_core/gpu_synch.h" |
| 10 | #include "video_core/renderer_base.h" | 11 | #include "video_core/renderer_base.h" |
| 11 | #include "video_core/renderer_opengl/renderer_opengl.h" | 12 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| 13 | #ifdef HAS_VULKAN | ||
| 14 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||
| 15 | #endif | ||
| 12 | #include "video_core/video_core.h" | 16 | #include "video_core/video_core.h" |
| 13 | 17 | ||
| 14 | namespace VideoCore { | 18 | namespace VideoCore { |
| 15 | 19 | ||
| 16 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, | 20 | std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, |
| 17 | Core::System& system) { | 21 | Core::System& system) { |
| 18 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); | 22 | switch (Settings::values.renderer_backend) { |
| 23 | case Settings::RendererBackend::OpenGL: | ||
| 24 | return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); | ||
| 25 | #ifdef HAS_VULKAN | ||
| 26 | case Settings::RendererBackend::Vulkan: | ||
| 27 | return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); | ||
| 28 | #endif | ||
| 29 | default: | ||
| 30 | return nullptr; | ||
| 31 | } | ||
| 19 | } | 32 | } |
| 20 | 33 | ||
| 21 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { | 34 | std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { |
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp index 9156ce802..7538389bf 100644 --- a/src/web_service/telemetry_json.cpp +++ b/src/web_service/telemetry_json.cpp | |||
| @@ -117,6 +117,7 @@ bool TelemetryJson::SubmitTestcase() { | |||
| 117 | impl->SerializeSection(Telemetry::FieldType::Session, "Session"); | 117 | impl->SerializeSection(Telemetry::FieldType::Session, "Session"); |
| 118 | impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); | 118 | impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); |
| 119 | impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); | 119 | impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); |
| 120 | impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); | ||
| 120 | 121 | ||
| 121 | auto content = impl->TopSection().dump(); | 122 | auto content = impl->TopSection().dump(); |
| 122 | Client client(impl->host, impl->username, impl->token); | 123 | Client client(impl->host, impl->username, impl->token); |
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp index 6683f459f..737ffe409 100644 --- a/src/web_service/web_backend.cpp +++ b/src/web_service/web_backend.cpp | |||
| @@ -73,14 +73,12 @@ struct Client::Impl { | |||
| 73 | if (!parsedUrl.GetPort(&port)) { | 73 | if (!parsedUrl.GetPort(&port)) { |
| 74 | port = HTTP_PORT; | 74 | port = HTTP_PORT; |
| 75 | } | 75 | } |
| 76 | cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port, | 76 | cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port); |
| 77 | TIMEOUT_SECONDS); | ||
| 78 | } else if (parsedUrl.m_Scheme == "https") { | 77 | } else if (parsedUrl.m_Scheme == "https") { |
| 79 | if (!parsedUrl.GetPort(&port)) { | 78 | if (!parsedUrl.GetPort(&port)) { |
| 80 | port = HTTPS_PORT; | 79 | port = HTTPS_PORT; |
| 81 | } | 80 | } |
| 82 | cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port, | 81 | cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port); |
| 83 | TIMEOUT_SECONDS); | ||
| 84 | } else { | 82 | } else { |
| 85 | LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); | 83 | LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme); |
| 86 | return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"}; | 84 | return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"}; |
| @@ -90,6 +88,7 @@ struct Client::Impl { | |||
| 90 | LOG_ERROR(WebService, "Invalid URL {}", host + path); | 88 | LOG_ERROR(WebService, "Invalid URL {}", host + path); |
| 91 | return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; | 89 | return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; |
| 92 | } | 90 | } |
| 91 | cli->set_timeout_sec(TIMEOUT_SECONDS); | ||
| 93 | 92 | ||
| 94 | httplib::Headers params; | 93 | httplib::Headers params; |
| 95 | if (!jwt.empty()) { | 94 | if (!jwt.empty()) { |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 11ae1e66e..b841e63fa 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -36,9 +36,6 @@ add_executable(yuzu | |||
| 36 | configuration/configure_filesystem.cpp | 36 | configuration/configure_filesystem.cpp |
| 37 | configuration/configure_filesystem.h | 37 | configuration/configure_filesystem.h |
| 38 | configuration/configure_filesystem.ui | 38 | configuration/configure_filesystem.ui |
| 39 | configuration/configure_gamelist.cpp | ||
| 40 | configuration/configure_gamelist.h | ||
| 41 | configuration/configure_gamelist.ui | ||
| 42 | configuration/configure_general.cpp | 39 | configuration/configure_general.cpp |
| 43 | configuration/configure_general.h | 40 | configuration/configure_general.h |
| 44 | configuration/configure_general.ui | 41 | configuration/configure_general.ui |
| @@ -75,6 +72,9 @@ add_executable(yuzu | |||
| 75 | configuration/configure_touchscreen_advanced.cpp | 72 | configuration/configure_touchscreen_advanced.cpp |
| 76 | configuration/configure_touchscreen_advanced.h | 73 | configuration/configure_touchscreen_advanced.h |
| 77 | configuration/configure_touchscreen_advanced.ui | 74 | configuration/configure_touchscreen_advanced.ui |
| 75 | configuration/configure_ui.cpp | ||
| 76 | configuration/configure_ui.h | ||
| 77 | configuration/configure_ui.ui | ||
| 78 | configuration/configure_web.cpp | 78 | configuration/configure_web.cpp |
| 79 | configuration/configure_web.h | 79 | configuration/configure_web.h |
| 80 | configuration/configure_web.ui | 80 | configuration/configure_web.ui |
| @@ -200,3 +200,8 @@ if (MSVC) | |||
| 200 | copy_yuzu_SDL_deps(yuzu) | 200 | copy_yuzu_SDL_deps(yuzu) |
| 201 | copy_yuzu_unicorn_deps(yuzu) | 201 | copy_yuzu_unicorn_deps(yuzu) |
| 202 | endif() | 202 | endif() |
| 203 | |||
| 204 | if (ENABLE_VULKAN) | ||
| 205 | target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) | ||
| 206 | target_compile_definitions(yuzu PRIVATE HAS_VULKAN) | ||
| 207 | endif() | ||
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 7490fb718..55a37fffa 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -2,19 +2,30 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <glad/glad.h> | ||
| 6 | |||
| 5 | #include <QApplication> | 7 | #include <QApplication> |
| 6 | #include <QHBoxLayout> | 8 | #include <QHBoxLayout> |
| 7 | #include <QKeyEvent> | 9 | #include <QKeyEvent> |
| 10 | #include <QMessageBox> | ||
| 8 | #include <QOffscreenSurface> | 11 | #include <QOffscreenSurface> |
| 9 | #include <QOpenGLWindow> | 12 | #include <QOpenGLWindow> |
| 10 | #include <QPainter> | 13 | #include <QPainter> |
| 11 | #include <QScreen> | 14 | #include <QScreen> |
| 15 | #include <QStringList> | ||
| 12 | #include <QWindow> | 16 | #include <QWindow> |
| 17 | #ifdef HAS_VULKAN | ||
| 18 | #include <QVulkanWindow> | ||
| 19 | #endif | ||
| 20 | |||
| 13 | #include <fmt/format.h> | 21 | #include <fmt/format.h> |
| 22 | |||
| 23 | #include "common/assert.h" | ||
| 14 | #include "common/microprofile.h" | 24 | #include "common/microprofile.h" |
| 15 | #include "common/scm_rev.h" | 25 | #include "common/scm_rev.h" |
| 16 | #include "core/core.h" | 26 | #include "core/core.h" |
| 17 | #include "core/frontend/framebuffer_layout.h" | 27 | #include "core/frontend/framebuffer_layout.h" |
| 28 | #include "core/frontend/scope_acquire_window_context.h" | ||
| 18 | #include "core/settings.h" | 29 | #include "core/settings.h" |
| 19 | #include "input_common/keyboard.h" | 30 | #include "input_common/keyboard.h" |
| 20 | #include "input_common/main.h" | 31 | #include "input_common/main.h" |
| @@ -114,19 +125,10 @@ private: | |||
| 114 | QOpenGLContext context; | 125 | QOpenGLContext context; |
| 115 | }; | 126 | }; |
| 116 | 127 | ||
| 117 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL | 128 | class GWidgetInternal : public QWindow { |
| 118 | // context. | ||
| 119 | // The corresponding functionality is handled in EmuThread instead | ||
| 120 | class GGLWidgetInternal : public QOpenGLWindow { | ||
| 121 | public: | 129 | public: |
| 122 | GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) | 130 | GWidgetInternal(GRenderWindow* parent) : parent(parent) {} |
| 123 | : QOpenGLWindow(shared_context), parent(parent) {} | 131 | virtual ~GWidgetInternal() = default; |
| 124 | |||
| 125 | void paintEvent(QPaintEvent* ev) override { | ||
| 126 | if (do_painting) { | ||
| 127 | QPainter painter(this); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | 132 | ||
| 131 | void resizeEvent(QResizeEvent* ev) override { | 133 | void resizeEvent(QResizeEvent* ev) override { |
| 132 | parent->OnClientAreaResized(ev->size().width(), ev->size().height()); | 134 | parent->OnClientAreaResized(ev->size().width(), ev->size().height()); |
| @@ -182,11 +184,47 @@ public: | |||
| 182 | do_painting = true; | 184 | do_painting = true; |
| 183 | } | 185 | } |
| 184 | 186 | ||
| 187 | std::pair<unsigned, unsigned> GetSize() const { | ||
| 188 | return std::make_pair(width(), height()); | ||
| 189 | } | ||
| 190 | |||
| 191 | protected: | ||
| 192 | bool IsPaintingEnabled() const { | ||
| 193 | return do_painting; | ||
| 194 | } | ||
| 195 | |||
| 185 | private: | 196 | private: |
| 186 | GRenderWindow* parent; | 197 | GRenderWindow* parent; |
| 187 | bool do_painting; | 198 | bool do_painting = false; |
| 199 | }; | ||
| 200 | |||
| 201 | // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL | ||
| 202 | // context. | ||
| 203 | // The corresponding functionality is handled in EmuThread instead | ||
| 204 | class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow { | ||
| 205 | public: | ||
| 206 | GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context) | ||
| 207 | : GWidgetInternal(parent), QOpenGLWindow(shared_context) {} | ||
| 208 | ~GGLWidgetInternal() override = default; | ||
| 209 | |||
| 210 | void paintEvent(QPaintEvent* ev) override { | ||
| 211 | if (IsPaintingEnabled()) { | ||
| 212 | QPainter painter(this); | ||
| 213 | } | ||
| 214 | } | ||
| 188 | }; | 215 | }; |
| 189 | 216 | ||
| 217 | #ifdef HAS_VULKAN | ||
| 218 | class GVKWidgetInternal final : public GWidgetInternal { | ||
| 219 | public: | ||
| 220 | GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) { | ||
| 221 | setSurfaceType(QSurface::SurfaceType::VulkanSurface); | ||
| 222 | setVulkanInstance(instance); | ||
| 223 | } | ||
| 224 | ~GVKWidgetInternal() override = default; | ||
| 225 | }; | ||
| 226 | #endif | ||
| 227 | |||
| 190 | GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) | 228 | GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) |
| 191 | : QWidget(parent), emu_thread(emu_thread) { | 229 | : QWidget(parent), emu_thread(emu_thread) { |
| 192 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") | 230 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| @@ -201,9 +239,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread) | |||
| 201 | 239 | ||
| 202 | GRenderWindow::~GRenderWindow() { | 240 | GRenderWindow::~GRenderWindow() { |
| 203 | InputCommon::Shutdown(); | 241 | InputCommon::Shutdown(); |
| 242 | |||
| 243 | // Avoid an unordered destruction that generates a segfault | ||
| 244 | delete child; | ||
| 204 | } | 245 | } |
| 205 | 246 | ||
| 206 | void GRenderWindow::moveContext() { | 247 | void GRenderWindow::moveContext() { |
| 248 | if (!context) { | ||
| 249 | return; | ||
| 250 | } | ||
| 207 | DoneCurrent(); | 251 | DoneCurrent(); |
| 208 | 252 | ||
| 209 | // If the thread started running, move the GL Context to the new thread. Otherwise, move it | 253 | // If the thread started running, move the GL Context to the new thread. Otherwise, move it |
| @@ -215,8 +259,9 @@ void GRenderWindow::moveContext() { | |||
| 215 | } | 259 | } |
| 216 | 260 | ||
| 217 | void GRenderWindow::SwapBuffers() { | 261 | void GRenderWindow::SwapBuffers() { |
| 218 | context->swapBuffers(child); | 262 | if (context) { |
| 219 | 263 | context->swapBuffers(child); | |
| 264 | } | ||
| 220 | if (!first_frame) { | 265 | if (!first_frame) { |
| 221 | first_frame = true; | 266 | first_frame = true; |
| 222 | emit FirstFrameDisplayed(); | 267 | emit FirstFrameDisplayed(); |
| @@ -224,15 +269,38 @@ void GRenderWindow::SwapBuffers() { | |||
| 224 | } | 269 | } |
| 225 | 270 | ||
| 226 | void GRenderWindow::MakeCurrent() { | 271 | void GRenderWindow::MakeCurrent() { |
| 227 | context->makeCurrent(child); | 272 | if (context) { |
| 273 | context->makeCurrent(child); | ||
| 274 | } | ||
| 228 | } | 275 | } |
| 229 | 276 | ||
| 230 | void GRenderWindow::DoneCurrent() { | 277 | void GRenderWindow::DoneCurrent() { |
| 231 | context->doneCurrent(); | 278 | if (context) { |
| 279 | context->doneCurrent(); | ||
| 280 | } | ||
| 232 | } | 281 | } |
| 233 | 282 | ||
| 234 | void GRenderWindow::PollEvents() {} | 283 | void GRenderWindow::PollEvents() {} |
| 235 | 284 | ||
| 285 | bool GRenderWindow::IsShown() const { | ||
| 286 | return !isMinimized(); | ||
| 287 | } | ||
| 288 | |||
| 289 | void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 290 | void* surface) const { | ||
| 291 | #ifdef HAS_VULKAN | ||
| 292 | const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); | ||
| 293 | const VkInstance instance_copy = vk_instance->vkInstance(); | ||
| 294 | const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child); | ||
| 295 | |||
| 296 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 297 | std::memcpy(instance, &instance_copy, sizeof(instance_copy)); | ||
| 298 | std::memcpy(surface, &surface_copy, sizeof(surface_copy)); | ||
| 299 | #else | ||
| 300 | UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); | ||
| 301 | #endif | ||
| 302 | } | ||
| 303 | |||
| 236 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). | 304 | // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels). |
| 237 | // | 305 | // |
| 238 | // Older versions get the window size (density independent pixels), | 306 | // Older versions get the window size (density independent pixels), |
| @@ -241,10 +309,9 @@ void GRenderWindow::PollEvents() {} | |||
| 241 | void GRenderWindow::OnFramebufferSizeChanged() { | 309 | void GRenderWindow::OnFramebufferSizeChanged() { |
| 242 | // Screen changes potentially incur a change in screen DPI, hence we should update the | 310 | // Screen changes potentially incur a change in screen DPI, hence we should update the |
| 243 | // framebuffer size | 311 | // framebuffer size |
| 244 | const qreal pixel_ratio = GetWindowPixelRatio(); | 312 | const qreal pixelRatio{GetWindowPixelRatio()}; |
| 245 | const u32 width = child->QPaintDevice::width() * pixel_ratio; | 313 | const auto size{child->GetSize()}; |
| 246 | const u32 height = child->QPaintDevice::height() * pixel_ratio; | 314 | UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio); |
| 247 | UpdateCurrentFramebufferLayout(width, height); | ||
| 248 | } | 315 | } |
| 249 | 316 | ||
| 250 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { | 317 | void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) { |
| @@ -290,7 +357,7 @@ qreal GRenderWindow::GetWindowPixelRatio() const { | |||
| 290 | } | 357 | } |
| 291 | 358 | ||
| 292 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { | 359 | std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { |
| 293 | const qreal pixel_ratio = GetWindowPixelRatio(); | 360 | const qreal pixel_ratio{GetWindowPixelRatio()}; |
| 294 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), | 361 | return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})), |
| 295 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; | 362 | static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; |
| 296 | } | 363 | } |
| @@ -356,50 +423,46 @@ std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedCont | |||
| 356 | return std::make_unique<GGLContext>(context.get()); | 423 | return std::make_unique<GGLContext>(context.get()); |
| 357 | } | 424 | } |
| 358 | 425 | ||
| 359 | void GRenderWindow::InitRenderTarget() { | 426 | bool GRenderWindow::InitRenderTarget() { |
| 360 | shared_context.reset(); | 427 | shared_context.reset(); |
| 361 | context.reset(); | 428 | context.reset(); |
| 362 | 429 | if (child) { | |
| 363 | delete child; | 430 | delete child; |
| 364 | child = nullptr; | 431 | } |
| 365 | 432 | if (container) { | |
| 366 | delete container; | 433 | delete container; |
| 367 | container = nullptr; | 434 | } |
| 368 | 435 | if (layout()) { | |
| 369 | delete layout(); | 436 | delete layout(); |
| 437 | } | ||
| 370 | 438 | ||
| 371 | first_frame = false; | 439 | first_frame = false; |
| 372 | 440 | ||
| 373 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 441 | switch (Settings::values.renderer_backend) { |
| 374 | // WA_DontShowOnScreen, WA_DeleteOnClose | 442 | case Settings::RendererBackend::OpenGL: |
| 375 | QSurfaceFormat fmt; | 443 | if (!InitializeOpenGL()) { |
| 376 | fmt.setVersion(4, 3); | 444 | return false; |
| 377 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | 445 | } |
| 378 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | 446 | break; |
| 379 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | 447 | case Settings::RendererBackend::Vulkan: |
| 380 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | 448 | if (!InitializeVulkan()) { |
| 381 | shared_context = std::make_unique<QOpenGLContext>(); | 449 | return false; |
| 382 | shared_context->setFormat(fmt); | 450 | } |
| 383 | shared_context->create(); | 451 | break; |
| 384 | context = std::make_unique<QOpenGLContext>(); | 452 | } |
| 385 | context->setShareContext(shared_context.get()); | ||
| 386 | context->setFormat(fmt); | ||
| 387 | context->create(); | ||
| 388 | fmt.setSwapInterval(0); | ||
| 389 | 453 | ||
| 390 | child = new GGLWidgetInternal(this, shared_context.get()); | ||
| 391 | container = QWidget::createWindowContainer(child, this); | 454 | container = QWidget::createWindowContainer(child, this); |
| 392 | |||
| 393 | QBoxLayout* layout = new QHBoxLayout(this); | 455 | QBoxLayout* layout = new QHBoxLayout(this); |
| 456 | |||
| 394 | layout->addWidget(container); | 457 | layout->addWidget(container); |
| 395 | layout->setMargin(0); | 458 | layout->setMargin(0); |
| 396 | setLayout(layout); | 459 | setLayout(layout); |
| 397 | 460 | ||
| 398 | // Reset minimum size to avoid unwanted resizes when this function is called for a second time. | 461 | // Reset minimum required size to avoid resizing issues on the main window after restarting. |
| 399 | setMinimumSize(1, 1); | 462 | setMinimumSize(1, 1); |
| 400 | 463 | ||
| 401 | // Show causes the window to actually be created and the OpenGL context as well, but we don't | 464 | // Show causes the window to actually be created and the gl context as well, but we don't want |
| 402 | // want the widget to be shown yet, so immediately hide it. | 465 | // the widget to be shown yet, so immediately hide it. |
| 403 | show(); | 466 | show(); |
| 404 | hide(); | 467 | hide(); |
| 405 | 468 | ||
| @@ -410,9 +473,17 @@ void GRenderWindow::InitRenderTarget() { | |||
| 410 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | 473 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); |
| 411 | 474 | ||
| 412 | OnFramebufferSizeChanged(); | 475 | OnFramebufferSizeChanged(); |
| 413 | NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height())); | 476 | NotifyClientAreaSizeChanged(child->GetSize()); |
| 414 | 477 | ||
| 415 | BackupGeometry(); | 478 | BackupGeometry(); |
| 479 | |||
| 480 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { | ||
| 481 | if (!LoadOpenGL()) { | ||
| 482 | return false; | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | return true; | ||
| 416 | } | 487 | } |
| 417 | 488 | ||
| 418 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { | 489 | void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { |
| @@ -441,6 +512,113 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
| 441 | setMinimumSize(minimal_size.first, minimal_size.second); | 512 | setMinimumSize(minimal_size.first, minimal_size.second); |
| 442 | } | 513 | } |
| 443 | 514 | ||
| 515 | bool GRenderWindow::InitializeOpenGL() { | ||
| 516 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | ||
| 517 | // WA_DontShowOnScreen, WA_DeleteOnClose | ||
| 518 | QSurfaceFormat fmt; | ||
| 519 | fmt.setVersion(4, 3); | ||
| 520 | fmt.setProfile(QSurfaceFormat::CompatibilityProfile); | ||
| 521 | fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); | ||
| 522 | // TODO: expose a setting for buffer value (ie default/single/double/triple) | ||
| 523 | fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); | ||
| 524 | shared_context = std::make_unique<QOpenGLContext>(); | ||
| 525 | shared_context->setFormat(fmt); | ||
| 526 | shared_context->create(); | ||
| 527 | context = std::make_unique<QOpenGLContext>(); | ||
| 528 | context->setShareContext(shared_context.get()); | ||
| 529 | context->setFormat(fmt); | ||
| 530 | context->create(); | ||
| 531 | fmt.setSwapInterval(false); | ||
| 532 | |||
| 533 | child = new GGLWidgetInternal(this, shared_context.get()); | ||
| 534 | return true; | ||
| 535 | } | ||
| 536 | |||
| 537 | bool GRenderWindow::InitializeVulkan() { | ||
| 538 | #ifdef HAS_VULKAN | ||
| 539 | vk_instance = std::make_unique<QVulkanInstance>(); | ||
| 540 | vk_instance->setApiVersion(QVersionNumber(1, 1, 0)); | ||
| 541 | vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect); | ||
| 542 | if (Settings::values.renderer_debug) { | ||
| 543 | const auto supported_layers{vk_instance->supportedLayers()}; | ||
| 544 | const bool found = | ||
| 545 | std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) { | ||
| 546 | constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation"; | ||
| 547 | return layer.name == searched_layer; | ||
| 548 | }); | ||
| 549 | if (found) { | ||
| 550 | vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); | ||
| 551 | vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | ||
| 552 | } | ||
| 553 | } | ||
| 554 | if (!vk_instance->create()) { | ||
| 555 | QMessageBox::critical( | ||
| 556 | this, tr("Error while initializing Vulkan 1.1!"), | ||
| 557 | tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the " | ||
| 558 | "latest graphics drivers.")); | ||
| 559 | return false; | ||
| 560 | } | ||
| 561 | |||
| 562 | child = new GVKWidgetInternal(this, vk_instance.get()); | ||
| 563 | return true; | ||
| 564 | #else | ||
| 565 | QMessageBox::critical(this, tr("Vulkan not available!"), | ||
| 566 | tr("yuzu has not been compiled with Vulkan support.")); | ||
| 567 | return false; | ||
| 568 | #endif | ||
| 569 | } | ||
| 570 | |||
| 571 | bool GRenderWindow::LoadOpenGL() { | ||
| 572 | Core::Frontend::ScopeAcquireWindowContext acquire_context{*this}; | ||
| 573 | if (!gladLoadGL()) { | ||
| 574 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"), | ||
| 575 | tr("Your GPU may not support OpenGL 4.3, or you do not have the " | ||
| 576 | "latest graphics driver.")); | ||
| 577 | return false; | ||
| 578 | } | ||
| 579 | |||
| 580 | QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); | ||
| 581 | if (!unsupported_gl_extensions.empty()) { | ||
| 582 | QMessageBox::critical( | ||
| 583 | this, tr("Error while initializing OpenGL!"), | ||
| 584 | tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you " | ||
| 585 | "have the latest graphics driver.<br><br>Unsupported extensions:<br>") + | ||
| 586 | unsupported_gl_extensions.join(QStringLiteral("<br>"))); | ||
| 587 | return false; | ||
| 588 | } | ||
| 589 | return true; | ||
| 590 | } | ||
| 591 | |||
| 592 | QStringList GRenderWindow::GetUnsupportedGLExtensions() const { | ||
| 593 | QStringList unsupported_ext; | ||
| 594 | |||
| 595 | if (!GLAD_GL_ARB_buffer_storage) | ||
| 596 | unsupported_ext.append(QStringLiteral("ARB_buffer_storage")); | ||
| 597 | if (!GLAD_GL_ARB_direct_state_access) | ||
| 598 | unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); | ||
| 599 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) | ||
| 600 | unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); | ||
| 601 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) | ||
| 602 | unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); | ||
| 603 | if (!GLAD_GL_ARB_multi_bind) | ||
| 604 | unsupported_ext.append(QStringLiteral("ARB_multi_bind")); | ||
| 605 | if (!GLAD_GL_ARB_clip_control) | ||
| 606 | unsupported_ext.append(QStringLiteral("ARB_clip_control")); | ||
| 607 | |||
| 608 | // Extensions required to support some texture formats. | ||
| 609 | if (!GLAD_GL_EXT_texture_compression_s3tc) | ||
| 610 | unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); | ||
| 611 | if (!GLAD_GL_ARB_texture_compression_rgtc) | ||
| 612 | unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); | ||
| 613 | if (!GLAD_GL_ARB_depth_buffer_float) | ||
| 614 | unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); | ||
| 615 | |||
| 616 | for (const QString& ext : unsupported_ext) | ||
| 617 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); | ||
| 618 | |||
| 619 | return unsupported_ext; | ||
| 620 | } | ||
| 621 | |||
| 444 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { | 622 | void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) { |
| 445 | this->emu_thread = emu_thread; | 623 | this->emu_thread = emu_thread; |
| 446 | child->DisablePainting(); | 624 | child->DisablePainting(); |
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 2fc64895f..71a2fa321 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h | |||
| @@ -7,17 +7,28 @@ | |||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <condition_variable> | 8 | #include <condition_variable> |
| 9 | #include <mutex> | 9 | #include <mutex> |
| 10 | |||
| 10 | #include <QImage> | 11 | #include <QImage> |
| 11 | #include <QThread> | 12 | #include <QThread> |
| 12 | #include <QWidget> | 13 | #include <QWidget> |
| 14 | |||
| 15 | #include "common/thread.h" | ||
| 13 | #include "core/core.h" | 16 | #include "core/core.h" |
| 14 | #include "core/frontend/emu_window.h" | 17 | #include "core/frontend/emu_window.h" |
| 15 | 18 | ||
| 16 | class QKeyEvent; | 19 | class QKeyEvent; |
| 17 | class QScreen; | 20 | class QScreen; |
| 18 | class QTouchEvent; | 21 | class QTouchEvent; |
| 22 | class QStringList; | ||
| 23 | class QSurface; | ||
| 24 | class QOpenGLContext; | ||
| 25 | #ifdef HAS_VULKAN | ||
| 26 | class QVulkanInstance; | ||
| 27 | #endif | ||
| 19 | 28 | ||
| 29 | class GWidgetInternal; | ||
| 20 | class GGLWidgetInternal; | 30 | class GGLWidgetInternal; |
| 31 | class GVKWidgetInternal; | ||
| 21 | class GMainWindow; | 32 | class GMainWindow; |
| 22 | class GRenderWindow; | 33 | class GRenderWindow; |
| 23 | class QSurface; | 34 | class QSurface; |
| @@ -123,6 +134,9 @@ public: | |||
| 123 | void MakeCurrent() override; | 134 | void MakeCurrent() override; |
| 124 | void DoneCurrent() override; | 135 | void DoneCurrent() override; |
| 125 | void PollEvents() override; | 136 | void PollEvents() override; |
| 137 | bool IsShown() const override; | ||
| 138 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 139 | void* surface) const override; | ||
| 126 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 140 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 127 | 141 | ||
| 128 | void ForwardKeyPressEvent(QKeyEvent* event); | 142 | void ForwardKeyPressEvent(QKeyEvent* event); |
| @@ -142,7 +156,7 @@ public: | |||
| 142 | 156 | ||
| 143 | void OnClientAreaResized(u32 width, u32 height); | 157 | void OnClientAreaResized(u32 width, u32 height); |
| 144 | 158 | ||
| 145 | void InitRenderTarget(); | 159 | bool InitRenderTarget(); |
| 146 | 160 | ||
| 147 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); | 161 | void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); |
| 148 | 162 | ||
| @@ -165,10 +179,13 @@ private: | |||
| 165 | 179 | ||
| 166 | void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; | 180 | void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override; |
| 167 | 181 | ||
| 168 | QWidget* container = nullptr; | 182 | bool InitializeOpenGL(); |
| 169 | GGLWidgetInternal* child = nullptr; | 183 | bool InitializeVulkan(); |
| 184 | bool LoadOpenGL(); | ||
| 185 | QStringList GetUnsupportedGLExtensions() const; | ||
| 170 | 186 | ||
| 171 | QByteArray geometry; | 187 | QWidget* container = nullptr; |
| 188 | GWidgetInternal* child = nullptr; | ||
| 172 | 189 | ||
| 173 | EmuThread* emu_thread; | 190 | EmuThread* emu_thread; |
| 174 | // Context that backs the GGLWidgetInternal (and will be used by core to render) | 191 | // Context that backs the GGLWidgetInternal (and will be used by core to render) |
| @@ -177,9 +194,14 @@ private: | |||
| 177 | // current | 194 | // current |
| 178 | std::unique_ptr<QOpenGLContext> shared_context; | 195 | std::unique_ptr<QOpenGLContext> shared_context; |
| 179 | 196 | ||
| 197 | #ifdef HAS_VULKAN | ||
| 198 | std::unique_ptr<QVulkanInstance> vk_instance; | ||
| 199 | #endif | ||
| 200 | |||
| 180 | /// Temporary storage of the screenshot taken | 201 | /// Temporary storage of the screenshot taken |
| 181 | QImage screenshot_image; | 202 | QImage screenshot_image; |
| 182 | 203 | ||
| 204 | QByteArray geometry; | ||
| 183 | bool first_frame = false; | 205 | bool first_frame = false; |
| 184 | 206 | ||
| 185 | protected: | 207 | protected: |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index f92a4b3c3..6209fff75 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "core/hle/service/acc/profile_manager.h" | 10 | #include "core/hle/service/acc/profile_manager.h" |
| 11 | #include "core/hle/service/hid/controllers/npad.h" | 11 | #include "core/hle/service/hid/controllers/npad.h" |
| 12 | #include "input_common/main.h" | 12 | #include "input_common/main.h" |
| 13 | #include "input_common/udp/client.h" | ||
| 13 | #include "yuzu/configuration/config.h" | 14 | #include "yuzu/configuration/config.h" |
| 14 | #include "yuzu/uisettings.h" | 15 | #include "yuzu/uisettings.h" |
| 15 | 16 | ||
| @@ -429,6 +430,16 @@ void Config::ReadControlValues() { | |||
| 429 | QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) | 430 | QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")) |
| 430 | .toString() | 431 | .toString() |
| 431 | .toStdString(); | 432 | .toStdString(); |
| 433 | Settings::values.udp_input_address = | ||
| 434 | ReadSetting(QStringLiteral("udp_input_address"), | ||
| 435 | QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR)) | ||
| 436 | .toString() | ||
| 437 | .toStdString(); | ||
| 438 | Settings::values.udp_input_port = static_cast<u16>( | ||
| 439 | ReadSetting(QStringLiteral("udp_input_port"), InputCommon::CemuhookUDP::DEFAULT_PORT) | ||
| 440 | .toInt()); | ||
| 441 | Settings::values.udp_pad_index = | ||
| 442 | static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt()); | ||
| 432 | 443 | ||
| 433 | qt_config->endGroup(); | 444 | qt_config->endGroup(); |
| 434 | } | 445 | } |
| @@ -613,8 +624,13 @@ void Config::ReadPathValues() { | |||
| 613 | void Config::ReadRendererValues() { | 624 | void Config::ReadRendererValues() { |
| 614 | qt_config->beginGroup(QStringLiteral("Renderer")); | 625 | qt_config->beginGroup(QStringLiteral("Renderer")); |
| 615 | 626 | ||
| 627 | Settings::values.renderer_backend = | ||
| 628 | static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt()); | ||
| 629 | Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool(); | ||
| 630 | Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt(); | ||
| 616 | Settings::values.resolution_factor = | 631 | Settings::values.resolution_factor = |
| 617 | ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); | 632 | ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); |
| 633 | Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt(); | ||
| 618 | Settings::values.use_frame_limit = | 634 | Settings::values.use_frame_limit = |
| 619 | ReadSetting(QStringLiteral("use_frame_limit"), true).toBool(); | 635 | ReadSetting(QStringLiteral("use_frame_limit"), true).toBool(); |
| 620 | Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); | 636 | Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); |
| @@ -727,7 +743,6 @@ void Config::ReadUIValues() { | |||
| 727 | void Config::ReadUIGamelistValues() { | 743 | void Config::ReadUIGamelistValues() { |
| 728 | qt_config->beginGroup(QStringLiteral("UIGameList")); | 744 | qt_config->beginGroup(QStringLiteral("UIGameList")); |
| 729 | 745 | ||
| 730 | UISettings::values.show_unknown = ReadSetting(QStringLiteral("show_unknown"), true).toBool(); | ||
| 731 | UISettings::values.show_add_ons = ReadSetting(QStringLiteral("show_add_ons"), true).toBool(); | 746 | UISettings::values.show_add_ons = ReadSetting(QStringLiteral("show_add_ons"), true).toBool(); |
| 732 | UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt(); | 747 | UISettings::values.icon_size = ReadSetting(QStringLiteral("icon_size"), 64).toUInt(); |
| 733 | UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt(); | 748 | UISettings::values.row_1_text_id = ReadSetting(QStringLiteral("row_1_text_id"), 3).toUInt(); |
| @@ -911,6 +926,12 @@ void Config::SaveControlValues() { | |||
| 911 | QString::fromStdString(Settings::values.motion_device), | 926 | QString::fromStdString(Settings::values.motion_device), |
| 912 | QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); | 927 | QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01")); |
| 913 | WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); | 928 | WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); |
| 929 | WriteSetting(QStringLiteral("udp_input_address"), | ||
| 930 | QString::fromStdString(Settings::values.udp_input_address), | ||
| 931 | QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR)); | ||
| 932 | WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port, | ||
| 933 | InputCommon::CemuhookUDP::DEFAULT_PORT); | ||
| 934 | WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0); | ||
| 914 | 935 | ||
| 915 | qt_config->endGroup(); | 936 | qt_config->endGroup(); |
| 916 | } | 937 | } |
| @@ -1039,8 +1060,12 @@ void Config::SavePathValues() { | |||
| 1039 | void Config::SaveRendererValues() { | 1060 | void Config::SaveRendererValues() { |
| 1040 | qt_config->beginGroup(QStringLiteral("Renderer")); | 1061 | qt_config->beginGroup(QStringLiteral("Renderer")); |
| 1041 | 1062 | ||
| 1063 | WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0); | ||
| 1064 | WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); | ||
| 1065 | WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); | ||
| 1042 | WriteSetting(QStringLiteral("resolution_factor"), | 1066 | WriteSetting(QStringLiteral("resolution_factor"), |
| 1043 | static_cast<double>(Settings::values.resolution_factor), 1.0); | 1067 | static_cast<double>(Settings::values.resolution_factor), 1.0); |
| 1068 | WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); | ||
| 1044 | WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); | 1069 | WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); |
| 1045 | WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); | 1070 | WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); |
| 1046 | WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, | 1071 | WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, |
| @@ -1135,7 +1160,6 @@ void Config::SaveUIValues() { | |||
| 1135 | void Config::SaveUIGamelistValues() { | 1160 | void Config::SaveUIGamelistValues() { |
| 1136 | qt_config->beginGroup(QStringLiteral("UIGameList")); | 1161 | qt_config->beginGroup(QStringLiteral("UIGameList")); |
| 1137 | 1162 | ||
| 1138 | WriteSetting(QStringLiteral("show_unknown"), UISettings::values.show_unknown, true); | ||
| 1139 | WriteSetting(QStringLiteral("show_add_ons"), UISettings::values.show_add_ons, true); | 1163 | WriteSetting(QStringLiteral("show_add_ons"), UISettings::values.show_add_ons, true); |
| 1140 | WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64); | 1164 | WriteSetting(QStringLiteral("icon_size"), UISettings::values.icon_size, 64); |
| 1141 | WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3); | 1165 | WriteSetting(QStringLiteral("row_1_text_id"), UISettings::values.row_1_text_id, 3); |
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui index 372427ae2..67b990f1a 100644 --- a/src/yuzu/configuration/configure.ui +++ b/src/yuzu/configuration/configure.ui | |||
| @@ -48,7 +48,7 @@ | |||
| 48 | <string>General</string> | 48 | <string>General</string> |
| 49 | </attribute> | 49 | </attribute> |
| 50 | </widget> | 50 | </widget> |
| 51 | <widget class="ConfigureGameList" name="gameListTab"> | 51 | <widget class="ConfigureUi" name="uiTab"> |
| 52 | <attribute name="title"> | 52 | <attribute name="title"> |
| 53 | <string>Game List</string> | 53 | <string>Game List</string> |
| 54 | </attribute> | 54 | </attribute> |
| @@ -166,9 +166,9 @@ | |||
| 166 | <container>1</container> | 166 | <container>1</container> |
| 167 | </customwidget> | 167 | </customwidget> |
| 168 | <customwidget> | 168 | <customwidget> |
| 169 | <class>ConfigureGameList</class> | 169 | <class>ConfigureUi</class> |
| 170 | <extends>QWidget</extends> | 170 | <extends>QWidget</extends> |
| 171 | <header>configuration/configure_gamelist.h</header> | 171 | <header>configuration/configure_ui.h</header> |
| 172 | <container>1</container> | 172 | <container>1</container> |
| 173 | </customwidget> | 173 | </customwidget> |
| 174 | <customwidget> | 174 | <customwidget> |
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 90c1f9459..9631059c7 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -36,6 +36,8 @@ void ConfigureDebug::SetConfiguration() { | |||
| 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); | 36 | ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); |
| 37 | ui->reporting_services->setChecked(Settings::values.reporting_services); | 37 | ui->reporting_services->setChecked(Settings::values.reporting_services); |
| 38 | ui->quest_flag->setChecked(Settings::values.quest_flag); | 38 | ui->quest_flag->setChecked(Settings::values.quest_flag); |
| 39 | ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); | ||
| 40 | ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); | ||
| 39 | } | 41 | } |
| 40 | 42 | ||
| 41 | void ConfigureDebug::ApplyConfiguration() { | 43 | void ConfigureDebug::ApplyConfiguration() { |
| @@ -46,6 +48,7 @@ void ConfigureDebug::ApplyConfiguration() { | |||
| 46 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); | 48 | Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); |
| 47 | Settings::values.reporting_services = ui->reporting_services->isChecked(); | 49 | Settings::values.reporting_services = ui->reporting_services->isChecked(); |
| 48 | Settings::values.quest_flag = ui->quest_flag->isChecked(); | 50 | Settings::values.quest_flag = ui->quest_flag->isChecked(); |
| 51 | Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); | ||
| 49 | Debugger::ToggleConsole(); | 52 | Debugger::ToggleConsole(); |
| 50 | Log::Filter filter; | 53 | Log::Filter filter; |
| 51 | filter.ParseFilterString(Settings::values.log_filter); | 54 | filter.ParseFilterString(Settings::values.log_filter); |
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index ce49569bb..e028c4c80 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>400</width> | 9 | <width>400</width> |
| 10 | <height>474</height> | 10 | <height>467</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -103,6 +103,80 @@ | |||
| 103 | </item> | 103 | </item> |
| 104 | </layout> | 104 | </layout> |
| 105 | </item> | 105 | </item> |
| 106 | </layout> | ||
| 107 | </widget> | ||
| 108 | </item> | ||
| 109 | <item> | ||
| 110 | <widget class="QGroupBox" name="groupBox_3"> | ||
| 111 | <property name="title"> | ||
| 112 | <string>Homebrew</string> | ||
| 113 | </property> | ||
| 114 | <layout class="QVBoxLayout" name="verticalLayout_5"> | ||
| 115 | <item> | ||
| 116 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 117 | <item> | ||
| 118 | <widget class="QLabel" name="label_3"> | ||
| 119 | <property name="text"> | ||
| 120 | <string>Arguments String</string> | ||
| 121 | </property> | ||
| 122 | </widget> | ||
| 123 | </item> | ||
| 124 | <item> | ||
| 125 | <widget class="QLineEdit" name="homebrew_args_edit"/> | ||
| 126 | </item> | ||
| 127 | </layout> | ||
| 128 | </item> | ||
| 129 | </layout> | ||
| 130 | </widget> | ||
| 131 | </item> | ||
| 132 | <item> | ||
| 133 | <widget class="QGroupBox" name="groupBox_4"> | ||
| 134 | <property name="title"> | ||
| 135 | <string>Graphics</string> | ||
| 136 | </property> | ||
| 137 | <layout class="QVBoxLayout" name="verticalLayout_6"> | ||
| 138 | <item> | ||
| 139 | <widget class="QCheckBox" name="enable_graphics_debugging"> | ||
| 140 | <property name="enabled"> | ||
| 141 | <bool>true</bool> | ||
| 142 | </property> | ||
| 143 | <property name="whatsThis"> | ||
| 144 | <string>When checked, the graphics API enters in a slower debugging mode</string> | ||
| 145 | </property> | ||
| 146 | <property name="text"> | ||
| 147 | <string>Enable Graphics Debugging</string> | ||
| 148 | </property> | ||
| 149 | </widget> | ||
| 150 | </item> | ||
| 151 | </layout> | ||
| 152 | </widget> | ||
| 153 | </item> | ||
| 154 | <item> | ||
| 155 | <widget class="QGroupBox" name="groupBox_5"> | ||
| 156 | <property name="title"> | ||
| 157 | <string>Dump</string> | ||
| 158 | </property> | ||
| 159 | <layout class="QVBoxLayout" name="verticalLayout_6"> | ||
| 160 | <item> | ||
| 161 | <widget class="QCheckBox" name="dump_decompressed_nso"> | ||
| 162 | <property name="whatsThis"> | ||
| 163 | <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string> | ||
| 164 | </property> | ||
| 165 | <property name="text"> | ||
| 166 | <string>Dump Decompressed NSOs</string> | ||
| 167 | </property> | ||
| 168 | </widget> | ||
| 169 | </item> | ||
| 170 | <item> | ||
| 171 | <widget class="QCheckBox" name="dump_exefs"> | ||
| 172 | <property name="whatsThis"> | ||
| 173 | <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string> | ||
| 174 | </property> | ||
| 175 | <property name="text"> | ||
| 176 | <string>Dump ExeFS</string> | ||
| 177 | </property> | ||
| 178 | </widget> | ||
| 179 | </item> | ||
| 106 | <item> | 180 | <item> |
| 107 | <widget class="QCheckBox" name="reporting_services"> | 181 | <widget class="QCheckBox" name="reporting_services"> |
| 108 | <property name="text"> | 182 | <property name="text"> |
| @@ -129,11 +203,11 @@ | |||
| 129 | </widget> | 203 | </widget> |
| 130 | </item> | 204 | </item> |
| 131 | <item> | 205 | <item> |
| 132 | <widget class="QGroupBox" name="groupBox_5"> | 206 | <widget class="QGroupBox" name="groupBox_6"> |
| 133 | <property name="title"> | 207 | <property name="title"> |
| 134 | <string>Advanced</string> | 208 | <string>Advanced</string> |
| 135 | </property> | 209 | </property> |
| 136 | <layout class="QVBoxLayout" name="verticalLayout"> | 210 | <layout class="QVBoxLayout" name="verticalLayout_7"> |
| 137 | <item> | 211 | <item> |
| 138 | <widget class="QCheckBox" name="quest_flag"> | 212 | <widget class="QCheckBox" name="quest_flag"> |
| 139 | <property name="text"> | 213 | <property name="text"> |
| @@ -145,29 +219,6 @@ | |||
| 145 | </widget> | 219 | </widget> |
| 146 | </item> | 220 | </item> |
| 147 | <item> | 221 | <item> |
| 148 | <widget class="QGroupBox" name="groupBox_3"> | ||
| 149 | <property name="title"> | ||
| 150 | <string>Homebrew</string> | ||
| 151 | </property> | ||
| 152 | <layout class="QVBoxLayout" name="verticalLayout_5"> | ||
| 153 | <item> | ||
| 154 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 155 | <item> | ||
| 156 | <widget class="QLabel" name="label_3"> | ||
| 157 | <property name="text"> | ||
| 158 | <string>Arguments String</string> | ||
| 159 | </property> | ||
| 160 | </widget> | ||
| 161 | </item> | ||
| 162 | <item> | ||
| 163 | <widget class="QLineEdit" name="homebrew_args_edit"/> | ||
| 164 | </item> | ||
| 165 | </layout> | ||
| 166 | </item> | ||
| 167 | </layout> | ||
| 168 | </widget> | ||
| 169 | </item> | ||
| 170 | <item> | ||
| 171 | <spacer name="verticalSpacer"> | 222 | <spacer name="verticalSpacer"> |
| 172 | <property name="orientation"> | 223 | <property name="orientation"> |
| 173 | <enum>Qt::Vertical</enum> | 224 | <enum>Qt::Vertical</enum> |
| @@ -185,6 +236,19 @@ | |||
| 185 | </item> | 236 | </item> |
| 186 | </layout> | 237 | </layout> |
| 187 | </widget> | 238 | </widget> |
| 239 | <tabstops> | ||
| 240 | <tabstop>toggle_gdbstub</tabstop> | ||
| 241 | <tabstop>gdbport_spinbox</tabstop> | ||
| 242 | <tabstop>log_filter_edit</tabstop> | ||
| 243 | <tabstop>toggle_console</tabstop> | ||
| 244 | <tabstop>open_log_button</tabstop> | ||
| 245 | <tabstop>homebrew_args_edit</tabstop> | ||
| 246 | <tabstop>enable_graphics_debugging</tabstop> | ||
| 247 | <tabstop>dump_decompressed_nso</tabstop> | ||
| 248 | <tabstop>dump_exefs</tabstop> | ||
| 249 | <tabstop>reporting_services</tabstop> | ||
| 250 | <tabstop>quest_flag</tabstop> | ||
| 251 | </tabstops> | ||
| 188 | <resources/> | 252 | <resources/> |
| 189 | <connections> | 253 | <connections> |
| 190 | <connection> | 254 | <connection> |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 8497eaa14..db3b19352 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -34,7 +34,7 @@ void ConfigureDialog::SetConfiguration() {} | |||
| 34 | 34 | ||
| 35 | void ConfigureDialog::ApplyConfiguration() { | 35 | void ConfigureDialog::ApplyConfiguration() { |
| 36 | ui->generalTab->ApplyConfiguration(); | 36 | ui->generalTab->ApplyConfiguration(); |
| 37 | ui->gameListTab->ApplyConfiguration(); | 37 | ui->uiTab->ApplyConfiguration(); |
| 38 | ui->systemTab->ApplyConfiguration(); | 38 | ui->systemTab->ApplyConfiguration(); |
| 39 | ui->profileManagerTab->ApplyConfiguration(); | 39 | ui->profileManagerTab->ApplyConfiguration(); |
| 40 | ui->filesystemTab->applyConfiguration(); | 40 | ui->filesystemTab->applyConfiguration(); |
| @@ -74,7 +74,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>); | |||
| 74 | 74 | ||
| 75 | void ConfigureDialog::PopulateSelectionList() { | 75 | void ConfigureDialog::PopulateSelectionList() { |
| 76 | const std::array<std::pair<QString, QList<QWidget*>>, 5> items{ | 76 | const std::array<std::pair<QString, QList<QWidget*>>, 5> items{ |
| 77 | {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, | 77 | {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}}, |
| 78 | {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, | 78 | {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, |
| 79 | {tr("Graphics"), {ui->graphicsTab}}, | 79 | {tr("Graphics"), {ui->graphicsTab}}, |
| 80 | {tr("Audio"), {ui->audioTab}}, | 80 | {tr("Audio"), {ui->audioTab}}, |
| @@ -108,7 +108,7 @@ void ConfigureDialog::UpdateVisibleTabs() { | |||
| 108 | {ui->audioTab, tr("Audio")}, | 108 | {ui->audioTab, tr("Audio")}, |
| 109 | {ui->debugTab, tr("Debug")}, | 109 | {ui->debugTab, tr("Debug")}, |
| 110 | {ui->webTab, tr("Web")}, | 110 | {ui->webTab, tr("Web")}, |
| 111 | {ui->gameListTab, tr("Game List")}, | 111 | {ui->uiTab, tr("UI")}, |
| 112 | {ui->filesystemTab, tr("Filesystem")}, | 112 | {ui->filesystemTab, tr("Filesystem")}, |
| 113 | {ui->serviceTab, tr("Services")}, | 113 | {ui->serviceTab, tr("Services")}, |
| 114 | }; | 114 | }; |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 34e1d7fea..5ef927114 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -15,11 +15,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 15 | 15 | ||
| 16 | ui->setupUi(this); | 16 | ui->setupUi(this); |
| 17 | 17 | ||
| 18 | for (const auto& theme : UISettings::themes) { | ||
| 19 | ui->theme_combobox->addItem(QString::fromUtf8(theme.first), | ||
| 20 | QString::fromUtf8(theme.second)); | ||
| 21 | } | ||
| 22 | |||
| 23 | SetConfiguration(); | 18 | SetConfiguration(); |
| 24 | 19 | ||
| 25 | connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); | 20 | connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); |
| @@ -30,7 +25,6 @@ ConfigureGeneral::~ConfigureGeneral() = default; | |||
| 30 | void ConfigureGeneral::SetConfiguration() { | 25 | void ConfigureGeneral::SetConfiguration() { |
| 31 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 26 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 32 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); | 27 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); |
| 33 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | ||
| 34 | ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); | 28 | ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); |
| 35 | 29 | ||
| 36 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); | 30 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); |
| @@ -41,8 +35,6 @@ void ConfigureGeneral::SetConfiguration() { | |||
| 41 | void ConfigureGeneral::ApplyConfiguration() { | 35 | void ConfigureGeneral::ApplyConfiguration() { |
| 42 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); | 36 | UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); |
| 43 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); | 37 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); |
| 44 | UISettings::values.theme = | ||
| 45 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); | ||
| 46 | UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); | 38 | UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); |
| 47 | 39 | ||
| 48 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); | 40 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index 26b3486ff..857119bb3 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -65,39 +65,12 @@ | |||
| 65 | </property> | 65 | </property> |
| 66 | </widget> | 66 | </widget> |
| 67 | </item> | 67 | </item> |
| 68 | <item> | ||
| 69 | <widget class="QCheckBox" name="toggle_background_pause"> | ||
| 70 | <property name="text"> | ||
| 71 | <string>Pause emulation when in background</string> | ||
| 72 | </property> | ||
| 73 | </widget> | ||
| 74 | </item> | ||
| 75 | </layout> | ||
| 76 | </item> | ||
| 77 | </layout> | ||
| 78 | </widget> | ||
| 79 | </item> | ||
| 80 | <item> | ||
| 81 | <widget class="QGroupBox" name="theme_group_box"> | ||
| 82 | <property name="title"> | ||
| 83 | <string>Theme</string> | ||
| 84 | </property> | ||
| 85 | <layout class="QHBoxLayout" name="theme_qhbox_layout"> | ||
| 86 | <item> | ||
| 87 | <layout class="QVBoxLayout" name="theme_qvbox_layout"> | ||
| 88 | <item> | 68 | <item> |
| 89 | <layout class="QHBoxLayout" name="theme_qhbox_layout_2"> | 69 | <widget class="QCheckBox" name="toggle_background_pause"> |
| 90 | <item> | 70 | <property name="text"> |
| 91 | <widget class="QLabel" name="theme_label"> | 71 | <string>Pause emulation when in background</string> |
| 92 | <property name="text"> | 72 | </property> |
| 93 | <string>Theme:</string> | 73 | </widget> |
| 94 | </property> | ||
| 95 | </widget> | ||
| 96 | </item> | ||
| 97 | <item> | ||
| 98 | <widget class="QComboBox" name="theme_combobox"/> | ||
| 99 | </item> | ||
| 100 | </layout> | ||
| 101 | </item> | 74 | </item> |
| 102 | </layout> | 75 | </layout> |
| 103 | </item> | 76 | </item> |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 2c9e322c9..ea899c080 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -3,6 +3,13 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <QColorDialog> | 5 | #include <QColorDialog> |
| 6 | #include <QComboBox> | ||
| 7 | #ifdef HAS_VULKAN | ||
| 8 | #include <QVulkanInstance> | ||
| 9 | #endif | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/logging/log.h" | ||
| 6 | #include "core/core.h" | 13 | #include "core/core.h" |
| 7 | #include "core/settings.h" | 14 | #include "core/settings.h" |
| 8 | #include "ui_configure_graphics.h" | 15 | #include "ui_configure_graphics.h" |
| @@ -51,10 +58,18 @@ Resolution FromResolutionFactor(float factor) { | |||
| 51 | 58 | ||
| 52 | ConfigureGraphics::ConfigureGraphics(QWidget* parent) | 59 | ConfigureGraphics::ConfigureGraphics(QWidget* parent) |
| 53 | : QWidget(parent), ui(new Ui::ConfigureGraphics) { | 60 | : QWidget(parent), ui(new Ui::ConfigureGraphics) { |
| 61 | vulkan_device = Settings::values.vulkan_device; | ||
| 62 | RetrieveVulkanDevices(); | ||
| 63 | |||
| 54 | ui->setupUi(this); | 64 | ui->setupUi(this); |
| 55 | 65 | ||
| 56 | SetConfiguration(); | 66 | SetConfiguration(); |
| 57 | 67 | ||
| 68 | connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | ||
| 69 | [this] { UpdateDeviceComboBox(); }); | ||
| 70 | connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, | ||
| 71 | [this](int device) { UpdateDeviceSelection(device); }); | ||
| 72 | |||
| 58 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { | 73 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { |
| 59 | const QColor new_bg_color = QColorDialog::getColor(bg_color); | 74 | const QColor new_bg_color = QColorDialog::getColor(bg_color); |
| 60 | if (!new_bg_color.isValid()) { | 75 | if (!new_bg_color.isValid()) { |
| @@ -64,13 +79,25 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | |||
| 64 | }); | 79 | }); |
| 65 | } | 80 | } |
| 66 | 81 | ||
| 82 | void ConfigureGraphics::UpdateDeviceSelection(int device) { | ||
| 83 | if (device == -1) { | ||
| 84 | return; | ||
| 85 | } | ||
| 86 | if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) { | ||
| 87 | vulkan_device = device; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 67 | ConfigureGraphics::~ConfigureGraphics() = default; | 91 | ConfigureGraphics::~ConfigureGraphics() = default; |
| 68 | 92 | ||
| 69 | void ConfigureGraphics::SetConfiguration() { | 93 | void ConfigureGraphics::SetConfiguration() { |
| 70 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); | 94 | const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); |
| 71 | 95 | ||
| 96 | ui->api->setEnabled(runtime_lock); | ||
| 97 | ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend)); | ||
| 72 | ui->resolution_factor_combobox->setCurrentIndex( | 98 | ui->resolution_factor_combobox->setCurrentIndex( |
| 73 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); | 99 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); |
| 100 | ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio); | ||
| 74 | ui->use_disk_shader_cache->setEnabled(runtime_lock); | 101 | ui->use_disk_shader_cache->setEnabled(runtime_lock); |
| 75 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); | 102 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); |
| 76 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 103 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| @@ -80,11 +107,15 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 80 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | 107 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); |
| 81 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 108 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| 82 | Settings::values.bg_blue)); | 109 | Settings::values.bg_blue)); |
| 110 | UpdateDeviceComboBox(); | ||
| 83 | } | 111 | } |
| 84 | 112 | ||
| 85 | void ConfigureGraphics::ApplyConfiguration() { | 113 | void ConfigureGraphics::ApplyConfiguration() { |
| 114 | Settings::values.renderer_backend = GetCurrentGraphicsBackend(); | ||
| 115 | Settings::values.vulkan_device = vulkan_device; | ||
| 86 | Settings::values.resolution_factor = | 116 | Settings::values.resolution_factor = |
| 87 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); | 117 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); |
| 118 | Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex(); | ||
| 88 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); | 119 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); |
| 89 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 120 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); |
| 90 | Settings::values.use_asynchronous_gpu_emulation = | 121 | Settings::values.use_asynchronous_gpu_emulation = |
| @@ -116,3 +147,68 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) { | |||
| 116 | const QIcon color_icon(pixmap); | 147 | const QIcon color_icon(pixmap); |
| 117 | ui->bg_button->setIcon(color_icon); | 148 | ui->bg_button->setIcon(color_icon); |
| 118 | } | 149 | } |
| 150 | |||
| 151 | void ConfigureGraphics::UpdateDeviceComboBox() { | ||
| 152 | ui->device->clear(); | ||
| 153 | |||
| 154 | bool enabled = false; | ||
| 155 | switch (GetCurrentGraphicsBackend()) { | ||
| 156 | case Settings::RendererBackend::OpenGL: | ||
| 157 | ui->device->addItem(tr("OpenGL Graphics Device")); | ||
| 158 | enabled = false; | ||
| 159 | break; | ||
| 160 | case Settings::RendererBackend::Vulkan: | ||
| 161 | for (const auto device : vulkan_devices) { | ||
| 162 | ui->device->addItem(device); | ||
| 163 | } | ||
| 164 | ui->device->setCurrentIndex(vulkan_device); | ||
| 165 | enabled = !vulkan_devices.empty(); | ||
| 166 | break; | ||
| 167 | } | ||
| 168 | ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); | ||
| 169 | } | ||
| 170 | |||
| 171 | void ConfigureGraphics::RetrieveVulkanDevices() { | ||
| 172 | #ifdef HAS_VULKAN | ||
| 173 | QVulkanInstance instance; | ||
| 174 | instance.setApiVersion(QVersionNumber(1, 1, 0)); | ||
| 175 | if (!instance.create()) { | ||
| 176 | LOG_INFO(Frontend, "Vulkan 1.1 not available"); | ||
| 177 | return; | ||
| 178 | } | ||
| 179 | const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>( | ||
| 180 | instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))}; | ||
| 181 | if (vkEnumeratePhysicalDevices == nullptr) { | ||
| 182 | LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices"); | ||
| 183 | return; | ||
| 184 | } | ||
| 185 | u32 physical_device_count; | ||
| 186 | if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) != | ||
| 187 | VK_SUCCESS) { | ||
| 188 | LOG_INFO(Frontend, "Failed to get physical devices count"); | ||
| 189 | return; | ||
| 190 | } | ||
| 191 | std::vector<VkPhysicalDevice> physical_devices(physical_device_count); | ||
| 192 | if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, | ||
| 193 | physical_devices.data()) != VK_SUCCESS) { | ||
| 194 | LOG_INFO(Frontend, "Failed to get physical devices"); | ||
| 195 | return; | ||
| 196 | } | ||
| 197 | |||
| 198 | const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>( | ||
| 199 | instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))}; | ||
| 200 | if (vkGetPhysicalDeviceProperties == nullptr) { | ||
| 201 | LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties"); | ||
| 202 | return; | ||
| 203 | } | ||
| 204 | for (const auto physical_device : physical_devices) { | ||
| 205 | VkPhysicalDeviceProperties properties; | ||
| 206 | vkGetPhysicalDeviceProperties(physical_device, &properties); | ||
| 207 | vulkan_devices.push_back(QString::fromUtf8(properties.deviceName)); | ||
| 208 | } | ||
| 209 | #endif | ||
| 210 | } | ||
| 211 | |||
| 212 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | ||
| 213 | return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); | ||
| 214 | } | ||
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index fae28d98e..7e0596d9c 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h | |||
| @@ -5,7 +5,10 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <vector> | ||
| 9 | #include <QString> | ||
| 8 | #include <QWidget> | 10 | #include <QWidget> |
| 11 | #include "core/settings.h" | ||
| 9 | 12 | ||
| 10 | namespace Ui { | 13 | namespace Ui { |
| 11 | class ConfigureGraphics; | 14 | class ConfigureGraphics; |
| @@ -27,7 +30,16 @@ private: | |||
| 27 | void SetConfiguration(); | 30 | void SetConfiguration(); |
| 28 | 31 | ||
| 29 | void UpdateBackgroundColorButton(QColor color); | 32 | void UpdateBackgroundColorButton(QColor color); |
| 33 | void UpdateDeviceComboBox(); | ||
| 34 | void UpdateDeviceSelection(int device); | ||
| 35 | |||
| 36 | void RetrieveVulkanDevices(); | ||
| 37 | |||
| 38 | Settings::RendererBackend GetCurrentGraphicsBackend() const; | ||
| 30 | 39 | ||
| 31 | std::unique_ptr<Ui::ConfigureGraphics> ui; | 40 | std::unique_ptr<Ui::ConfigureGraphics> ui; |
| 32 | QColor bg_color; | 41 | QColor bg_color; |
| 42 | |||
| 43 | std::vector<QString> vulkan_devices; | ||
| 44 | u32 vulkan_device{}; | ||
| 33 | }; | 45 | }; |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 0309ee300..db60426ab 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -7,21 +7,69 @@ | |||
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>400</width> | 9 | <width>400</width> |
| 10 | <height>300</height> | 10 | <height>321</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| 14 | <string>Form</string> | 14 | <string>Form</string> |
| 15 | </property> | 15 | </property> |
| 16 | <layout class="QVBoxLayout" name="verticalLayout"> | 16 | <layout class="QVBoxLayout" name="verticalLayout_1"> |
| 17 | <item> | 17 | <item> |
| 18 | <layout class="QVBoxLayout" name="verticalLayout_3"> | 18 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
| 19 | <item> | ||
| 20 | <widget class="QGroupBox" name="groupBox_2"> | ||
| 21 | <property name="title"> | ||
| 22 | <string>API Settings</string> | ||
| 23 | </property> | ||
| 24 | <layout class="QVBoxLayout" name="verticalLayout_3"> | ||
| 25 | <item> | ||
| 26 | <layout class="QHBoxLayout" name="horizontalLayout_4"> | ||
| 27 | <item> | ||
| 28 | <widget class="QLabel" name="label_2"> | ||
| 29 | <property name="text"> | ||
| 30 | <string>API:</string> | ||
| 31 | </property> | ||
| 32 | </widget> | ||
| 33 | </item> | ||
| 34 | <item> | ||
| 35 | <widget class="QComboBox" name="api"> | ||
| 36 | <item> | ||
| 37 | <property name="text"> | ||
| 38 | <string notr="true">OpenGL</string> | ||
| 39 | </property> | ||
| 40 | </item> | ||
| 41 | <item> | ||
| 42 | <property name="text"> | ||
| 43 | <string notr="true">Vulkan</string> | ||
| 44 | </property> | ||
| 45 | </item> | ||
| 46 | </widget> | ||
| 47 | </item> | ||
| 48 | </layout> | ||
| 49 | </item> | ||
| 50 | <item> | ||
| 51 | <layout class="QHBoxLayout" name="horizontalLayout_5"> | ||
| 52 | <item> | ||
| 53 | <widget class="QLabel" name="label_3"> | ||
| 54 | <property name="text"> | ||
| 55 | <string>Device:</string> | ||
| 56 | </property> | ||
| 57 | </widget> | ||
| 58 | </item> | ||
| 59 | <item> | ||
| 60 | <widget class="QComboBox" name="device"/> | ||
| 61 | </item> | ||
| 62 | </layout> | ||
| 63 | </item> | ||
| 64 | </layout> | ||
| 65 | </widget> | ||
| 66 | </item> | ||
| 19 | <item> | 67 | <item> |
| 20 | <widget class="QGroupBox" name="groupBox"> | 68 | <widget class="QGroupBox" name="groupBox"> |
| 21 | <property name="title"> | 69 | <property name="title"> |
| 22 | <string>Graphics</string> | 70 | <string>Graphics Settings</string> |
| 23 | </property> | 71 | </property> |
| 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> | 72 | <layout class="QVBoxLayout" name="verticalLayout_4"> |
| 25 | <item> | 73 | <item> |
| 26 | <widget class="QCheckBox" name="use_disk_shader_cache"> | 74 | <widget class="QCheckBox" name="use_disk_shader_cache"> |
| 27 | <property name="text"> | 75 | <property name="text"> |
| @@ -30,16 +78,16 @@ | |||
| 30 | </widget> | 78 | </widget> |
| 31 | </item> | 79 | </item> |
| 32 | <item> | 80 | <item> |
| 33 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> | 81 | <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> |
| 34 | <property name="text"> | 82 | <property name="text"> |
| 35 | <string>Use accurate GPU emulation (slow)</string> | 83 | <string>Use asynchronous GPU emulation</string> |
| 36 | </property> | 84 | </property> |
| 37 | </widget> | 85 | </widget> |
| 38 | </item> | 86 | </item> |
| 39 | <item> | 87 | <item> |
| 40 | <widget class="QCheckBox" name="use_asynchronous_gpu_emulation"> | 88 | <widget class="QCheckBox" name="use_accurate_gpu_emulation"> |
| 41 | <property name="text"> | 89 | <property name="text"> |
| 42 | <string>Use asynchronous GPU emulation</string> | 90 | <string>Use accurate GPU emulation (slow)</string> |
| 43 | </property> | 91 | </property> |
| 44 | </widget> | 92 | </widget> |
| 45 | </item> | 93 | </item> |
| @@ -51,11 +99,11 @@ | |||
| 51 | </widget> | 99 | </widget> |
| 52 | </item> | 100 | </item> |
| 53 | <item> | 101 | <item> |
| 54 | <layout class="QHBoxLayout" name="horizontalLayout"> | 102 | <layout class="QHBoxLayout" name="horizontalLayout_2"> |
| 55 | <item> | 103 | <item> |
| 56 | <widget class="QLabel" name="label"> | 104 | <widget class="QLabel" name="label"> |
| 57 | <property name="text"> | 105 | <property name="text"> |
| 58 | <string>Internal Resolution</string> | 106 | <string>Internal Resolution:</string> |
| 59 | </property> | 107 | </property> |
| 60 | </widget> | 108 | </widget> |
| 61 | </item> | 109 | </item> |
| @@ -93,6 +141,41 @@ | |||
| 93 | <item> | 141 | <item> |
| 94 | <layout class="QHBoxLayout" name="horizontalLayout_6"> | 142 | <layout class="QHBoxLayout" name="horizontalLayout_6"> |
| 95 | <item> | 143 | <item> |
| 144 | <widget class="QLabel" name="ar_label"> | ||
| 145 | <property name="text"> | ||
| 146 | <string>Aspect Ratio:</string> | ||
| 147 | </property> | ||
| 148 | </widget> | ||
| 149 | </item> | ||
| 150 | <item> | ||
| 151 | <widget class="QComboBox" name="aspect_ratio_combobox"> | ||
| 152 | <item> | ||
| 153 | <property name="text"> | ||
| 154 | <string>Default (16:9)</string> | ||
| 155 | </property> | ||
| 156 | </item> | ||
| 157 | <item> | ||
| 158 | <property name="text"> | ||
| 159 | <string>Force 4:3</string> | ||
| 160 | </property> | ||
| 161 | </item> | ||
| 162 | <item> | ||
| 163 | <property name="text"> | ||
| 164 | <string>Force 21:9</string> | ||
| 165 | </property> | ||
| 166 | </item> | ||
| 167 | <item> | ||
| 168 | <property name="text"> | ||
| 169 | <string>Stretch to Window</string> | ||
| 170 | </property> | ||
| 171 | </item> | ||
| 172 | </widget> | ||
| 173 | </item> | ||
| 174 | </layout> | ||
| 175 | </item> | ||
| 176 | <item> | ||
| 177 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 178 | <item> | ||
| 96 | <widget class="QLabel" name="bg_label"> | 179 | <widget class="QLabel" name="bg_label"> |
| 97 | <property name="text"> | 180 | <property name="text"> |
| 98 | <string>Background Color:</string> | 181 | <string>Background Color:</string> |
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 67c9a7c6d..96dec50e2 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp | |||
| @@ -236,6 +236,8 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 236 | widget->setVisible(false); | 236 | widget->setVisible(false); |
| 237 | 237 | ||
| 238 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; | 238 | analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog}; |
| 239 | analog_map_deadzone = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone}; | ||
| 240 | analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone}; | ||
| 239 | 241 | ||
| 240 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { | 242 | for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) { |
| 241 | auto* const button = button_map[button_id]; | 243 | auto* const button = button_map[button_id]; |
| @@ -326,6 +328,11 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i | |||
| 326 | InputCommon::Polling::DeviceType::Analog); | 328 | InputCommon::Polling::DeviceType::Analog); |
| 327 | } | 329 | } |
| 328 | }); | 330 | }); |
| 331 | connect(analog_map_deadzone[analog_id], &QSlider::valueChanged, [=] { | ||
| 332 | const float deadzone = analog_map_deadzone[analog_id]->value() / 100.0f; | ||
| 333 | analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1").arg(deadzone)); | ||
| 334 | analogs_param[analog_id].Set("deadzone", deadzone); | ||
| 335 | }); | ||
| 329 | } | 336 | } |
| 330 | 337 | ||
| 331 | connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); | 338 | connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); |
| @@ -484,7 +491,7 @@ void ConfigureInputPlayer::ClearAll() { | |||
| 484 | continue; | 491 | continue; |
| 485 | } | 492 | } |
| 486 | 493 | ||
| 487 | analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); | 494 | analogs_param[analog_id].Clear(); |
| 488 | } | 495 | } |
| 489 | } | 496 | } |
| 490 | 497 | ||
| @@ -508,6 +515,23 @@ void ConfigureInputPlayer::UpdateButtonLabels() { | |||
| 508 | AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); | 515 | AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id])); |
| 509 | } | 516 | } |
| 510 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); | 517 | analog_map_stick[analog_id]->setText(tr("Set Analog Stick")); |
| 518 | |||
| 519 | auto& param = analogs_param[analog_id]; | ||
| 520 | auto* const analog_deadzone_slider = analog_map_deadzone[analog_id]; | ||
| 521 | auto* const analog_deadzone_label = analog_map_deadzone_label[analog_id]; | ||
| 522 | |||
| 523 | if (param.Has("engine") && param.Get("engine", "") == "sdl") { | ||
| 524 | if (!param.Has("deadzone")) { | ||
| 525 | param.Set("deadzone", 0.1f); | ||
| 526 | } | ||
| 527 | |||
| 528 | analog_deadzone_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100)); | ||
| 529 | analog_deadzone_slider->setVisible(true); | ||
| 530 | analog_deadzone_label->setVisible(true); | ||
| 531 | } else { | ||
| 532 | analog_deadzone_slider->setVisible(false); | ||
| 533 | analog_deadzone_label->setVisible(false); | ||
| 534 | } | ||
| 511 | } | 535 | } |
| 512 | } | 536 | } |
| 513 | 537 | ||
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index c66027651..045704e47 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h | |||
| @@ -97,6 +97,8 @@ private: | |||
| 97 | /// Analog inputs are also represented each with a single button, used to configure with an | 97 | /// Analog inputs are also represented each with a single button, used to configure with an |
| 98 | /// actual analog stick | 98 | /// actual analog stick |
| 99 | std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; | 99 | std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick; |
| 100 | std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone; | ||
| 101 | std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label; | ||
| 100 | 102 | ||
| 101 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; | 103 | static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons; |
| 102 | 104 | ||
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 42db020be..1556481d0 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui | |||
| @@ -170,6 +170,44 @@ | |||
| 170 | </item> | 170 | </item> |
| 171 | </layout> | 171 | </layout> |
| 172 | </item> | 172 | </item> |
| 173 | <item row="4" column="0" colspan="2"> | ||
| 174 | <layout class="QVBoxLayout" name="sliderRStickDeadzoneVerticalLayout"> | ||
| 175 | <item> | ||
| 176 | <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout"> | ||
| 177 | <item> | ||
| 178 | <widget class="QLabel" name="labelRStickDeadzone"> | ||
| 179 | <property name="text"> | ||
| 180 | <string>Deadzone: 0</string> | ||
| 181 | </property> | ||
| 182 | <property name="alignment"> | ||
| 183 | <enum>Qt::AlignHCenter</enum> | ||
| 184 | </property> | ||
| 185 | </widget> | ||
| 186 | </item> | ||
| 187 | </layout> | ||
| 188 | </item> | ||
| 189 | <item> | ||
| 190 | <widget class="QSlider" name="sliderRStickDeadzone"> | ||
| 191 | <property name="orientation"> | ||
| 192 | <enum>Qt::Horizontal</enum> | ||
| 193 | </property> | ||
| 194 | </widget> | ||
| 195 | </item> | ||
| 196 | </layout> | ||
| 197 | </item> | ||
| 198 | <item row="5" column="0"> | ||
| 199 | <spacer name="RStick_verticalSpacer"> | ||
| 200 | <property name="orientation"> | ||
| 201 | <enum>Qt::Vertical</enum> | ||
| 202 | </property> | ||
| 203 | <property name="sizeHint" stdset="0"> | ||
| 204 | <size> | ||
| 205 | <width>0</width> | ||
| 206 | <height>0</height> | ||
| 207 | </size> | ||
| 208 | </property> | ||
| 209 | </spacer> | ||
| 210 | </item> | ||
| 173 | </layout> | 211 | </layout> |
| 174 | </widget> | 212 | </widget> |
| 175 | </item> | 213 | </item> |
| @@ -745,6 +783,47 @@ | |||
| 745 | </item> | 783 | </item> |
| 746 | </layout> | 784 | </layout> |
| 747 | </item> | 785 | </item> |
| 786 | <item row="5" column="1" colspan="2"> | ||
| 787 | <layout class="QVBoxLayout" name="sliderLStickDeadzoneVerticalLayout"> | ||
| 788 | <property name="sizeConstraint"> | ||
| 789 | <enum>QLayout::SetDefaultConstraint</enum> | ||
| 790 | </property> | ||
| 791 | <item> | ||
| 792 | <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout"> | ||
| 793 | <item> | ||
| 794 | <widget class="QLabel" name="labelLStickDeadzone"> | ||
| 795 | <property name="text"> | ||
| 796 | <string>Deadzone: 0</string> | ||
| 797 | </property> | ||
| 798 | <property name="alignment"> | ||
| 799 | <enum>Qt::AlignHCenter</enum> | ||
| 800 | </property> | ||
| 801 | </widget> | ||
| 802 | </item> | ||
| 803 | </layout> | ||
| 804 | </item> | ||
| 805 | <item> | ||
| 806 | <widget class="QSlider" name="sliderLStickDeadzone"> | ||
| 807 | <property name="orientation"> | ||
| 808 | <enum>Qt::Horizontal</enum> | ||
| 809 | </property> | ||
| 810 | </widget> | ||
| 811 | </item> | ||
| 812 | </layout> | ||
| 813 | </item> | ||
| 814 | <item row="6" column="1"> | ||
| 815 | <spacer name="LStick_verticalSpacer"> | ||
| 816 | <property name="orientation"> | ||
| 817 | <enum>Qt::Vertical</enum> | ||
| 818 | </property> | ||
| 819 | <property name="sizeHint" stdset="0"> | ||
| 820 | <size> | ||
| 821 | <width>0</width> | ||
| 822 | <height>0</height> | ||
| 823 | </size> | ||
| 824 | </property> | ||
| 825 | </spacer> | ||
| 826 | </item> | ||
| 748 | </layout> | 827 | </layout> |
| 749 | </widget> | 828 | </widget> |
| 750 | </item> | 829 | </item> |
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_ui.cpp index e43e84d39..94424ee44 100644 --- a/src/yuzu/configuration/configure_gamelist.cpp +++ b/src/yuzu/configuration/configure_ui.cpp | |||
| @@ -7,8 +7,8 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/settings.h" | 9 | #include "core/settings.h" |
| 10 | #include "ui_configure_gamelist.h" | 10 | #include "ui_configure_ui.h" |
| 11 | #include "yuzu/configuration/configure_gamelist.h" | 11 | #include "yuzu/configuration/configure_ui.h" |
| 12 | #include "yuzu/uisettings.h" | 12 | #include "yuzu/uisettings.h" |
| 13 | 13 | ||
| 14 | namespace { | 14 | namespace { |
| @@ -26,36 +26,39 @@ constexpr std::array row_text_names{ | |||
| 26 | }; | 26 | }; |
| 27 | } // Anonymous namespace | 27 | } // Anonymous namespace |
| 28 | 28 | ||
| 29 | ConfigureGameList::ConfigureGameList(QWidget* parent) | 29 | ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) { |
| 30 | : QWidget(parent), ui(new Ui::ConfigureGameList) { | ||
| 31 | ui->setupUi(this); | 30 | ui->setupUi(this); |
| 32 | 31 | ||
| 32 | for (const auto& theme : UISettings::themes) { | ||
| 33 | ui->theme_combobox->addItem(QString::fromUtf8(theme.first), | ||
| 34 | QString::fromUtf8(theme.second)); | ||
| 35 | } | ||
| 36 | |||
| 33 | InitializeIconSizeComboBox(); | 37 | InitializeIconSizeComboBox(); |
| 34 | InitializeRowComboBoxes(); | 38 | InitializeRowComboBoxes(); |
| 35 | 39 | ||
| 36 | SetConfiguration(); | 40 | SetConfiguration(); |
| 37 | 41 | ||
| 38 | // Force game list reload if any of the relevant settings are changed. | 42 | // Force game list reload if any of the relevant settings are changed. |
| 39 | connect(ui->show_unknown, &QCheckBox::stateChanged, this, | ||
| 40 | &ConfigureGameList::RequestGameListUpdate); | ||
| 41 | connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | 43 | connect(ui->icon_size_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, |
| 42 | &ConfigureGameList::RequestGameListUpdate); | 44 | &ConfigureUi::RequestGameListUpdate); |
| 43 | connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | 45 | connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, |
| 44 | &ConfigureGameList::RequestGameListUpdate); | 46 | &ConfigureUi::RequestGameListUpdate); |
| 45 | connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | 47 | connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, |
| 46 | &ConfigureGameList::RequestGameListUpdate); | 48 | &ConfigureUi::RequestGameListUpdate); |
| 47 | 49 | ||
| 48 | // Update text ComboBoxes after user interaction. | 50 | // Update text ComboBoxes after user interaction. |
| 49 | connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::activated), | 51 | connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::activated), |
| 50 | [=]() { ConfigureGameList::UpdateSecondRowComboBox(); }); | 52 | [=]() { ConfigureUi::UpdateSecondRowComboBox(); }); |
| 51 | connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::activated), | 53 | connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::activated), |
| 52 | [=]() { ConfigureGameList::UpdateFirstRowComboBox(); }); | 54 | [=]() { ConfigureUi::UpdateFirstRowComboBox(); }); |
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | ConfigureGameList::~ConfigureGameList() = default; | 57 | ConfigureUi::~ConfigureUi() = default; |
| 56 | 58 | ||
| 57 | void ConfigureGameList::ApplyConfiguration() { | 59 | void ConfigureUi::ApplyConfiguration() { |
| 58 | UISettings::values.show_unknown = ui->show_unknown->isChecked(); | 60 | UISettings::values.theme = |
| 61 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); | ||
| 59 | UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); | 62 | UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); |
| 60 | UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); | 63 | UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); |
| 61 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); | 64 | UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); |
| @@ -63,18 +66,18 @@ void ConfigureGameList::ApplyConfiguration() { | |||
| 63 | Settings::Apply(); | 66 | Settings::Apply(); |
| 64 | } | 67 | } |
| 65 | 68 | ||
| 66 | void ConfigureGameList::RequestGameListUpdate() { | 69 | void ConfigureUi::RequestGameListUpdate() { |
| 67 | UISettings::values.is_game_list_reload_pending.exchange(true); | 70 | UISettings::values.is_game_list_reload_pending.exchange(true); |
| 68 | } | 71 | } |
| 69 | 72 | ||
| 70 | void ConfigureGameList::SetConfiguration() { | 73 | void ConfigureUi::SetConfiguration() { |
| 71 | ui->show_unknown->setChecked(UISettings::values.show_unknown); | 74 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| 72 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); | 75 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); |
| 73 | ui->icon_size_combobox->setCurrentIndex( | 76 | ui->icon_size_combobox->setCurrentIndex( |
| 74 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); | 77 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); |
| 75 | } | 78 | } |
| 76 | 79 | ||
| 77 | void ConfigureGameList::changeEvent(QEvent* event) { | 80 | void ConfigureUi::changeEvent(QEvent* event) { |
| 78 | if (event->type() == QEvent::LanguageChange) { | 81 | if (event->type() == QEvent::LanguageChange) { |
| 79 | RetranslateUI(); | 82 | RetranslateUI(); |
| 80 | } | 83 | } |
| @@ -82,7 +85,7 @@ void ConfigureGameList::changeEvent(QEvent* event) { | |||
| 82 | QWidget::changeEvent(event); | 85 | QWidget::changeEvent(event); |
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | void ConfigureGameList::RetranslateUI() { | 88 | void ConfigureUi::RetranslateUI() { |
| 86 | ui->retranslateUi(this); | 89 | ui->retranslateUi(this); |
| 87 | 90 | ||
| 88 | for (int i = 0; i < ui->icon_size_combobox->count(); i++) { | 91 | for (int i = 0; i < ui->icon_size_combobox->count(); i++) { |
| @@ -97,18 +100,18 @@ void ConfigureGameList::RetranslateUI() { | |||
| 97 | } | 100 | } |
| 98 | } | 101 | } |
| 99 | 102 | ||
| 100 | void ConfigureGameList::InitializeIconSizeComboBox() { | 103 | void ConfigureUi::InitializeIconSizeComboBox() { |
| 101 | for (const auto& size : default_icon_sizes) { | 104 | for (const auto& size : default_icon_sizes) { |
| 102 | ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first); | 105 | ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first); |
| 103 | } | 106 | } |
| 104 | } | 107 | } |
| 105 | 108 | ||
| 106 | void ConfigureGameList::InitializeRowComboBoxes() { | 109 | void ConfigureUi::InitializeRowComboBoxes() { |
| 107 | UpdateFirstRowComboBox(true); | 110 | UpdateFirstRowComboBox(true); |
| 108 | UpdateSecondRowComboBox(true); | 111 | UpdateSecondRowComboBox(true); |
| 109 | } | 112 | } |
| 110 | 113 | ||
| 111 | void ConfigureGameList::UpdateFirstRowComboBox(bool init) { | 114 | void ConfigureUi::UpdateFirstRowComboBox(bool init) { |
| 112 | const int currentIndex = | 115 | const int currentIndex = |
| 113 | init ? UISettings::values.row_1_text_id | 116 | init ? UISettings::values.row_1_text_id |
| 114 | : ui->row_1_text_combobox->findData(ui->row_1_text_combobox->currentData()); | 117 | : ui->row_1_text_combobox->findData(ui->row_1_text_combobox->currentData()); |
| @@ -127,7 +130,7 @@ void ConfigureGameList::UpdateFirstRowComboBox(bool init) { | |||
| 127 | ui->row_1_text_combobox->findData(ui->row_2_text_combobox->currentData())); | 130 | ui->row_1_text_combobox->findData(ui->row_2_text_combobox->currentData())); |
| 128 | } | 131 | } |
| 129 | 132 | ||
| 130 | void ConfigureGameList::UpdateSecondRowComboBox(bool init) { | 133 | void ConfigureUi::UpdateSecondRowComboBox(bool init) { |
| 131 | const int currentIndex = | 134 | const int currentIndex = |
| 132 | init ? UISettings::values.row_2_text_id | 135 | init ? UISettings::values.row_2_text_id |
| 133 | : ui->row_2_text_combobox->findData(ui->row_2_text_combobox->currentData()); | 136 | : ui->row_2_text_combobox->findData(ui->row_2_text_combobox->currentData()); |
diff --git a/src/yuzu/configuration/configure_gamelist.h b/src/yuzu/configuration/configure_ui.h index ecd3fa174..d471afe99 100644 --- a/src/yuzu/configuration/configure_gamelist.h +++ b/src/yuzu/configuration/configure_ui.h | |||
| @@ -8,15 +8,15 @@ | |||
| 8 | #include <QWidget> | 8 | #include <QWidget> |
| 9 | 9 | ||
| 10 | namespace Ui { | 10 | namespace Ui { |
| 11 | class ConfigureGameList; | 11 | class ConfigureUi; |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | class ConfigureGameList : public QWidget { | 14 | class ConfigureUi : public QWidget { |
| 15 | Q_OBJECT | 15 | Q_OBJECT |
| 16 | 16 | ||
| 17 | public: | 17 | public: |
| 18 | explicit ConfigureGameList(QWidget* parent = nullptr); | 18 | explicit ConfigureUi(QWidget* parent = nullptr); |
| 19 | ~ConfigureGameList() override; | 19 | ~ConfigureUi() override; |
| 20 | 20 | ||
| 21 | void ApplyConfiguration(); | 21 | void ApplyConfiguration(); |
| 22 | 22 | ||
| @@ -34,5 +34,5 @@ private: | |||
| 34 | void UpdateFirstRowComboBox(bool init = false); | 34 | void UpdateFirstRowComboBox(bool init = false); |
| 35 | void UpdateSecondRowComboBox(bool init = false); | 35 | void UpdateSecondRowComboBox(bool init = false); |
| 36 | 36 | ||
| 37 | std::unique_ptr<Ui::ConfigureGameList> ui; | 37 | std::unique_ptr<Ui::ConfigureUi> ui; |
| 38 | }; | 38 | }; |
diff --git a/src/yuzu/configuration/configure_gamelist.ui b/src/yuzu/configuration/configure_ui.ui index 7a69377e7..bd5c5d3c2 100644 --- a/src/yuzu/configuration/configure_gamelist.ui +++ b/src/yuzu/configuration/configure_ui.ui | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <ui version="4.0"> | 2 | <ui version="4.0"> |
| 3 | <class>ConfigureGameList</class> | 3 | <class>ConfigureUi</class> |
| 4 | <widget class="QWidget" name="ConfigureGameList"> | 4 | <widget class="QWidget" name="ConfigureUi"> |
| 5 | <property name="geometry"> | 5 | <property name="geometry"> |
| 6 | <rect> | 6 | <rect> |
| 7 | <x>0</x> | 7 | <x>0</x> |
| @@ -21,22 +21,22 @@ | |||
| 21 | <property name="title"> | 21 | <property name="title"> |
| 22 | <string>General</string> | 22 | <string>General</string> |
| 23 | </property> | 23 | </property> |
| 24 | <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> | 24 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 25 | <item> | 25 | <item> |
| 26 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> | 26 | <layout class="QVBoxLayout" name="verticalLayout"> |
| 27 | <item> | 27 | <item> |
| 28 | <widget class="QCheckBox" name="show_unknown"> | 28 | <layout class="QHBoxLayout" name="horizontalLayout_3"> |
| 29 | <property name="text"> | 29 | <item> |
| 30 | <string>Show files with type 'Unknown'</string> | 30 | <widget class="QLabel" name="theme_label"> |
| 31 | </property> | 31 | <property name="text"> |
| 32 | </widget> | 32 | <string>Theme:</string> |
| 33 | </item> | 33 | </property> |
| 34 | <item> | 34 | </widget> |
| 35 | <widget class="QCheckBox" name="show_add_ons"> | 35 | </item> |
| 36 | <property name="text"> | 36 | <item> |
| 37 | <string>Show Add-Ons Column</string> | 37 | <widget class="QComboBox" name="theme_combobox"/> |
| 38 | </property> | 38 | </item> |
| 39 | </widget> | 39 | </layout> |
| 40 | </item> | 40 | </item> |
| 41 | </layout> | 41 | </layout> |
| 42 | </item> | 42 | </item> |
| @@ -44,13 +44,20 @@ | |||
| 44 | </widget> | 44 | </widget> |
| 45 | </item> | 45 | </item> |
| 46 | <item> | 46 | <item> |
| 47 | <widget class="QGroupBox" name="IconSizeGroupBox"> | 47 | <widget class="QGroupBox" name="GameListGroupBox"> |
| 48 | <property name="title"> | 48 | <property name="title"> |
| 49 | <string>Icon Size</string> | 49 | <string>Game List</string> |
| 50 | </property> | 50 | </property> |
| 51 | <layout class="QHBoxLayout" name="icon_size_qhbox_layout"> | 51 | <layout class="QHBoxLayout" name="GameListHorizontalLayout"> |
| 52 | <item> | 52 | <item> |
| 53 | <layout class="QVBoxLayout" name="icon_size_qvbox_layout"> | 53 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> |
| 54 | <item> | ||
| 55 | <widget class="QCheckBox" name="show_add_ons"> | ||
| 56 | <property name="text"> | ||
| 57 | <string>Show Add-Ons Column</string> | ||
| 58 | </property> | ||
| 59 | </widget> | ||
| 60 | </item> | ||
| 54 | <item> | 61 | <item> |
| 55 | <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> | 62 | <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> |
| 56 | <item> | 63 | <item> |
| @@ -65,19 +72,6 @@ | |||
| 65 | </item> | 72 | </item> |
| 66 | </layout> | 73 | </layout> |
| 67 | </item> | 74 | </item> |
| 68 | </layout> | ||
| 69 | </item> | ||
| 70 | </layout> | ||
| 71 | </widget> | ||
| 72 | </item> | ||
| 73 | <item> | ||
| 74 | <widget class="QGroupBox" name="RowGroupBox"> | ||
| 75 | <property name="title"> | ||
| 76 | <string>Row Text</string> | ||
| 77 | </property> | ||
| 78 | <layout class="QHBoxLayout" name="RowHorizontalLayout"> | ||
| 79 | <item> | ||
| 80 | <layout class="QVBoxLayout" name="RowVerticalLayout"> | ||
| 81 | <item> | 75 | <item> |
| 82 | <layout class="QHBoxLayout" name="row_1_qhbox_layout"> | 76 | <layout class="QHBoxLayout" name="row_1_qhbox_layout"> |
| 83 | <item> | 77 | <item> |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 727bd8a94..3f1a94627 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -12,8 +12,8 @@ | |||
| 12 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/kernel/readable_event.h" | 13 | #include "core/hle/kernel/readable_event.h" |
| 14 | #include "core/hle/kernel/scheduler.h" | 14 | #include "core/hle/kernel/scheduler.h" |
| 15 | #include "core/hle/kernel/synchronization_object.h" | ||
| 15 | #include "core/hle/kernel/thread.h" | 16 | #include "core/hle/kernel/thread.h" |
| 16 | #include "core/hle/kernel/wait_object.h" | ||
| 17 | #include "core/memory.h" | 17 | #include "core/memory.h" |
| 18 | 18 | ||
| 19 | WaitTreeItem::WaitTreeItem() = default; | 19 | WaitTreeItem::WaitTreeItem() = default; |
| @@ -133,8 +133,9 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons | |||
| 133 | return list; | 133 | return list; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | WaitTreeWaitObject::WaitTreeWaitObject(const Kernel::WaitObject& o) : object(o) {} | 136 | WaitTreeSynchronizationObject::WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& o) |
| 137 | WaitTreeWaitObject::~WaitTreeWaitObject() = default; | 137 | : object(o) {} |
| 138 | WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default; | ||
| 138 | 139 | ||
| 139 | WaitTreeExpandableItem::WaitTreeExpandableItem() = default; | 140 | WaitTreeExpandableItem::WaitTreeExpandableItem() = default; |
| 140 | WaitTreeExpandableItem::~WaitTreeExpandableItem() = default; | 141 | WaitTreeExpandableItem::~WaitTreeExpandableItem() = default; |
| @@ -143,25 +144,26 @@ bool WaitTreeExpandableItem::IsExpandable() const { | |||
| 143 | return true; | 144 | return true; |
| 144 | } | 145 | } |
| 145 | 146 | ||
| 146 | QString WaitTreeWaitObject::GetText() const { | 147 | QString WaitTreeSynchronizationObject::GetText() const { |
| 147 | return tr("[%1]%2 %3") | 148 | return tr("[%1]%2 %3") |
| 148 | .arg(object.GetObjectId()) | 149 | .arg(object.GetObjectId()) |
| 149 | .arg(QString::fromStdString(object.GetTypeName()), | 150 | .arg(QString::fromStdString(object.GetTypeName()), |
| 150 | QString::fromStdString(object.GetName())); | 151 | QString::fromStdString(object.GetName())); |
| 151 | } | 152 | } |
| 152 | 153 | ||
| 153 | std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitObject& object) { | 154 | std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::make( |
| 155 | const Kernel::SynchronizationObject& object) { | ||
| 154 | switch (object.GetHandleType()) { | 156 | switch (object.GetHandleType()) { |
| 155 | case Kernel::HandleType::ReadableEvent: | 157 | case Kernel::HandleType::ReadableEvent: |
| 156 | return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); | 158 | return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); |
| 157 | case Kernel::HandleType::Thread: | 159 | case Kernel::HandleType::Thread: |
| 158 | return std::make_unique<WaitTreeThread>(static_cast<const Kernel::Thread&>(object)); | 160 | return std::make_unique<WaitTreeThread>(static_cast<const Kernel::Thread&>(object)); |
| 159 | default: | 161 | default: |
| 160 | return std::make_unique<WaitTreeWaitObject>(object); | 162 | return std::make_unique<WaitTreeSynchronizationObject>(object); |
| 161 | } | 163 | } |
| 162 | } | 164 | } |
| 163 | 165 | ||
| 164 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeWaitObject::GetChildren() const { | 166 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeSynchronizationObject::GetChildren() const { |
| 165 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 167 | std::vector<std::unique_ptr<WaitTreeItem>> list; |
| 166 | 168 | ||
| 167 | const auto& threads = object.GetWaitingThreads(); | 169 | const auto& threads = object.GetWaitingThreads(); |
| @@ -173,8 +175,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeWaitObject::GetChildren() con | |||
| 173 | return list; | 175 | return list; |
| 174 | } | 176 | } |
| 175 | 177 | ||
| 176 | WaitTreeObjectList::WaitTreeObjectList(const std::vector<std::shared_ptr<Kernel::WaitObject>>& list, | 178 | WaitTreeObjectList::WaitTreeObjectList( |
| 177 | bool w_all) | 179 | const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& list, bool w_all) |
| 178 | : object_list(list), wait_all(w_all) {} | 180 | : object_list(list), wait_all(w_all) {} |
| 179 | 181 | ||
| 180 | WaitTreeObjectList::~WaitTreeObjectList() = default; | 182 | WaitTreeObjectList::~WaitTreeObjectList() = default; |
| @@ -188,11 +190,12 @@ QString WaitTreeObjectList::GetText() const { | |||
| 188 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeObjectList::GetChildren() const { | 190 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeObjectList::GetChildren() const { |
| 189 | std::vector<std::unique_ptr<WaitTreeItem>> list(object_list.size()); | 191 | std::vector<std::unique_ptr<WaitTreeItem>> list(object_list.size()); |
| 190 | std::transform(object_list.begin(), object_list.end(), list.begin(), | 192 | std::transform(object_list.begin(), object_list.end(), list.begin(), |
| 191 | [](const auto& t) { return WaitTreeWaitObject::make(*t); }); | 193 | [](const auto& t) { return WaitTreeSynchronizationObject::make(*t); }); |
| 192 | return list; | 194 | return list; |
| 193 | } | 195 | } |
| 194 | 196 | ||
| 195 | WaitTreeThread::WaitTreeThread(const Kernel::Thread& thread) : WaitTreeWaitObject(thread) {} | 197 | WaitTreeThread::WaitTreeThread(const Kernel::Thread& thread) |
| 198 | : WaitTreeSynchronizationObject(thread) {} | ||
| 196 | WaitTreeThread::~WaitTreeThread() = default; | 199 | WaitTreeThread::~WaitTreeThread() = default; |
| 197 | 200 | ||
| 198 | QString WaitTreeThread::GetText() const { | 201 | QString WaitTreeThread::GetText() const { |
| @@ -241,7 +244,8 @@ QString WaitTreeThread::GetText() const { | |||
| 241 | const QString pc_info = tr(" PC = 0x%1 LR = 0x%2") | 244 | const QString pc_info = tr(" PC = 0x%1 LR = 0x%2") |
| 242 | .arg(context.pc, 8, 16, QLatin1Char{'0'}) | 245 | .arg(context.pc, 8, 16, QLatin1Char{'0'}) |
| 243 | .arg(context.cpu_registers[30], 8, 16, QLatin1Char{'0'}); | 246 | .arg(context.cpu_registers[30], 8, 16, QLatin1Char{'0'}); |
| 244 | return QStringLiteral("%1%2 (%3) ").arg(WaitTreeWaitObject::GetText(), pc_info, status); | 247 | return QStringLiteral("%1%2 (%3) ") |
| 248 | .arg(WaitTreeSynchronizationObject::GetText(), pc_info, status); | ||
| 245 | } | 249 | } |
| 246 | 250 | ||
| 247 | QColor WaitTreeThread::GetColor() const { | 251 | QColor WaitTreeThread::GetColor() const { |
| @@ -273,7 +277,7 @@ QColor WaitTreeThread::GetColor() const { | |||
| 273 | } | 277 | } |
| 274 | 278 | ||
| 275 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | 279 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { |
| 276 | std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren()); | 280 | std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeSynchronizationObject::GetChildren()); |
| 277 | 281 | ||
| 278 | const auto& thread = static_cast<const Kernel::Thread&>(object); | 282 | const auto& thread = static_cast<const Kernel::Thread&>(object); |
| 279 | 283 | ||
| @@ -314,7 +318,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 314 | } | 318 | } |
| 315 | 319 | ||
| 316 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { | 320 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { |
| 317 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(), | 321 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetSynchronizationObjects(), |
| 318 | thread.IsSleepingOnWait())); | 322 | thread.IsSleepingOnWait())); |
| 319 | } | 323 | } |
| 320 | 324 | ||
| @@ -323,7 +327,8 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 323 | return list; | 327 | return list; |
| 324 | } | 328 | } |
| 325 | 329 | ||
| 326 | WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) : WaitTreeWaitObject(object) {} | 330 | WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) |
| 331 | : WaitTreeSynchronizationObject(object) {} | ||
| 327 | WaitTreeEvent::~WaitTreeEvent() = default; | 332 | WaitTreeEvent::~WaitTreeEvent() = default; |
| 328 | 333 | ||
| 329 | WaitTreeThreadList::WaitTreeThreadList(const std::vector<std::shared_ptr<Kernel::Thread>>& list) | 334 | WaitTreeThreadList::WaitTreeThreadList(const std::vector<std::shared_ptr<Kernel::Thread>>& list) |
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index 631274a5f..8e3bc4b24 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h | |||
| @@ -19,7 +19,7 @@ class EmuThread; | |||
| 19 | namespace Kernel { | 19 | namespace Kernel { |
| 20 | class HandleTable; | 20 | class HandleTable; |
| 21 | class ReadableEvent; | 21 | class ReadableEvent; |
| 22 | class WaitObject; | 22 | class SynchronizationObject; |
| 23 | class Thread; | 23 | class Thread; |
| 24 | } // namespace Kernel | 24 | } // namespace Kernel |
| 25 | 25 | ||
| @@ -99,35 +99,37 @@ private: | |||
| 99 | const Kernel::Thread& thread; | 99 | const Kernel::Thread& thread; |
| 100 | }; | 100 | }; |
| 101 | 101 | ||
| 102 | class WaitTreeWaitObject : public WaitTreeExpandableItem { | 102 | class WaitTreeSynchronizationObject : public WaitTreeExpandableItem { |
| 103 | Q_OBJECT | 103 | Q_OBJECT |
| 104 | public: | 104 | public: |
| 105 | explicit WaitTreeWaitObject(const Kernel::WaitObject& object); | 105 | explicit WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& object); |
| 106 | ~WaitTreeWaitObject() override; | 106 | ~WaitTreeSynchronizationObject() override; |
| 107 | 107 | ||
| 108 | static std::unique_ptr<WaitTreeWaitObject> make(const Kernel::WaitObject& object); | 108 | static std::unique_ptr<WaitTreeSynchronizationObject> make( |
| 109 | const Kernel::SynchronizationObject& object); | ||
| 109 | QString GetText() const override; | 110 | QString GetText() const override; |
| 110 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; | 111 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; |
| 111 | 112 | ||
| 112 | protected: | 113 | protected: |
| 113 | const Kernel::WaitObject& object; | 114 | const Kernel::SynchronizationObject& object; |
| 114 | }; | 115 | }; |
| 115 | 116 | ||
| 116 | class WaitTreeObjectList : public WaitTreeExpandableItem { | 117 | class WaitTreeObjectList : public WaitTreeExpandableItem { |
| 117 | Q_OBJECT | 118 | Q_OBJECT |
| 118 | public: | 119 | public: |
| 119 | WaitTreeObjectList(const std::vector<std::shared_ptr<Kernel::WaitObject>>& list, bool wait_all); | 120 | WaitTreeObjectList(const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& list, |
| 121 | bool wait_all); | ||
| 120 | ~WaitTreeObjectList() override; | 122 | ~WaitTreeObjectList() override; |
| 121 | 123 | ||
| 122 | QString GetText() const override; | 124 | QString GetText() const override; |
| 123 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; | 125 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; |
| 124 | 126 | ||
| 125 | private: | 127 | private: |
| 126 | const std::vector<std::shared_ptr<Kernel::WaitObject>>& object_list; | 128 | const std::vector<std::shared_ptr<Kernel::SynchronizationObject>>& object_list; |
| 127 | bool wait_all; | 129 | bool wait_all; |
| 128 | }; | 130 | }; |
| 129 | 131 | ||
| 130 | class WaitTreeThread : public WaitTreeWaitObject { | 132 | class WaitTreeThread : public WaitTreeSynchronizationObject { |
| 131 | Q_OBJECT | 133 | Q_OBJECT |
| 132 | public: | 134 | public: |
| 133 | explicit WaitTreeThread(const Kernel::Thread& thread); | 135 | explicit WaitTreeThread(const Kernel::Thread& thread); |
| @@ -138,7 +140,7 @@ public: | |||
| 138 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; | 140 | std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; |
| 139 | }; | 141 | }; |
| 140 | 142 | ||
| 141 | class WaitTreeEvent : public WaitTreeWaitObject { | 143 | class WaitTreeEvent : public WaitTreeSynchronizationObject { |
| 142 | Q_OBJECT | 144 | Q_OBJECT |
| 143 | public: | 145 | public: |
| 144 | explicit WaitTreeEvent(const Kernel::ReadableEvent& object); | 146 | explicit WaitTreeEvent(const Kernel::ReadableEvent& object); |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 4c81ef12b..da2c27aa2 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -298,8 +298,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa | |||
| 298 | } | 298 | } |
| 299 | 299 | ||
| 300 | const auto file_type = loader->GetFileType(); | 300 | const auto file_type = loader->GetFileType(); |
| 301 | if ((file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) && | 301 | if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { |
| 302 | !UISettings::values.show_unknown) { | ||
| 303 | return true; | 302 | return true; |
| 304 | } | 303 | } |
| 305 | 304 | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b5dd3e0d6..54ca2dc1d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -454,7 +454,6 @@ void GMainWindow::InitializeWidgets() { | |||
| 454 | // Create status bar | 454 | // Create status bar |
| 455 | message_label = new QLabel(); | 455 | message_label = new QLabel(); |
| 456 | // Configured separately for left alignment | 456 | // Configured separately for left alignment |
| 457 | message_label->setVisible(false); | ||
| 458 | message_label->setFrameStyle(QFrame::NoFrame); | 457 | message_label->setFrameStyle(QFrame::NoFrame); |
| 459 | message_label->setContentsMargins(4, 0, 4, 0); | 458 | message_label->setContentsMargins(4, 0, 4, 0); |
| 460 | message_label->setAlignment(Qt::AlignLeft); | 459 | message_label->setAlignment(Qt::AlignLeft); |
| @@ -476,8 +475,73 @@ void GMainWindow::InitializeWidgets() { | |||
| 476 | label->setVisible(false); | 475 | label->setVisible(false); |
| 477 | label->setFrameStyle(QFrame::NoFrame); | 476 | label->setFrameStyle(QFrame::NoFrame); |
| 478 | label->setContentsMargins(4, 0, 4, 0); | 477 | label->setContentsMargins(4, 0, 4, 0); |
| 479 | statusBar()->addPermanentWidget(label, 0); | 478 | statusBar()->addPermanentWidget(label); |
| 480 | } | 479 | } |
| 480 | |||
| 481 | // Setup Dock button | ||
| 482 | dock_status_button = new QPushButton(); | ||
| 483 | dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | ||
| 484 | dock_status_button->setFocusPolicy(Qt::NoFocus); | ||
| 485 | connect(dock_status_button, &QPushButton::clicked, [&] { | ||
| 486 | Settings::values.use_docked_mode = !Settings::values.use_docked_mode; | ||
| 487 | dock_status_button->setChecked(Settings::values.use_docked_mode); | ||
| 488 | OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode); | ||
| 489 | }); | ||
| 490 | dock_status_button->setText(tr("DOCK")); | ||
| 491 | dock_status_button->setCheckable(true); | ||
| 492 | dock_status_button->setChecked(Settings::values.use_docked_mode); | ||
| 493 | statusBar()->insertPermanentWidget(0, dock_status_button); | ||
| 494 | |||
| 495 | // Setup ASync button | ||
| 496 | async_status_button = new QPushButton(); | ||
| 497 | async_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); | ||
| 498 | async_status_button->setFocusPolicy(Qt::NoFocus); | ||
| 499 | connect(async_status_button, &QPushButton::clicked, [&] { | ||
| 500 | if (emulation_running) { | ||
| 501 | return; | ||
| 502 | } | ||
| 503 | Settings::values.use_asynchronous_gpu_emulation = | ||
| 504 | !Settings::values.use_asynchronous_gpu_emulation; | ||
| 505 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | ||
| 506 | Settings::Apply(); | ||
| 507 | }); | ||
| 508 | async_status_button->setText(tr("ASYNC")); | ||
| 509 | async_status_button->setCheckable(true); | ||
| 510 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | ||
| 511 | statusBar()->insertPermanentWidget(0, async_status_button); | ||
| 512 | |||
| 513 | // Setup Renderer API button | ||
| 514 | renderer_status_button = new QPushButton(); | ||
| 515 | renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); | ||
| 516 | renderer_status_button->setCheckable(true); | ||
| 517 | renderer_status_button->setFocusPolicy(Qt::NoFocus); | ||
| 518 | connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) { | ||
| 519 | renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); | ||
| 520 | }); | ||
| 521 | renderer_status_button->toggle(); | ||
| 522 | |||
| 523 | #ifndef HAS_VULKAN | ||
| 524 | renderer_status_button->setChecked(false); | ||
| 525 | renderer_status_button->setCheckable(false); | ||
| 526 | renderer_status_button->setDisabled(true); | ||
| 527 | #else | ||
| 528 | renderer_status_button->setChecked(Settings::values.renderer_backend == | ||
| 529 | Settings::RendererBackend::Vulkan); | ||
| 530 | connect(renderer_status_button, &QPushButton::clicked, [=] { | ||
| 531 | if (emulation_running) { | ||
| 532 | return; | ||
| 533 | } | ||
| 534 | if (renderer_status_button->isChecked()) { | ||
| 535 | Settings::values.renderer_backend = Settings::RendererBackend::Vulkan; | ||
| 536 | } else { | ||
| 537 | Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; | ||
| 538 | } | ||
| 539 | |||
| 540 | Settings::Apply(); | ||
| 541 | }); | ||
| 542 | #endif // HAS_VULKAN | ||
| 543 | statusBar()->insertPermanentWidget(0, renderer_status_button); | ||
| 544 | |||
| 481 | statusBar()->setVisible(true); | 545 | statusBar()->setVisible(true); |
| 482 | setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); | 546 | setStyleSheet(QStringLiteral("QStatusBar::item{border: none;}")); |
| 483 | } | 547 | } |
| @@ -640,6 +704,7 @@ void GMainWindow::InitializeHotkeys() { | |||
| 640 | Settings::values.use_docked_mode = !Settings::values.use_docked_mode; | 704 | Settings::values.use_docked_mode = !Settings::values.use_docked_mode; |
| 641 | OnDockedModeChanged(!Settings::values.use_docked_mode, | 705 | OnDockedModeChanged(!Settings::values.use_docked_mode, |
| 642 | Settings::values.use_docked_mode); | 706 | Settings::values.use_docked_mode); |
| 707 | dock_status_button->setChecked(Settings::values.use_docked_mode); | ||
| 643 | }); | 708 | }); |
| 644 | } | 709 | } |
| 645 | 710 | ||
| @@ -806,70 +871,12 @@ void GMainWindow::AllowOSSleep() { | |||
| 806 | #endif | 871 | #endif |
| 807 | } | 872 | } |
| 808 | 873 | ||
| 809 | QStringList GMainWindow::GetUnsupportedGLExtensions() { | ||
| 810 | QStringList unsupported_ext; | ||
| 811 | |||
| 812 | if (!GLAD_GL_ARB_buffer_storage) { | ||
| 813 | unsupported_ext.append(QStringLiteral("ARB_buffer_storage")); | ||
| 814 | } | ||
| 815 | if (!GLAD_GL_ARB_direct_state_access) { | ||
| 816 | unsupported_ext.append(QStringLiteral("ARB_direct_state_access")); | ||
| 817 | } | ||
| 818 | if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) { | ||
| 819 | unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev")); | ||
| 820 | } | ||
| 821 | if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) { | ||
| 822 | unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge")); | ||
| 823 | } | ||
| 824 | if (!GLAD_GL_ARB_multi_bind) { | ||
| 825 | unsupported_ext.append(QStringLiteral("ARB_multi_bind")); | ||
| 826 | } | ||
| 827 | if (!GLAD_GL_ARB_clip_control) { | ||
| 828 | unsupported_ext.append(QStringLiteral("ARB_clip_control")); | ||
| 829 | } | ||
| 830 | |||
| 831 | // Extensions required to support some texture formats. | ||
| 832 | if (!GLAD_GL_EXT_texture_compression_s3tc) { | ||
| 833 | unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc")); | ||
| 834 | } | ||
| 835 | if (!GLAD_GL_ARB_texture_compression_rgtc) { | ||
| 836 | unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc")); | ||
| 837 | } | ||
| 838 | if (!GLAD_GL_ARB_depth_buffer_float) { | ||
| 839 | unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float")); | ||
| 840 | } | ||
| 841 | |||
| 842 | for (const QString& ext : unsupported_ext) { | ||
| 843 | LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString()); | ||
| 844 | } | ||
| 845 | |||
| 846 | return unsupported_ext; | ||
| 847 | } | ||
| 848 | |||
| 849 | bool GMainWindow::LoadROM(const QString& filename) { | 874 | bool GMainWindow::LoadROM(const QString& filename) { |
| 850 | // Shutdown previous session if the emu thread is still active... | 875 | // Shutdown previous session if the emu thread is still active... |
| 851 | if (emu_thread != nullptr) | 876 | if (emu_thread != nullptr) |
| 852 | ShutdownGame(); | 877 | ShutdownGame(); |
| 853 | 878 | ||
| 854 | render_window->InitRenderTarget(); | 879 | if (!render_window->InitRenderTarget()) { |
| 855 | |||
| 856 | { | ||
| 857 | Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window}; | ||
| 858 | if (!gladLoadGL()) { | ||
| 859 | QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"), | ||
| 860 | tr("Your GPU may not support OpenGL 4.3, or you do not " | ||
| 861 | "have the latest graphics driver.")); | ||
| 862 | return false; | ||
| 863 | } | ||
| 864 | } | ||
| 865 | |||
| 866 | const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions(); | ||
| 867 | if (!unsupported_gl_extensions.empty()) { | ||
| 868 | QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"), | ||
| 869 | tr("Your GPU may not support one or more required OpenGL" | ||
| 870 | "extensions. Please ensure you have the latest graphics " | ||
| 871 | "driver.<br><br>Unsupported extensions:<br>") + | ||
| 872 | unsupported_gl_extensions.join(QStringLiteral("<br>"))); | ||
| 873 | return false; | 880 | return false; |
| 874 | } | 881 | } |
| 875 | 882 | ||
| @@ -980,7 +987,9 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 980 | // Create and start the emulation thread | 987 | // Create and start the emulation thread |
| 981 | emu_thread = std::make_unique<EmuThread>(render_window); | 988 | emu_thread = std::make_unique<EmuThread>(render_window); |
| 982 | emit EmulationStarting(emu_thread.get()); | 989 | emit EmulationStarting(emu_thread.get()); |
| 983 | render_window->moveContext(); | 990 | if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { |
| 991 | render_window->moveContext(); | ||
| 992 | } | ||
| 984 | emu_thread->start(); | 993 | emu_thread->start(); |
| 985 | 994 | ||
| 986 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); | 995 | connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); |
| @@ -1000,6 +1009,8 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 1000 | game_list_placeholder->hide(); | 1009 | game_list_placeholder->hide(); |
| 1001 | } | 1010 | } |
| 1002 | status_bar_update_timer.start(2000); | 1011 | status_bar_update_timer.start(2000); |
| 1012 | async_status_button->setDisabled(true); | ||
| 1013 | renderer_status_button->setDisabled(true); | ||
| 1003 | 1014 | ||
| 1004 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 1015 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 1005 | 1016 | ||
| @@ -1065,10 +1076,13 @@ void GMainWindow::ShutdownGame() { | |||
| 1065 | 1076 | ||
| 1066 | // Disable status bar updates | 1077 | // Disable status bar updates |
| 1067 | status_bar_update_timer.stop(); | 1078 | status_bar_update_timer.stop(); |
| 1068 | message_label->setVisible(false); | ||
| 1069 | emu_speed_label->setVisible(false); | 1079 | emu_speed_label->setVisible(false); |
| 1070 | game_fps_label->setVisible(false); | 1080 | game_fps_label->setVisible(false); |
| 1071 | emu_frametime_label->setVisible(false); | 1081 | emu_frametime_label->setVisible(false); |
| 1082 | async_status_button->setEnabled(true); | ||
| 1083 | #ifdef HAS_VULKAN | ||
| 1084 | renderer_status_button->setEnabled(true); | ||
| 1085 | #endif | ||
| 1072 | 1086 | ||
| 1073 | emulation_running = false; | 1087 | emulation_running = false; |
| 1074 | 1088 | ||
| @@ -1836,6 +1850,13 @@ void GMainWindow::OnConfigure() { | |||
| 1836 | } | 1850 | } |
| 1837 | 1851 | ||
| 1838 | config->Save(); | 1852 | config->Save(); |
| 1853 | |||
| 1854 | dock_status_button->setChecked(Settings::values.use_docked_mode); | ||
| 1855 | async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation); | ||
| 1856 | #ifdef HAS_VULKAN | ||
| 1857 | renderer_status_button->setChecked(Settings::values.renderer_backend == | ||
| 1858 | Settings::RendererBackend::Vulkan); | ||
| 1859 | #endif | ||
| 1839 | } | 1860 | } |
| 1840 | 1861 | ||
| 1841 | void GMainWindow::OnLoadAmiibo() { | 1862 | void GMainWindow::OnLoadAmiibo() { |
| @@ -2028,7 +2049,6 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det | |||
| 2028 | if (emu_thread) { | 2049 | if (emu_thread) { |
| 2029 | emu_thread->SetRunning(true); | 2050 | emu_thread->SetRunning(true); |
| 2030 | message_label->setText(status_message); | 2051 | message_label->setText(status_message); |
| 2031 | message_label->setVisible(true); | ||
| 2032 | } | 2052 | } |
| 2033 | } | 2053 | } |
| 2034 | } | 2054 | } |
| @@ -2195,6 +2215,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) { | |||
| 2195 | QWidget::closeEvent(event); | 2215 | QWidget::closeEvent(event); |
| 2196 | } | 2216 | } |
| 2197 | 2217 | ||
| 2218 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | ||
| 2219 | if (render_window) { | ||
| 2220 | render_window->ForwardKeyPressEvent(event); | ||
| 2221 | } | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 2225 | if (render_window) { | ||
| 2226 | render_window->ForwardKeyReleaseEvent(event); | ||
| 2227 | } | ||
| 2228 | } | ||
| 2229 | |||
| 2198 | static bool IsSingleFileDropEvent(QDropEvent* event) { | 2230 | static bool IsSingleFileDropEvent(QDropEvent* event) { |
| 2199 | const QMimeData* mimeData = event->mimeData(); | 2231 | const QMimeData* mimeData = event->mimeData(); |
| 2200 | return mimeData->hasUrls() && mimeData->urls().length() == 1; | 2232 | return mimeData->hasUrls() && mimeData->urls().length() == 1; |
| @@ -2227,18 +2259,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) { | |||
| 2227 | event->acceptProposedAction(); | 2259 | event->acceptProposedAction(); |
| 2228 | } | 2260 | } |
| 2229 | 2261 | ||
| 2230 | void GMainWindow::keyPressEvent(QKeyEvent* event) { | ||
| 2231 | if (render_window) { | ||
| 2232 | render_window->ForwardKeyPressEvent(event); | ||
| 2233 | } | ||
| 2234 | } | ||
| 2235 | |||
| 2236 | void GMainWindow::keyReleaseEvent(QKeyEvent* event) { | ||
| 2237 | if (render_window) { | ||
| 2238 | render_window->ForwardKeyReleaseEvent(event); | ||
| 2239 | } | ||
| 2240 | } | ||
| 2241 | |||
| 2242 | bool GMainWindow::ConfirmChangeGame() { | 2262 | bool GMainWindow::ConfirmChangeGame() { |
| 2243 | if (emu_thread == nullptr) | 2263 | if (emu_thread == nullptr) |
| 2244 | return true; | 2264 | return true; |
| @@ -2290,8 +2310,16 @@ void GMainWindow::UpdateUITheme() { | |||
| 2290 | QStringList theme_paths(default_theme_paths); | 2310 | QStringList theme_paths(default_theme_paths); |
| 2291 | 2311 | ||
| 2292 | if (is_default_theme || current_theme.isEmpty()) { | 2312 | if (is_default_theme || current_theme.isEmpty()) { |
| 2293 | qApp->setStyleSheet({}); | 2313 | const QString theme_uri(QStringLiteral(":default/style.qss")); |
| 2294 | setStyleSheet({}); | 2314 | QFile f(theme_uri); |
| 2315 | if (f.open(QFile::ReadOnly | QFile::Text)) { | ||
| 2316 | QTextStream ts(&f); | ||
| 2317 | qApp->setStyleSheet(ts.readAll()); | ||
| 2318 | setStyleSheet(ts.readAll()); | ||
| 2319 | } else { | ||
| 2320 | qApp->setStyleSheet({}); | ||
| 2321 | setStyleSheet({}); | ||
| 2322 | } | ||
| 2295 | theme_paths.append(default_icons); | 2323 | theme_paths.append(default_icons); |
| 2296 | QIcon::setThemeName(default_icons); | 2324 | QIcon::setThemeName(default_icons); |
| 2297 | } else { | 2325 | } else { |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index a56f9a981..8eba2172c 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -27,6 +27,7 @@ class LoadingScreen; | |||
| 27 | class MicroProfileDialog; | 27 | class MicroProfileDialog; |
| 28 | class ProfilerWidget; | 28 | class ProfilerWidget; |
| 29 | class QLabel; | 29 | class QLabel; |
| 30 | class QPushButton; | ||
| 30 | class WaitTreeWidget; | 31 | class WaitTreeWidget; |
| 31 | enum class GameListOpenTarget; | 32 | enum class GameListOpenTarget; |
| 32 | class GameListPlaceholder; | 33 | class GameListPlaceholder; |
| @@ -130,7 +131,6 @@ private: | |||
| 130 | void PreventOSSleep(); | 131 | void PreventOSSleep(); |
| 131 | void AllowOSSleep(); | 132 | void AllowOSSleep(); |
| 132 | 133 | ||
| 133 | QStringList GetUnsupportedGLExtensions(); | ||
| 134 | bool LoadROM(const QString& filename); | 134 | bool LoadROM(const QString& filename); |
| 135 | void BootGame(const QString& filename); | 135 | void BootGame(const QString& filename); |
| 136 | void ShutdownGame(); | 136 | void ShutdownGame(); |
| @@ -229,6 +229,9 @@ private: | |||
| 229 | QLabel* emu_speed_label = nullptr; | 229 | QLabel* emu_speed_label = nullptr; |
| 230 | QLabel* game_fps_label = nullptr; | 230 | QLabel* game_fps_label = nullptr; |
| 231 | QLabel* emu_frametime_label = nullptr; | 231 | QLabel* emu_frametime_label = nullptr; |
| 232 | QPushButton* async_status_button = nullptr; | ||
| 233 | QPushButton* renderer_status_button = nullptr; | ||
| 234 | QPushButton* dock_status_button = nullptr; | ||
| 232 | QTimer status_bar_update_timer; | 235 | QTimer status_bar_update_timer; |
| 233 | 236 | ||
| 234 | std::unique_ptr<Config> config; | 237 | std::unique_ptr<Config> config; |
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index bc7725a01..a675ecf4d 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h | |||
| @@ -89,7 +89,6 @@ struct Values { | |||
| 89 | int profile_index; | 89 | int profile_index; |
| 90 | 90 | ||
| 91 | // Game List | 91 | // Game List |
| 92 | bool show_unknown; | ||
| 93 | bool show_add_ons; | 92 | bool show_add_ons; |
| 94 | uint32_t icon_size; | 93 | uint32_t icon_size; |
| 95 | uint8_t row_1_text_id; | 94 | uint8_t row_1_text_id; |
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index b5f06ab9e..a15719a0f 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt | |||
| @@ -8,11 +8,22 @@ add_executable(yuzu-cmd | |||
| 8 | emu_window/emu_window_sdl2_gl.h | 8 | emu_window/emu_window_sdl2_gl.h |
| 9 | emu_window/emu_window_sdl2.cpp | 9 | emu_window/emu_window_sdl2.cpp |
| 10 | emu_window/emu_window_sdl2.h | 10 | emu_window/emu_window_sdl2.h |
| 11 | emu_window/emu_window_sdl2_gl.cpp | ||
| 12 | emu_window/emu_window_sdl2_gl.h | ||
| 11 | resource.h | 13 | resource.h |
| 12 | yuzu.cpp | 14 | yuzu.cpp |
| 13 | yuzu.rc | 15 | yuzu.rc |
| 14 | ) | 16 | ) |
| 15 | 17 | ||
| 18 | if (ENABLE_VULKAN) | ||
| 19 | target_sources(yuzu-cmd PRIVATE | ||
| 20 | emu_window/emu_window_sdl2_vk.cpp | ||
| 21 | emu_window/emu_window_sdl2_vk.h) | ||
| 22 | |||
| 23 | target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include) | ||
| 24 | target_compile_definitions(yuzu-cmd PRIVATE HAS_VULKAN) | ||
| 25 | endif() | ||
| 26 | |||
| 16 | create_target_directory_groups(yuzu-cmd) | 27 | create_target_directory_groups(yuzu-cmd) |
| 17 | 28 | ||
| 18 | target_link_libraries(yuzu-cmd PRIVATE common core input_common) | 29 | target_link_libraries(yuzu-cmd PRIVATE common core input_common) |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 1a812cb87..96f1ce3af 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "core/hle/service/acc/profile_manager.h" | 12 | #include "core/hle/service/acc/profile_manager.h" |
| 13 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| 14 | #include "input_common/main.h" | 14 | #include "input_common/main.h" |
| 15 | #include "input_common/udp/client.h" | ||
| 15 | #include "yuzu_cmd/config.h" | 16 | #include "yuzu_cmd/config.h" |
| 16 | #include "yuzu_cmd/default_ini.h" | 17 | #include "yuzu_cmd/default_ini.h" |
| 17 | 18 | ||
| @@ -297,6 +298,10 @@ void Config::ReadValues() { | |||
| 297 | sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); | 298 | sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); |
| 298 | Settings::values.touchscreen.diameter_y = | 299 | Settings::values.touchscreen.diameter_y = |
| 299 | sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); | 300 | sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); |
| 301 | Settings::values.udp_input_address = | ||
| 302 | sdl2_config->Get("Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_ADDR); | ||
| 303 | Settings::values.udp_input_port = static_cast<u16>(sdl2_config->GetInteger( | ||
| 304 | "Controls", "udp_input_port", InputCommon::CemuhookUDP::DEFAULT_PORT)); | ||
| 300 | 305 | ||
| 301 | std::transform(keyboard_keys.begin(), keyboard_keys.end(), | 306 | std::transform(keyboard_keys.begin(), keyboard_keys.end(), |
| 302 | Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); | 307 | Settings::values.keyboard_keys.begin(), InputCommon::GenerateKeyboardParam); |
| @@ -366,8 +371,16 @@ void Config::ReadValues() { | |||
| 366 | Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); | 371 | Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); |
| 367 | 372 | ||
| 368 | // Renderer | 373 | // Renderer |
| 374 | const int renderer_backend = sdl2_config->GetInteger( | ||
| 375 | "Renderer", "backend", static_cast<int>(Settings::RendererBackend::OpenGL)); | ||
| 376 | Settings::values.renderer_backend = static_cast<Settings::RendererBackend>(renderer_backend); | ||
| 377 | Settings::values.renderer_debug = sdl2_config->GetBoolean("Renderer", "debug", false); | ||
| 378 | Settings::values.vulkan_device = sdl2_config->GetInteger("Renderer", "vulkan_device", 0); | ||
| 379 | |||
| 369 | Settings::values.resolution_factor = | 380 | Settings::values.resolution_factor = |
| 370 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); | 381 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); |
| 382 | Settings::values.aspect_ratio = | ||
| 383 | static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); | ||
| 371 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); | 384 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); |
| 372 | Settings::values.frame_limit = | 385 | Settings::values.frame_limit = |
| 373 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); | 386 | static_cast<u16>(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 8d18a4a5a..8a2b658cd 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -69,18 +69,46 @@ rstick= | |||
| 69 | # - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: | 69 | # - "motion_emu" (default) for emulating motion input from mouse input. Required parameters: |
| 70 | # - "update_period": update period in milliseconds (default to 100) | 70 | # - "update_period": update period in milliseconds (default to 100) |
| 71 | # - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01) | 71 | # - "sensitivity": the coefficient converting mouse movement to tilting angle (default to 0.01) |
| 72 | # - "cemuhookudp" reads motion input from a udp server that uses cemuhook's udp protocol | ||
| 72 | motion_device= | 73 | motion_device= |
| 73 | 74 | ||
| 74 | # for touch input, the following devices are available: | 75 | # for touch input, the following devices are available: |
| 75 | # - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required | 76 | # - "emu_window" (default) for emulating touch input from mouse input to the emulation window. No parameters required |
| 77 | # - "cemuhookudp" reads touch input from a udp server that uses cemuhook's udp protocol | ||
| 78 | # - "min_x", "min_y", "max_x", "max_y": defines the udp device's touch screen coordinate system | ||
| 76 | touch_device= | 79 | touch_device= |
| 77 | 80 | ||
| 81 | # Most desktop operating systems do not expose a way to poll the motion state of the controllers | ||
| 82 | # so as a way around it, cemuhook created a udp client/server protocol to broadcast the data directly | ||
| 83 | # from a controller device to the client program. Citra has a client that can connect and read | ||
| 84 | # from any cemuhook compatible motion program. | ||
| 85 | |||
| 86 | # IPv4 address of the udp input server (Default "127.0.0.1") | ||
| 87 | udp_input_address= | ||
| 88 | |||
| 89 | # Port of the udp input server. (Default 26760) | ||
| 90 | udp_input_port= | ||
| 91 | |||
| 92 | # The pad to request data on. Should be between 0 (Pad 1) and 3 (Pad 4). (Default 0) | ||
| 93 | udp_pad_index= | ||
| 94 | |||
| 78 | [Core] | 95 | [Core] |
| 79 | # Whether to use multi-core for CPU emulation | 96 | # Whether to use multi-core for CPU emulation |
| 80 | # 0 (default): Disabled, 1: Enabled | 97 | # 0 (default): Disabled, 1: Enabled |
| 81 | use_multi_core= | 98 | use_multi_core= |
| 82 | 99 | ||
| 83 | [Renderer] | 100 | [Renderer] |
| 101 | # Which backend API to use. | ||
| 102 | # 0 (default): OpenGL, 1: Vulkan | ||
| 103 | backend = | ||
| 104 | |||
| 105 | # Enable graphics API debugging mode. | ||
| 106 | # 0 (default): Disabled, 1: Enabled | ||
| 107 | debug = | ||
| 108 | |||
| 109 | # Which Vulkan physical device to use (defaults to 0) | ||
| 110 | vulkan_device = | ||
| 111 | |||
| 84 | # Whether to use software or hardware rendering. | 112 | # Whether to use software or hardware rendering. |
| 85 | # 0: Software, 1 (default): Hardware | 113 | # 0: Software, 1 (default): Hardware |
| 86 | use_hw_renderer = | 114 | use_hw_renderer = |
| @@ -94,6 +122,10 @@ use_shader_jit = | |||
| 94 | # factor for the Switch resolution | 122 | # factor for the Switch resolution |
| 95 | resolution_factor = | 123 | resolution_factor = |
| 96 | 124 | ||
| 125 | # Aspect ratio | ||
| 126 | # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window | ||
| 127 | aspect_ratio = | ||
| 128 | |||
| 97 | # Whether to enable V-Sync (caps the framerate at 60FPS) or not. | 129 | # Whether to enable V-Sync (caps the framerate at 60FPS) or not. |
| 98 | # 0 (default): Off, 1: On | 130 | # 0 (default): Off, 1: On |
| 99 | use_vsync = | 131 | use_vsync = |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index b1c512db1..e96139885 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -89,6 +89,10 @@ bool EmuWindow_SDL2::IsOpen() const { | |||
| 89 | return is_open; | 89 | return is_open; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | bool EmuWindow_SDL2::IsShown() const { | ||
| 93 | return is_shown; | ||
| 94 | } | ||
| 95 | |||
| 92 | void EmuWindow_SDL2::OnResize() { | 96 | void EmuWindow_SDL2::OnResize() { |
| 93 | int width, height; | 97 | int width, height; |
| 94 | SDL_GetWindowSize(render_window, &width, &height); | 98 | SDL_GetWindowSize(render_window, &width, &height); |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index eaa971f77..b38f56661 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h | |||
| @@ -21,6 +21,9 @@ public: | |||
| 21 | /// Whether the window is still open, and a close request hasn't yet been sent | 21 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 22 | bool IsOpen() const; | 22 | bool IsOpen() const; |
| 23 | 23 | ||
| 24 | /// Returns if window is shown (not minimized) | ||
| 25 | bool IsShown() const override; | ||
| 26 | |||
| 24 | protected: | 27 | protected: |
| 25 | /// Called by PollEvents when a key is pressed or released. | 28 | /// Called by PollEvents when a key is pressed or released. |
| 26 | void OnKeyEvent(int key, u8 state); | 29 | void OnKeyEvent(int key, u8 state); |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index 6fde694a2..7ffa0ac09 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <SDL.h> | 9 | #include <SDL.h> |
| 10 | #include <fmt/format.h> | 10 | #include <fmt/format.h> |
| 11 | #include <glad/glad.h> | 11 | #include <glad/glad.h> |
| 12 | #include "common/assert.h" | ||
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "common/scm_rev.h" | 14 | #include "common/scm_rev.h" |
| 14 | #include "common/string_util.h" | 15 | #include "common/string_util.h" |
| @@ -151,6 +152,12 @@ void EmuWindow_SDL2_GL::DoneCurrent() { | |||
| 151 | SDL_GL_MakeCurrent(render_window, nullptr); | 152 | SDL_GL_MakeCurrent(render_window, nullptr); |
| 152 | } | 153 | } |
| 153 | 154 | ||
| 155 | void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 156 | void* surface) const { | ||
| 157 | // Should not have been called from OpenGL | ||
| 158 | UNREACHABLE(); | ||
| 159 | } | ||
| 160 | |||
| 154 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { | 161 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const { |
| 155 | return std::make_unique<SDLGLContext>(); | 162 | return std::make_unique<SDLGLContext>(); |
| 156 | } | 163 | } |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h index 630deba93..c753085a8 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h | |||
| @@ -22,6 +22,10 @@ public: | |||
| 22 | /// Releases the GL context from the caller thread | 22 | /// Releases the GL context from the caller thread |
| 23 | void DoneCurrent() override; | 23 | void DoneCurrent() override; |
| 24 | 24 | ||
| 25 | /// Ignored in OpenGL | ||
| 26 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 27 | void* surface) const override; | ||
| 28 | |||
| 25 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | 29 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; |
| 26 | 30 | ||
| 27 | private: | 31 | private: |
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp new file mode 100644 index 000000000..a203f0da9 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <string> | ||
| 7 | #include <vector> | ||
| 8 | #include <SDL.h> | ||
| 9 | #include <SDL_vulkan.h> | ||
| 10 | #include <fmt/format.h> | ||
| 11 | #include <vulkan/vulkan.h> | ||
| 12 | #include "common/assert.h" | ||
| 13 | #include "common/logging/log.h" | ||
| 14 | #include "common/scm_rev.h" | ||
| 15 | #include "core/settings.h" | ||
| 16 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" | ||
| 17 | |||
| 18 | EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) { | ||
| 19 | if (SDL_Vulkan_LoadLibrary(nullptr) != 0) { | ||
| 20 | LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError()); | ||
| 21 | exit(EXIT_FAILURE); | ||
| 22 | } | ||
| 23 | |||
| 24 | vkGetInstanceProcAddr = | ||
| 25 | reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr()); | ||
| 26 | if (vkGetInstanceProcAddr == nullptr) { | ||
| 27 | LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); | ||
| 28 | exit(EXIT_FAILURE); | ||
| 29 | } | ||
| 30 | |||
| 31 | const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name, | ||
| 32 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 33 | render_window = | ||
| 34 | SDL_CreateWindow(window_title.c_str(), | ||
| 35 | SDL_WINDOWPOS_UNDEFINED, // x position | ||
| 36 | SDL_WINDOWPOS_UNDEFINED, // y position | ||
| 37 | Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height, | ||
| 38 | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN); | ||
| 39 | |||
| 40 | const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr); | ||
| 41 | |||
| 42 | u32 extra_ext_count{}; | ||
| 43 | if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) { | ||
| 44 | LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}", | ||
| 45 | SDL_GetError()); | ||
| 46 | exit(1); | ||
| 47 | } | ||
| 48 | |||
| 49 | auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count); | ||
| 50 | if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) { | ||
| 51 | LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError()); | ||
| 52 | exit(1); | ||
| 53 | } | ||
| 54 | std::vector<const char*> enabled_extensions; | ||
| 55 | enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(), | ||
| 56 | extra_ext_names.get() + extra_ext_count); | ||
| 57 | |||
| 58 | std::vector<const char*> enabled_layers; | ||
| 59 | if (use_standard_layers) { | ||
| 60 | enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | ||
| 61 | enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation"); | ||
| 62 | } | ||
| 63 | |||
| 64 | VkApplicationInfo app_info{}; | ||
| 65 | app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | ||
| 66 | app_info.apiVersion = VK_API_VERSION_1_1; | ||
| 67 | app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); | ||
| 68 | app_info.pApplicationName = "yuzu-emu"; | ||
| 69 | app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); | ||
| 70 | app_info.pEngineName = "yuzu-emu"; | ||
| 71 | |||
| 72 | VkInstanceCreateInfo instance_ci{}; | ||
| 73 | instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | ||
| 74 | instance_ci.pApplicationInfo = &app_info; | ||
| 75 | instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size()); | ||
| 76 | instance_ci.ppEnabledExtensionNames = enabled_extensions.data(); | ||
| 77 | if (Settings::values.renderer_debug) { | ||
| 78 | instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size()); | ||
| 79 | instance_ci.ppEnabledLayerNames = enabled_layers.data(); | ||
| 80 | } | ||
| 81 | |||
| 82 | const auto vkCreateInstance = | ||
| 83 | reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance")); | ||
| 84 | if (vkCreateInstance == nullptr || | ||
| 85 | vkCreateInstance(&instance_ci, nullptr, &vk_instance) != VK_SUCCESS) { | ||
| 86 | LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!"); | ||
| 87 | exit(EXIT_FAILURE); | ||
| 88 | } | ||
| 89 | |||
| 90 | vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>( | ||
| 91 | vkGetInstanceProcAddr(vk_instance, "vkDestroyInstance")); | ||
| 92 | if (vkDestroyInstance == nullptr) { | ||
| 93 | LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); | ||
| 94 | exit(EXIT_FAILURE); | ||
| 95 | } | ||
| 96 | |||
| 97 | if (!SDL_Vulkan_CreateSurface(render_window, vk_instance, &vk_surface)) { | ||
| 98 | LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError()); | ||
| 99 | exit(EXIT_FAILURE); | ||
| 100 | } | ||
| 101 | |||
| 102 | OnResize(); | ||
| 103 | OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); | ||
| 104 | SDL_PumpEvents(); | ||
| 105 | LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name, | ||
| 106 | Common::g_scm_branch, Common::g_scm_desc); | ||
| 107 | } | ||
| 108 | |||
| 109 | EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() { | ||
| 110 | vkDestroyInstance(vk_instance, nullptr); | ||
| 111 | } | ||
| 112 | |||
| 113 | void EmuWindow_SDL2_VK::SwapBuffers() {} | ||
| 114 | |||
| 115 | void EmuWindow_SDL2_VK::MakeCurrent() { | ||
| 116 | // Unused on Vulkan | ||
| 117 | } | ||
| 118 | |||
| 119 | void EmuWindow_SDL2_VK::DoneCurrent() { | ||
| 120 | // Unused on Vulkan | ||
| 121 | } | ||
| 122 | |||
| 123 | void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 124 | void* surface) const { | ||
| 125 | const auto instance_proc_addr = vkGetInstanceProcAddr; | ||
| 126 | std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); | ||
| 127 | std::memcpy(instance, &vk_instance, sizeof(vk_instance)); | ||
| 128 | std::memcpy(surface, &vk_surface, sizeof(vk_surface)); | ||
| 129 | } | ||
| 130 | |||
| 131 | std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const { | ||
| 132 | return nullptr; | ||
| 133 | } | ||
| 134 | |||
| 135 | bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const { | ||
| 136 | if (!Settings::values.renderer_debug) { | ||
| 137 | return false; | ||
| 138 | } | ||
| 139 | |||
| 140 | const auto vkEnumerateInstanceLayerProperties = | ||
| 141 | reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>( | ||
| 142 | vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties")); | ||
| 143 | if (vkEnumerateInstanceLayerProperties == nullptr) { | ||
| 144 | LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!"); | ||
| 145 | return false; | ||
| 146 | } | ||
| 147 | |||
| 148 | u32 available_layers_count{}; | ||
| 149 | if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) { | ||
| 150 | LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); | ||
| 151 | return false; | ||
| 152 | } | ||
| 153 | std::vector<VkLayerProperties> layers(available_layers_count); | ||
| 154 | if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) { | ||
| 155 | LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!"); | ||
| 156 | return false; | ||
| 157 | } | ||
| 158 | |||
| 159 | return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) { | ||
| 160 | return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation"); | ||
| 161 | }) != layers.end(); | ||
| 162 | } | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h new file mode 100644 index 000000000..2a7c06a24 --- /dev/null +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vulkan/vulkan.h> | ||
| 8 | #include "core/frontend/emu_window.h" | ||
| 9 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | ||
| 10 | |||
| 11 | class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 { | ||
| 12 | public: | ||
| 13 | explicit EmuWindow_SDL2_VK(bool fullscreen); | ||
| 14 | ~EmuWindow_SDL2_VK(); | ||
| 15 | |||
| 16 | /// Swap buffers to display the next frame | ||
| 17 | void SwapBuffers() override; | ||
| 18 | |||
| 19 | /// Makes the graphics context current for the caller thread | ||
| 20 | void MakeCurrent() override; | ||
| 21 | |||
| 22 | /// Releases the GL context from the caller thread | ||
| 23 | void DoneCurrent() override; | ||
| 24 | |||
| 25 | /// Retrieves Vulkan specific handlers from the window | ||
| 26 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 27 | void* surface) const override; | ||
| 28 | |||
| 29 | std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override; | ||
| 30 | |||
| 31 | private: | ||
| 32 | bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const; | ||
| 33 | |||
| 34 | VkInstance vk_instance{}; | ||
| 35 | VkSurfaceKHR vk_surface{}; | ||
| 36 | |||
| 37 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{}; | ||
| 38 | PFN_vkDestroyInstance vkDestroyInstance{}; | ||
| 39 | }; | ||
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 3ee088a91..325795321 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -32,6 +32,9 @@ | |||
| 32 | #include "yuzu_cmd/config.h" | 32 | #include "yuzu_cmd/config.h" |
| 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 33 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 34 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" | 34 | #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" |
| 35 | #ifdef HAS_VULKAN | ||
| 36 | #include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h" | ||
| 37 | #endif | ||
| 35 | 38 | ||
| 36 | #include "core/file_sys/registered_cache.h" | 39 | #include "core/file_sys/registered_cache.h" |
| 37 | 40 | ||
| @@ -174,7 +177,20 @@ int main(int argc, char** argv) { | |||
| 174 | Settings::values.use_gdbstub = use_gdbstub; | 177 | Settings::values.use_gdbstub = use_gdbstub; |
| 175 | Settings::Apply(); | 178 | Settings::Apply(); |
| 176 | 179 | ||
| 177 | std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)}; | 180 | std::unique_ptr<EmuWindow_SDL2> emu_window; |
| 181 | switch (Settings::values.renderer_backend) { | ||
| 182 | case Settings::RendererBackend::OpenGL: | ||
| 183 | emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen); | ||
| 184 | break; | ||
| 185 | case Settings::RendererBackend::Vulkan: | ||
| 186 | #ifdef HAS_VULKAN | ||
| 187 | emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen); | ||
| 188 | break; | ||
| 189 | #else | ||
| 190 | LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!"); | ||
| 191 | return 1; | ||
| 192 | #endif | ||
| 193 | } | ||
| 178 | 194 | ||
| 179 | if (!Settings::values.use_multi_core) { | 195 | if (!Settings::values.use_multi_core) { |
| 180 | // Single core mode must acquire OpenGL context for entire emulation session | 196 | // Single core mode must acquire OpenGL context for entire emulation session |
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp index 84ab4d687..0ac93b62a 100644 --- a/src/yuzu_tester/config.cpp +++ b/src/yuzu_tester/config.cpp | |||
| @@ -118,6 +118,8 @@ void Config::ReadValues() { | |||
| 118 | // Renderer | 118 | // Renderer |
| 119 | Settings::values.resolution_factor = | 119 | Settings::values.resolution_factor = |
| 120 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); | 120 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); |
| 121 | Settings::values.aspect_ratio = | ||
| 122 | static_cast<int>(sdl2_config->GetInteger("Renderer", "aspect_ratio", 0)); | ||
| 121 | Settings::values.use_frame_limit = false; | 123 | Settings::values.use_frame_limit = false; |
| 122 | Settings::values.frame_limit = 100; | 124 | Settings::values.frame_limit = 100; |
| 123 | Settings::values.use_disk_shader_cache = | 125 | Settings::values.use_disk_shader_cache = |
diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h index 9a3e86d68..8d93f7b88 100644 --- a/src/yuzu_tester/default_ini.h +++ b/src/yuzu_tester/default_ini.h | |||
| @@ -26,6 +26,10 @@ use_shader_jit = | |||
| 26 | # factor for the Switch resolution | 26 | # factor for the Switch resolution |
| 27 | resolution_factor = | 27 | resolution_factor = |
| 28 | 28 | ||
| 29 | # Aspect ratio | ||
| 30 | # 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window | ||
| 31 | aspect_ratio = | ||
| 32 | |||
| 29 | # Whether to enable V-Sync (caps the framerate at 60FPS) or not. | 33 | # Whether to enable V-Sync (caps the framerate at 60FPS) or not. |
| 30 | # 0 (default): Off, 1: On | 34 | # 0 (default): Off, 1: On |
| 31 | use_vsync = | 35 | use_vsync = |
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp index e7fe8decf..f2cc4a797 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp | |||
| @@ -5,10 +5,15 @@ | |||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cstdlib> | 6 | #include <cstdlib> |
| 7 | #include <string> | 7 | #include <string> |
| 8 | |||
| 9 | #include <fmt/format.h> | ||
| 10 | |||
| 8 | #define SDL_MAIN_HANDLED | 11 | #define SDL_MAIN_HANDLED |
| 9 | #include <SDL.h> | 12 | #include <SDL.h> |
| 10 | #include <fmt/format.h> | 13 | |
| 11 | #include <glad/glad.h> | 14 | #include <glad/glad.h> |
| 15 | |||
| 16 | #include "common/assert.h" | ||
| 12 | #include "common/logging/log.h" | 17 | #include "common/logging/log.h" |
| 13 | #include "common/scm_rev.h" | 18 | #include "common/scm_rev.h" |
| 14 | #include "core/settings.h" | 19 | #include "core/settings.h" |
| @@ -120,3 +125,11 @@ void EmuWindow_SDL2_Hide::MakeCurrent() { | |||
| 120 | void EmuWindow_SDL2_Hide::DoneCurrent() { | 125 | void EmuWindow_SDL2_Hide::DoneCurrent() { |
| 121 | SDL_GL_MakeCurrent(render_window, nullptr); | 126 | SDL_GL_MakeCurrent(render_window, nullptr); |
| 122 | } | 127 | } |
| 128 | |||
| 129 | bool EmuWindow_SDL2_Hide::IsShown() const { | ||
| 130 | return false; | ||
| 131 | } | ||
| 132 | |||
| 133 | void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const { | ||
| 134 | UNREACHABLE(); | ||
| 135 | } | ||
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h index 1a8953c75..c7fccc002 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h | |||
| @@ -25,6 +25,13 @@ public: | |||
| 25 | /// Releases the GL context from the caller thread | 25 | /// Releases the GL context from the caller thread |
| 26 | void DoneCurrent() override; | 26 | void DoneCurrent() override; |
| 27 | 27 | ||
| 28 | /// Whether the screen is being shown or not. | ||
| 29 | bool IsShown() const override; | ||
| 30 | |||
| 31 | /// Retrieves Vulkan specific handlers from the window | ||
| 32 | void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, | ||
| 33 | void* surface) const override; | ||
| 34 | |||
| 28 | /// Whether the window is still open, and a close request hasn't yet been sent | 35 | /// Whether the window is still open, and a close request hasn't yet been sent |
| 29 | bool IsOpen() const; | 36 | bool IsOpen() const; |
| 30 | 37 | ||