diff options
107 files changed, 2628 insertions, 613 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/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 9814b06dd..7d088a719 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss | |||
| @@ -1236,3 +1236,41 @@ QToolButton:disabled, | |||
| 1236 | QPlainTextEdit:disabled { | 1236 | QPlainTextEdit:disabled { |
| 1237 | background-color: #2b2e31; | 1237 | background-color: #2b2e31; |
| 1238 | } | 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/src/core/core.cpp b/src/core/core.cpp index c53d122be..0eb0c0dca 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -268,7 +268,9 @@ struct System::Impl { | |||
| 268 | is_powered_on = false; | 268 | is_powered_on = false; |
| 269 | exit_lock = false; | 269 | exit_lock = false; |
| 270 | 270 | ||
| 271 | gpu_core->WaitIdle(); | 271 | if (gpu_core) { |
| 272 | gpu_core->WaitIdle(); | ||
| 273 | } | ||
| 272 | 274 | ||
| 273 | // Shutdown emulation session | 275 | // Shutdown emulation session |
| 274 | renderer.reset(); | 276 | renderer.reset(); |
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/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/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 2db28dcf0..ab05788d7 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -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/svc.cpp b/src/core/hle/kernel/svc.cpp index 1d99bf7a2..9cae5c73d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -1863,10 +1863,14 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd | |||
| 1863 | } | 1863 | } |
| 1864 | 1864 | ||
| 1865 | auto& kernel = system.Kernel(); | 1865 | auto& kernel = system.Kernel(); |
| 1866 | auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); | 1866 | auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms); |
| 1867 | |||
| 1868 | if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) { | ||
| 1869 | return reserve_result; | ||
| 1870 | } | ||
| 1867 | 1871 | ||
| 1868 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); | 1872 | auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); |
| 1869 | const auto result = handle_table.Create(std::move(transfer_mem_handle)); | 1873 | const auto result{handle_table.Create(std::move(transfer_mem_handle))}; |
| 1870 | if (result.Failed()) { | 1874 | if (result.Failed()) { |
| 1871 | return result.Code(); | 1875 | return result.Code(); |
| 1872 | } | 1876 | } |
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/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index a0c806e8f..1838260fd 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp | |||
| @@ -50,17 +50,8 @@ std::shared_ptr<Thread> WaitObject::GetHighestPriorityReadyThread() const { | |||
| 50 | if (ShouldWait(thread.get())) | 50 | if (ShouldWait(thread.get())) |
| 51 | continue; | 51 | continue; |
| 52 | 52 | ||
| 53 | // A thread is ready to run if it's either in ThreadStatus::WaitSynch | 53 | candidate = thread.get(); |
| 54 | // and the rest of the objects it is waiting on are ready. | 54 | candidate_priority = thread->GetPriority(); |
| 55 | bool ready_to_run = true; | ||
| 56 | if (thread_status == ThreadStatus::WaitSynch) { | ||
| 57 | ready_to_run = thread->AllWaitObjectsReady(); | ||
| 58 | } | ||
| 59 | |||
| 60 | if (ready_to_run) { | ||
| 61 | candidate = thread.get(); | ||
| 62 | candidate_priority = thread->GetPriority(); | ||
| 63 | } | ||
| 64 | } | 55 | } |
| 65 | 56 | ||
| 66 | return SharedFrom(candidate); | 57 | return SharedFrom(candidate); |
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/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/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/settings.h b/src/core/settings.h index 421e76f5f..e1a9a0ffa 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; |
| @@ -419,6 +424,10 @@ struct Values { | |||
| 419 | SDMCSize sdmc_size; | 424 | SDMCSize sdmc_size; |
| 420 | 425 | ||
| 421 | // Renderer | 426 | // Renderer |
| 427 | RendererBackend renderer_backend; | ||
| 428 | bool renderer_debug; | ||
| 429 | int vulkan_device; | ||
| 430 | |||
| 422 | float resolution_factor; | 431 | float resolution_factor; |
| 423 | bool use_frame_limit; | 432 | bool use_frame_limit; |
| 424 | u16 frame_limit; | 433 | u16 frame_limit; |
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/input_common/main.cpp b/src/input_common/main.cpp index 9e028da89..c98c848cf 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -41,6 +41,7 @@ void Shutdown() { | |||
| 41 | Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); | 41 | Input::UnregisterFactory<Input::MotionDevice>("motion_emu"); |
| 42 | motion_emu.reset(); | 42 | motion_emu.reset(); |
| 43 | sdl.reset(); | 43 | sdl.reset(); |
| 44 | udp.reset(); | ||
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | Keyboard* GetKeyboard() { | 47 | Keyboard* GetKeyboard() { |
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 index 5f5a9989c..2228571a6 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp | |||
| @@ -14,7 +14,6 @@ | |||
| 14 | #include "input_common/udp/client.h" | 14 | #include "input_common/udp/client.h" |
| 15 | #include "input_common/udp/protocol.h" | 15 | #include "input_common/udp/protocol.h" |
| 16 | 16 | ||
| 17 | using boost::asio::ip::address_v4; | ||
| 18 | using boost::asio::ip::udp; | 17 | using boost::asio::ip::udp; |
| 19 | 18 | ||
| 20 | namespace InputCommon::CemuhookUDP { | 19 | namespace InputCommon::CemuhookUDP { |
| @@ -31,10 +30,10 @@ public: | |||
| 31 | 30 | ||
| 32 | explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, | 31 | explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id, |
| 33 | SocketCallback callback) | 32 | SocketCallback callback) |
| 34 | : client_id(client_id), timer(io_service), | 33 | : callback(std::move(callback)), timer(io_service), |
| 35 | send_endpoint(udp::endpoint(address_v4::from_string(host), port)), | 34 | socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id), |
| 36 | socket(io_service, udp::endpoint(udp::v4(), 0)), pad_index(pad_index), | 35 | pad_index(pad_index), |
| 37 | callback(std::move(callback)) {} | 36 | send_endpoint(udp::endpoint(boost::asio::ip::make_address_v4(host), port)) {} |
| 38 | 37 | ||
| 39 | void Stop() { | 38 | void Stop() { |
| 40 | io_service.stop(); | 39 | io_service.stop(); |
| @@ -126,7 +125,7 @@ static void SocketLoop(Socket* socket) { | |||
| 126 | 125 | ||
| 127 | Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, | 126 | Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port, |
| 128 | u8 pad_index, u32 client_id) | 127 | u8 pad_index, u32 client_id) |
| 129 | : status(status) { | 128 | : status(std::move(status)) { |
| 130 | StartCommunication(host, port, pad_index, client_id); | 129 | StartCommunication(host, port, pad_index, client_id); |
| 131 | } | 130 | } |
| 132 | 131 | ||
| @@ -207,7 +206,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie | |||
| 207 | Common::Event success_event; | 206 | Common::Event success_event; |
| 208 | SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, | 207 | SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {}, |
| 209 | [&](Response::PadData data) { success_event.Set(); }}; | 208 | [&](Response::PadData data) { success_event.Set(); }}; |
| 210 | Socket socket{host, port, pad_index, client_id, callback}; | 209 | Socket socket{host, port, pad_index, client_id, std::move(callback)}; |
| 211 | std::thread worker_thread{SocketLoop, &socket}; | 210 | std::thread worker_thread{SocketLoop, &socket}; |
| 212 | bool result = success_event.WaitFor(std::chrono::seconds(8)); | 211 | bool result = success_event.WaitFor(std::chrono::seconds(8)); |
| 213 | socket.Stop(); | 212 | socket.Stop(); |
| @@ -267,7 +266,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( | |||
| 267 | complete_event.Set(); | 266 | complete_event.Set(); |
| 268 | } | 267 | } |
| 269 | }}; | 268 | }}; |
| 270 | Socket socket{host, port, pad_index, client_id, callback}; | 269 | Socket socket{host, port, pad_index, client_id, std::move(callback)}; |
| 271 | std::thread worker_thread{SocketLoop, &socket}; | 270 | std::thread worker_thread{SocketLoop, &socket}; |
| 272 | complete_event.Wait(); | 271 | complete_event.Wait(); |
| 273 | socket.Stop(); | 272 | socket.Stop(); |
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h index 0b21f4da6..b8c654755 100644 --- a/src/input_common/udp/client.h +++ b/src/input_common/udp/client.h | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | #include <string> | 11 | #include <string> |
| 12 | #include <thread> | 12 | #include <thread> |
| 13 | #include <tuple> | 13 | #include <tuple> |
| 14 | #include <vector> | ||
| 15 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 16 | #include "common/thread.h" | 15 | #include "common/thread.h" |
| 17 | #include "common/vector_math.h" | 16 | #include "common/vector_math.h" |
diff --git a/src/input_common/udp/protocol.h b/src/input_common/udp/protocol.h index 1b521860a..3ba4d1fc8 100644 --- a/src/input_common/udp/protocol.h +++ b/src/input_common/udp/protocol.h | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <optional> | 8 | #include <optional> |
| 9 | #include <type_traits> | 9 | #include <type_traits> |
| 10 | #include <vector> | ||
| 11 | #include <boost/crc.hpp> | 10 | #include <boost/crc.hpp> |
| 12 | #include "common/bit_field.h" | 11 | #include "common/bit_field.h" |
| 13 | #include "common/swap.h" | 12 | #include "common/swap.h" |
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index a80f38614..ca99cc22f 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp | |||
| @@ -2,7 +2,9 @@ | |||
| 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 "common/logging/log.h" | 5 | #include <mutex> |
| 6 | #include <tuple> | ||
| 7 | |||
| 6 | #include "common/param_package.h" | 8 | #include "common/param_package.h" |
| 7 | #include "core/frontend/input.h" | 9 | #include "core/frontend/input.h" |
| 8 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| @@ -14,7 +16,7 @@ namespace InputCommon::CemuhookUDP { | |||
| 14 | class UDPTouchDevice final : public Input::TouchDevice { | 16 | class UDPTouchDevice final : public Input::TouchDevice { |
| 15 | public: | 17 | public: |
| 16 | explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | 18 | explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} |
| 17 | std::tuple<float, float, bool> GetStatus() const { | 19 | std::tuple<float, float, bool> GetStatus() const override { |
| 18 | std::lock_guard guard(status->update_mutex); | 20 | std::lock_guard guard(status->update_mutex); |
| 19 | return status->touch_status; | 21 | return status->touch_status; |
| 20 | } | 22 | } |
| @@ -26,7 +28,7 @@ private: | |||
| 26 | class UDPMotionDevice final : public Input::MotionDevice { | 28 | class UDPMotionDevice final : public Input::MotionDevice { |
| 27 | public: | 29 | public: |
| 28 | explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} | 30 | explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {} |
| 29 | std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const { | 31 | std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override { |
| 30 | std::lock_guard guard(status->update_mutex); | 32 | std::lock_guard guard(status->update_mutex); |
| 31 | return status->motion_status; | 33 | return status->motion_status; |
| 32 | } | 34 | } |
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h index ea3de60bb..4f83f0441 100644 --- a/src/input_common/udp/udp.h +++ b/src/input_common/udp/udp.h | |||
| @@ -2,15 +2,13 @@ | |||
| 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 | #pragma once | ||
| 6 | |||
| 5 | #include <memory> | 7 | #include <memory> |
| 6 | #include <unordered_map> | ||
| 7 | #include "input_common/main.h" | ||
| 8 | #include "input_common/udp/client.h" | ||
| 9 | 8 | ||
| 10 | namespace InputCommon::CemuhookUDP { | 9 | namespace InputCommon::CemuhookUDP { |
| 11 | 10 | ||
| 12 | class UDPTouchDevice; | 11 | class Client; |
| 13 | class UDPMotionDevice; | ||
| 14 | 12 | ||
| 15 | class State { | 13 | class State { |
| 16 | public: | 14 | public: |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index ccfed4f2e..db9332d00 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -29,6 +29,8 @@ 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 |
| @@ -154,6 +156,7 @@ if (ENABLE_VULKAN) | |||
| 154 | renderer_vulkan/maxwell_to_vk.cpp | 156 | renderer_vulkan/maxwell_to_vk.cpp |
| 155 | renderer_vulkan/maxwell_to_vk.h | 157 | renderer_vulkan/maxwell_to_vk.h |
| 156 | renderer_vulkan/renderer_vulkan.h | 158 | renderer_vulkan/renderer_vulkan.h |
| 159 | renderer_vulkan/renderer_vulkan.cpp | ||
| 157 | renderer_vulkan/vk_blit_screen.cpp | 160 | renderer_vulkan/vk_blit_screen.cpp |
| 158 | renderer_vulkan/vk_blit_screen.h | 161 | renderer_vulkan/vk_blit_screen.h |
| 159 | renderer_vulkan/vk_buffer_cache.cpp | 162 | renderer_vulkan/vk_buffer_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..7cea146f0 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -784,4 +784,12 @@ SamplerDescriptor Maxwell3D::AccessBindlessSampler(ShaderType stage, u64 const_b | |||
| 784 | return result; | 784 | return result; |
| 785 | } | 785 | } |
| 786 | 786 | ||
| 787 | VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() { | ||
| 788 | return rasterizer.AccessGuestDriverProfile(); | ||
| 789 | } | ||
| 790 | |||
| 791 | const VideoCore::GuestDriverProfile& Maxwell3D::AccessGuestDriverProfile() const { | ||
| 792 | return rasterizer.AccessGuestDriverProfile(); | ||
| 793 | } | ||
| 794 | |||
| 787 | } // namespace Tegra::Engines | 795 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index ee79260fc..8808bbf76 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -1306,6 +1306,10 @@ public: | |||
| 1306 | return regs.tex_cb_index; | 1306 | return regs.tex_cb_index; |
| 1307 | } | 1307 | } |
| 1308 | 1308 | ||
| 1309 | VideoCore::GuestDriverProfile& AccessGuestDriverProfile() override; | ||
| 1310 | |||
| 1311 | const VideoCore::GuestDriverProfile& AccessGuestDriverProfile() const override; | ||
| 1312 | |||
| 1309 | /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than | 1313 | /// Memory for macro code - it's undetermined how big this is, however 1MB is much larger than |
| 1310 | /// we've seen used. | 1314 | /// we've seen used. |
| 1311 | using MacroMemory = std::array<u32, 0x40000>; | 1315 | using MacroMemory = std::array<u32, 0x40000>; |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index f443ec0fe..402869fde 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -624,6 +624,19 @@ enum class ShuffleOperation : u64 { | |||
| 624 | Bfly = 3, // shuffleXorNV | 624 | Bfly = 3, // shuffleXorNV |
| 625 | }; | 625 | }; |
| 626 | 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 | |||
| 627 | union Instruction { | 640 | union Instruction { |
| 628 | constexpr Instruction& operator=(const Instruction& instr) { | 641 | constexpr Instruction& operator=(const Instruction& instr) { |
| 629 | value = instr.value; | 642 | value = instr.value; |
| @@ -776,6 +789,13 @@ union Instruction { | |||
| 776 | } shr; | 789 | } shr; |
| 777 | 790 | ||
| 778 | 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 { | ||
| 779 | BitField<39, 5, u64> shift_amount; | 799 | BitField<39, 5, u64> shift_amount; |
| 780 | BitField<48, 1, u64> negate_b; | 800 | BitField<48, 1, u64> negate_b; |
| 781 | BitField<49, 1, u64> negate_a; | 801 | BitField<49, 1, u64> negate_a; |
| @@ -1124,6 +1144,11 @@ union Instruction { | |||
| 1124 | } fset; | 1144 | } fset; |
| 1125 | 1145 | ||
| 1126 | union { | 1146 | union { |
| 1147 | BitField<47, 1, u64> ftz; | ||
| 1148 | BitField<48, 4, PredCondition> cond; | ||
| 1149 | } fcmp; | ||
| 1150 | |||
| 1151 | union { | ||
| 1127 | BitField<49, 1, u64> bf; | 1152 | BitField<49, 1, u64> bf; |
| 1128 | BitField<35, 3, PredCondition> cond; | 1153 | BitField<35, 3, PredCondition> cond; |
| 1129 | BitField<50, 1, u64> ftz; | 1154 | BitField<50, 1, u64> ftz; |
| @@ -1703,6 +1728,7 @@ public: | |||
| 1703 | BFE_C, | 1728 | BFE_C, |
| 1704 | BFE_R, | 1729 | BFE_R, |
| 1705 | BFE_IMM, | 1730 | BFE_IMM, |
| 1731 | BFI_RC, | ||
| 1706 | BFI_IMM_R, | 1732 | BFI_IMM_R, |
| 1707 | BRA, | 1733 | BRA, |
| 1708 | BRX, | 1734 | BRX, |
| @@ -1800,6 +1826,7 @@ public: | |||
| 1800 | ICMP_R, | 1826 | ICMP_R, |
| 1801 | ICMP_CR, | 1827 | ICMP_CR, |
| 1802 | ICMP_IMM, | 1828 | ICMP_IMM, |
| 1829 | FCMP_R, | ||
| 1803 | MUFU, // Multi-Function Operator | 1830 | MUFU, // Multi-Function Operator |
| 1804 | RRO_C, // Range Reduction Operator | 1831 | RRO_C, // Range Reduction Operator |
| 1805 | RRO_R, | 1832 | RRO_R, |
| @@ -2104,6 +2131,7 @@ private: | |||
| 2104 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), | 2131 | INST("0101110100100---", Id::HSETP2_R, Type::HalfSetPredicate, "HSETP2_R"), |
| 2105 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), | 2132 | INST("0111111-0-------", Id::HSETP2_IMM, Type::HalfSetPredicate, "HSETP2_IMM"), |
| 2106 | 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"), | ||
| 2107 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), | 2135 | INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), |
| 2108 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), | 2136 | INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"), |
| 2109 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), | 2137 | INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"), |
| @@ -2128,6 +2156,7 @@ private: | |||
| 2128 | INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), | 2156 | INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), |
| 2129 | INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), | 2157 | INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), |
| 2130 | 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"), | ||
| 2131 | 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"), |
| 2132 | INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), | 2161 | INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"), |
| 2133 | 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..062ca83b8 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -23,7 +23,7 @@ MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); | |||
| 23 | GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async) | 23 | GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async) |
| 24 | : system{system}, renderer{renderer}, is_async{is_async} { | 24 | : system{system}, renderer{renderer}, is_async{is_async} { |
| 25 | auto& rasterizer{renderer.Rasterizer()}; | 25 | auto& rasterizer{renderer.Rasterizer()}; |
| 26 | memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); | 26 | memory_manager = std::make_unique<Tegra::MemoryManager>(system); |
| 27 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); | 27 | dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); |
| 28 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); | 28 | maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); |
| 29 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer); | 29 | fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer); |
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..f1d50be3e 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp | |||
| @@ -9,13 +9,12 @@ | |||
| 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 | 14 | ||
| 15 | namespace Tegra { | 15 | namespace Tegra { |
| 16 | 16 | ||
| 17 | MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | 17 | MemoryManager::MemoryManager(Core::System& system) : system{system} { |
| 18 | : rasterizer{rasterizer}, system{system} { | ||
| 19 | std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); | 18 | std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); |
| 20 | std::fill(page_table.attributes.begin(), page_table.attributes.end(), | 19 | std::fill(page_table.attributes.begin(), page_table.attributes.end(), |
| 21 | Common::PageType::Unmapped); | 20 | Common::PageType::Unmapped); |
| @@ -84,7 +83,8 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { | |||
| 84 | const auto cpu_addr = GpuToCpuAddress(gpu_addr); | 83 | const auto cpu_addr = GpuToCpuAddress(gpu_addr); |
| 85 | ASSERT(cpu_addr); | 84 | ASSERT(cpu_addr); |
| 86 | 85 | ||
| 87 | rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size); | 86 | system.GPU().FlushAndInvalidateRegion(cache_addr, aligned_size); |
| 87 | |||
| 88 | UnmapRange(gpu_addr, aligned_size); | 88 | UnmapRange(gpu_addr, aligned_size); |
| 89 | ASSERT(system.CurrentProcess() | 89 | ASSERT(system.CurrentProcess() |
| 90 | ->VMManager() | 90 | ->VMManager() |
| @@ -242,7 +242,7 @@ void MemoryManager::ReadBlock(GPUVAddr src_addr, void* dest_buffer, const std::s | |||
| 242 | switch (page_table.attributes[page_index]) { | 242 | switch (page_table.attributes[page_index]) { |
| 243 | case Common::PageType::Memory: { | 243 | case Common::PageType::Memory: { |
| 244 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; | 244 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; |
| 245 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); | 245 | system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount); |
| 246 | std::memcpy(dest_buffer, src_ptr, copy_amount); | 246 | std::memcpy(dest_buffer, src_ptr, copy_amount); |
| 247 | break; | 247 | break; |
| 248 | } | 248 | } |
| @@ -292,7 +292,7 @@ void MemoryManager::WriteBlock(GPUVAddr dest_addr, const void* src_buffer, const | |||
| 292 | switch (page_table.attributes[page_index]) { | 292 | switch (page_table.attributes[page_index]) { |
| 293 | case Common::PageType::Memory: { | 293 | case Common::PageType::Memory: { |
| 294 | u8* dest_ptr{page_table.pointers[page_index] + page_offset}; | 294 | u8* dest_ptr{page_table.pointers[page_index] + page_offset}; |
| 295 | rasterizer.InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount); | 295 | system.GPU().InvalidateRegion(ToCacheAddr(dest_ptr), copy_amount); |
| 296 | std::memcpy(dest_ptr, src_buffer, copy_amount); | 296 | std::memcpy(dest_ptr, src_buffer, copy_amount); |
| 297 | break; | 297 | break; |
| 298 | } | 298 | } |
| @@ -340,7 +340,7 @@ void MemoryManager::CopyBlock(GPUVAddr dest_addr, GPUVAddr src_addr, const std:: | |||
| 340 | switch (page_table.attributes[page_index]) { | 340 | switch (page_table.attributes[page_index]) { |
| 341 | case Common::PageType::Memory: { | 341 | case Common::PageType::Memory: { |
| 342 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; | 342 | const u8* src_ptr{page_table.pointers[page_index] + page_offset}; |
| 343 | rasterizer.FlushRegion(ToCacheAddr(src_ptr), copy_amount); | 343 | system.GPU().FlushRegion(ToCacheAddr(src_ptr), copy_amount); |
| 344 | WriteBlock(dest_addr, src_ptr, copy_amount); | 344 | WriteBlock(dest_addr, src_ptr, copy_amount); |
| 345 | break; | 345 | break; |
| 346 | } | 346 | } |
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index aea010087..393447eb4 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h | |||
| @@ -10,10 +10,6 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/page_table.h" | 11 | #include "common/page_table.h" |
| 12 | 12 | ||
| 13 | namespace VideoCore { | ||
| 14 | class RasterizerInterface; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace Core { | 13 | namespace Core { |
| 18 | class System; | 14 | class System; |
| 19 | } | 15 | } |
| @@ -51,7 +47,7 @@ struct VirtualMemoryArea { | |||
| 51 | 47 | ||
| 52 | class MemoryManager final { | 48 | class MemoryManager final { |
| 53 | public: | 49 | public: |
| 54 | explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer); | 50 | explicit MemoryManager(Core::System& system); |
| 55 | ~MemoryManager(); | 51 | ~MemoryManager(); |
| 56 | 52 | ||
| 57 | GPUVAddr AllocateSpace(u64 size, u64 align); | 53 | GPUVAddr AllocateSpace(u64 size, u64 align); |
| @@ -176,7 +172,6 @@ private: | |||
| 176 | 172 | ||
| 177 | Common::PageTable page_table{page_bits}; | 173 | Common::PageTable page_table{page_bits}; |
| 178 | VMAMap vma_map; | 174 | VMAMap vma_map; |
| 179 | VideoCore::RasterizerInterface& rasterizer; | ||
| 180 | 175 | ||
| 181 | Core::System& system; | 176 | Core::System& system; |
| 182 | }; | 177 | }; |
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 5b0eca9e2..c586cd6fe 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/engines/fermi_2d.h" | 10 | #include "video_core/engines/fermi_2d.h" |
| 11 | #include "video_core/gpu.h" | 11 | #include "video_core/gpu.h" |
| 12 | #include "video_core/guest_driver.h" | ||
| 12 | 13 | ||
| 13 | namespace Tegra { | 14 | namespace Tegra { |
| 14 | class MemoryManager; | 15 | class MemoryManager; |
| @@ -78,5 +79,18 @@ public: | |||
| 78 | /// Initialize disk cached resources for the game being emulated | 79 | /// Initialize disk cached resources for the game being emulated |
| 79 | virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, | 80 | virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false, |
| 80 | const DiskResourceLoadCallback& callback = {}) {} | 81 | const DiskResourceLoadCallback& callback = {}) {} |
| 82 | |||
| 83 | /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. | ||
| 84 | GuestDriverProfile& AccessGuestDriverProfile() { | ||
| 85 | return guest_driver_profile; | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver. | ||
| 89 | const GuestDriverProfile& AccessGuestDriverProfile() const { | ||
| 90 | return guest_driver_profile; | ||
| 91 | } | ||
| 92 | |||
| 93 | private: | ||
| 94 | GuestDriverProfile guest_driver_profile{}; | ||
| 81 | }; | 95 | }; |
| 82 | } // namespace VideoCore | 96 | } // namespace VideoCore |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index c428f06e4..46a7433ea 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -55,16 +55,20 @@ namespace { | |||
| 55 | 55 | ||
| 56 | template <typename Engine, typename Entry> | 56 | template <typename Engine, typename Entry> |
| 57 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, | 57 | Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry, |
| 58 | Tegra::Engines::ShaderType shader_type) { | 58 | Tegra::Engines::ShaderType shader_type, |
| 59 | std::size_t index = 0) { | ||
| 59 | if (entry.IsBindless()) { | 60 | if (entry.IsBindless()) { |
| 60 | const Tegra::Texture::TextureHandle tex_handle = | 61 | const Tegra::Texture::TextureHandle tex_handle = |
| 61 | engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset()); | 62 | engine.AccessConstBuffer32(shader_type, entry.GetBuffer(), entry.GetOffset()); |
| 62 | return engine.GetTextureInfo(tex_handle); | 63 | return engine.GetTextureInfo(tex_handle); |
| 63 | } | 64 | } |
| 65 | const auto& gpu_profile = engine.AccessGuestDriverProfile(); | ||
| 66 | const u32 offset = | ||
| 67 | entry.GetOffset() + static_cast<u32>(index * gpu_profile.GetTextureHandlerSize()); | ||
| 64 | if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { | 68 | if constexpr (std::is_same_v<Engine, Tegra::Engines::Maxwell3D>) { |
| 65 | return engine.GetStageTexture(shader_type, entry.GetOffset()); | 69 | return engine.GetStageTexture(shader_type, offset); |
| 66 | } else { | 70 | } else { |
| 67 | return engine.GetTexture(entry.GetOffset()); | 71 | return engine.GetTexture(offset); |
| 68 | } | 72 | } |
| 69 | } | 73 | } |
| 70 | 74 | ||
| @@ -244,9 +248,6 @@ void RasterizerOpenGL::SetupVertexInstances(GLuint vao) { | |||
| 244 | } | 248 | } |
| 245 | 249 | ||
| 246 | GLintptr RasterizerOpenGL::SetupIndexBuffer() { | 250 | GLintptr RasterizerOpenGL::SetupIndexBuffer() { |
| 247 | if (accelerate_draw != AccelDraw::Indexed) { | ||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | MICROPROFILE_SCOPE(OpenGL_Index); | 251 | MICROPROFILE_SCOPE(OpenGL_Index); |
| 251 | const auto& regs = system.GPU().Maxwell3D().regs; | 252 | const auto& regs = system.GPU().Maxwell3D().regs; |
| 252 | const std::size_t size = CalculateIndexBufferSize(); | 253 | const std::size_t size = CalculateIndexBufferSize(); |
| @@ -542,7 +543,8 @@ void RasterizerOpenGL::Clear() { | |||
| 542 | } | 543 | } |
| 543 | } | 544 | } |
| 544 | 545 | ||
| 545 | void RasterizerOpenGL::DrawPrelude() { | 546 | void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { |
| 547 | MICROPROFILE_SCOPE(OpenGL_Drawing); | ||
| 546 | auto& gpu = system.GPU().Maxwell3D(); | 548 | auto& gpu = system.GPU().Maxwell3D(); |
| 547 | 549 | ||
| 548 | SyncRasterizeEnable(state); | 550 | SyncRasterizeEnable(state); |
| @@ -563,9 +565,6 @@ void RasterizerOpenGL::DrawPrelude() { | |||
| 563 | 565 | ||
| 564 | buffer_cache.Acquire(); | 566 | buffer_cache.Acquire(); |
| 565 | 567 | ||
| 566 | // Draw the vertex batch | ||
| 567 | const bool is_indexed = accelerate_draw == AccelDraw::Indexed; | ||
| 568 | |||
| 569 | std::size_t buffer_size = CalculateVertexArraysSize(); | 568 | std::size_t buffer_size = CalculateVertexArraysSize(); |
| 570 | 569 | ||
| 571 | // Add space for index buffer | 570 | // Add space for index buffer |
| @@ -592,7 +591,11 @@ void RasterizerOpenGL::DrawPrelude() { | |||
| 592 | // Upload vertex and index data. | 591 | // Upload vertex and index data. |
| 593 | SetupVertexBuffer(vao); | 592 | SetupVertexBuffer(vao); |
| 594 | SetupVertexInstances(vao); | 593 | SetupVertexInstances(vao); |
| 595 | index_buffer_offset = SetupIndexBuffer(); | 594 | |
| 595 | GLintptr index_buffer_offset; | ||
| 596 | if (is_indexed) { | ||
| 597 | index_buffer_offset = SetupIndexBuffer(); | ||
| 598 | } | ||
| 596 | 599 | ||
| 597 | // Prepare packed bindings. | 600 | // Prepare packed bindings. |
| 598 | bind_ubo_pushbuffer.Setup(); | 601 | bind_ubo_pushbuffer.Setup(); |
| @@ -626,6 +629,7 @@ void RasterizerOpenGL::DrawPrelude() { | |||
| 626 | // As all cached buffers are invalidated, we need to recheck their state. | 629 | // As all cached buffers are invalidated, we need to recheck their state. |
| 627 | gpu.dirty.ResetVertexArrays(); | 630 | gpu.dirty.ResetVertexArrays(); |
| 628 | } | 631 | } |
| 632 | gpu.dirty.memory_general = false; | ||
| 629 | 633 | ||
| 630 | shader_program_manager->ApplyTo(state); | 634 | shader_program_manager->ApplyTo(state); |
| 631 | state.Apply(); | 635 | state.Apply(); |
| @@ -633,106 +637,33 @@ void RasterizerOpenGL::DrawPrelude() { | |||
| 633 | if (texture_cache.TextureBarrier()) { | 637 | if (texture_cache.TextureBarrier()) { |
| 634 | glTextureBarrier(); | 638 | glTextureBarrier(); |
| 635 | } | 639 | } |
| 636 | } | ||
| 637 | |||
| 638 | struct DrawParams { | ||
| 639 | bool is_indexed{}; | ||
| 640 | bool is_instanced{}; | ||
| 641 | GLenum primitive_mode{}; | ||
| 642 | GLint count{}; | ||
| 643 | GLint base_vertex{}; | ||
| 644 | |||
| 645 | // Indexed settings | ||
| 646 | GLenum index_format{}; | ||
| 647 | GLintptr index_buffer_offset{}; | ||
| 648 | |||
| 649 | // Instanced setting | ||
| 650 | GLint num_instances{}; | ||
| 651 | GLint base_instance{}; | ||
| 652 | |||
| 653 | void DispatchDraw() { | ||
| 654 | if (is_indexed) { | ||
| 655 | const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); | ||
| 656 | if (is_instanced) { | ||
| 657 | glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, | ||
| 658 | index_buffer_ptr, num_instances, | ||
| 659 | base_vertex, base_instance); | ||
| 660 | } else { | ||
| 661 | glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, | ||
| 662 | base_vertex); | ||
| 663 | } | ||
| 664 | } else { | ||
| 665 | if (is_instanced) { | ||
| 666 | glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances, | ||
| 667 | base_instance); | ||
| 668 | } else { | ||
| 669 | glDrawArrays(primitive_mode, base_vertex, count); | ||
| 670 | } | ||
| 671 | } | ||
| 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 | 640 | ||
| 682 | auto& maxwell3d = system.GPU().Maxwell3D(); | 641 | const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance); |
| 683 | const auto& regs = maxwell3d.regs; | 642 | const GLsizei num_instances = |
| 684 | const auto current_instance = maxwell3d.state.current_instance; | 643 | static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1); |
| 685 | DrawParams draw_call{}; | 644 | if (is_indexed) { |
| 686 | draw_call.is_indexed = is_indexed; | 645 | const GLenum index_format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format); |
| 687 | draw_call.num_instances = static_cast<GLint>(1); | 646 | const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base); |
| 688 | draw_call.base_instance = static_cast<GLint>(current_instance); | 647 | const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count); |
| 689 | draw_call.is_instanced = current_instance > 0; | 648 | glDrawElementsInstancedBaseVertexBaseInstance( |
| 690 | draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); | 649 | primitive_mode, num_vertices, index_format, |
| 691 | if (draw_call.is_indexed) { | 650 | reinterpret_cast<const void*>(index_buffer_offset), num_instances, base_vertex, |
| 692 | draw_call.count = static_cast<GLint>(regs.index_array.count); | 651 | base_instance); |
| 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 { | 652 | } else { |
| 697 | draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); | 653 | const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first); |
| 698 | draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); | 654 | const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count); |
| 655 | glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, num_instances, | ||
| 656 | base_instance); | ||
| 699 | } | 657 | } |
| 700 | draw_call.DispatchDraw(); | 658 | } |
| 701 | 659 | ||
| 702 | maxwell3d.dirty.memory_general = false; | 660 | bool RasterizerOpenGL::DrawBatch(bool is_indexed) { |
| 703 | accelerate_draw = AccelDraw::Disabled; | 661 | Draw(is_indexed, false); |
| 704 | return true; | 662 | return true; |
| 705 | } | 663 | } |
| 706 | 664 | ||
| 707 | bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) { | 665 | bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) { |
| 708 | accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; | 666 | Draw(is_indexed, true); |
| 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 | } | ||
| 732 | draw_call.DispatchDraw(); | ||
| 733 | |||
| 734 | maxwell3d.dirty.memory_general = false; | ||
| 735 | accelerate_draw = AccelDraw::Disabled; | ||
| 736 | return true; | 667 | return true; |
| 737 | } | 668 | } |
| 738 | 669 | ||
| @@ -942,8 +873,15 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, const Shader& | |||
| 942 | u32 binding = device.GetBaseBindings(stage_index).sampler; | 873 | u32 binding = device.GetBaseBindings(stage_index).sampler; |
| 943 | for (const auto& entry : shader->GetShaderEntries().samplers) { | 874 | for (const auto& entry : shader->GetShaderEntries().samplers) { |
| 944 | const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); | 875 | const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index); |
| 945 | const auto texture = GetTextureInfo(maxwell3d, entry, shader_type); | 876 | if (!entry.IsIndexed()) { |
| 946 | SetupTexture(binding++, texture, entry); | 877 | const auto texture = GetTextureInfo(maxwell3d, entry, shader_type); |
| 878 | SetupTexture(binding++, texture, entry); | ||
| 879 | } else { | ||
| 880 | for (std::size_t i = 0; i < entry.Size(); ++i) { | ||
| 881 | const auto texture = GetTextureInfo(maxwell3d, entry, shader_type, i); | ||
| 882 | SetupTexture(binding++, texture, entry); | ||
| 883 | } | ||
| 884 | } | ||
| 947 | } | 885 | } |
| 948 | } | 886 | } |
| 949 | 887 | ||
| @@ -952,8 +890,17 @@ void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) { | |||
| 952 | const auto& compute = system.GPU().KeplerCompute(); | 890 | const auto& compute = system.GPU().KeplerCompute(); |
| 953 | u32 binding = 0; | 891 | u32 binding = 0; |
| 954 | for (const auto& entry : kernel->GetShaderEntries().samplers) { | 892 | for (const auto& entry : kernel->GetShaderEntries().samplers) { |
| 955 | const auto texture = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute); | 893 | if (!entry.IsIndexed()) { |
| 956 | SetupTexture(binding++, texture, entry); | 894 | const auto texture = |
| 895 | GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute); | ||
| 896 | SetupTexture(binding++, texture, entry); | ||
| 897 | } else { | ||
| 898 | for (std::size_t i = 0; i < entry.Size(); ++i) { | ||
| 899 | const auto texture = | ||
| 900 | GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute, i); | ||
| 901 | SetupTexture(binding++, texture, entry); | ||
| 902 | } | ||
| 903 | } | ||
| 957 | } | 904 | } |
| 958 | } | 905 | } |
| 959 | 906 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 6a27cf497..0501f3828 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -103,7 +103,7 @@ private: | |||
| 103 | std::size_t size); | 103 | std::size_t size); |
| 104 | 104 | ||
| 105 | /// Syncs all the state, shaders, render targets and textures setting before a draw call. | 105 | /// Syncs all the state, shaders, render targets and textures setting before a draw call. |
| 106 | void DrawPrelude(); | 106 | void Draw(bool is_indexed, bool is_instanced); |
| 107 | 107 | ||
| 108 | /// Configures the current textures to use for the draw command. | 108 | /// Configures the current textures to use for the draw command. |
| 109 | void SetupDrawTextures(std::size_t stage_index, const Shader& shader); | 109 | void SetupDrawTextures(std::size_t stage_index, const Shader& shader); |
| @@ -220,12 +220,7 @@ private: | |||
| 220 | 220 | ||
| 221 | GLintptr SetupIndexBuffer(); | 221 | GLintptr SetupIndexBuffer(); |
| 222 | 222 | ||
| 223 | GLintptr index_buffer_offset; | ||
| 224 | |||
| 225 | void SetupShaders(GLenum primitive_mode); | 223 | void SetupShaders(GLenum primitive_mode); |
| 226 | |||
| 227 | enum class AccelDraw { Disabled, Arrays, Indexed }; | ||
| 228 | AccelDraw accelerate_draw = AccelDraw::Disabled; | ||
| 229 | }; | 224 | }; |
| 230 | 225 | ||
| 231 | } // namespace OpenGL | 226 | } // 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 a1ac3d7a9..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) { |
| @@ -1098,7 +1119,11 @@ private: | |||
| 1098 | } else if (!meta->ptp.empty()) { | 1119 | } else if (!meta->ptp.empty()) { |
| 1099 | expr += "Offsets"; | 1120 | expr += "Offsets"; |
| 1100 | } | 1121 | } |
| 1101 | 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 | } | ||
| 1102 | expr += coord_constructors.at(count + (has_array ? 1 : 0) + | 1127 | expr += coord_constructors.at(count + (has_array ? 1 : 0) + |
| 1103 | (has_shadow && !separate_dc ? 1 : 0) - 1); | 1128 | (has_shadow && !separate_dc ? 1 : 0) - 1); |
| 1104 | expr += '('; | 1129 | expr += '('; |
| @@ -1310,6 +1335,8 @@ private: | |||
| 1310 | const std::string final_offset = fmt::format("({} - {}) >> 2", real, base); | 1335 | const std::string final_offset = fmt::format("({} - {}) >> 2", real, base); |
| 1311 | target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset), | 1336 | target = {fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset), |
| 1312 | Type::Uint}; | 1337 | Type::Uint}; |
| 1338 | } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) { | ||
| 1339 | target = {GetCustomVariable(cv->GetIndex()), Type::Float}; | ||
| 1313 | } else { | 1340 | } else { |
| 1314 | UNREACHABLE_MSG("Assign called without a proper target"); | 1341 | UNREACHABLE_MSG("Assign called without a proper target"); |
| 1315 | } | 1342 | } |
| @@ -2237,6 +2264,10 @@ private: | |||
| 2237 | return GetDeclarationWithSuffix(index, "gpr"); | 2264 | return GetDeclarationWithSuffix(index, "gpr"); |
| 2238 | } | 2265 | } |
| 2239 | 2266 | ||
| 2267 | std::string GetCustomVariable(u32 index) const { | ||
| 2268 | return GetDeclarationWithSuffix(index, "custom_var"); | ||
| 2269 | } | ||
| 2270 | |||
| 2240 | std::string GetPredicate(Tegra::Shader::Pred pred) const { | 2271 | std::string GetPredicate(Tegra::Shader::Pred pred) const { |
| 2241 | return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); | 2272 | return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); |
| 2242 | } | 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/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/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..9840f26e5 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -400,8 +400,10 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 400 | VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); | 400 | VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); |
| 401 | Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, | 401 | Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, |
| 402 | false); | 402 | false); |
| 403 | Test(extension, nv_device_diagnostic_checkpoints, | 403 | if (Settings::values.renderer_debug) { |
| 404 | VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); | 404 | Test(extension, nv_device_diagnostic_checkpoints, |
| 405 | VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME, true); | ||
| 406 | } | ||
| 405 | } | 407 | } |
| 406 | 408 | ||
| 407 | if (khr_shader_float16_int8) { | 409 | if (khr_shader_float16_int8) { |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d2c6b1189..aada38702 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -571,7 +571,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | |||
| 571 | color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); | 571 | color_attachments[rt] = texture_cache.GetColorBufferSurface(rt, true); |
| 572 | } | 572 | } |
| 573 | if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { | 573 | if (color_attachments[rt] && WalkAttachmentOverlaps(*color_attachments[rt])) { |
| 574 | texceptions.set(rt); | 574 | texceptions[rt] = true; |
| 575 | } | 575 | } |
| 576 | } | 576 | } |
| 577 | 577 | ||
| @@ -579,7 +579,7 @@ RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() { | |||
| 579 | zeta_attachment = texture_cache.GetDepthBufferSurface(true); | 579 | zeta_attachment = texture_cache.GetDepthBufferSurface(true); |
| 580 | } | 580 | } |
| 581 | if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { | 581 | if (zeta_attachment && WalkAttachmentOverlaps(*zeta_attachment)) { |
| 582 | texceptions.set(ZETA_TEXCEPTION_INDEX); | 582 | texceptions[ZETA_TEXCEPTION_INDEX] = true; |
| 583 | } | 583 | } |
| 584 | 584 | ||
| 585 | texture_cache.GuardRenderTargets(false); | 585 | texture_cache.GuardRenderTargets(false); |
| @@ -1122,11 +1122,12 @@ RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions) | |||
| 1122 | 1122 | ||
| 1123 | for (std::size_t rt = 0; rt < static_cast<std::size_t>(regs.rt_control.count); ++rt) { | 1123 | for (std::size_t rt = 0; rt < static_cast<std::size_t>(regs.rt_control.count); ++rt) { |
| 1124 | const auto& rendertarget = regs.rt[rt]; | 1124 | const auto& rendertarget = regs.rt[rt]; |
| 1125 | if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) | 1125 | if (rendertarget.Address() == 0 || rendertarget.format == Tegra::RenderTargetFormat::NONE) { |
| 1126 | continue; | 1126 | continue; |
| 1127 | } | ||
| 1127 | renderpass_params.color_attachments.push_back(RenderPassParams::ColorAttachment{ | 1128 | renderpass_params.color_attachments.push_back(RenderPassParams::ColorAttachment{ |
| 1128 | static_cast<u32>(rt), PixelFormatFromRenderTargetFormat(rendertarget.format), | 1129 | static_cast<u32>(rt), PixelFormatFromRenderTargetFormat(rendertarget.format), |
| 1129 | texceptions.test(rt)}); | 1130 | texceptions[rt]}); |
| 1130 | } | 1131 | } |
| 1131 | 1132 | ||
| 1132 | renderpass_params.has_zeta = regs.zeta_enable; | 1133 | renderpass_params.has_zeta = regs.zeta_enable; |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 1ab22251e..24a658dce 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -353,6 +353,7 @@ private: | |||
| 353 | DeclareFragment(); | 353 | DeclareFragment(); |
| 354 | DeclareCompute(); | 354 | DeclareCompute(); |
| 355 | DeclareRegisters(); | 355 | DeclareRegisters(); |
| 356 | DeclareCustomVariables(); | ||
| 356 | DeclarePredicates(); | 357 | DeclarePredicates(); |
| 357 | DeclareLocalMemory(); | 358 | DeclareLocalMemory(); |
| 358 | DeclareSharedMemory(); | 359 | DeclareSharedMemory(); |
| @@ -586,6 +587,15 @@ private: | |||
| 586 | } | 587 | } |
| 587 | } | 588 | } |
| 588 | 589 | ||
| 590 | void DeclareCustomVariables() { | ||
| 591 | const u32 num_custom_variables = ir.GetNumCustomVariables(); | ||
| 592 | for (u32 i = 0; i < num_custom_variables; ++i) { | ||
| 593 | const Id id = OpVariable(t_prv_float, spv::StorageClass::Private, v_float_zero); | ||
| 594 | Name(id, fmt::format("custom_var_{}", i)); | ||
| 595 | custom_variables.emplace(i, AddGlobalVariable(id)); | ||
| 596 | } | ||
| 597 | } | ||
| 598 | |||
| 589 | void DeclarePredicates() { | 599 | void DeclarePredicates() { |
| 590 | for (const auto pred : ir.GetPredicates()) { | 600 | for (const auto pred : ir.GetPredicates()) { |
| 591 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); | 601 | const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false); |
| @@ -982,6 +992,11 @@ private: | |||
| 982 | return {OpLoad(t_float, registers.at(index)), Type::Float}; | 992 | return {OpLoad(t_float, registers.at(index)), Type::Float}; |
| 983 | } | 993 | } |
| 984 | 994 | ||
| 995 | if (const auto cv = std::get_if<CustomVarNode>(&*node)) { | ||
| 996 | const u32 index = cv->GetIndex(); | ||
| 997 | return {OpLoad(t_float, custom_variables.at(index)), Type::Float}; | ||
| 998 | } | ||
| 999 | |||
| 985 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { | 1000 | if (const auto immediate = std::get_if<ImmediateNode>(&*node)) { |
| 986 | return {Constant(t_uint, immediate->GetValue()), Type::Uint}; | 1001 | return {Constant(t_uint, immediate->GetValue()), Type::Uint}; |
| 987 | } | 1002 | } |
| @@ -1333,6 +1348,9 @@ private: | |||
| 1333 | } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { | 1348 | } else if (const auto gmem = std::get_if<GmemNode>(&*dest)) { |
| 1334 | target = {GetGlobalMemoryPointer(*gmem), Type::Uint}; | 1349 | target = {GetGlobalMemoryPointer(*gmem), Type::Uint}; |
| 1335 | 1350 | ||
| 1351 | } else if (const auto cv = std::get_if<CustomVarNode>(&*dest)) { | ||
| 1352 | target = {custom_variables.at(cv->GetIndex()), Type::Float}; | ||
| 1353 | |||
| 1336 | } else { | 1354 | } else { |
| 1337 | UNIMPLEMENTED(); | 1355 | UNIMPLEMENTED(); |
| 1338 | } | 1356 | } |
| @@ -2508,6 +2526,7 @@ private: | |||
| 2508 | Id out_vertex{}; | 2526 | Id out_vertex{}; |
| 2509 | Id in_vertex{}; | 2527 | Id in_vertex{}; |
| 2510 | std::map<u32, Id> registers; | 2528 | std::map<u32, Id> registers; |
| 2529 | std::map<u32, Id> custom_variables; | ||
| 2511 | std::map<Tegra::Shader::Pred, Id> predicates; | 2530 | std::map<Tegra::Shader::Pred, Id> predicates; |
| 2512 | std::map<u32, Id> flow_variables; | 2531 | std::map<u32, Id> flow_variables; |
| 2513 | Id local_memory{}; | 2532 | Id local_memory{}; |
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..e60875cc4 100644 --- a/src/video_core/shader/decode/arithmetic_integer.cpp +++ b/src/video_core/shader/decode/arithmetic_integer.cpp | |||
| @@ -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..f992bbe2a 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.offset)}; | ||
| 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/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 0b567e39d..351c8c2f1 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 | ||
| @@ -167,9 +168,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 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 = | 172 | const Sampler* sampler = |
| 172 | is_bindless ? GetBindlessSampler(base_reg, {{texture_type, is_array, false}}) | 173 | is_bindless ? GetBindlessSampler(base_reg, index_var, {{texture_type, is_array, false}}) |
| 173 | : GetSampler(instr.sampler, {{texture_type, is_array, false}}); | 174 | : GetSampler(instr.sampler, {{texture_type, is_array, false}}); |
| 174 | Node4 values; | 175 | Node4 values; |
| 175 | if (sampler == nullptr) { | 176 | if (sampler == nullptr) { |
| @@ -200,7 +201,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 200 | } | 201 | } |
| 201 | 202 | ||
| 202 | for (u32 element = 0; element < values.size(); ++element) { | 203 | for (u32 element = 0; element < values.size(); ++element) { |
| 203 | MetaTexture meta{*sampler, array_node, {}, {}, {}, derivates, {}, {}, {}, element}; | 204 | MetaTexture meta{*sampler, array_node, {}, {}, {}, derivates, |
| 205 | {}, {}, {}, element, index_var}; | ||
| 204 | values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords); | 206 | values[element] = Operation(OperationCode::TextureGradient, std::move(meta), coords); |
| 205 | } | 207 | } |
| 206 | 208 | ||
| @@ -215,8 +217,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 215 | // 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. |
| 216 | // 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 |
| 217 | // uses. This must be fixed at a later instance. | 219 | // uses. This must be fixed at a later instance. |
| 220 | Node index_var{}; | ||
| 218 | const Sampler* sampler = | 221 | const Sampler* sampler = |
| 219 | is_bindless ? GetBindlessSampler(instr.gpr8) : GetSampler(instr.sampler); | 222 | is_bindless ? GetBindlessSampler(instr.gpr8, index_var) : GetSampler(instr.sampler); |
| 220 | 223 | ||
| 221 | if (sampler == nullptr) { | 224 | if (sampler == nullptr) { |
| 222 | u32 indexer = 0; | 225 | u32 indexer = 0; |
| @@ -240,7 +243,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 240 | if (!instr.txq.IsComponentEnabled(element)) { | 243 | if (!instr.txq.IsComponentEnabled(element)) { |
| 241 | continue; | 244 | continue; |
| 242 | } | 245 | } |
| 243 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element}; | 246 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var}; |
| 244 | const Node value = | 247 | const Node value = |
| 245 | Operation(OperationCode::TextureQueryDimensions, meta, | 248 | Operation(OperationCode::TextureQueryDimensions, meta, |
| 246 | GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0))); | 249 | GetRegister(instr.gpr8.Value() + (is_bindless ? 1 : 0))); |
| @@ -266,8 +269,9 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 266 | 269 | ||
| 267 | auto texture_type = instr.tmml.texture_type.Value(); | 270 | auto texture_type = instr.tmml.texture_type.Value(); |
| 268 | const bool is_array = instr.tmml.array != 0; | 271 | const bool is_array = instr.tmml.array != 0; |
| 272 | Node index_var{}; | ||
| 269 | const Sampler* sampler = | 273 | const Sampler* sampler = |
| 270 | is_bindless ? GetBindlessSampler(instr.gpr20) : GetSampler(instr.sampler); | 274 | is_bindless ? GetBindlessSampler(instr.gpr20, index_var) : GetSampler(instr.sampler); |
| 271 | 275 | ||
| 272 | if (sampler == nullptr) { | 276 | if (sampler == nullptr) { |
| 273 | u32 indexer = 0; | 277 | u32 indexer = 0; |
| @@ -309,7 +313,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) { | |||
| 309 | continue; | 313 | continue; |
| 310 | } | 314 | } |
| 311 | auto params = coords; | 315 | auto params = coords; |
| 312 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element}; | 316 | MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var}; |
| 313 | const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); | 317 | const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params)); |
| 314 | SetTemporary(bb, indexer++, value); | 318 | SetTemporary(bb, indexer++, value); |
| 315 | } | 319 | } |
| @@ -383,37 +387,65 @@ const Sampler* ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, | |||
| 383 | // Otherwise create a new mapping for this sampler | 387 | // Otherwise create a new mapping for this sampler |
| 384 | const auto next_index = static_cast<u32>(used_samplers.size()); | 388 | const auto next_index = static_cast<u32>(used_samplers.size()); |
| 385 | 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, |
| 386 | info.is_buffer); | 390 | info.is_buffer, false); |
| 387 | } | 391 | } |
| 388 | 392 | ||
| 389 | const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, | 393 | const Sampler* ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var, |
| 390 | std::optional<SamplerInfo> sampler_info) { | 394 | std::optional<SamplerInfo> sampler_info) { |
| 391 | const Node sampler_register = GetRegister(reg); | 395 | const Node sampler_register = GetRegister(reg); |
| 392 | const auto [base_sampler, buffer, offset] = | 396 | const auto [base_node, tracked_sampler_info] = |
| 393 | TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size())); | 397 | TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size())); |
| 394 | ASSERT(base_sampler != nullptr); | 398 | ASSERT(base_node != nullptr); |
| 395 | if (base_sampler == nullptr) { | 399 | if (base_node == nullptr) { |
| 396 | return nullptr; | 400 | return nullptr; |
| 397 | } | 401 | } |
| 398 | 402 | ||
| 399 | 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 | } | ||
| 400 | 420 | ||
| 401 | // If this sampler has already been used, return the existing mapping. | 421 | // Otherwise create a new mapping for this sampler |
| 402 | const auto it = | 422 | const auto next_index = static_cast<u32>(used_samplers.size()); |
| 403 | std::find_if(used_samplers.begin(), used_samplers.end(), | 423 | return &used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array, |
| 404 | [buffer = buffer, offset = offset](const Sampler& entry) { | 424 | info.is_shadow, info.is_buffer, false); |
| 405 | return entry.GetBuffer() == buffer && entry.GetOffset() == offset; | 425 | } else if (const auto array_sampler_info = |
| 406 | }); | 426 | std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) { |
| 407 | if (it != used_samplers.end()) { | 427 | const u32 base_offset = array_sampler_info->GetBaseOffset() / 4; |
| 408 | ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array && | 428 | index_var = GetCustomVariable(array_sampler_info->GetIndexVar()); |
| 409 | it->IsShadow() == info.is_shadow); | 429 | const auto info = GetSamplerInfo(sampler_info, base_offset); |
| 410 | return &*it; | 430 | |
| 411 | } | 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 | } | ||
| 412 | 441 | ||
| 413 | // Otherwise create a new mapping for this sampler | 442 | uses_indexed_samplers = true; |
| 414 | const auto next_index = static_cast<u32>(used_samplers.size()); | 443 | // Otherwise create a new mapping for this sampler |
| 415 | 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()); |
| 416 | 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; | ||
| 417 | } | 449 | } |
| 418 | 450 | ||
| 419 | void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { | 451 | void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { |
| @@ -499,8 +531,9 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, | |||
| 499 | "This method is not supported."); | 531 | "This method is not supported."); |
| 500 | 532 | ||
| 501 | const SamplerInfo info{texture_type, is_array, is_shadow, false}; | 533 | const SamplerInfo info{texture_type, is_array, is_shadow, false}; |
| 502 | const Sampler* sampler = | 534 | Node index_var{}; |
| 503 | is_bindless ? GetBindlessSampler(*bindless_reg, info) : GetSampler(instr.sampler, info); | 535 | const Sampler* sampler = is_bindless ? GetBindlessSampler(*bindless_reg, index_var, info) |
| 536 | : GetSampler(instr.sampler, info); | ||
| 504 | Node4 values; | 537 | Node4 values; |
| 505 | if (sampler == nullptr) { | 538 | if (sampler == nullptr) { |
| 506 | for (u32 element = 0; element < values.size(); ++element) { | 539 | for (u32 element = 0; element < values.size(); ++element) { |
| @@ -548,7 +581,8 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type, | |||
| 548 | 581 | ||
| 549 | for (u32 element = 0; element < values.size(); ++element) { | 582 | for (u32 element = 0; element < values.size(); ++element) { |
| 550 | auto copy_coords = coords; | 583 | auto copy_coords = coords; |
| 551 | MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, lod, {}, element}; | 584 | MetaTexture meta{*sampler, array, depth_compare, aoffi, {}, {}, bias, |
| 585 | lod, {}, element, index_var}; | ||
| 552 | values[element] = Operation(read_method, meta, std::move(copy_coords)); | 586 | values[element] = Operation(read_method, meta, std::move(copy_coords)); |
| 553 | } | 587 | } |
| 554 | 588 | ||
| @@ -596,7 +630,7 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type, | |||
| 596 | aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false); | 630 | aoffi = GetAoffiCoordinates(GetRegister(parameter_register++), coord_count, false); |
| 597 | } | 631 | } |
| 598 | 632 | ||
| 599 | Node dc{}; | 633 | Node dc; |
| 600 | if (depth_compare) { | 634 | if (depth_compare) { |
| 601 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod | 635 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod |
| 602 | // or bias are used | 636 | // or bias are used |
| @@ -632,7 +666,7 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type, | |||
| 632 | 666 | ||
| 633 | const Node array = is_array ? GetRegister(array_register) : nullptr; | 667 | const Node array = is_array ? GetRegister(array_register) : nullptr; |
| 634 | 668 | ||
| 635 | Node dc{}; | 669 | Node dc; |
| 636 | if (depth_compare) { | 670 | if (depth_compare) { |
| 637 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod | 671 | // Depth is always stored in the register signaled by gpr20 or in the next register if lod |
| 638 | // or bias are used | 672 | // or bias are used |
| @@ -663,7 +697,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de | |||
| 663 | u64 parameter_register = instr.gpr20.Value(); | 697 | u64 parameter_register = instr.gpr20.Value(); |
| 664 | 698 | ||
| 665 | const SamplerInfo info{texture_type, is_array, depth_compare, false}; | 699 | const SamplerInfo info{texture_type, is_array, depth_compare, false}; |
| 666 | const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, info) | 700 | Node index_var{}; |
| 701 | const Sampler* sampler = is_bindless ? GetBindlessSampler(parameter_register++, index_var, info) | ||
| 667 | : GetSampler(instr.sampler, info); | 702 | : GetSampler(instr.sampler, info); |
| 668 | Node4 values; | 703 | Node4 values; |
| 669 | if (sampler == nullptr) { | 704 | if (sampler == nullptr) { |
| @@ -692,7 +727,8 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de | |||
| 692 | for (u32 element = 0; element < values.size(); ++element) { | 727 | for (u32 element = 0; element < values.size(); ++element) { |
| 693 | auto coords_copy = coords; | 728 | auto coords_copy = coords; |
| 694 | MetaTexture meta{ | 729 | MetaTexture meta{ |
| 695 | *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element}; | 730 | *sampler, GetRegister(array_register), dc, aoffi, ptp, {}, {}, {}, component, element, |
| 731 | index_var}; | ||
| 696 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); | 732 | values[element] = Operation(OperationCode::TextureGather, meta, std::move(coords_copy)); |
| 697 | } | 733 | } |
| 698 | 734 | ||
| @@ -725,7 +761,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) { | |||
| 725 | Node4 values; | 761 | Node4 values; |
| 726 | for (u32 element = 0; element < values.size(); ++element) { | 762 | for (u32 element = 0; element < values.size(); ++element) { |
| 727 | auto coords_copy = coords; | 763 | auto coords_copy = coords; |
| 728 | MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element}; | 764 | MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element, {}}; |
| 729 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); | 765 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); |
| 730 | } | 766 | } |
| 731 | 767 | ||
| @@ -775,7 +811,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is | |||
| 775 | Node4 values; | 811 | Node4 values; |
| 776 | for (u32 element = 0; element < values.size(); ++element) { | 812 | for (u32 element = 0; element < values.size(); ++element) { |
| 777 | auto coords_copy = coords; | 813 | auto coords_copy = coords; |
| 778 | MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element}; | 814 | MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element, {}}; |
| 779 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); | 815 | values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); |
| 780 | } | 816 | } |
| 781 | return values; | 817 | return values; |
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index 9af1f0228..a0a7b9111 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h | |||
| @@ -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/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/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index a3fb91d29..b841e63fa 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -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 c4a07935a..cd94693c1 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -624,6 +624,10 @@ void Config::ReadPathValues() { | |||
| 624 | void Config::ReadRendererValues() { | 624 | void Config::ReadRendererValues() { |
| 625 | qt_config->beginGroup(QStringLiteral("Renderer")); | 625 | qt_config->beginGroup(QStringLiteral("Renderer")); |
| 626 | 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(); | ||
| 627 | Settings::values.resolution_factor = | 631 | Settings::values.resolution_factor = |
| 628 | ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); | 632 | ReadSetting(QStringLiteral("resolution_factor"), 1.0).toFloat(); |
| 629 | Settings::values.use_frame_limit = | 633 | Settings::values.use_frame_limit = |
| @@ -1055,6 +1059,9 @@ void Config::SavePathValues() { | |||
| 1055 | void Config::SaveRendererValues() { | 1059 | void Config::SaveRendererValues() { |
| 1056 | qt_config->beginGroup(QStringLiteral("Renderer")); | 1060 | qt_config->beginGroup(QStringLiteral("Renderer")); |
| 1057 | 1061 | ||
| 1062 | WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0); | ||
| 1063 | WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); | ||
| 1064 | WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); | ||
| 1058 | WriteSetting(QStringLiteral("resolution_factor"), | 1065 | WriteSetting(QStringLiteral("resolution_factor"), |
| 1059 | static_cast<double>(Settings::values.resolution_factor), 1.0); | 1066 | static_cast<double>(Settings::values.resolution_factor), 1.0); |
| 1060 | WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); | 1067 | WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); |
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_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 2c9e322c9..f57a24e36 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,11 +79,22 @@ 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))); |
| 74 | ui->use_disk_shader_cache->setEnabled(runtime_lock); | 100 | ui->use_disk_shader_cache->setEnabled(runtime_lock); |
| @@ -80,9 +106,12 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 80 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); | 106 | ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode); |
| 81 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, | 107 | UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, |
| 82 | Settings::values.bg_blue)); | 108 | Settings::values.bg_blue)); |
| 109 | UpdateDeviceComboBox(); | ||
| 83 | } | 110 | } |
| 84 | 111 | ||
| 85 | void ConfigureGraphics::ApplyConfiguration() { | 112 | void ConfigureGraphics::ApplyConfiguration() { |
| 113 | Settings::values.renderer_backend = GetCurrentGraphicsBackend(); | ||
| 114 | Settings::values.vulkan_device = vulkan_device; | ||
| 86 | Settings::values.resolution_factor = | 115 | Settings::values.resolution_factor = |
| 87 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); | 116 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); |
| 88 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); | 117 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); |
| @@ -116,3 +145,68 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) { | |||
| 116 | const QIcon color_icon(pixmap); | 145 | const QIcon color_icon(pixmap); |
| 117 | ui->bg_button->setIcon(color_icon); | 146 | ui->bg_button->setIcon(color_icon); |
| 118 | } | 147 | } |
| 148 | |||
| 149 | void ConfigureGraphics::UpdateDeviceComboBox() { | ||
| 150 | ui->device->clear(); | ||
| 151 | |||
| 152 | bool enabled = false; | ||
| 153 | switch (GetCurrentGraphicsBackend()) { | ||
| 154 | case Settings::RendererBackend::OpenGL: | ||
| 155 | ui->device->addItem(tr("OpenGL Graphics Device")); | ||
| 156 | enabled = false; | ||
| 157 | break; | ||
| 158 | case Settings::RendererBackend::Vulkan: | ||
| 159 | for (const auto device : vulkan_devices) { | ||
| 160 | ui->device->addItem(device); | ||
| 161 | } | ||
| 162 | ui->device->setCurrentIndex(vulkan_device); | ||
| 163 | enabled = !vulkan_devices.empty(); | ||
| 164 | break; | ||
| 165 | } | ||
| 166 | ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn()); | ||
| 167 | } | ||
| 168 | |||
| 169 | void ConfigureGraphics::RetrieveVulkanDevices() { | ||
| 170 | #ifdef HAS_VULKAN | ||
| 171 | QVulkanInstance instance; | ||
| 172 | instance.setApiVersion(QVersionNumber(1, 1, 0)); | ||
| 173 | if (!instance.create()) { | ||
| 174 | LOG_INFO(Frontend, "Vulkan 1.1 not available"); | ||
| 175 | return; | ||
| 176 | } | ||
| 177 | const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>( | ||
| 178 | instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))}; | ||
| 179 | if (vkEnumeratePhysicalDevices == nullptr) { | ||
| 180 | LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices"); | ||
| 181 | return; | ||
| 182 | } | ||
| 183 | u32 physical_device_count; | ||
| 184 | if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) != | ||
| 185 | VK_SUCCESS) { | ||
| 186 | LOG_INFO(Frontend, "Failed to get physical devices count"); | ||
| 187 | return; | ||
| 188 | } | ||
| 189 | std::vector<VkPhysicalDevice> physical_devices(physical_device_count); | ||
| 190 | if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, | ||
| 191 | physical_devices.data()) != VK_SUCCESS) { | ||
| 192 | LOG_INFO(Frontend, "Failed to get physical devices"); | ||
| 193 | return; | ||
| 194 | } | ||
| 195 | |||
| 196 | const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>( | ||
| 197 | instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))}; | ||
| 198 | if (vkGetPhysicalDeviceProperties == nullptr) { | ||
| 199 | LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties"); | ||
| 200 | return; | ||
| 201 | } | ||
| 202 | for (const auto physical_device : physical_devices) { | ||
| 203 | VkPhysicalDeviceProperties properties; | ||
| 204 | vkGetPhysicalDeviceProperties(physical_device, &properties); | ||
| 205 | vulkan_devices.push_back(QString::fromUtf8(properties.deviceName)); | ||
| 206 | } | ||
| 207 | #endif | ||
| 208 | } | ||
| 209 | |||
| 210 | Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { | ||
| 211 | return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); | ||
| 212 | } | ||
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..e24372204 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> |
| @@ -91,7 +139,7 @@ | |||
| 91 | </layout> | 139 | </layout> |
| 92 | </item> | 140 | </item> |
| 93 | <item> | 141 | <item> |
| 94 | <layout class="QHBoxLayout" name="horizontalLayout_6"> | 142 | <layout class="QHBoxLayout" name="horizontalLayout_3"> |
| 95 | <item> | 143 | <item> |
| 96 | <widget class="QLabel" name="bg_label"> | 144 | <widget class="QLabel" name="bg_label"> |
| 97 | <property name="text"> | 145 | <property name="text"> |
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/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_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 161583b54..b01a36023 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -371,6 +371,12 @@ void Config::ReadValues() { | |||
| 371 | 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); |
| 372 | 372 | ||
| 373 | // 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 | |||
| 374 | Settings::values.resolution_factor = | 380 | Settings::values.resolution_factor = |
| 375 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); | 381 | static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0)); |
| 376 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); | 382 | Settings::values.use_frame_limit = sdl2_config->GetBoolean("Renderer", "use_frame_limit", true); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index e829f8695..00fd88279 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -98,6 +98,17 @@ udp_pad_index= | |||
| 98 | use_multi_core= | 98 | use_multi_core= |
| 99 | 99 | ||
| 100 | [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 | |||
| 101 | # Whether to use software or hardware rendering. | 112 | # Whether to use software or hardware rendering. |
| 102 | # 0: Software, 1 (default): Hardware | 113 | # 0: Software, 1 (default): Hardware |
| 103 | use_hw_renderer = | 114 | use_hw_renderer = |
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/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 | ||