Browse Source

Rebase to master, fix merge conflicts

Ameer 6 years ago
parent
commit
4489ea6f53

+ 10 - 1
.github/ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE/bug-report-feature-request.md

@@ -1,4 +1,13 @@
-<!--
+---
+name: Bug Report / Feature Request
+about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+<!---
 Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
 Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
 
 
 Please read the FAQ:
 Please read the FAQ:

+ 8 - 0
.github/ISSUE_TEMPLATE/config.yml

@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+  - name: yuzu Discord
+    url: https://discord.com/invite/u77vRWY
+    about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
+  - name: Community forums
+    url: https://community.citra-emu.org
+    about: This is an alternative place for tech support, however helpers there are not as active.

+ 4 - 2
CMakeLists.txt

@@ -331,8 +331,10 @@ endif()
 
 
 # Ensure libusb is properly configured (based on dolphin libusb include)
 # Ensure libusb is properly configured (based on dolphin libusb include)
 find_package(LibUSB)
 find_package(LibUSB)
-add_subdirectory(externals/libusb)
-set(LIBUSB_LIBRARIES usb)
+if (NOT LIBUSB_FOUND)
+    add_subdirectory(externals/libusb)
+    set(LIBUSB_LIBRARIES usb)
+endif()
 
 
 
 
 # Prefer the -pthread flag on Linux.
 # Prefer the -pthread flag on Linux.

+ 1 - 1
externals/Vulkan-Headers

@@ -1 +1 @@
-Subproject commit 9250d5ae8f50202005233dc0512a1d460c8b4833
+Subproject commit 8188e3fbbc105591064093440f88081fb957d4f0

+ 43 - 0
externals/find-modules/FindLibUSB.cmake

@@ -0,0 +1,43 @@
+# - Find libusb-1.0 library
+# This module defines
+#  LIBUSB_INCLUDE_DIR, where to find bluetooth.h
+#  LIBUSB_LIBRARIES, the libraries needed to use libusb-1.0.
+#  LIBUSB_FOUND, If false, do not try to use libusb-1.0.
+#
+# Copyright (c) 2009, Michal Cihar, <michal@cihar.com>
+#
+# vim: expandtab sw=4 ts=4 sts=4:
+
+if(ANDROID)
+       set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
+       message(STATUS "libusb-1.0 not found.")
+elseif (NOT LIBUSB_FOUND)
+    pkg_check_modules (LIBUSB_PKG libusb-1.0)
+
+    find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h
+       PATHS
+       ${LIBUSB_PKG_INCLUDE_DIRS}
+       /usr/include/libusb-1.0
+       /usr/include
+       /usr/local/include/libusb-1.0
+       /usr/local/include
+    )
+
+    find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb
+       PATHS
+       ${LIBUSB_PKG_LIBRARY_DIRS}
+       /usr/lib
+       /usr/local/lib
+    )
+
+    if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
+       set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found")
+       message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}")
+    else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
+       set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found")
+       message(STATUS "libusb-1.0 not found.")
+    endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES)
+
+    mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES)
+endif ()
+

+ 0 - 2
src/core/arm/cpu_interrupt_handler.cpp

@@ -2,8 +2,6 @@
 // Licensed under GPLv2 or any later version
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
-#pragma once
-
 #include "common/thread.h"
 #include "common/thread.h"
 #include "core/arm/cpu_interrupt_handler.h"
 #include "core/arm/cpu_interrupt_handler.h"
 
 

+ 16 - 4
src/core/hle/service/am/am.cpp

@@ -10,6 +10,7 @@
 #include "core/core.h"
 #include "core/core.h"
 #include "core/file_sys/control_metadata.h"
 #include "core/file_sys/control_metadata.h"
 #include "core/file_sys/patch_manager.h"
 #include "core/file_sys/patch_manager.h"
+#include "core/file_sys/registered_cache.h"
 #include "core/file_sys/savedata_factory.h"
 #include "core/file_sys/savedata_factory.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/kernel.h"
@@ -1353,14 +1354,25 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
 
 
     std::array<u8, 0x10> version_string{};
     std::array<u8, 0x10> version_string{};
 
 
-    FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
-    const auto res = pm.GetControlMetadata();
+    const auto res = [this] {
+        const auto title_id = system.CurrentProcess()->GetTitleID();
+
+        FileSys::PatchManager pm{title_id};
+        auto res = pm.GetControlMetadata();
+        if (res.first != nullptr) {
+            return res;
+        }
+
+        FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
+        return pm_update.GetControlMetadata();
+    }();
+
     if (res.first != nullptr) {
     if (res.first != nullptr) {
         const auto& version = res.first->GetVersionString();
         const auto& version = res.first->GetVersionString();
         std::copy(version.begin(), version.end(), version_string.begin());
         std::copy(version.begin(), version.end(), version_string.begin());
     } else {
     } else {
-        constexpr u128 default_version = {1, 0};
-        std::memcpy(version_string.data(), default_version.data(), sizeof(u128));
+        constexpr char default_version[]{"1.0.0"};
+        std::memcpy(version_string.data(), default_version, sizeof(default_version));
     }
     }
 
 
     IPC::ResponseBuilder rb{ctx, 6};
     IPC::ResponseBuilder rb{ctx, 6};

+ 5 - 2
src/core/memory.cpp

@@ -548,9 +548,9 @@ struct Memory::Impl {
                         // longer exist, and we should just leave the pagetable entry blank.
                         // longer exist, and we should just leave the pagetable entry blank.
                         page_type = Common::PageType::Unmapped;
                         page_type = Common::PageType::Unmapped;
                     } else {
                     } else {
-                        page_type = Common::PageType::Memory;
                         current_page_table->pointers[vaddr >> PAGE_BITS] =
                         current_page_table->pointers[vaddr >> PAGE_BITS] =
                             pointer - (vaddr & ~PAGE_MASK);
                             pointer - (vaddr & ~PAGE_MASK);
+                        page_type = Common::PageType::Memory;
                     }
                     }
                     break;
                     break;
                 }
                 }
