summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/settings_common.cpp2
-rw-r--r--src/common/settings_enums.h16
-rw-r--r--src/common/settings_setting.h10
-rw-r--r--src/core/hle/service/ssl/ssl_backend_securetransport.cpp2
-rw-r--r--src/core/memory.h125
-rw-r--r--src/video_core/engines/maxwell_dma.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp179
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h9
-rw-r--r--src/yuzu/game_list.cpp8
9 files changed, 202 insertions, 150 deletions
diff --git a/src/common/settings_common.cpp b/src/common/settings_common.cpp
index dedf5ef90..137b65d5f 100644
--- a/src/common/settings_common.cpp
+++ b/src/common/settings_common.cpp
@@ -1,7 +1,9 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <functional>
4#include <string> 5#include <string>
6#include <vector>
5#include "common/settings_common.h" 7#include "common/settings_common.h"
6 8
7namespace Settings { 9namespace Settings {
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index a1a29ebf6..e7cb59ea5 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -12,8 +12,8 @@ namespace Settings {
12 12
13template <typename T> 13template <typename T>
14struct EnumMetadata { 14struct EnumMetadata {
15 static constexpr std::vector<std::pair<std::string, T>> Canonicalizations(); 15 static std::vector<std::pair<std::string, T>> Canonicalizations();
16 static constexpr u32 Index(); 16 static u32 Index();
17}; 17};
18 18
19#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__)) 19#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
@@ -66,11 +66,11 @@ struct EnumMetadata {
66#define ENUM(NAME, ...) \ 66#define ENUM(NAME, ...) \
67 enum class NAME : u32 { __VA_ARGS__ }; \ 67 enum class NAME : u32 { __VA_ARGS__ }; \
68 template <> \ 68 template <> \
69 constexpr std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \ 69 inline std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \
70 return {PAIR(NAME, __VA_ARGS__)}; \ 70 return {PAIR(NAME, __VA_ARGS__)}; \
71 } \ 71 } \
72 template <> \ 72 template <> \
73 constexpr u32 EnumMetadata<NAME>::Index() { \ 73 inline u32 EnumMetadata<NAME>::Index() { \
74 return __COUNTER__; \ 74 return __COUNTER__; \
75 } 75 }
76 76
@@ -85,7 +85,7 @@ enum class AudioEngine : u32 {
85}; 85};
86 86
87template <> 87template <>
88constexpr std::vector<std::pair<std::string, AudioEngine>> 88inline std::vector<std::pair<std::string, AudioEngine>>
89EnumMetadata<AudioEngine>::Canonicalizations() { 89EnumMetadata<AudioEngine>::Canonicalizations() {
90 return { 90 return {
91 {"auto", AudioEngine::Auto}, 91 {"auto", AudioEngine::Auto},
@@ -96,7 +96,7 @@ EnumMetadata<AudioEngine>::Canonicalizations() {
96} 96}
97 97
98template <> 98template <>
99constexpr u32 EnumMetadata<AudioEngine>::Index() { 99inline u32 EnumMetadata<AudioEngine>::Index() {
100 // This is just a sufficiently large number that is more than the number of other enums declared 100 // This is just a sufficiently large number that is more than the number of other enums declared
101 // here 101 // here
102 return 100; 102 return 100;
@@ -147,7 +147,7 @@ ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
147ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch); 147ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
148 148
149template <typename Type> 149template <typename Type>
150constexpr std::string CanonicalizeEnum(Type id) { 150inline std::string CanonicalizeEnum(Type id) {
151 const auto group = EnumMetadata<Type>::Canonicalizations(); 151 const auto group = EnumMetadata<Type>::Canonicalizations();
152 for (auto& [name, value] : group) { 152 for (auto& [name, value] : group) {
153 if (value == id) { 153 if (value == id) {
@@ -158,7 +158,7 @@ constexpr std::string CanonicalizeEnum(Type id) {
158} 158}
159 159
160template <typename Type> 160template <typename Type>
161constexpr Type ToEnum(const std::string& canonicalization) { 161inline Type ToEnum(const std::string& canonicalization) {
162 const auto group = EnumMetadata<Type>::Canonicalizations(); 162 const auto group = EnumMetadata<Type>::Canonicalizations();
163 for (auto& [name, value] : group) { 163 for (auto& [name, value] : group) {
164 if (name == canonicalization) { 164 if (name == canonicalization) {
diff --git a/src/common/settings_setting.h b/src/common/settings_setting.h
index a8beb06e9..e10843c73 100644
--- a/src/common/settings_setting.h
+++ b/src/common/settings_setting.h
@@ -190,7 +190,7 @@ public:
190 } 190 }
191 } 191 }
192 192
193 [[nodiscard]] std::string constexpr Canonicalize() const override final { 193 [[nodiscard]] std::string Canonicalize() const override final {
194 if constexpr (std::is_enum_v<Type>) { 194 if constexpr (std::is_enum_v<Type>) {
195 return CanonicalizeEnum(this->GetValue()); 195 return CanonicalizeEnum(this->GetValue());
196 } else { 196 } else {
@@ -256,11 +256,11 @@ public:
256 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded 256 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
257 * @param other_setting_ A second Setting to associate to this one in metadata 257 * @param other_setting_ A second Setting to associate to this one in metadata
258 */ 258 */
259 template <typename T = BasicSetting>
259 explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name, 260 explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
260 Category category_, u32 specialization_ = Specialization::Default, 261 Category category_, u32 specialization_ = Specialization::Default,
261 bool save_ = true, bool runtime_modifiable_ = false, 262 bool save_ = true, bool runtime_modifiable_ = false,
262 BasicSetting* other_setting_ = nullptr) 263 typename std::enable_if<!ranged, T*>::type other_setting_ = nullptr)
263 requires(!ranged)
264 : Setting<Type, false>{ 264 : Setting<Type, false>{
265 linkage, default_val, name, category_, specialization_, 265 linkage, default_val, name, category_, specialization_,
266 save_, runtime_modifiable_, other_setting_} { 266 save_, runtime_modifiable_, other_setting_} {
@@ -282,12 +282,12 @@ public:
282 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded 282 * @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
283 * @param other_setting_ A second Setting to associate to this one in metadata 283 * @param other_setting_ A second Setting to associate to this one in metadata
284 */ 284 */
285 template <typename T = BasicSetting>
285 explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val, 286 explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
286 const Type& max_val, const std::string& name, Category category_, 287 const Type& max_val, const std::string& name, Category category_,
287 u32 specialization_ = Specialization::Default, bool save_ = true, 288 u32 specialization_ = Specialization::Default, bool save_ = true,
288 bool runtime_modifiable_ = false, 289 bool runtime_modifiable_ = false,
289 BasicSetting* other_setting_ = nullptr) 290 typename std::enable_if<ranged, T*>::type other_setting_ = nullptr)
290 requires(ranged)
291 : Setting<Type, true>{linkage, default_val, min_val, 291 : Setting<Type, true>{linkage, default_val, min_val,
292 max_val, name, category_, 292 max_val, name, category_,
293 specialization_, save_, runtime_modifiable_, 293 specialization_, save_, runtime_modifiable_,
diff --git a/src/core/hle/service/ssl/ssl_backend_securetransport.cpp b/src/core/hle/service/ssl/ssl_backend_securetransport.cpp
index 370678f48..c48914f64 100644
--- a/src/core/hle/service/ssl/ssl_backend_securetransport.cpp
+++ b/src/core/hle/service/ssl/ssl_backend_securetransport.cpp
@@ -100,7 +100,7 @@ public:
100 100
101 Result DoHandshake() override { 101 Result DoHandshake() override {
102 OSStatus status = SSLHandshake(context); 102 OSStatus status = SSLHandshake(context);
103 return HandleReturn("SSLHandshake", 0, status).Code(); 103 return HandleReturn("SSLHandshake", 0, status);
104 } 104 }
105 105
106 Result Read(size_t* out_size, std::span<u8> data) override { 106 Result Read(size_t* out_size, std::span<u8> data) override {
diff --git a/src/core/memory.h b/src/core/memory.h
index 2eb61ffd3..13047a545 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -509,9 +509,9 @@ class GuestMemory {
509 509
510public: 510public:
511 GuestMemory() = delete; 511 GuestMemory() = delete;
512 explicit GuestMemory(M& memory_, u64 addr_, std::size_t size_, 512 explicit GuestMemory(M& memory, u64 addr, std::size_t size,
513 Common::ScratchBuffer<T>* backup = nullptr) 513 Common::ScratchBuffer<T>* backup = nullptr)
514 : memory{memory_}, addr{addr_}, size{size_} { 514 : m_memory{memory}, m_addr{addr}, m_size{size} {
515 static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write); 515 static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write);
516 if constexpr (FLAGS & GuestMemoryFlags::Read) { 516 if constexpr (FLAGS & GuestMemoryFlags::Read) {
517 Read(addr, size, backup); 517 Read(addr, size, backup);
@@ -521,89 +521,97 @@ public:
521 ~GuestMemory() = default; 521 ~GuestMemory() = default;
522 522
523 T* data() noexcept { 523 T* data() noexcept {
524 return data_span.data(); 524 return m_data_span.data();
525 } 525 }
526 526
527 const T* data() const noexcept { 527 const T* data() const noexcept {
528 return data_span.data(); 528 return m_data_span.data();
529 }
530
531 size_t size() const noexcept {
532 return m_size;
533 }
534
535 size_t size_bytes() const noexcept {
536 return this->size() * sizeof(T);
529 } 537 }
530 538
531 [[nodiscard]] T* begin() noexcept { 539 [[nodiscard]] T* begin() noexcept {
532 return data(); 540 return this->data();
533 } 541 }
534 542
535 [[nodiscard]] const T* begin() const noexcept { 543 [[nodiscard]] const T* begin() const noexcept {
536 return data(); 544 return this->data();
537 } 545 }
538 546
539 [[nodiscard]] T* end() noexcept { 547 [[nodiscard]] T* end() noexcept {
540 return data() + size; 548 return this->data() + this->size();
541 } 549 }
542 550
543 [[nodiscard]] const T* end() const noexcept { 551 [[nodiscard]] const T* end() const noexcept {
544 return data() + size; 552 return this->data() + this->size();
545 } 553 }
546 554
547 T& operator[](size_t index) noexcept { 555 T& operator[](size_t index) noexcept {
548 return data_span[index]; 556 return m_data_span[index];
549 } 557 }
550 558
551 const T& operator[](size_t index) const noexcept { 559 const T& operator[](size_t index) const noexcept {
552 return data_span[index]; 560 return m_data_span[index];
553 } 561 }
554 562
555 void SetAddressAndSize(u64 addr_, std::size_t size_) noexcept { 563 void SetAddressAndSize(u64 addr, std::size_t size) noexcept {
556 addr = addr_; 564 m_addr = addr;
557 size = size_; 565 m_size = size;
558 addr_changed = true; 566 m_addr_changed = true;
559 } 567 }
560 568
561 std::span<T> Read(u64 addr_, std::size_t size_, 569 std::span<T> Read(u64 addr, std::size_t size,
562 Common::ScratchBuffer<T>* backup = nullptr) noexcept { 570 Common::ScratchBuffer<T>* backup = nullptr) noexcept {
563 addr = addr_; 571 m_addr = addr;
564 size = size_; 572 m_size = size;
565 if (size == 0) { 573 if (m_size == 0) {
566 is_data_copy = true; 574 m_is_data_copy = true;
567 return {}; 575 return {};
568 } 576 }
569 577
570 if (TrySetSpan()) { 578 if (this->TrySetSpan()) {
571 if constexpr (FLAGS & GuestMemoryFlags::Safe) { 579 if constexpr (FLAGS & GuestMemoryFlags::Safe) {
572 memory.FlushRegion(addr, size * sizeof(T)); 580 m_memory.FlushRegion(m_addr, this->size_bytes());
573 } 581 }
574 } else { 582 } else {
575 if (backup) { 583 if (backup) {
576 backup->resize_destructive(size); 584 backup->resize_destructive(this->size());
577 data_span = *backup; 585 m_data_span = *backup;
578 } else { 586 } else {
579 data_copy.resize(size); 587 m_data_copy.resize(this->size());
580 data_span = std::span(data_copy); 588 m_data_span = std::span(m_data_copy);
581 } 589 }
582 is_data_copy = true; 590 m_is_data_copy = true;
583 span_valid = true; 591 m_span_valid = true;
584 if constexpr (FLAGS & GuestMemoryFlags::Safe) { 592 if constexpr (FLAGS & GuestMemoryFlags::Safe) {
585 memory.ReadBlock(addr, data_span.data(), size * sizeof(T)); 593 m_memory.ReadBlock(m_addr, this->data(), this->size_bytes());
586 } else { 594 } else {
587 memory.ReadBlockUnsafe(addr, data_span.data(), size * sizeof(T)); 595 m_memory.ReadBlockUnsafe(m_addr, this->data(), this->size_bytes());
588 } 596 }
589 } 597 }
590 return data_span; 598 return m_data_span;
591 } 599 }
592 600
593 void Write(std::span<T> write_data) noexcept { 601 void Write(std::span<T> write_data) noexcept {
594 if constexpr (FLAGS & GuestMemoryFlags::Cached) { 602 if constexpr (FLAGS & GuestMemoryFlags::Cached) {
595 memory.WriteBlockCached(addr, write_data.data(), size * sizeof(T)); 603 m_memory.WriteBlockCached(m_addr, write_data.data(), this->size_bytes());
596 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { 604 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
597 memory.WriteBlock(addr, write_data.data(), size * sizeof(T)); 605 m_memory.WriteBlock(m_addr, write_data.data(), this->size_bytes());
598 } else { 606 } else {
599 memory.WriteBlockUnsafe(addr, write_data.data(), size * sizeof(T)); 607 m_memory.WriteBlockUnsafe(m_addr, write_data.data(), this->size_bytes());
600 } 608 }
601 } 609 }
602 610
603 bool TrySetSpan() noexcept { 611 bool TrySetSpan() noexcept {
604 if (u8* ptr = memory.GetSpan(addr, size * sizeof(T)); ptr) { 612 if (u8* ptr = m_memory.GetSpan(m_addr, this->size_bytes()); ptr) {
605 data_span = {reinterpret_cast<T*>(ptr), size}; 613 m_data_span = {reinterpret_cast<T*>(ptr), this->size()};
606 span_valid = true; 614 m_span_valid = true;
607 return true; 615 return true;
608 } 616 }
609 return false; 617 return false;
@@ -611,36 +619,36 @@ public:
611 619
612protected: 620protected:
613 bool IsDataCopy() const noexcept { 621 bool IsDataCopy() const noexcept {
614 return is_data_copy; 622 return m_is_data_copy;
615 } 623 }
616 624
617 bool AddressChanged() const noexcept { 625 bool AddressChanged() const noexcept {
618 return addr_changed; 626 return m_addr_changed;
619 } 627 }
620 628
621 M& memory; 629 M& m_memory;
622 u64 addr; 630 u64 m_addr{};
623 size_t size; 631 size_t m_size{};
624 std::span<T> data_span{}; 632 std::span<T> m_data_span{};
625 std::vector<T> data_copy; 633 std::vector<T> m_data_copy{};
626 bool span_valid{false}; 634 bool m_span_valid{false};
627 bool is_data_copy{false}; 635 bool m_is_data_copy{false};
628 bool addr_changed{false}; 636 bool m_addr_changed{false};
629}; 637};
630 638
631template <typename M, typename T, GuestMemoryFlags FLAGS> 639template <typename M, typename T, GuestMemoryFlags FLAGS>
632class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { 640class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> {
633public: 641public:
634 GuestMemoryScoped() = delete; 642 GuestMemoryScoped() = delete;
635 explicit GuestMemoryScoped(M& memory_, u64 addr_, std::size_t size_, 643 explicit GuestMemoryScoped(M& memory, u64 addr, std::size_t size,
636 Common::ScratchBuffer<T>* backup = nullptr) 644 Common::ScratchBuffer<T>* backup = nullptr)
637 : GuestMemory<M, T, FLAGS>(memory_, addr_, size_, backup) { 645 : GuestMemory<M, T, FLAGS>(memory, addr, size, backup) {
638 if constexpr (!(FLAGS & GuestMemoryFlags::Read)) { 646 if constexpr (!(FLAGS & GuestMemoryFlags::Read)) {
639 if (!this->TrySetSpan()) { 647 if (!this->TrySetSpan()) {
640 if (backup) { 648 if (backup) {
641 this->data_span = *backup; 649 this->m_data_span = *backup;
642 this->span_valid = true; 650 this->m_span_valid = true;
643 this->is_data_copy = true; 651 this->m_is_data_copy = true;
644 } 652 }
645 } 653 }
646 } 654 }
@@ -648,24 +656,21 @@ public:
648 656
649 ~GuestMemoryScoped() { 657 ~GuestMemoryScoped() {
650 if constexpr (FLAGS & GuestMemoryFlags::Write) { 658 if constexpr (FLAGS & GuestMemoryFlags::Write) {
651 if (this->size == 0) [[unlikely]] { 659 if (this->size() == 0) [[unlikely]] {
652 return; 660 return;
653 } 661 }
654 662
655 if (this->AddressChanged() || this->IsDataCopy()) { 663 if (this->AddressChanged() || this->IsDataCopy()) {
656 ASSERT(this->span_valid); 664 ASSERT(this->m_span_valid);
657 if constexpr (FLAGS & GuestMemoryFlags::Cached) { 665 if constexpr (FLAGS & GuestMemoryFlags::Cached) {
658 this->memory.WriteBlockCached(this->addr, this->data_span.data(), 666 this->m_memory.WriteBlockCached(this->m_addr, this->data(), this->size_bytes());
659 this->size * sizeof(T));
660 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { 667 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
661 this->memory.WriteBlock(this->addr, this->data_span.data(), 668 this->m_memory.WriteBlock(this->m_addr, this->data(), this->size_bytes());
662 this->size * sizeof(T));
663 } else { 669 } else {
664 this->memory.WriteBlockUnsafe(this->addr, this->data_span.data(), 670 this->m_memory.WriteBlockUnsafe(this->m_addr, this->data(), this->size_bytes());
665 this->size * sizeof(T));
666 } 671 }
667 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { 672 } else if constexpr (FLAGS & GuestMemoryFlags::Safe) {
668 this->memory.InvalidateRegion(this->addr, this->size * sizeof(T)); 673 this->m_memory.InvalidateRegion(this->m_addr, this->size_bytes());
669 } 674 }
670 } 675 }
671 } 676 }
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index cd8e24b0b..da8eab7ee 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -5,6 +5,7 @@
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/microprofile.h" 7#include "common/microprofile.h"
8#include "common/polyfill_ranges.h"
8#include "common/settings.h" 9#include "common/settings.h"
9#include "core/core.h" 10#include "core/core.h"
10#include "core/memory.h" 11#include "core/memory.h"
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 710929ac5..adde96aa5 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -326,6 +326,43 @@ std::vector<const char*> ExtensionListForVulkan(
326 326
327} // Anonymous namespace 327} // Anonymous namespace
328 328
329void Device::RemoveExtension(bool& extension, const std::string& extension_name) {
330 extension = false;
331 loaded_extensions.erase(extension_name);
332}
333
334void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
335 if (loaded_extensions.contains(extension_name) && !is_suitable) {
336 LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
337 this->RemoveExtension(is_suitable, extension_name);
338 }
339}
340
341template <typename Feature>
342void Device::RemoveExtensionFeature(bool& extension, Feature& feature,
343 const std::string& extension_name) {
344 // Unload extension.
345 this->RemoveExtension(extension, extension_name);
346
347 // Save sType and pNext for chain.
348 VkStructureType sType = feature.sType;
349 void* pNext = feature.pNext;
350
351 // Clear feature struct and restore chain.
352 feature = {};
353 feature.sType = sType;
354 feature.pNext = pNext;
355}
356
357template <typename Feature>
358void Device::RemoveExtensionFeatureIfUnsuitable(bool is_suitable, Feature& feature,
359 const std::string& extension_name) {
360 if (loaded_extensions.contains(extension_name) && !is_suitable) {
361 LOG_WARNING(Render_Vulkan, "Removing features for unsuitable extension {}", extension_name);
362 this->RemoveExtensionFeature(is_suitable, feature, extension_name);
363 }
364}
365
329Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, 366Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
330 const vk::InstanceDispatch& dld_) 367 const vk::InstanceDispatch& dld_)
331 : instance{instance_}, dld{dld_}, physical{physical_}, 368 : instance{instance_}, dld{dld_}, physical{physical_},
@@ -397,21 +434,20 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
397 if (is_qualcomm || is_turnip) { 434 if (is_qualcomm || is_turnip) {
398 LOG_WARNING(Render_Vulkan, 435 LOG_WARNING(Render_Vulkan,
399 "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color"); 436 "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
400 extensions.custom_border_color = false; 437 RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
401 loaded_extensions.erase(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); 438 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
402 } 439 }
403 440
404 if (is_qualcomm) { 441 if (is_qualcomm) {
405 must_emulate_scaled_formats = true; 442 must_emulate_scaled_formats = true;
406 443
407 LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state"); 444 LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken VK_EXT_extended_dynamic_state");
408 extensions.extended_dynamic_state = false; 445 RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
409 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 446 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
410 447
411 LOG_WARNING(Render_Vulkan, 448 LOG_WARNING(Render_Vulkan,
412 "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation"); 449 "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
413 extensions.push_descriptor = false; 450 RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
414 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
415 451
416#if defined(ANDROID) && defined(ARCHITECTURE_arm64) 452#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
417 // Patch the driver to enable BCn textures. 453 // Patch the driver to enable BCn textures.
@@ -440,15 +476,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
440 must_emulate_scaled_formats = true; 476 must_emulate_scaled_formats = true;
441 477
442 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state"); 478 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state");
443 extensions.extended_dynamic_state = false; 479 RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state,
444 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 480 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
445 481
446 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2"); 482 LOG_WARNING(Render_Vulkan, "ARM drivers have broken VK_EXT_extended_dynamic_state2");
447 features.extended_dynamic_state2.extendedDynamicState2 = false; 483 RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2,
448 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; 484 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
449 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false;
450 extensions.extended_dynamic_state2 = false;
451 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
452 } 485 }
453 486
454 if (is_nvidia) { 487 if (is_nvidia) {
@@ -464,8 +497,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
464 case NvidiaArchitecture::VoltaOrOlder: 497 case NvidiaArchitecture::VoltaOrOlder:
465 if (nv_major_version < 527) { 498 if (nv_major_version < 527) {
466 LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor"); 499 LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
467 extensions.push_descriptor = false; 500 RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
468 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
469 } 501 }
470 break; 502 break;
471 } 503 }
@@ -480,8 +512,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
480 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { 512 if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
481 LOG_WARNING(Render_Vulkan, 513 LOG_WARNING(Render_Vulkan,
482 "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state"); 514 "RADV versions older than 21.2 have broken VK_EXT_extended_dynamic_state");
483 extensions.extended_dynamic_state = false; 515 RemoveExtensionFeature(extensions.extended_dynamic_state,
484 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 516 features.extended_dynamic_state,
517 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
485 } 518 }
486 } 519 }
487 if (extensions.extended_dynamic_state2 && is_radv) { 520 if (extensions.extended_dynamic_state2 && is_radv) {
@@ -490,11 +523,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
490 LOG_WARNING( 523 LOG_WARNING(
491 Render_Vulkan, 524 Render_Vulkan,
492 "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2"); 525 "RADV versions older than 22.3.1 have broken VK_EXT_extended_dynamic_state2");
493 features.extended_dynamic_state2.extendedDynamicState2 = false; 526 RemoveExtensionFeature(extensions.extended_dynamic_state2,
494 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; 527 features.extended_dynamic_state2,
495 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; 528 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
496 extensions.extended_dynamic_state2 = false;
497 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
498 } 529 }
499 } 530 }
500 if (extensions.extended_dynamic_state2 && is_qualcomm) { 531 if (extensions.extended_dynamic_state2 && is_qualcomm) {
@@ -504,11 +535,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
504 // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2. 535 // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
505 LOG_WARNING(Render_Vulkan, 536 LOG_WARNING(Render_Vulkan,
506 "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2"); 537 "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
507 features.extended_dynamic_state2.extendedDynamicState2 = false; 538 RemoveExtensionFeature(extensions.extended_dynamic_state2,
508 features.extended_dynamic_state2.extendedDynamicState2LogicOp = false; 539 features.extended_dynamic_state2,
509 features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints = false; 540 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
510 extensions.extended_dynamic_state2 = false;
511 loaded_extensions.erase(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
512 } 541 }
513 } 542 }
514 if (extensions.extended_dynamic_state3 && is_radv) { 543 if (extensions.extended_dynamic_state3 && is_radv) {
@@ -540,9 +569,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
540 if (is_rdna2) { 569 if (is_rdna2) {
541 LOG_WARNING(Render_Vulkan, 570 LOG_WARNING(Render_Vulkan,
542 "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware"); 571 "RADV has broken VK_EXT_vertex_input_dynamic_state on RDNA2 hardware");
543 features.vertex_input_dynamic_state.vertexInputDynamicState = false; 572 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
544 extensions.vertex_input_dynamic_state = false; 573 features.vertex_input_dynamic_state,
545 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 574 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
546 } 575 }
547 } 576 }
548 if (extensions.vertex_input_dynamic_state && is_qualcomm) { 577 if (extensions.vertex_input_dynamic_state && is_qualcomm) {
@@ -553,9 +582,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
553 LOG_WARNING( 582 LOG_WARNING(
554 Render_Vulkan, 583 Render_Vulkan,
555 "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state"); 584 "Qualcomm Adreno 7xx drivers have broken VK_EXT_vertex_input_dynamic_state");
556 features.vertex_input_dynamic_state.vertexInputDynamicState = false; 585 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
557 extensions.vertex_input_dynamic_state = false; 586 features.vertex_input_dynamic_state,
558 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 587 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
559 } 588 }
560 } 589 }
561 590
@@ -575,8 +604,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
575 if (!features.shader_float16_int8.shaderFloat16) { 604 if (!features.shader_float16_int8.shaderFloat16) {
576 LOG_WARNING(Render_Vulkan, 605 LOG_WARNING(Render_Vulkan,
577 "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax"); 606 "AMD GCN4 and earlier have broken VK_EXT_sampler_filter_minmax");
578 extensions.sampler_filter_minmax = false; 607 RemoveExtension(extensions.sampler_filter_minmax,
579 loaded_extensions.erase(VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME); 608 VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME);
580 } 609 }
581 } 610 }
582 611
@@ -584,8 +613,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
584 const u32 version = (properties.properties.driverVersion << 3) >> 3; 613 const u32 version = (properties.properties.driverVersion << 3) >> 3;
585 if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) { 614 if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) {
586 LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state"); 615 LOG_WARNING(Render_Vulkan, "Intel has broken VK_EXT_vertex_input_dynamic_state");
587 extensions.vertex_input_dynamic_state = false; 616 RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
588 loaded_extensions.erase(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 617 features.vertex_input_dynamic_state,
618 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
589 } 619 }
590 } 620 }
591 if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) { 621 if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
@@ -612,8 +642,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
612 // mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc 642 // mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc
613 LOG_WARNING(Render_Vulkan, 643 LOG_WARNING(Render_Vulkan,
614 "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor"); 644 "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor");
615 extensions.push_descriptor = false; 645 RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
616 loaded_extensions.erase(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
617 } 646 }
618 } 647 }
619 if (is_mvk) { 648 if (is_mvk) {
@@ -1007,34 +1036,29 @@ bool Device::GetSuitability(bool requires_swapchain) {
1007 return suitable; 1036 return suitable;
1008} 1037}
1009 1038
1010void Device::RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name) {
1011 if (loaded_extensions.contains(extension_name) && !is_suitable) {
1012 LOG_WARNING(Render_Vulkan, "Removing unsuitable extension {}", extension_name);
1013 loaded_extensions.erase(extension_name);
1014 }
1015}
1016
1017void Device::RemoveUnsuitableExtensions() { 1039void Device::RemoveUnsuitableExtensions() {
1018 // VK_EXT_custom_border_color 1040 // VK_EXT_custom_border_color
1019 extensions.custom_border_color = features.custom_border_color.customBorderColors && 1041 extensions.custom_border_color = features.custom_border_color.customBorderColors &&
1020 features.custom_border_color.customBorderColorWithoutFormat; 1042 features.custom_border_color.customBorderColorWithoutFormat;
1021 RemoveExtensionIfUnsuitable(extensions.custom_border_color, 1043 RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
1022 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); 1044 VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
1023 1045
1024 // VK_EXT_depth_clip_control 1046 // VK_EXT_depth_clip_control
1025 extensions.depth_clip_control = features.depth_clip_control.depthClipControl; 1047 extensions.depth_clip_control = features.depth_clip_control.depthClipControl;
1026 RemoveExtensionIfUnsuitable(extensions.depth_clip_control, 1048 RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control,
1027 VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); 1049 VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
1028 1050
1029 // VK_EXT_extended_dynamic_state 1051 // VK_EXT_extended_dynamic_state
1030 extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; 1052 extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
1031 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state, 1053 RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,
1032 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); 1054 features.extended_dynamic_state,
1055 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
1033 1056
1034 // VK_EXT_extended_dynamic_state2 1057 // VK_EXT_extended_dynamic_state2
1035 extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2; 1058 extensions.extended_dynamic_state2 = features.extended_dynamic_state2.extendedDynamicState2;
1036 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state2, 1059 RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state2,
1037 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); 1060 features.extended_dynamic_state2,
1061 VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
1038 1062
1039 // VK_EXT_extended_dynamic_state3 1063 // VK_EXT_extended_dynamic_state3
1040 dynamic_state3_blending = 1064 dynamic_state3_blending =
@@ -1048,35 +1072,38 @@ void Device::RemoveUnsuitableExtensions() {
1048 extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables; 1072 extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables;
1049 dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3; 1073 dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3;
1050 dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3; 1074 dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3;
1051 RemoveExtensionIfUnsuitable(extensions.extended_dynamic_state3, 1075 RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3,
1052 VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); 1076 features.extended_dynamic_state3,
1077 VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME);
1053 1078
1054 // VK_EXT_provoking_vertex 1079 // VK_EXT_provoking_vertex
1055 extensions.provoking_vertex = 1080 extensions.provoking_vertex =
1056 features.provoking_vertex.provokingVertexLast && 1081 features.provoking_vertex.provokingVertexLast &&
1057 features.provoking_vertex.transformFeedbackPreservesProvokingVertex; 1082 features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
1058 RemoveExtensionIfUnsuitable(extensions.provoking_vertex, 1083 RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, features.provoking_vertex,
1059 VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); 1084 VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
1060 1085
1061 // VK_KHR_shader_atomic_int64 1086 // VK_KHR_shader_atomic_int64
1062 extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics && 1087 extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics &&
1063 features.shader_atomic_int64.shaderSharedInt64Atomics; 1088 features.shader_atomic_int64.shaderSharedInt64Atomics;
1064 RemoveExtensionIfUnsuitable(extensions.shader_atomic_int64, 1089 RemoveExtensionFeatureIfUnsuitable(extensions.shader_atomic_int64, features.shader_atomic_int64,
1065 VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); 1090 VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
1066 1091
1067 // VK_EXT_shader_demote_to_helper_invocation 1092 // VK_EXT_shader_demote_to_helper_invocation
1068 extensions.shader_demote_to_helper_invocation = 1093 extensions.shader_demote_to_helper_invocation =
1069 features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation; 1094 features.shader_demote_to_helper_invocation.shaderDemoteToHelperInvocation;
1070 RemoveExtensionIfUnsuitable(extensions.shader_demote_to_helper_invocation, 1095 RemoveExtensionFeatureIfUnsuitable(extensions.shader_demote_to_helper_invocation,
1071 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME); 1096 features.shader_demote_to_helper_invocation,
1097 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME);
1072 1098
1073 // VK_EXT_subgroup_size_control 1099 // VK_EXT_subgroup_size_control
1074 extensions.subgroup_size_control = 1100 extensions.subgroup_size_control =
1075 features.subgroup_size_control.subgroupSizeControl && 1101 features.subgroup_size_control.subgroupSizeControl &&
1076 properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize && 1102 properties.subgroup_size_control.minSubgroupSize <= GuestWarpSize &&
1077 properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize; 1103 properties.subgroup_size_control.maxSubgroupSize >= GuestWarpSize;
1078 RemoveExtensionIfUnsuitable(extensions.subgroup_size_control, 1104 RemoveExtensionFeatureIfUnsuitable(extensions.subgroup_size_control,
1079 VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); 1105 features.subgroup_size_control,
1106 VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME);
1080 1107
1081 // VK_EXT_transform_feedback 1108 // VK_EXT_transform_feedback
1082 extensions.transform_feedback = 1109 extensions.transform_feedback =
@@ -1086,24 +1113,27 @@ void Device::RemoveUnsuitableExtensions() {
1086 properties.transform_feedback.maxTransformFeedbackBuffers > 0 && 1113 properties.transform_feedback.maxTransformFeedbackBuffers > 0 &&
1087 properties.transform_feedback.transformFeedbackQueries && 1114 properties.transform_feedback.transformFeedbackQueries &&
1088 properties.transform_feedback.transformFeedbackDraw; 1115 properties.transform_feedback.transformFeedbackDraw;
1089 RemoveExtensionIfUnsuitable(extensions.transform_feedback, 1116 RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
1090 VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); 1117 VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
1091 1118
1092 // VK_EXT_vertex_input_dynamic_state 1119 // VK_EXT_vertex_input_dynamic_state
1093 extensions.vertex_input_dynamic_state = 1120 extensions.vertex_input_dynamic_state =
1094 features.vertex_input_dynamic_state.vertexInputDynamicState; 1121 features.vertex_input_dynamic_state.vertexInputDynamicState;
1095 RemoveExtensionIfUnsuitable(extensions.vertex_input_dynamic_state, 1122 RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
1096 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); 1123 features.vertex_input_dynamic_state,
1124 VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
1097 1125
1098 // VK_KHR_pipeline_executable_properties 1126 // VK_KHR_pipeline_executable_properties
1099 if (Settings::values.renderer_shader_feedback.GetValue()) { 1127 if (Settings::values.renderer_shader_feedback.GetValue()) {
1100 extensions.pipeline_executable_properties = 1128 extensions.pipeline_executable_properties =
1101 features.pipeline_executable_properties.pipelineExecutableInfo; 1129 features.pipeline_executable_properties.pipelineExecutableInfo;
1102 RemoveExtensionIfUnsuitable(extensions.pipeline_executable_properties, 1130 RemoveExtensionFeatureIfUnsuitable(extensions.pipeline_executable_properties,
1103 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); 1131 features.pipeline_executable_properties,
1132 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
1104 } else { 1133 } else {
1105 extensions.pipeline_executable_properties = false; 1134 RemoveExtensionFeature(extensions.pipeline_executable_properties,
1106 loaded_extensions.erase(VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME); 1135 features.pipeline_executable_properties,
1136 VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
1107 } 1137 }
1108 1138
1109 // VK_KHR_workgroup_memory_explicit_layout 1139 // VK_KHR_workgroup_memory_explicit_layout
@@ -1113,8 +1143,9 @@ void Device::RemoveUnsuitableExtensions() {
1113 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess && 1143 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess &&
1114 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess && 1144 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess &&
1115 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout; 1145 features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout;
1116 RemoveExtensionIfUnsuitable(extensions.workgroup_memory_explicit_layout, 1146 RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout,
1117 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); 1147 features.workgroup_memory_explicit_layout,
1148 VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME);
1118} 1149}
1119 1150
1120void Device::SetupFamilies(VkSurfaceKHR surface) { 1151void Device::SetupFamilies(VkSurfaceKHR surface) {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index d8dd41e51..488fdd313 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -639,8 +639,17 @@ private:
639 639
640 // Remove extensions which have incomplete feature support. 640 // Remove extensions which have incomplete feature support.
641 void RemoveUnsuitableExtensions(); 641 void RemoveUnsuitableExtensions();
642
643 void RemoveExtension(bool& extension, const std::string& extension_name);
642 void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name); 644 void RemoveExtensionIfUnsuitable(bool is_suitable, const std::string& extension_name);
643 645
646 template <typename Feature>
647 void RemoveExtensionFeature(bool& extension, Feature& feature,
648 const std::string& extension_name);
649 template <typename Feature>
650 void RemoveExtensionFeatureIfUnsuitable(bool is_suitable, Feature& feature,
651 const std::string& extension_name);
652
644 /// Sets up queue families. 653 /// Sets up queue families.
645 void SetupFamilies(VkSurfaceKHR surface); 654 void SetupFamilies(VkSurfaceKHR surface);
646 655
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 465084fea..b5a02700d 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -214,13 +214,17 @@ void GameList::OnTextChanged(const QString& new_text) {
214 const int children_count = folder->rowCount(); 214 const int children_count = folder->rowCount();
215 for (int j = 0; j < children_count; ++j) { 215 for (int j = 0; j < children_count; ++j) {
216 ++children_total; 216 ++children_total;
217
217 const QStandardItem* child = folder->child(j, 0); 218 const QStandardItem* child = folder->child(j, 0);
219
220 const auto program_id = child->data(GameListItemPath::ProgramIdRole).toULongLong();
221
218 const QString file_path = 222 const QString file_path =
219 child->data(GameListItemPath::FullPathRole).toString().toLower(); 223 child->data(GameListItemPath::FullPathRole).toString().toLower();
220 const QString file_title = 224 const QString file_title =
221 child->data(GameListItemPath::TitleRole).toString().toLower(); 225 child->data(GameListItemPath::TitleRole).toString().toLower();
222 const QString file_program_id = 226 const QString file_program_id =
223 child->data(GameListItemPath::ProgramIdRole).toString().toLower(); 227 QStringLiteral("%1").arg(program_id, 16, 16, QLatin1Char{'0'});
224 228
225 // Only items which filename in combination with its title contains all words 229 // Only items which filename in combination with its title contains all words
226 // that are in the searchfield will be visible in the gamelist 230 // that are in the searchfield will be visible in the gamelist
@@ -231,7 +235,7 @@ void GameList::OnTextChanged(const QString& new_text) {
231 file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} + 235 file_path.mid(file_path.lastIndexOf(QLatin1Char{'/'}) + 1) + QLatin1Char{' '} +
232 file_title; 236 file_title;
233 if (ContainsAllWords(file_name, edit_filter_text) || 237 if (ContainsAllWords(file_name, edit_filter_text) ||
234 (file_program_id.count() == 16 && edit_filter_text.contains(file_program_id))) { 238 (file_program_id.count() == 16 && file_program_id.contains(edit_filter_text))) {
235 tree_view->setRowHidden(j, folder_index, false); 239 tree_view->setRowHidden(j, folder_index, false);
236 ++result_count; 240 ++result_count;
237 } else { 241 } else {