@@ -591,9 +591,12 @@ struct Memory::Impl {
                    base + page_table.pointers.size());
                    base + page_table.pointers.size());
 
 
         if (!target) {
         if (!target) {
+            ASSERT_MSG(type != Common::PageType::Memory,
+                       "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
+
             while (base != end) {
             while (base != end) {
-                page_table.pointers[base] = nullptr;
                 page_table.attributes[base] = type;
                 page_table.attributes[base] = type;
+                page_table.pointers[base] = nullptr;
                 page_table.backing_addr[base] = 0;
                 page_table.backing_addr[base] = 0;
 
 
                 base += 1;
                 base += 1;

+ 29 - 10
src/input_common/gcadapter/gc_adapter.cpp

@@ -24,10 +24,14 @@ Adapter::Adapter() {
     LOG_INFO(Input, "GC Adapter Initialization started");
     LOG_INFO(Input, "GC Adapter Initialization started");
 
 
     current_status = NO_ADAPTER_DETECTED;
     current_status = NO_ADAPTER_DETECTED;
-    libusb_init(&libusb_ctx);
     get_origin.fill(true);
     get_origin.fill(true);
 
 
-    StartScanThread();
+    const int init_res = libusb_init(&libusb_ctx);
+    if (init_res == LIBUSB_SUCCESS) {
+        StartScanThread();
+    } else {
+        LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
+    }
 }
 }
 
 
 GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
 GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
@@ -218,17 +222,26 @@ void Adapter::Setup() {
     adapter_controllers_status.fill(ControllerTypes::None);
     adapter_controllers_status.fill(ControllerTypes::None);
 
 
     // pointer to list of connected usb devices
     // pointer to list of connected usb devices
-    libusb_device** devices;
+    libusb_device** devices{};
 
 
     // populate the list of devices, get the count
     // populate the list of devices, get the count
-    const std::size_t device_count = libusb_get_device_list(libusb_ctx, &devices);
+    const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
+    if (device_count < 0) {
+        LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
+        detect_thread_running = false; // Stop the loop constantly checking for gc adapter
+        // TODO: For hotplug+gc adapter checkbox implementation, revert this.
+        return;
+    }
 
 
-    for (std::size_t index = 0; index < device_count; ++index) {
-        if (CheckDeviceAccess(devices[index])) {
-            // GC Adapter found and accessible, registering it
-            GetGCEndpoint(devices[index]);
-            break;
+    if (devices != nullptr) {
+        for (std::size_t index = 0; index < device_count; ++index) {
+            if (CheckDeviceAccess(devices[index])) {
+                // GC Adapter found and accessible, registering it
+                GetGCEndpoint(devices[index]);
+                break;
+            }
         }
         }
+        libusb_free_device_list(devices, 1);
     }
     }
 }
 }
 
 
@@ -286,7 +299,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
 
 
 void Adapter::GetGCEndpoint(libusb_device* device) {
 void Adapter::GetGCEndpoint(libusb_device* device) {
     libusb_config_descriptor* config = nullptr;
     libusb_config_descriptor* config = nullptr;
-    libusb_get_config_descriptor(device, 0, &config);
+    const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
+    if (config_descriptor_return != LIBUSB_SUCCESS) {
+        LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
+                  config_descriptor_return);
+        return;
+    }
+
     for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
     for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
         const libusb_interface* interfaceContainer = &config->interface[ic];
         const libusb_interface* interfaceContainer = &config->interface[ic];
         for (int i = 0; i < interfaceContainer->num_altsetting; i++) {
         for (int i = 0; i < interfaceContainer->num_altsetting; i++) {

+ 1 - 0
src/input_common/gcadapter/gc_adapter.h

@@ -7,6 +7,7 @@
 #include <functional>
 #include <functional>
 #include <mutex>
 #include <mutex>
 #include <thread>
 #include <thread>
+#include <unordered_map>
 #include <libusb.h>
 #include <libusb.h>
 #include "common/common_types.h"
 #include "common/common_types.h"
 #include "common/threadsafe_queue.h"
 #include "common/threadsafe_queue.h"

+ 68 - 47
src/video_core/renderer_vulkan/fixed_pipeline_state.cpp

@@ -39,52 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
 
 
 } // Anonymous namespace
 } // Anonymous namespace
 
 
-void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept {
-    raw = 0;
-    front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
-    front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
-    front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
-    front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
-    if (regs.stencil_two_side_enable) {
-        back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
-        back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
-        back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
-        back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
-    } else {
-        back.action_stencil_fail.Assign(front.action_stencil_fail);
-        back.action_depth_fail.Assign(front.action_depth_fail);
-        back.action_depth_pass.Assign(front.action_depth_pass);
-        back.test_func.Assign(front.test_func);
-    }
-    depth_test_enable.Assign(regs.depth_test_enable);
-    depth_write_enable.Assign(regs.depth_write_enabled);
-    depth_bounds_enable.Assign(regs.depth_bounds_enable);
-    stencil_enable.Assign(regs.stencil_enable);
-    depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
-}
-
-void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
+void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) {
     const auto& clip = regs.view_volume_clip_control;
     const auto& clip = regs.view_volume_clip_control;
     const std::array enabled_lut = {regs.polygon_offset_point_enable,
     const std::array enabled_lut = {regs.polygon_offset_point_enable,
                                     regs.polygon_offset_line_enable,
                                     regs.polygon_offset_line_enable,
                                     regs.polygon_offset_fill_enable};
                                     regs.polygon_offset_fill_enable};
     const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
     const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
 
 
-    u32 packed_front_face = PackFrontFace(regs.front_face);
-    if (regs.screen_y_control.triangle_rast_flip != 0) {
-        // Flip front face
-        packed_front_face = 1 - packed_front_face;
-    }
-
     raw = 0;
     raw = 0;
-    topology.Assign(topology_index);
     primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
     primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
-    cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
     depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
     depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
     depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
     depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
     ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
     ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
-    cull_face.Assign(PackCullFace(regs.cull_face));
-    front_face.Assign(packed_front_face);
     polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
     polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
     patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
     patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
     tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
     tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
@@ -93,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
     logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
     logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
     logic_op.Assign(PackLogicOp(regs.logic_op.operation));
     logic_op.Assign(PackLogicOp(regs.logic_op.operation));
     rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
     rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
+
     std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
     std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
-}
 
 
-void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept {
+    for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
+        binding_divisors[index] =
+            regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;
+    }
+
+    for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
+        const auto& input = regs.vertex_attrib_format[index];
+        auto& attribute = attributes[index];
+        attribute.raw = 0;
+        attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
+        attribute.buffer.Assign(input.buffer);
+        attribute.offset.Assign(input.offset);
+        attribute.type.Assign(static_cast<u32>(input.type.Value()));
+        attribute.size.Assign(static_cast<u32>(input.size.Value()));
+    }
+
     for (std::size_t index = 0; index < std::size(attachments); ++index) {
     for (std::size_t index = 0; index < std::size(attachments); ++index) {
         attachments[index].Fill(regs, index);
         attachments[index].Fill(regs, index);
     }
     }
-}
 
 
-void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept {
     const auto& transform = regs.viewport_transform;
     const auto& transform = regs.viewport_transform;
-    std::transform(transform.begin(), transform.end(), swizzles.begin(),
+    std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(),
                    [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
                    [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
+
+    if (!has_extended_dynamic_state) {
+        no_extended_dynamic_state.Assign(1);
+        dynamic_state.Fill(regs);
+    }
 }
 }
 
 
 void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
 void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
@@ -147,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
     enable.Assign(1);
     enable.Assign(1);
 }
 }
 
 
-void FixedPipelineState::Fill(const Maxwell& regs) {
-    rasterizer.Fill(regs);
-    depth_stencil.Fill(regs);
-    color_blending.Fill(regs);
-    viewport_swizzles.Fill(regs);
+void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
+    const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
+    u32 packed_front_face = PackFrontFace(regs.front_face);
+    if (regs.screen_y_control.triangle_rast_flip != 0) {
+        // Flip front face
+        packed_front_face = 1 - packed_front_face;
+    }
+
+    raw1 = 0;
+    raw2 = 0;
+    front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
+    front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
+    front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
+    front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
+    if (regs.stencil_two_side_enable) {
+        back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
+        back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
+        back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
+        back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
+    } else {
+        back.action_stencil_fail.Assign(front.action_stencil_fail);
+        back.action_depth_fail.Assign(front.action_depth_fail);
+        back.action_depth_pass.Assign(front.action_depth_pass);
+        back.test_func.Assign(front.test_func);
+    }
+    stencil_enable.Assign(regs.stencil_enable);
+    depth_write_enable.Assign(regs.depth_write_enabled);
+    depth_bounds_enable.Assign(regs.depth_bounds_enable);
+    depth_test_enable.Assign(regs.depth_test_enable);
+    front_face.Assign(packed_front_face);
+    depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
+    topology.Assign(topology_index);
+    cull_face.Assign(PackCullFace(regs.cull_face));
+    cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
+
+    for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
+        const auto& input = regs.vertex_array[index];
+        VertexBinding& binding = vertex_bindings[index];
+        binding.raw = 0;
+        binding.enabled.Assign(input.IsEnabled() ? 1 : 0);
+        binding.stride.Assign(static_cast<u16>(input.stride.Value()));
+    }
 }
 }
 
 
 std::size_t FixedPipelineState::Hash() const noexcept {
 std::size_t FixedPipelineState::Hash() const noexcept {
-    const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
+    const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
     return static_cast<std::size_t>(hash);
     return static_cast<std::size_t>(hash);
 }
 }
 
 
 bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
 bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
-    return std::memcmp(this, &rhs, sizeof *this) == 0;
+    return std::memcmp(this, &rhs, Size()) == 0;
 }
 }
 
 
 u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {
 u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {

+ 82 - 125
src/video_core/renderer_vulkan/fixed_pipeline_state.h

@@ -60,14 +60,6 @@ struct FixedPipelineState {
 
 
         void Fill(const Maxwell& regs, std::size_t index);
         void Fill(const Maxwell& regs, std::size_t index);
 
 
-        std::size_t Hash() const noexcept;
-
-        bool operator==(const BlendingAttachment& rhs) const noexcept;
-
-        bool operator!=(const BlendingAttachment& rhs) const noexcept {
-            return !operator==(rhs);
-        }
-
         constexpr std::array<bool, 4> Mask() const noexcept {
         constexpr std::array<bool, 4> Mask() const noexcept {
             return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
             return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
         }
         }
@@ -97,156 +89,116 @@ struct FixedPipelineState {
         }
         }
     };
     };
 
 
-    struct VertexInput {
-        union Binding {
-            u16 raw;
-            BitField<0, 1, u16> enabled;
-            BitField<1, 12, u16> stride;
-        };
+    union VertexAttribute {
+        u32 raw;
+        BitField<0, 1, u32> enabled;
+        BitField<1, 5, u32> buffer;
+        BitField<6, 14, u32> offset;
+        BitField<20, 3, u32> type;
+        BitField<23, 6, u32> size;
 
 
-        union Attribute {
-            u32 raw;
-            BitField<0, 1, u32> enabled;
-            BitField<1, 5, u32> buffer;
-            BitField<6, 14, u32> offset;
-            BitField<20, 3, u32> type;
-            BitField<23, 6, u32> size;
-
-            constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
-                return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
-            }
-
-            constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
-                return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
-            }
-        };
-
-        std::array<Binding, Maxwell::NumVertexArrays> bindings;
-        std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
-        std::array<Attribute, Maxwell::NumVertexAttributes> attributes;
-
-        void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept {
-            auto& binding = bindings[index];
-            binding.raw = 0;
-            binding.enabled.Assign(enabled ? 1 : 0);
-            binding.stride.Assign(static_cast<u16>(stride));
-            binding_divisors[index] = divisor;
+        constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
+            return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
         }
         }
 
 
-        void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset,
-                          Maxwell::VertexAttribute::Type type,
-                          Maxwell::VertexAttribute::Size size) noexcept {
-            auto& attribute = attributes[index];
-            attribute.raw = 0;
-            attribute.enabled.Assign(enabled ? 1 : 0);
-            attribute.buffer.Assign(buffer);
-            attribute.offset.Assign(offset);
-            attribute.type.Assign(static_cast<u32>(type));
-            attribute.size.Assign(static_cast<u32>(size));
+        constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
+            return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
         }
         }
     };
     };
 
 
-    struct Rasterizer {
-        union {
-            u32 raw;
-            BitField<0, 4, u32> topology;
-            BitField<4, 1, u32> primitive_restart_enable;
-            BitField<5, 1, u32> cull_enable;
-            BitField<6, 1, u32> depth_bias_enable;
-            BitField<7, 1, u32> depth_clamp_disabled;
-            BitField<8, 1, u32> ndc_minus_one_to_one;
-            BitField<9, 2, u32> cull_face;
-            BitField<11, 1, u32> front_face;
-            BitField<12, 2, u32> polygon_mode;
-            BitField<14, 5, u32> patch_control_points_minus_one;
-            BitField<19, 2, u32> tessellation_primitive;
-            BitField<21, 2, u32> tessellation_spacing;
-            BitField<23, 1, u32> tessellation_clockwise;
-            BitField<24, 1, u32> logic_op_enable;
-            BitField<25, 4, u32> logic_op;
-            BitField<29, 1, u32> rasterize_enable;
-        };
-
-        // TODO(Rodrigo): Move this to push constants
-        u32 point_size;
+    template <std::size_t Position>
+    union StencilFace {
+        BitField<Position + 0, 3, u32> action_stencil_fail;
+        BitField<Position + 3, 3, u32> action_depth_fail;
+        BitField<Position + 6, 3, u32> action_depth_pass;
+        BitField<Position + 9, 3, u32> test_func;
 
 
-        void Fill(const Maxwell& regs) noexcept;
+        Maxwell::StencilOp ActionStencilFail() const noexcept {
+            return UnpackStencilOp(action_stencil_fail);
+        }
 
 
-        constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
-            return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
+        Maxwell::StencilOp ActionDepthFail() const noexcept {
+            return UnpackStencilOp(action_depth_fail);
         }
         }
 
 
-        Maxwell::CullFace CullFace() const noexcept {
-            return UnpackCullFace(cull_face.Value());
+        Maxwell::StencilOp ActionDepthPass() const noexcept {
+            return UnpackStencilOp(action_depth_pass);
         }
         }
 
 
-        Maxwell::FrontFace FrontFace() const noexcept {
-            return UnpackFrontFace(front_face.Value());
+        Maxwell::ComparisonOp TestFunc() const noexcept {
+            return UnpackComparisonOp(test_func);
         }
         }
     };
     };
 
 
-    struct DepthStencil {
-        template <std::size_t Position>
-        union StencilFace {
-            BitField<Position + 0, 3, u32> action_stencil_fail;
-            BitField<Position + 3, 3, u32> action_depth_fail;
-            BitField<Position + 6, 3, u32> action_depth_pass;
-            BitField<Position + 9, 3, u32> test_func;
-
-            Maxwell::StencilOp ActionStencilFail() const noexcept {
-                return UnpackStencilOp(action_stencil_fail);
-            }
-
-            Maxwell::StencilOp ActionDepthFail() const noexcept {
-                return UnpackStencilOp(action_depth_fail);
-            }
-
-            Maxwell::StencilOp ActionDepthPass() const noexcept {
-                return UnpackStencilOp(action_depth_pass);
-            }
-
-            Maxwell::ComparisonOp TestFunc() const noexcept {
-                return UnpackComparisonOp(test_func);
-            }
-        };
+    union VertexBinding {
+        u16 raw;
+        BitField<0, 12, u16> stride;
+        BitField<12, 1, u16> enabled;
+    };
 
 
+    struct DynamicState {
         union {
         union {
-            u32 raw;
+            u32 raw1;
             StencilFace<0> front;
             StencilFace<0> front;
             StencilFace<12> back;
             StencilFace<12> back;
-            BitField<24, 1, u32> depth_test_enable;
+            BitField<24, 1, u32> stencil_enable;
             BitField<25, 1, u32> depth_write_enable;
             BitField<25, 1, u32> depth_write_enable;
             BitField<26, 1, u32> depth_bounds_enable;
             BitField<26, 1, u32> depth_bounds_enable;
-            BitField<27, 1, u32> stencil_enable;
-            BitField<28, 3, u32> depth_test_func;
+            BitField<27, 1, u32> depth_test_enable;
+            BitField<28, 1, u32> front_face;
+            BitField<29, 3, u32> depth_test_func;
+        };
+        union {
+            u32 raw2;
+            BitField<0, 4, u32> topology;
+            BitField<4, 2, u32> cull_face;
+            BitField<6, 1, u32> cull_enable;
         };
         };
+        std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings;
 
 
-        void Fill(const Maxwell& regs) noexcept;
+        void Fill(const Maxwell& regs);
 
 
         Maxwell::ComparisonOp DepthTestFunc() const noexcept {
         Maxwell::ComparisonOp DepthTestFunc() const noexcept {
             return UnpackComparisonOp(depth_test_func);
             return UnpackComparisonOp(depth_test_func);
         }
         }
-    };
-
-    struct ColorBlending {
-        std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
 
 
-        void Fill(const Maxwell& regs) noexcept;
-    };
+        Maxwell::CullFace CullFace() const noexcept {
+            return UnpackCullFace(cull_face.Value());
+        }
 
 
-    struct ViewportSwizzles {
-        std::array<u16, Maxwell::NumViewports> swizzles;
+        Maxwell::FrontFace FrontFace() const noexcept {
+            return UnpackFrontFace(front_face.Value());
+        }
 
 
-        void Fill(const Maxwell& regs) noexcept;
+        constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
+            return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
+        }
     };
     };
 
 
-    VertexInput vertex_input;
-    Rasterizer rasterizer;
-    DepthStencil depth_stencil;
-    ColorBlending color_blending;
-    ViewportSwizzles viewport_swizzles;
+    union {
+        u32 raw;
+        BitField<0, 1, u32> no_extended_dynamic_state;
+        BitField<2, 1, u32> primitive_restart_enable;
+        BitField<3, 1, u32> depth_bias_enable;
+        BitField<4, 1, u32> depth_clamp_disabled;
+        BitField<5, 1, u32> ndc_minus_one_to_one;
+        BitField<6, 2, u32> polygon_mode;
+        BitField<8, 5, u32> patch_control_points_minus_one;
+        BitField<13, 2, u32> tessellation_primitive;
+        BitField<15, 2, u32> tessellation_spacing;
+        BitField<17, 1, u32> tessellation_clockwise;
+        BitField<18, 1, u32> logic_op_enable;
+        BitField<19, 4, u32> logic_op;
+        BitField<23, 1, u32> rasterize_enable;
+    };
+    u32 point_size;
+    std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
+    std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
+    std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
+    std::array<u16, Maxwell::NumViewports> viewport_swizzles;
+    DynamicState dynamic_state;
 
 
-    void Fill(const Maxwell& regs);
+    void Fill(const Maxwell& regs, bool has_extended_dynamic_state);
 
 
     std::size_t Hash() const noexcept;
     std::size_t Hash() const noexcept;
 
 
@@ -255,6 +207,11 @@ struct FixedPipelineState {
     bool operator!=(const FixedPipelineState& rhs) const noexcept {
     bool operator!=(const FixedPipelineState& rhs) const noexcept {
         return !operator==(rhs);
         return !operator==(rhs);
     }
     }
+
+    std::size_t Size() const noexcept {
+        const std::size_t total_size = sizeof *this;
+        return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState));
+    }
 };
 };
 static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
 static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
 static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
 static_assert(std::is_trivially_copyable_v<FixedPipelineState>);

+ 26 - 0
src/video_core/renderer_vulkan/vk_device.cpp

@@ -313,6 +313,16 @@ bool VKDevice::Create() {
         LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
         LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
     }
     }
 
 
+    VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
+    if (ext_extended_dynamic_state) {
+        dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
+        dynamic_state.pNext = nullptr;
+        dynamic_state.extendedDynamicState = VK_TRUE;
+        SetNext(next, dynamic_state);
+    } else {
+        LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
+    }
+
     if (!ext_depth_range_unrestricted) {
     if (!ext_depth_range_unrestricted) {
         LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
         LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
     }
     }
@@ -541,6 +551,7 @@ std::vector<const char*> VKDevice::LoadExtensions() {
     bool has_ext_subgroup_size_control{};
     bool has_ext_subgroup_size_control{};
     bool has_ext_transform_feedback{};
     bool has_ext_transform_feedback{};
     bool has_ext_custom_border_color{};
     bool has_ext_custom_border_color{};
+    bool has_ext_extended_dynamic_state{};
     for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) {
     for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) {
         Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
         Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
         Test(extension, khr_uniform_buffer_standard_layout,
         Test(extension, khr_uniform_buffer_standard_layout,
@@ -558,6 +569,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
              false);
              false);
         Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,
         Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,
              false);
              false);
+        Test(extension, has_ext_extended_dynamic_state,
+             VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
         if (Settings::values.renderer_debug) {
         if (Settings::values.renderer_debug) {
             Test(extension, nv_device_diagnostics_config,
             Test(extension, nv_device_diagnostics_config,
                  VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
                  VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
@@ -643,6 +656,19 @@ std::vector<const char*> VKDevice::LoadExtensions() {
         }
         }
     }
     }
 
 
+    if (has_ext_extended_dynamic_state) {
+        VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
+        dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
+        dynamic_state.pNext = nullptr;
+        features.pNext = &dynamic_state;
+        physical.GetFeatures2KHR(features);
+
+        if (dynamic_state.extendedDynamicState) {
+            extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
+            ext_extended_dynamic_state = true;
+        }
+    }
+
     return extensions;
     return extensions;
 }
 }
 
 

+ 6 - 0
src/video_core/renderer_vulkan/vk_device.h

@@ -182,6 +182,11 @@ public:
         return ext_custom_border_color;
         return ext_custom_border_color;
     }
     }
 
 
+    /// Returns true if the device supports VK_EXT_extended_dynamic_state.
+    bool IsExtExtendedDynamicStateSupported() const {
+        return ext_extended_dynamic_state;
+    }
+
     /// Returns the vendor name reported from Vulkan.
     /// Returns the vendor name reported from Vulkan.
     std::string_view GetVendorName() const {
     std::string_view GetVendorName() const {
         return vendor_name;
         return vendor_name;
@@ -239,6 +244,7 @@ private:
     bool ext_shader_viewport_index_layer{};    ///< Support for VK_EXT_shader_viewport_index_layer.
     bool ext_shader_viewport_index_layer{};    ///< Support for VK_EXT_shader_viewport_index_layer.
     bool ext_transform_feedback{};             ///< Support for VK_EXT_transform_feedback.
     bool ext_transform_feedback{};             ///< Support for VK_EXT_transform_feedback.
     bool ext_custom_border_color{};            ///< Support for VK_EXT_custom_border_color.
     bool ext_custom_border_color{};            ///< Support for VK_EXT_custom_border_color.
+    bool ext_extended_dynamic_state{};         ///< Support for VK_EXT_extended_dynamic_state.
     bool nv_device_diagnostics_config{};       ///< Support for VK_NV_device_diagnostics_config.
     bool nv_device_diagnostics_config{};       ///< Support for VK_NV_device_diagnostics_config.
 
 
     // Telemetry parameters
     // Telemetry parameters

+ 59 - 30
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp

@@ -176,20 +176,32 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
 
 
 vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
 vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
                                                 const SPIRVProgram& program) const {
                                                 const SPIRVProgram& program) const {
-    const auto& vi = fixed_state.vertex_input;
-    const auto& ds = fixed_state.depth_stencil;
-    const auto& cd = fixed_state.color_blending;
-    const auto& rs = fixed_state.rasterizer;
-    const auto& viewport_swizzles = fixed_state.viewport_swizzles.swizzles;
+    const auto& state = fixed_state;
+    const auto& viewport_swizzles = state.viewport_swizzles;
+
+    FixedPipelineState::DynamicState dynamic;
+    if (device.IsExtExtendedDynamicStateSupported()) {
+        // Insert dummy values, as long as they are valid they don't matter as extended dynamic
+        // state is ignored
+        dynamic.raw1 = 0;
+        dynamic.raw2 = 0;
+        for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) {
+            // Enable all vertex bindings
+            binding.raw = 0;
+            binding.enabled.Assign(1);
+        }
+    } else {
+        dynamic = state.dynamic_state;
+    }
 
 
     std::vector<VkVertexInputBindingDescription> vertex_bindings;
     std::vector<VkVertexInputBindingDescription> vertex_bindings;
     std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
     std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
-    for (std::size_t index = 0; index < std::size(vi.bindings); ++index) {
-        const auto& binding = vi.bindings[index];
+    for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
+        const auto& binding = dynamic.vertex_bindings[index];
         if (!binding.enabled) {
         if (!binding.enabled) {
             continue;
             continue;
         }
         }
-        const bool instanced = vi.binding_divisors[index] != 0;
+        const bool instanced = state.binding_divisors[index] != 0;
         const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
         const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
 
 
         auto& vertex_binding = vertex_bindings.emplace_back();
         auto& vertex_binding = vertex_bindings.emplace_back();
@@ -200,14 +212,14 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
         if (instanced) {
         if (instanced) {
             auto& binding_divisor = vertex_binding_divisors.emplace_back();
             auto& binding_divisor = vertex_binding_divisors.emplace_back();
             binding_divisor.binding = static_cast<u32>(index);
             binding_divisor.binding = static_cast<u32>(index);
-            binding_divisor.divisor = vi.binding_divisors[index];
+            binding_divisor.divisor = state.binding_divisors[index];
         }
         }
     }
     }
 
 
     std::vector<VkVertexInputAttributeDescription> vertex_attributes;
     std::vector<VkVertexInputAttributeDescription> vertex_attributes;
     const auto& input_attributes = program[0]->entries.attributes;
     const auto& input_attributes = program[0]->entries.attributes;
-    for (std::size_t index = 0; index < std::size(vi.attributes); ++index) {
-        const auto& attribute = vi.attributes[index];
+    for (std::size_t index = 0; index < state.attributes.size(); ++index) {
+        const auto& attribute = state.attributes[index];
         if (!attribute.enabled) {
         if (!attribute.enabled) {
             continue;
             continue;
         }
         }
@@ -244,15 +256,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
     input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
     input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
     input_assembly_ci.pNext = nullptr;
     input_assembly_ci.pNext = nullptr;
     input_assembly_ci.flags = 0;
     input_assembly_ci.flags = 0;
-    input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology());
+    input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology());
     input_assembly_ci.primitiveRestartEnable =
     input_assembly_ci.primitiveRestartEnable =
-        rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
+        state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
 
 
     VkPipelineTessellationStateCreateInfo tessellation_ci;
     VkPipelineTessellationStateCreateInfo tessellation_ci;
     tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
     tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
     tessellation_ci.pNext = nullptr;
     tessellation_ci.pNext = nullptr;
     tessellation_ci.flags = 0;
     tessellation_ci.flags = 0;
-    tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1;
+    tessellation_ci.patchControlPoints = state.patch_control_points_minus_one.Value() + 1;
 
 
     VkPipelineViewportStateCreateInfo viewport_ci;
     VkPipelineViewportStateCreateInfo viewport_ci;
     viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
     viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
@@ -280,13 +292,13 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
     rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
     rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
     rasterization_ci.pNext = nullptr;
     rasterization_ci.pNext = nullptr;
     rasterization_ci.flags = 0;
     rasterization_ci.flags = 0;
-    rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
-    rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
+    rasterization_ci.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
+    rasterization_ci.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
     rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
     rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
     rasterization_ci.cullMode =
     rasterization_ci.cullMode =
-        rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE;
-    rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace());
-    rasterization_ci.depthBiasEnable = rs.depth_bias_enable;
+        dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE;
+    rasterization_ci.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace());
+    rasterization_ci.depthBiasEnable = state.depth_bias_enable;
     rasterization_ci.depthBiasConstantFactor = 0.0f;
     rasterization_ci.depthBiasConstantFactor = 0.0f;
     rasterization_ci.depthBiasClamp = 0.0f;
     rasterization_ci.depthBiasClamp = 0.0f;
     rasterization_ci.depthBiasSlopeFactor = 0.0f;
     rasterization_ci.depthBiasSlopeFactor = 0.0f;
@@ -307,14 +319,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
     depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
     depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
     depth_stencil_ci.pNext = nullptr;
     depth_stencil_ci.pNext = nullptr;
     depth_stencil_ci.flags = 0;
     depth_stencil_ci.flags = 0;
-    depth_stencil_ci.depthTestEnable = ds.depth_test_enable;
-    depth_stencil_ci.depthWriteEnable = ds.depth_write_enable;
-    depth_stencil_ci.depthCompareOp =
-        ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS;
-    depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable;
-    depth_stencil_ci.stencilTestEnable = ds.stencil_enable;
-    depth_stencil_ci.front = GetStencilFaceState(ds.front);
-    depth_stencil_ci.back = GetStencilFaceState(ds.back);
+    depth_stencil_ci.depthTestEnable = dynamic.depth_test_enable;
+    depth_stencil_ci.depthWriteEnable = dynamic.depth_write_enable;
+    depth_stencil_ci.depthCompareOp = dynamic.depth_test_enable
+                                          ? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc())
+                                          : VK_COMPARE_OP_ALWAYS;
+    depth_stencil_ci.depthBoundsTestEnable = dynamic.depth_bounds_enable;
+    depth_stencil_ci.stencilTestEnable = dynamic.stencil_enable;
+    depth_stencil_ci.front = GetStencilFaceState(dynamic.front);
+    depth_stencil_ci.back = GetStencilFaceState(dynamic.back);
     depth_stencil_ci.minDepthBounds = 0.0f;
     depth_stencil_ci.minDepthBounds = 0.0f;
     depth_stencil_ci.maxDepthBounds = 0.0f;
     depth_stencil_ci.maxDepthBounds = 0.0f;
 
 
@@ -324,7 +337,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
         static constexpr std::array COMPONENT_TABLE = {
         static constexpr std::array COMPONENT_TABLE = {
             VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT,
             VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT,
             VK_COLOR_COMPONENT_A_BIT};
             VK_COLOR_COMPONENT_A_BIT};
-        const auto& blend = cd.attachments[index];
+        const auto& blend = state.attachments[index];
 
 
         VkColorComponentFlags color_components = 0;
         VkColorComponentFlags color_components = 0;
         for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) {
         for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) {
@@ -354,11 +367,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
     color_blend_ci.pAttachments = cb_attachments.data();
     color_blend_ci.pAttachments = cb_attachments.data();
     std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants));
     std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants));
 
 
-    static constexpr std::array dynamic_states = {
+    std::vector dynamic_states = {
         VK_DYNAMIC_STATE_VIEWPORT,           VK_DYNAMIC_STATE_SCISSOR,
         VK_DYNAMIC_STATE_VIEWPORT,           VK_DYNAMIC_STATE_SCISSOR,
         VK_DYNAMIC_STATE_DEPTH_BIAS,         VK_DYNAMIC_STATE_BLEND_CONSTANTS,
         VK_DYNAMIC_STATE_DEPTH_BIAS,         VK_DYNAMIC_STATE_BLEND_CONSTANTS,
         VK_DYNAMIC_STATE_DEPTH_BOUNDS,       VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
         VK_DYNAMIC_STATE_DEPTH_BOUNDS,       VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
-        VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE};
+        VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
+    };
+    if (device.IsExtExtendedDynamicStateSupported()) {
+        static constexpr std::array extended = {
+            VK_DYNAMIC_STATE_CULL_MODE_EXT,
+            VK_DYNAMIC_STATE_FRONT_FACE_EXT,
+            VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
+            VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT,
+            VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
+            VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
+            VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
+            VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT,
+            VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
+            VK_DYNAMIC_STATE_STENCIL_OP_EXT,
+        };
+        dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
+    }
 
 
     VkPipelineDynamicStateCreateInfo dynamic_state_ci;
     VkPipelineDynamicStateCreateInfo dynamic_state_ci;
     dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
     dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;

+ 7 - 6
src/video_core/renderer_vulkan/vk_pipeline_cache.cpp

@@ -116,12 +116,12 @@ u32 FillDescriptorLayout(const ShaderEntries& entries,
 } // Anonymous namespace
 } // Anonymous namespace
 
 
 std::size_t GraphicsPipelineCacheKey::Hash() const noexcept {
 std::size_t GraphicsPipelineCacheKey::Hash() const noexcept {
-    const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
+    const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
     return static_cast<std::size_t>(hash);
     return static_cast<std::size_t>(hash);
 }
 }
 
 
 bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept {
 bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept {
-    return std::memcmp(&rhs, this, sizeof *this) == 0;
+    return std::memcmp(&rhs, this, Size()) == 0;
 }
 }
 
 
 std::size_t ComputePipelineCacheKey::Hash() const noexcept {
 std::size_t ComputePipelineCacheKey::Hash() const noexcept {
@@ -312,18 +312,19 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
     const auto& gpu = system.GPU().Maxwell3D();
     const auto& gpu = system.GPU().Maxwell3D();
 
 
     Specialization specialization;
     Specialization specialization;
-    if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) {
+    if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points ||
+        device.IsExtExtendedDynamicStateSupported()) {
         float point_size;
         float point_size;
-        std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float));
+        std::memcpy(&point_size, &fixed_state.point_size, sizeof(float));
         specialization.point_size = point_size;
         specialization.point_size = point_size;
         ASSERT(point_size != 0.0f);
         ASSERT(point_size != 0.0f);
     }
     }
     for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
     for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
-        const auto& attribute = fixed_state.vertex_input.attributes[i];
+        const auto& attribute = fixed_state.attributes[i];
         specialization.enabled_attributes[i] = attribute.enabled.Value() != 0;
         specialization.enabled_attributes[i] = attribute.enabled.Value() != 0;
         specialization.attribute_types[i] = attribute.Type();
         specialization.attribute_types[i] = attribute.Type();
     }
     }
-    specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one;
+    specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one;
 
 
     SPIRVProgram program;
     SPIRVProgram program;
     std::vector<VkDescriptorSetLayoutBinding> bindings;
     std::vector<VkDescriptorSetLayoutBinding> bindings;

+ 6 - 2
src/video_core/renderer_vulkan/vk_pipeline_cache.h

@@ -44,10 +44,10 @@ class VKUpdateDescriptorQueue;
 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 
 
 struct GraphicsPipelineCacheKey {
 struct GraphicsPipelineCacheKey {
-    FixedPipelineState fixed_state;
     RenderPassParams renderpass_params;
     RenderPassParams renderpass_params;
+    u32 padding;
     std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
     std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
-    u64 padding; // This is necessary for unique object representations
+    FixedPipelineState fixed_state;
 
 
     std::size_t Hash() const noexcept;
     std::size_t Hash() const noexcept;
 
 
@@ -56,6 +56,10 @@ struct GraphicsPipelineCacheKey {
     bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
     bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
         return !operator==(rhs);
         return !operator==(rhs);
     }
     }
+
+    std::size_t Size() const noexcept {
+        return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
+    }
 };
 };
 static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
 static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
 static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
 static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);

+ 213 - 63
src/video_core/renderer_vulkan/vk_rasterizer.cpp

@@ -186,13 +186,22 @@ bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) {
            scissor.max_y < regs.zeta_height;
            scissor.max_y < regs.zeta_height;
 }
 }
 
 
+template <std::size_t N>
+std::array<VkDeviceSize, N> ExpandStrides(const std::array<u16, N>& strides) {
+    std::array<VkDeviceSize, N> expanded;
+    std::copy(strides.begin(), strides.end(), expanded.begin());
+    return expanded;
+}
+
 } // Anonymous namespace
 } // Anonymous namespace
 
 
 class BufferBindings final {
 class BufferBindings final {
 public:
 public:
-    void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset) {
+    void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, u32 stride) {
         vertex.buffers[vertex.num_buffers] = buffer;
         vertex.buffers[vertex.num_buffers] = buffer;
         vertex.offsets[vertex.num_buffers] = offset;
         vertex.offsets[vertex.num_buffers] = offset;
+        vertex.sizes[vertex.num_buffers] = size;
+        vertex.strides[vertex.num_buffers] = static_cast<u16>(stride);
         ++vertex.num_buffers;
         ++vertex.num_buffers;
     }
     }
 
 
@@ -202,76 +211,76 @@ public:
         index.type = type;
         index.type = type;
     }
     }
 
 
-    void Bind(VKScheduler& scheduler) const {
+    void Bind(const VKDevice& device, VKScheduler& scheduler) const {
         // Use this large switch case to avoid dispatching more memory in the record lambda than
         // Use this large switch case to avoid dispatching more memory in the record lambda than
         // what we need. It looks horrible, but it's the best we can do on standard C++.
         // what we need. It looks horrible, but it's the best we can do on standard C++.
         switch (vertex.num_buffers) {
         switch (vertex.num_buffers) {
         case 0:
         case 0:
-            return BindStatic<0>(scheduler);
+            return BindStatic<0>(device, scheduler);
         case 1:
         case 1:
-            return BindStatic<1>(scheduler);
+            return BindStatic<1>(device, scheduler);
         case 2:
         case 2:
-            return BindStatic<2>(scheduler);
+            return BindStatic<2>(device, scheduler);
         case 3:
         case 3:
-            return BindStatic<3>(scheduler);
+            return BindStatic<3>(device, scheduler);
         case 4:
         case 4:
-            return BindStatic<4>(scheduler);
+            return BindStatic<4>(device, scheduler);
         case 5:
         case 5:
-            return BindStatic<5>(scheduler);
+            return BindStatic<5>(device, scheduler);
         case 6:
         case 6:
-            return BindStatic<6>(scheduler);
+            return BindStatic<6>(device, scheduler);
         case 7:
         case 7:
-            return BindStatic<7>(scheduler);
+            return BindStatic<7>(device, scheduler);
         case 8:
         case 8:
-            return BindStatic<8>(scheduler);
+            return BindStatic<8>(device, scheduler);
         case 9:
         case 9:
-            return BindStatic<9>(scheduler);
+            return BindStatic<9>(device, scheduler);
         case 10:
         case 10:
-            return BindStatic<10>(scheduler);
+            return BindStatic<10>(device, scheduler);
         case 11:
         case 11:
-            return BindStatic<11>(scheduler);
+            return BindStatic<11>(device, scheduler);
         case 12:
         case 12:
-            return BindStatic<12>(scheduler);
+            return BindStatic<12>(device, scheduler);
         case 13:
         case 13:
-            return BindStatic<13>(scheduler);
+            return BindStatic<13>(device, scheduler);
         case 14:
         case 14:
-            return BindStatic<14>(scheduler);
+            return BindStatic<14>(device, scheduler);
         case 15:
         case 15:
-            return BindStatic<15>(scheduler);
+            return BindStatic<15>(device, scheduler);
         case 16:
         case 16:
-            return BindStatic<16>(scheduler);
+            return BindStatic<16>(device, scheduler);
         case 17:
         case 17:
-            return BindStatic<17>(scheduler);
+            return BindStatic<17>(device, scheduler);
         case 18:
         case 18:
-            return BindStatic<18>(scheduler);
+            return BindStatic<18>(device, scheduler);
         case 19:
         case 19:
-            return BindStatic<19>(scheduler);
+            return BindStatic<19>(device, scheduler);
         case 20:
         case 20:
-            return BindStatic<20>(scheduler);
+            return BindStatic<20>(device, scheduler);
         case 21:
         case 21:
-            return BindStatic<21>(scheduler);
+            return BindStatic<21>(device, scheduler);
         case 22:
         case 22:
-            return BindStatic<22>(scheduler);
+            return BindStatic<22>(device, scheduler);
         case 23:
         case 23:
-            return BindStatic<23>(scheduler);
+            return BindStatic<23>(device, scheduler);
         case 24:
         case 24:
-            return BindStatic<24>(scheduler);
+            return BindStatic<24>(device, scheduler);
         case 25:
         case 25:
-            return BindStatic<25>(scheduler);
+            return BindStatic<25>(device, scheduler);
         case 26:
         case 26:
-            return BindStatic<26>(scheduler);
+            return BindStatic<26>(device, scheduler);
         case 27:
         case 27:
-            return BindStatic<27>(scheduler);
+            return BindStatic<27>(device, scheduler);
         case 28:
         case 28:
-            return BindStatic<28>(scheduler);
+            return BindStatic<28>(device, scheduler);
         case 29:
         case 29:
-            return BindStatic<29>(scheduler);
+            return BindStatic<29>(device, scheduler);
         case 30:
         case 30:
-            return BindStatic<30>(scheduler);
+            return BindStatic<30>(device, scheduler);
         case 31:
         case 31:
-            return BindStatic<31>(scheduler);
+            return BindStatic<31>(device, scheduler);
         case 32:
         case 32:
-            return BindStatic<32>(scheduler);
+            return BindStatic<32>(device, scheduler);
         }
         }
         UNREACHABLE();
         UNREACHABLE();
     }
     }
@@ -282,6 +291,8 @@ private:
         std::size_t num_buffers = 0;
         std::size_t num_buffers = 0;
         std::array<VkBuffer, Maxwell::NumVertexArrays> buffers;
         std::array<VkBuffer, Maxwell::NumVertexArrays> buffers;
         std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets;
         std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets;
+        std::array<VkDeviceSize, Maxwell::NumVertexArrays> sizes;
+        std::array<u16, Maxwell::NumVertexArrays> strides;
     } vertex;
     } vertex;
 
 
     struct {
     struct {
@@ -291,15 +302,23 @@ private:
     } index;
     } index;
 
 
     template <std::size_t N>
     template <std::size_t N>
-    void BindStatic(VKScheduler& scheduler) const {
-        if (index.buffer) {
-            BindStatic<N, true>(scheduler);
+    void BindStatic(const VKDevice& device, VKScheduler& scheduler) const {
+        if (device.IsExtExtendedDynamicStateSupported()) {
+            if (index.buffer) {
+                BindStatic<N, true, true>(scheduler);
+            } else {
+                BindStatic<N, false, true>(scheduler);
+            }
         } else {
         } else {
-            BindStatic<N, false>(scheduler);
+            if (index.buffer) {
+                BindStatic<N, true, false>(scheduler);
+            } else {
+                BindStatic<N, false, false>(scheduler);
+            }
         }
         }
     }
     }
 
 
-    template <std::size_t N, bool is_indexed>
+    template <std::size_t N, bool is_indexed, bool has_extended_dynamic_state>
     void BindStatic(VKScheduler& scheduler) const {
     void BindStatic(VKScheduler& scheduler) const {
         static_assert(N <= Maxwell::NumVertexArrays);
         static_assert(N <= Maxwell::NumVertexArrays);
         if constexpr (N == 0) {
         if constexpr (N == 0) {
@@ -311,6 +330,31 @@ private:
         std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin());
         std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin());
         std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin());
         std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin());
 
 
+        if constexpr (has_extended_dynamic_state) {
+            // With extended dynamic states we can specify the length and stride of a vertex buffer
+            // std::array<VkDeviceSize, N> sizes;
+            std::array<u16, N> strides;
+            // std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin());
+            std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin());
+
+            if constexpr (is_indexed) {
+                scheduler.Record(
+                    [buffers, offsets, strides, index = index](vk::CommandBuffer cmdbuf) {
+                        cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type);
+                        cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
+                                                     offsets.data(), nullptr,
+                                                     ExpandStrides(strides).data());
+                    });
+            } else {
+                scheduler.Record([buffers, offsets, strides](vk::CommandBuffer cmdbuf) {
+                    cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
+                                                 offsets.data(), nullptr,
+                                                 ExpandStrides(strides).data());
+                });
+            }
+            return;
+        }
+
         if constexpr (is_indexed) {
         if constexpr (is_indexed) {
             // Indexed draw
             // Indexed draw
             scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) {
             scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) {
@@ -369,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
 
 
     const auto& gpu = system.GPU().Maxwell3D();
     const auto& gpu = system.GPU().Maxwell3D();
     GraphicsPipelineCacheKey key;
     GraphicsPipelineCacheKey key;
-    key.fixed_state.Fill(gpu.regs);
+    key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
 
 
     buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed));
     buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed));
 
 
@@ -402,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
 
 
     UpdateDynamicStates();
     UpdateDynamicStates();
 
 
-    buffer_bindings.Bind(scheduler);
+    buffer_bindings.Bind(device, scheduler);
 
 
     BeginTransformFeedback();
     BeginTransformFeedback();
 
 
@@ -822,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
     const auto& gpu = system.GPU().Maxwell3D();
     const auto& gpu = system.GPU().Maxwell3D();
     const auto& regs = gpu.regs;
     const auto& regs = gpu.regs;
 
 
-    SetupVertexArrays(fixed_state.vertex_input, buffer_bindings);
+    SetupVertexArrays(buffer_bindings);
 
 
     const u32 base_instance = regs.vb_base_instance;
     const u32 base_instance = regs.vb_base_instance;
     const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1;
     const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1;
@@ -893,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() {
     UpdateBlendConstants(regs);
     UpdateBlendConstants(regs);
     UpdateDepthBounds(regs);
     UpdateDepthBounds(regs);
     UpdateStencilFaces(regs);
     UpdateStencilFaces(regs);
+    if (device.IsExtExtendedDynamicStateSupported()) {
+        UpdateCullMode(regs);
+        UpdateDepthBoundsTestEnable(regs);
+        UpdateDepthTestEnable(regs);
+        UpdateDepthWriteEnable(regs);
+        UpdateDepthCompareOp(regs);
+        UpdateFrontFace(regs);
+        UpdatePrimitiveTopology(regs);
+        UpdateStencilOp(regs);
+        UpdateStencilTestEnable(regs);
+    }
 }
 }
 
 
 void RasterizerVulkan::BeginTransformFeedback() {
 void RasterizerVulkan::BeginTransformFeedback() {
@@ -940,41 +995,25 @@ void RasterizerVulkan::EndTransformFeedback() {
         [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); });
         [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); });
 }
 }
 
 
-void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
-                                         BufferBindings& buffer_bindings) {
+void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) {
     const auto& regs = system.GPU().Maxwell3D().regs;
     const auto& regs = system.GPU().Maxwell3D().regs;
 
 
-    for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
-        const auto& attrib = regs.vertex_attrib_format[index];
-        if (attrib.IsConstant()) {
-            vertex_input.SetAttribute(index, false, 0, 0, {}, {});
-            continue;
-        }
-        vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(),
-                                  attrib.size.Value());
-    }
-
     for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
     for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
         const auto& vertex_array = regs.vertex_array[index];
         const auto& vertex_array = regs.vertex_array[index];
         if (!vertex_array.IsEnabled()) {
         if (!vertex_array.IsEnabled()) {
-            vertex_input.SetBinding(index, false, 0, 0);
             continue;
             continue;
         }
         }
-        vertex_input.SetBinding(
-            index, true, vertex_array.stride,
-            regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
-
         const GPUVAddr start{vertex_array.StartAddress()};
         const GPUVAddr start{vertex_array.StartAddress()};
         const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
         const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
 
 
         ASSERT(end >= start);
         ASSERT(end >= start);
-        const std::size_t size{end - start};
+        const std::size_t size = end - start;
         if (size == 0) {
         if (size == 0) {
-            buffer_bindings.AddVertexBinding(DefaultBuffer(), 0);
+            buffer_bindings.AddVertexBinding(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE, 0);
             continue;
             continue;
         }
         }
         const auto info = buffer_cache.UploadMemory(start, size);
         const auto info = buffer_cache.UploadMemory(start, size);
-        buffer_bindings.AddVertexBinding(info.handle, info.offset);
+        buffer_bindings.AddVertexBinding(info.handle, info.offset, size, vertex_array.stride);
     }
     }
 }
 }
 
 
@@ -1326,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
     }
     }
 }
 }
 
 
+void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchCullMode()) {
+        return;
+    }
+    scheduler.Record(
+        [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) {
+            cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
+        });
+}
+
+void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchDepthBoundsTestEnable()) {
+        return;
+    }
+    scheduler.Record([enable = regs.depth_bounds_enable](vk::CommandBuffer cmdbuf) {
+        cmdbuf.SetDepthBoundsTestEnableEXT(enable);
+    });
+}
+
+void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchDepthTestEnable()) {
+        return;
+    }
+    scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) {
+        cmdbuf.SetDepthTestEnableEXT(enable);
+    });
+}
+
+void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchDepthWriteEnable()) {
+        return;
+    }
+    scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) {
+        cmdbuf.SetDepthWriteEnableEXT(enable);
+    });
+}
+
+void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchDepthCompareOp()) {
+        return;
+    }
+    scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) {
+        cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func));
+    });
+}
+
+void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchFrontFace()) {
+        return;
+    }
+
+    VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face);
+    if (regs.screen_y_control.triangle_rast_flip != 0) {
+        front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
+                                                           : VK_FRONT_FACE_CLOCKWISE;
+    }
+    scheduler.Record(
+        [front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); });
+}
+
+void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchPrimitiveTopology()) {
+        return;
+    }
+    const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
+    scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
+        cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
+    });
+}
+
+void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchStencilOp()) {
+        return;
+    }
+    const Maxwell::StencilOp fail = regs.stencil_front_op_fail;
+    const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail;
+    const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass;
+    const Maxwell::ComparisonOp compare = regs.stencil_front_func_func;
+    if (regs.stencil_two_side_enable) {
+        scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
+            cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
+                                   MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
+                                   MaxwellToVK::ComparisonOp(compare));
+        });
+    } else {
+        const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail;
+        const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail;
+        const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass;
+        const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func;
+        scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass,
+                          back_compare](vk::CommandBuffer cmdbuf) {
+            cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
+                                   MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
+                                   MaxwellToVK::ComparisonOp(compare));
+            cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail),
+                                   MaxwellToVK::StencilOp(back_zpass),
+                                   MaxwellToVK::StencilOp(back_zfail),
+                                   MaxwellToVK::ComparisonOp(back_compare));
+        });
+    }
+}
+
+void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
+    if (!state_tracker.TouchStencilTestEnable()) {
+        return;
+    }
+    scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) {
+        cmdbuf.SetStencilTestEnableEXT(enable);
+    });
+}
+
 std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const {
 std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const {
     std::size_t size = CalculateVertexArraysSize();
     std::size_t size = CalculateVertexArraysSize();
     if (is_indexed) {
     if (is_indexed) {

+ 11 - 2
src/video_core/renderer_vulkan/vk_rasterizer.h

@@ -185,8 +185,7 @@ private:
 
 
     bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment);
     bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment);
 
 
-    void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
-                           BufferBindings& buffer_bindings);
+    void SetupVertexArrays(BufferBindings& buffer_bindings);
 
 
     void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed);
     void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed);
 
 
@@ -246,6 +245,16 @@ private:
     void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
     void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
 
 
+    void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
+
     std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
     std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
 
 
     std::size_t CalculateComputeStreamBufferSize() const;
     std::size_t CalculateComputeStreamBufferSize() const;

+ 68 - 0
src/video_core/renderer_vulkan/vk_state_tracker.cpp

@@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() {
     flags[BlendConstants] = true;
     flags[BlendConstants] = true;
     flags[DepthBounds] = true;
     flags[DepthBounds] = true;
     flags[StencilProperties] = true;
     flags[StencilProperties] = true;
+    flags[CullMode] = true;
+    flags[DepthBoundsEnable] = true;
+    flags[DepthTestEnable] = true;
+    flags[DepthWriteEnable] = true;
+    flags[DepthCompareOp] = true;
+    flags[FrontFace] = true;
+    flags[PrimitiveTopology] = true;
+    flags[StencilOp] = true;
+    flags[StencilTestEnable] = true;
     return flags;
     return flags;
 }
 }
 
 
@@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) {
     table[OFF(stencil_back_func_mask)] = StencilProperties;
     table[OFF(stencil_back_func_mask)] = StencilProperties;
 }
 }
 
 
+void SetupDirtyCullMode(Tables& tables) {
+    auto& table = tables[0];
+    table[OFF(cull_face)] = CullMode;
+    table[OFF(cull_test_enabled)] = CullMode;
+}
+
+void SetupDirtyDepthBoundsEnable(Tables& tables) {
+    tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable;
+}
+
+void SetupDirtyDepthTestEnable(Tables& tables) {
+    tables[0][OFF(depth_test_enable)] = DepthTestEnable;
+}
+
+void SetupDirtyDepthWriteEnable(Tables& tables) {
+    tables[0][OFF(depth_write_enabled)] = DepthWriteEnable;
+}
+
+void SetupDirtyDepthCompareOp(Tables& tables) {
+    tables[0][OFF(depth_test_func)] = DepthCompareOp;
+}
+
+void SetupDirtyFrontFace(Tables& tables) {
+    auto& table = tables[0];
+    table[OFF(front_face)] = FrontFace;
+    table[OFF(screen_y_control)] = FrontFace;
+}
+
+void SetupDirtyPrimitiveTopology(Tables& tables) {
+    tables[0][OFF(draw.topology)] = PrimitiveTopology;
+}
+
+void SetupDirtyStencilOp(Tables& tables) {
+    auto& table = tables[0];
+    table[OFF(stencil_front_op_fail)] = StencilOp;
+    table[OFF(stencil_front_op_zfail)] = StencilOp;
+    table[OFF(stencil_front_op_zpass)] = StencilOp;
+    table[OFF(stencil_front_func_func)] = StencilOp;
+    table[OFF(stencil_back_op_fail)] = StencilOp;
+    table[OFF(stencil_back_op_zfail)] = StencilOp;
+    table[OFF(stencil_back_op_zpass)] = StencilOp;
+    table[OFF(stencil_back_func_func)] = StencilOp;
+
+    // Table 0 is used by StencilProperties
+    tables[1][OFF(stencil_two_side_enable)] = StencilOp;
+}
+
+void SetupDirtyStencilTestEnable(Tables& tables) {
+    tables[0][OFF(stencil_enable)] = StencilTestEnable;
+}
+
 } // Anonymous namespace
 } // Anonymous namespace
 
 
 StateTracker::StateTracker(Core::System& system)
 StateTracker::StateTracker(Core::System& system)
@@ -90,6 +150,14 @@ void StateTracker::Initialize() {
     SetupDirtyBlendConstants(tables);
     SetupDirtyBlendConstants(tables);
     SetupDirtyDepthBounds(tables);
     SetupDirtyDepthBounds(tables);
     SetupDirtyStencilProperties(tables);
     SetupDirtyStencilProperties(tables);
+    SetupDirtyCullMode(tables);
+    SetupDirtyDepthBoundsEnable(tables);
+    SetupDirtyDepthTestEnable(tables);
+    SetupDirtyDepthWriteEnable(tables);
+    SetupDirtyDepthCompareOp(tables);
+    SetupDirtyFrontFace(tables);
+    SetupDirtyPrimitiveTopology(tables);
+    SetupDirtyStencilOp(tables);
 }
 }
 
 
 void StateTracker::InvalidateCommandBufferState() {
 void StateTracker::InvalidateCommandBufferState() {

+ 50 - 0
src/video_core/renderer_vulkan/vk_state_tracker.h

@@ -26,6 +26,16 @@ enum : u8 {
     DepthBounds,
     DepthBounds,
     StencilProperties,
     StencilProperties,
 
 
+    CullMode,
+    DepthBoundsEnable,
+    DepthTestEnable,
+    DepthWriteEnable,
+    DepthCompareOp,
+    FrontFace,
+    PrimitiveTopology,
+    StencilOp,
+    StencilTestEnable,
+
     Last
     Last
 };
 };
 static_assert(Last <= std::numeric_limits<u8>::max());
 static_assert(Last <= std::numeric_limits<u8>::max());
@@ -64,6 +74,46 @@ public:
         return Exchange(Dirty::StencilProperties, false);
         return Exchange(Dirty::StencilProperties, false);
     }
     }
 
 
+    bool TouchCullMode() {
+        return Exchange(Dirty::CullMode, false);
+    }
+
+    bool TouchDepthBoundsTestEnable() {
+        return Exchange(Dirty::DepthBoundsEnable, false);
+    }
+
+    bool TouchDepthTestEnable() {
+        return Exchange(Dirty::DepthTestEnable, false);
+    }
+
+    bool TouchDepthBoundsEnable() {
+        return Exchange(Dirty::DepthBoundsEnable, false);
+    }
+
+    bool TouchDepthWriteEnable() {
+        return Exchange(Dirty::DepthWriteEnable, false);
+    }
+
+    bool TouchDepthCompareOp() {
+        return Exchange(Dirty::DepthCompareOp, false);
+    }
+
+    bool TouchFrontFace() {
+        return Exchange(Dirty::FrontFace, false);
+    }
+
+    bool TouchPrimitiveTopology() {
+        return Exchange(Dirty::PrimitiveTopology, false);
+    }
+
+    bool TouchStencilOp() {
+        return Exchange(Dirty::StencilOp, false);
+    }
+
+    bool TouchStencilTestEnable() {
+        return Exchange(Dirty::StencilTestEnable, false);
+    }
+
 private:
 private:
     bool Exchange(std::size_t id, bool new_value) const noexcept {
     bool Exchange(std::size_t id, bool new_value) const noexcept {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         auto& flags = system.GPU().Maxwell3D().dirty.flags;

+ 10 - 0
src/video_core/renderer_vulkan/wrapper.cpp

@@ -88,6 +88,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
     X(vkCmdSetStencilWriteMask);
     X(vkCmdSetStencilWriteMask);
     X(vkCmdSetViewport);
     X(vkCmdSetViewport);
     X(vkCmdWaitEvents);
     X(vkCmdWaitEvents);
+    X(vkCmdBindVertexBuffers2EXT);
+    X(vkCmdSetCullModeEXT);
+    X(vkCmdSetDepthBoundsTestEnableEXT);
+    X(vkCmdSetDepthCompareOpEXT);
+    X(vkCmdSetDepthTestEnableEXT);
+    X(vkCmdSetDepthWriteEnableEXT);
+    X(vkCmdSetFrontFaceEXT);
+    X(vkCmdSetPrimitiveTopologyEXT);
+    X(vkCmdSetStencilOpEXT);
+    X(vkCmdSetStencilTestEnableEXT);
     X(vkCreateBuffer);
     X(vkCreateBuffer);
     X(vkCreateBufferView);
     X(vkCreateBufferView);
     X(vkCreateCommandPool);
     X(vkCreateCommandPool);

+ 54 - 0
src/video_core/renderer_vulkan/wrapper.h

@@ -207,6 +207,16 @@ struct DeviceDispatch : public InstanceDispatch {
     PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
     PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
     PFN_vkCmdSetViewport vkCmdSetViewport;
     PFN_vkCmdSetViewport vkCmdSetViewport;
     PFN_vkCmdWaitEvents vkCmdWaitEvents;
     PFN_vkCmdWaitEvents vkCmdWaitEvents;
+    PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT;
+    PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT;
+    PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT;
+    PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT;
+    PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT;
+    PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT;
+    PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT;
+    PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT;
+    PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT;
+    PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT;
     PFN_vkCreateBuffer vkCreateBuffer;
     PFN_vkCreateBuffer vkCreateBuffer;
     PFN_vkCreateBufferView vkCreateBufferView;
     PFN_vkCreateBufferView vkCreateBufferView;
     PFN_vkCreateCommandPool vkCreateCommandPool;
     PFN_vkCreateCommandPool vkCreateCommandPool;
@@ -969,6 +979,50 @@ public:
                              buffer_barriers.data(), image_barriers.size(), image_barriers.data());
                              buffer_barriers.data(), image_barriers.size(), image_barriers.data());
     }
     }
 
 
+    void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers,
+                               const VkDeviceSize* offsets, const VkDeviceSize* sizes,
+                               const VkDeviceSize* strides) const noexcept {
+        dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets,
+                                        sizes, strides);
+    }
+
+    void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept {
+        dld->vkCmdSetCullModeEXT(handle, cull_mode);
+    }
+
+    void SetDepthBoundsTestEnableEXT(bool enable) const noexcept {
+        dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
+    }
+
+    void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept {
+        dld->vkCmdSetDepthCompareOpEXT(handle, compare_op);
+    }
+
+    void SetDepthTestEnableEXT(bool enable) const noexcept {
+        dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
+    }
+
+    void SetDepthWriteEnableEXT(bool enable) const noexcept {
+        dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
+    }
+
+    void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
+        dld->vkCmdSetFrontFaceEXT(handle, front_face);
+    }
+
+    void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept {
+        dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology);
+    }
+
+    void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op,
+                         VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept {
+        dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op);
+    }
+
+    void SetStencilTestEnableEXT(bool enable) const noexcept {
+        dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
+    }
+
     void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
     void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
                                          const VkDeviceSize* offsets,
                                          const VkDeviceSize* offsets,
                                          const VkDeviceSize* sizes) const noexcept {
                                          const VkDeviceSize* sizes) const noexcept {

+ 7 - 5
src/yuzu/configuration/config.cpp

@@ -212,7 +212,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
 // UISetting::values.shortcuts, which is alphabetically ordered.
 // UISetting::values.shortcuts, which is alphabetically ordered.
 // clang-format off
 // clang-format off
 const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
 const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
-    {QStringLiteral("Capture Screenshot"),       QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
+    {QStringLiteral("Capture Screenshot"),       QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
     {QStringLiteral("Change Docked Mode"),       QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
     {QStringLiteral("Change Docked Mode"),       QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
     {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
     {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
     {QStringLiteral("Decrease Speed Limit"),     QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
     {QStringLiteral("Decrease Speed Limit"),     QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
@@ -220,8 +220,8 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
     {QStringLiteral("Exit yuzu"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
     {QStringLiteral("Exit yuzu"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
     {QStringLiteral("Fullscreen"),               QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
     {QStringLiteral("Fullscreen"),               QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
     {QStringLiteral("Increase Speed Limit"),     QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
     {QStringLiteral("Increase Speed Limit"),     QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
-    {QStringLiteral("Load Amiibo"),              QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}},
-    {QStringLiteral("Load File"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}},
+    {QStringLiteral("Load Amiibo"),              QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
+    {QStringLiteral("Load File"),                QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
     {QStringLiteral("Mute Audio"),               QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
     {QStringLiteral("Mute Audio"),               QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
     {QStringLiteral("Restart Emulation"),        QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
     {QStringLiteral("Restart Emulation"),        QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
     {QStringLiteral("Stop Emulation"),           QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
     {QStringLiteral("Stop Emulation"),           QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
@@ -665,11 +665,13 @@ void Config::ReadShortcutValues() {
         const auto& [keyseq, context] = shortcut;
         const auto& [keyseq, context] = shortcut;
         qt_config->beginGroup(group);
         qt_config->beginGroup(group);
         qt_config->beginGroup(name);
         qt_config->beginGroup(name);
+        // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
+        // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
+        // a file dialog in windowed mode
         UISettings::values.shortcuts.push_back(
         UISettings::values.shortcuts.push_back(
             {name,
             {name,
              group,
              group,
-             {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(),
-              ReadSetting(QStringLiteral("Context"), context).toInt()}});
+             {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}});
         qt_config->endGroup();
         qt_config->endGroup();
         qt_config->endGroup();
         qt_config->endGroup();
     }
     }

+ 3 - 3
src/yuzu/configuration/configure_graphics.cpp

@@ -28,9 +28,9 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
 
 
     SetConfiguration();
     SetConfiguration();
 
 
-    connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
+    connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this,
             [this] { UpdateDeviceComboBox(); });
             [this] { UpdateDeviceComboBox(); });
-    connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
+    connect(ui->device, qOverload<int>(&QComboBox::activated), this,
             [this](int device) { UpdateDeviceSelection(device); });
             [this](int device) { UpdateDeviceSelection(device); });
 
 
     connect(ui->bg_button, &QPushButton::clicked, this, [this] {
     connect(ui->bg_button, &QPushButton::clicked, this, [this] {
@@ -112,7 +112,7 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
         enabled = false;
         enabled = false;
         break;
         break;
     case Settings::RendererBackend::Vulkan:
     case Settings::RendererBackend::Vulkan:
-        for (const auto device : vulkan_devices) {
+        for (const auto& device : vulkan_devices) {
             ui->device->addItem(device);
             ui->device->addItem(device);
         }
         }
         ui->device->setCurrentIndex(vulkan_device);
         ui->device->setCurrentIndex(vulkan_device);

+ 1 - 1
src/yuzu/main.cpp

@@ -753,7 +753,7 @@ void GMainWindow::InitializeHotkeys() {
             });
             });
     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this),
     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this),
             &QShortcut::activated, this, [&] {
             &QShortcut::activated, this, [&] {
-                if (emu_thread->IsRunning()) {
+                if (emu_thread != nullptr && emu_thread->IsRunning()) {
                     OnCaptureScreenshot();
                     OnCaptureScreenshot();
                 }
                 }
             });
             });