summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp2
-rw-r--r--src/audio_core/opus/decoder.cpp2
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp9
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h4
-rw-r--r--src/core/hle/service/hid/controllers/npad.h7
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp7
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.cpp99
-rw-r--r--src/core/hle/service/hid/hid_firmware_settings.h54
-rw-r--r--src/core/hle/service/hid/hid_server.cpp146
-rw-r--r--src/core/hle/service/hid/hid_server.h5
-rw-r--r--src/core/hle/service/hid/resource_manager.cpp12
-rw-r--r--src/core/hle/service/hid/resource_manager.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp27
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_consumer.h9
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_core.cpp12
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_core.h3
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.cpp19
-rw-r--r--src/core/hle/service/nvnflinger/buffer_queue_producer.h3
-rw-r--r--src/core/hle/service/nvnflinger/buffer_slot.h2
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.cpp20
-rw-r--r--src/core/hle/service/nvnflinger/consumer_base.h2
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp2
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp22
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h2
-rw-r--r--src/core/hle/service/nvnflinger/status.h2
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp34
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.h25
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp6
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp6
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp56
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h2
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp4
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h2
-rw-r--r--src/shader_recompiler/frontend/ir/modifiers.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp29
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp10
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h5
-rw-r--r--src/video_core/engines/fermi_2d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h1
45 files changed, 496 insertions, 181 deletions
diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
index 7f1ed0450..05cf3975d 100644
--- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
+++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp
@@ -12,7 +12,7 @@ bool IsValidChannelCount(u32 channel_count) {
12} 12}
13 13
14bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { 14bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
15 return total_stream_count > 0 && stereo_stream_count > 0 && 15 return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 &&
16 stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); 16 stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count);
17} 17}
18} // namespace 18} // namespace
diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp
index c6fd45f47..b7fed5304 100644
--- a/src/audio_core/opus/decoder.cpp
+++ b/src/audio_core/opus/decoder.cpp
@@ -148,7 +148,7 @@ Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out
148 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; 148 auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())};
149 OpusPacketHeader header{ReverseHeader(*header_p)}; 149 OpusPacketHeader header{ReverseHeader(*header_p)};
150 150
151 LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", 151 LOG_TRACE(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}",
152 header.size, input_data.size_bytes(), in_data.size_bytes()); 152 header.size, input_data.size_bytes(), in_data.size_bytes());
153 153
154 R_UNLESS(in_data.size_bytes() >= header.size && 154 R_UNLESS(in_data.size_bytes() >= header.size &&
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 3aa2e4340..597890655 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -523,6 +523,8 @@ add_library(core STATIC
523 hle/service/hid/hid.h 523 hle/service/hid/hid.h
524 hle/service/hid/hid_debug_server.cpp 524 hle/service/hid/hid_debug_server.cpp
525 hle/service/hid/hid_debug_server.h 525 hle/service/hid/hid_debug_server.h
526 hle/service/hid/hid_firmware_settings.cpp
527 hle/service/hid/hid_firmware_settings.h
526 hle/service/hid/hid_server.cpp 528 hle/service/hid/hid_server.cpp
527 hle/service/hid/hid_server.h 529 hle/service/hid/hid_server.h
528 hle/service/hid/hid_system_server.cpp 530 hle/service/hid/hid_system_server.cpp
@@ -723,6 +725,7 @@ add_library(core STATIC
723 hle/service/nvnflinger/producer_listener.h 725 hle/service/nvnflinger/producer_listener.h
724 hle/service/nvnflinger/status.h 726 hle/service/nvnflinger/status.h
725 hle/service/nvnflinger/ui/fence.h 727 hle/service/nvnflinger/ui/fence.h
728 hle/service/nvnflinger/ui/graphic_buffer.cpp
726 hle/service/nvnflinger/ui/graphic_buffer.h 729 hle/service/nvnflinger/ui/graphic_buffer.h
727 hle/service/nvnflinger/window.h 730 hle/service/nvnflinger/window.h
728 hle/service/olsc/olsc.cpp 731 hle/service/olsc/olsc.cpp
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index c58d67d7d..0bcd87062 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -8,12 +8,17 @@ namespace Service::HID {
8ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {} 8ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
9ControllerBase::~ControllerBase() = default; 9ControllerBase::~ControllerBase() = default;
10 10
11void ControllerBase::ActivateController() { 11Result ControllerBase::Activate() {
12 if (is_activated) { 12 if (is_activated) {
13 return; 13 return ResultSuccess;
14 } 14 }
15 is_activated = true; 15 is_activated = true;
16 OnInit(); 16 OnInit();
17 return ResultSuccess;
18}
19
20Result ControllerBase::Activate(u64 aruid) {
21 return Activate();
17} 22}
18 23
19void ControllerBase::DeactivateController() { 24void ControllerBase::DeactivateController() {
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index d6f7a5073..9a44ee41e 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/result.h"
7 8
8namespace Core::Timing { 9namespace Core::Timing {
9class CoreTiming; 10class CoreTiming;
@@ -31,7 +32,8 @@ public:
31 // When the controller is requesting a motion update for the shared memory 32 // When the controller is requesting a motion update for the shared memory
32 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {} 33 virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
33 34
34 void ActivateController(); 35 Result Activate();
36 Result Activate(u64 aruid);
35 37
36 void DeactivateController(); 38 void DeactivateController();
37 39
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 949e58a4c..e23b4986c 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -86,6 +86,13 @@ public:
86 Default = 3, 86 Default = 3,
87 }; 87 };
88 88
89 enum class NpadRevision : u32 {
90 Revision0 = 0,
91 Revision1 = 1,
92 Revision2 = 2,
93 Revision3 = 3,
94 };
95
89 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 96 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
90 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 97 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
91 98
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp
index 73a2a2b91..51a18335f 100644
--- a/src/core/hle/service/hid/controllers/palma.cpp
+++ b/src/core/hle/service/hid/controllers/palma.cpp
@@ -44,7 +44,7 @@ Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
44 if (handle.npad_id != active_handle.npad_id) { 44 if (handle.npad_id != active_handle.npad_id) {
45 return InvalidPalmaHandle; 45 return InvalidPalmaHandle;
46 } 46 }
47 ActivateController(); 47 Activate();
48 return ResultSuccess; 48 return ResultSuccess;
49} 49}
50 50
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 801a4d08f..1b7381d8d 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -3,6 +3,7 @@
3 3
4#include "core/hle/service/hid/hid.h" 4#include "core/hle/service/hid/hid.h"
5#include "core/hle/service/hid/hid_debug_server.h" 5#include "core/hle/service/hid/hid_debug_server.h"
6#include "core/hle/service/hid/hid_firmware_settings.h"
6#include "core/hle/service/hid/hid_server.h" 7#include "core/hle/service/hid/hid_server.h"
7#include "core/hle/service/hid/hid_system_server.h" 8#include "core/hle/service/hid/hid_system_server.h"
8#include "core/hle/service/hid/hidbus.h" 9#include "core/hle/service/hid/hidbus.h"
@@ -16,9 +17,11 @@ namespace Service::HID {
16void LoopProcess(Core::System& system) { 17void LoopProcess(Core::System& system) {
17 auto server_manager = std::make_unique<ServerManager>(system); 18 auto server_manager = std::make_unique<ServerManager>(system);
18 std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system); 19 std::shared_ptr<ResourceManager> resouce_manager = std::make_shared<ResourceManager>(system);
20 std::shared_ptr<HidFirmwareSettings> firmware_settings =
21 std::make_shared<HidFirmwareSettings>();
19 22
20 server_manager->RegisterNamedService("hid", 23 server_manager->RegisterNamedService(
21 std::make_shared<IHidServer>(system, resouce_manager)); 24 "hid", std::make_shared<IHidServer>(system, resouce_manager, firmware_settings));
22 server_manager->RegisterNamedService( 25 server_manager->RegisterNamedService(
23 "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager)); 26 "hid:dbg", std::make_shared<IHidDebugServer>(system, resouce_manager));
24 server_manager->RegisterNamedService( 27 server_manager->RegisterNamedService(
diff --git a/src/core/hle/service/hid/hid_firmware_settings.cpp b/src/core/hle/service/hid/hid_firmware_settings.cpp
new file mode 100644
index 000000000..59bd6825c
--- /dev/null
+++ b/src/core/hle/service/hid/hid_firmware_settings.cpp
@@ -0,0 +1,99 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/hid/hid_firmware_settings.h"
5
6namespace Service::HID {
7
8HidFirmwareSettings::HidFirmwareSettings() {
9 LoadSettings(true);
10}
11
12void HidFirmwareSettings::Reload() {
13 LoadSettings(true);
14}
15
16void HidFirmwareSettings::LoadSettings(bool reload_config) {
17 if (is_initalized && !reload_config) {
18 return;
19 }
20
21 // TODO: Use nn::settings::fwdbg::GetSettingsItemValue to load config values
22
23 is_debug_pad_enabled = true;
24 is_device_managed = true;
25 is_touch_i2c_managed = is_device_managed;
26 is_future_devices_emulated = false;
27 is_mcu_hardware_error_emulated = false;
28 is_rail_enabled = true;
29 is_firmware_update_failure_emulated = false;
30 is_firmware_update_failure = {};
31 is_ble_disabled = false;
32 is_dscale_disabled = false;
33 is_handheld_forced = true;
34 features_per_id_disabled = {};
35 is_touch_firmware_auto_update_disabled = false;
36 is_initalized = true;
37}
38
39bool HidFirmwareSettings::IsDebugPadEnabled() {
40 LoadSettings(false);
41 return is_debug_pad_enabled;
42}
43
44bool HidFirmwareSettings::IsDeviceManaged() {
45 LoadSettings(false);
46 return is_device_managed;
47}
48
49bool HidFirmwareSettings::IsEmulateFutureDevice() {
50 LoadSettings(false);
51 return is_future_devices_emulated;
52}
53
54bool HidFirmwareSettings::IsTouchI2cManaged() {
55 LoadSettings(false);
56 return is_touch_i2c_managed;
57}
58
59bool HidFirmwareSettings::IsHandheldForced() {
60 LoadSettings(false);
61 return is_handheld_forced;
62}
63
64bool HidFirmwareSettings::IsRailEnabled() {
65 LoadSettings(false);
66 return is_rail_enabled;
67}
68
69bool HidFirmwareSettings::IsHardwareErrorEmulated() {
70 LoadSettings(false);
71 return is_mcu_hardware_error_emulated;
72}
73
74bool HidFirmwareSettings::IsBleDisabled() {
75 LoadSettings(false);
76 return is_ble_disabled;
77}
78
79bool HidFirmwareSettings::IsDscaleDisabled() {
80 LoadSettings(false);
81 return is_dscale_disabled;
82}
83
84bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
85 LoadSettings(false);
86 return is_touch_firmware_auto_update_disabled;
87}
88
89HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
90 LoadSettings(false);
91 return is_firmware_update_failure;
92}
93
94HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() {
95 LoadSettings(false);
96 return features_per_id_disabled;
97}
98
99} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_firmware_settings.h b/src/core/hle/service/hid/hid_firmware_settings.h
new file mode 100644
index 000000000..6c10c440b
--- /dev/null
+++ b/src/core/hle/service/hid/hid_firmware_settings.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::HID {
9
10/// Loads firmware config from nn::settings::fwdbg
11class HidFirmwareSettings {
12public:
13 using FirmwareSetting = std::array<u8, 4>;
14 using FeaturesPerId = std::array<bool, 0xA8>;
15
16 HidFirmwareSettings();
17
18 void Reload();
19 void LoadSettings(bool reload_config);
20
21 bool IsDebugPadEnabled();
22 bool IsDeviceManaged();
23 bool IsEmulateFutureDevice();
24 bool IsTouchI2cManaged();
25 bool IsHandheldForced();
26 bool IsRailEnabled();
27 bool IsHardwareErrorEmulated();
28 bool IsBleDisabled();
29 bool IsDscaleDisabled();
30 bool IsTouchAutoUpdateDisabled();
31
32 FirmwareSetting GetFirmwareUpdateFailure();
33 FeaturesPerId FeaturesDisabledPerId();
34
35private:
36 bool is_initalized{};
37
38 // Debug settings
39 bool is_debug_pad_enabled{};
40 bool is_device_managed{};
41 bool is_touch_i2c_managed{};
42 bool is_future_devices_emulated{};
43 bool is_mcu_hardware_error_emulated{};
44 bool is_rail_enabled{};
45 bool is_firmware_update_failure_emulated{};
46 bool is_ble_disabled{};
47 bool is_dscale_disabled{};
48 bool is_handheld_forced{};
49 bool is_touch_firmware_auto_update_disabled{};
50 FirmwareSetting is_firmware_update_failure{};
51 FeaturesPerId features_per_id_disabled{};
52};
53
54} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index 2fb31cf47..0be6a7186 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -10,6 +10,7 @@
10#include "core/hle/kernel/k_transfer_memory.h" 10#include "core/hle/kernel/k_transfer_memory.h"
11#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/hid/errors.h" 12#include "core/hle/service/hid/errors.h"
13#include "core/hle/service/hid/hid_firmware_settings.h"
13#include "core/hle/service/hid/hid_server.h" 14#include "core/hle/service/hid/hid_server.h"
14#include "core/hle/service/hid/resource_manager.h" 15#include "core/hle/service/hid/resource_manager.h"
15#include "core/hle/service/ipc_helpers.h" 16#include "core/hle/service/ipc_helpers.h"
@@ -64,8 +65,9 @@ private:
64 std::shared_ptr<ResourceManager> resource_manager; 65 std::shared_ptr<ResourceManager> resource_manager;
65}; 66};
66 67
67IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) 68IHidServer::IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
68 : ServiceFramework{system_, "hid"}, resource_manager{resource} { 69 std::shared_ptr<HidFirmwareSettings> settings)
70 : ServiceFramework{system_, "hid"}, resource_manager{resource}, firmware_settings{settings} {
69 // clang-format off 71 // clang-format off
70 static const FunctionInfo functions[] = { 72 static const FunctionInfo functions[] = {
71 {0, &IHidServer::CreateAppletResource, "CreateAppletResource"}, 73 {0, &IHidServer::CreateAppletResource, "CreateAppletResource"},
@@ -230,48 +232,87 @@ void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {
230 IPC::RequestParser rp{ctx}; 232 IPC::RequestParser rp{ctx};
231 const auto applet_resource_user_id{rp.Pop<u64>()}; 233 const auto applet_resource_user_id{rp.Pop<u64>()};
232 234
233 GetResourceManager()->ActivateController(HidController::DebugPad);
234
235 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 235 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
236 236
237 Result result = ResultSuccess;
238 auto& debug_pad =
239 GetResourceManager()->GetController<Controller_DebugPad>(HidController::DebugPad);
240
241 if (!firmware_settings->IsDeviceManaged()) {
242 result = debug_pad.Activate();
243 }
244
245 if (result.IsSuccess()) {
246 result = debug_pad.Activate(applet_resource_user_id);
247 }
248
237 IPC::ResponseBuilder rb{ctx, 2}; 249 IPC::ResponseBuilder rb{ctx, 2};
238 rb.Push(ResultSuccess); 250 rb.Push(result);
239} 251}
240 252
241void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) { 253void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) {
242 IPC::RequestParser rp{ctx}; 254 IPC::RequestParser rp{ctx};
243 const auto applet_resource_user_id{rp.Pop<u64>()}; 255 const auto applet_resource_user_id{rp.Pop<u64>()};
244 256
245 GetResourceManager()->ActivateController(HidController::Touchscreen);
246
247 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 257 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
248 258
259 Result result = ResultSuccess;
260 auto& touch_screen =
261 GetResourceManager()->GetController<Controller_Touchscreen>(HidController::Touchscreen);
262
263 if (!firmware_settings->IsDeviceManaged()) {
264 result = touch_screen.Activate();
265 }
266
267 if (result.IsSuccess()) {
268 result = touch_screen.Activate(applet_resource_user_id);
269 }
270
249 IPC::ResponseBuilder rb{ctx, 2}; 271 IPC::ResponseBuilder rb{ctx, 2};
250 rb.Push(ResultSuccess); 272 rb.Push(result);
251} 273}
252 274
253void IHidServer::ActivateMouse(HLERequestContext& ctx) { 275void IHidServer::ActivateMouse(HLERequestContext& ctx) {
254 IPC::RequestParser rp{ctx}; 276 IPC::RequestParser rp{ctx};
255 const auto applet_resource_user_id{rp.Pop<u64>()}; 277 const auto applet_resource_user_id{rp.Pop<u64>()};
256 278
257 GetResourceManager()->ActivateController(HidController::Mouse);
258
259 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 279 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
260 280
281 Result result = ResultSuccess;
282 auto& mouse = GetResourceManager()->GetController<Controller_Mouse>(HidController::Mouse);
283
284 if (!firmware_settings->IsDeviceManaged()) {
285 result = mouse.Activate();
286 }
287
288 if (result.IsSuccess()) {
289 result = mouse.Activate(applet_resource_user_id);
290 }
291
261 IPC::ResponseBuilder rb{ctx, 2}; 292 IPC::ResponseBuilder rb{ctx, 2};
262 rb.Push(ResultSuccess); 293 rb.Push(result);
263} 294}
264 295
265void IHidServer::ActivateKeyboard(HLERequestContext& ctx) { 296void IHidServer::ActivateKeyboard(HLERequestContext& ctx) {
266 IPC::RequestParser rp{ctx}; 297 IPC::RequestParser rp{ctx};
267 const auto applet_resource_user_id{rp.Pop<u64>()}; 298 const auto applet_resource_user_id{rp.Pop<u64>()};
268 299
269 GetResourceManager()->ActivateController(HidController::Keyboard);
270
271 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 300 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
272 301
302 Result result = ResultSuccess;
303 auto& keyboard =
304 GetResourceManager()->GetController<Controller_Keyboard>(HidController::Keyboard);
305
306 if (!firmware_settings->IsDeviceManaged()) {
307 result = keyboard.Activate();
308 }
309
310 if (result.IsSuccess()) {
311 result = keyboard.Activate(applet_resource_user_id);
312 }
313
273 IPC::ResponseBuilder rb{ctx, 2}; 314 IPC::ResponseBuilder rb{ctx, 2};
274 rb.Push(ResultSuccess); 315 rb.Push(result);
275} 316}
276 317
277void IHidServer::SendKeyboardLockKeyEvent(HLERequestContext& ctx) { 318void IHidServer::SendKeyboardLockKeyEvent(HLERequestContext& ctx) {
@@ -898,7 +939,7 @@ void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx)
898void IHidServer::ActivateGesture(HLERequestContext& ctx) { 939void IHidServer::ActivateGesture(HLERequestContext& ctx) {
899 IPC::RequestParser rp{ctx}; 940 IPC::RequestParser rp{ctx};
900 struct Parameters { 941 struct Parameters {
901 u32 unknown; 942 u32 basic_gesture_id;
902 INSERT_PADDING_WORDS_NOINIT(1); 943 INSERT_PADDING_WORDS_NOINIT(1);
903 u64 applet_resource_user_id; 944 u64 applet_resource_user_id;
904 }; 945 };
@@ -906,13 +947,23 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) {
906 947
907 const auto parameters{rp.PopRaw<Parameters>()}; 948 const auto parameters{rp.PopRaw<Parameters>()};
908 949
909 GetResourceManager()->ActivateController(HidController::Gesture); 950 LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}",
951 parameters.basic_gesture_id, parameters.applet_resource_user_id);
910 952
911 LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", 953 Result result = ResultSuccess;
912 parameters.unknown, parameters.applet_resource_user_id); 954 auto& gesture = GetResourceManager()->GetController<Controller_Gesture>(HidController::Gesture);
955
956 if (!firmware_settings->IsDeviceManaged()) {
957 result = gesture.Activate();
958 }
959
960 if (result.IsSuccess()) {
961 // TODO: Use gesture id here
962 result = gesture.Activate(parameters.applet_resource_user_id);
963 }
913 964
914 IPC::ResponseBuilder rb{ctx, 2}; 965 IPC::ResponseBuilder rb{ctx, 2};
915 rb.Push(ResultSuccess); 966 rb.Push(result);
916} 967}
917 968
918void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) { 969void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) {
@@ -969,22 +1020,25 @@ void IHidServer::ActivateNpad(HLERequestContext& ctx) {
969 IPC::RequestParser rp{ctx}; 1020 IPC::RequestParser rp{ctx};
970 const auto applet_resource_user_id{rp.Pop<u64>()}; 1021 const auto applet_resource_user_id{rp.Pop<u64>()};
971 1022
972 GetResourceManager()->ActivateController(HidController::NPad);
973
974 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1023 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
975 1024
1025 auto& npad = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad);
1026
1027 // TODO: npad->SetRevision(applet_resource_user_id, NpadRevision::Revision0);
1028 const Result result = npad.Activate(applet_resource_user_id);
1029
976 IPC::ResponseBuilder rb{ctx, 2}; 1030 IPC::ResponseBuilder rb{ctx, 2};
977 rb.Push(ResultSuccess); 1031 rb.Push(result);
978} 1032}
979 1033
980void IHidServer::DeactivateNpad(HLERequestContext& ctx) { 1034void IHidServer::DeactivateNpad(HLERequestContext& ctx) {
981 IPC::RequestParser rp{ctx}; 1035 IPC::RequestParser rp{ctx};
982 const auto applet_resource_user_id{rp.Pop<u64>()}; 1036 const auto applet_resource_user_id{rp.Pop<u64>()};
983 1037
984 GetResourceManager()->DeactivateController(HidController::NPad);
985
986 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1038 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
987 1039
1040 // This function does nothing since 10.0.0+
1041
988 IPC::ResponseBuilder rb{ctx, 2}; 1042 IPC::ResponseBuilder rb{ctx, 2};
989 rb.Push(ResultSuccess); 1043 rb.Push(ResultSuccess);
990} 1044}
@@ -1053,10 +1107,9 @@ void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) {
1053} 1107}
1054 1108
1055void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { 1109void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1056 // Should have no effect with how our npad sets up the data
1057 IPC::RequestParser rp{ctx}; 1110 IPC::RequestParser rp{ctx};
1058 struct Parameters { 1111 struct Parameters {
1059 s32 revision; 1112 Controller_NPad::NpadRevision revision;
1060 INSERT_PADDING_WORDS_NOINIT(1); 1113 INSERT_PADDING_WORDS_NOINIT(1);
1061 u64 applet_resource_user_id; 1114 u64 applet_resource_user_id;
1062 }; 1115 };
@@ -1064,13 +1117,16 @@ void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) {
1064 1117
1065 const auto parameters{rp.PopRaw<Parameters>()}; 1118 const auto parameters{rp.PopRaw<Parameters>()};
1066 1119
1067 GetResourceManager()->ActivateController(HidController::NPad);
1068
1069 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, 1120 LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision,
1070 parameters.applet_resource_user_id); 1121 parameters.applet_resource_user_id);
1071 1122
1123 auto& npad = GetResourceManager()->GetController<Controller_NPad>(HidController::NPad);
1124
1125 // TODO: npad->SetRevision(applet_resource_user_id, revision);
1126 const auto result = npad.Activate(parameters.applet_resource_user_id);
1127
1072 IPC::ResponseBuilder rb{ctx, 2}; 1128 IPC::ResponseBuilder rb{ctx, 2};
1073 rb.Push(ResultSuccess); 1129 rb.Push(result);
1074} 1130}
1075 1131
1076void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) { 1132void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) {
@@ -1718,12 +1774,22 @@ void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) {
1718 IPC::RequestParser rp{ctx}; 1774 IPC::RequestParser rp{ctx};
1719 const auto applet_resource_user_id{rp.Pop<u64>()}; 1775 const auto applet_resource_user_id{rp.Pop<u64>()};
1720 1776
1721 GetResourceManager()->ActivateController(HidController::ConsoleSixAxisSensor); 1777 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1722 1778
1723 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1779 Result result = ResultSuccess;
1780 auto console_sixaxis = GetResourceManager()->GetController<Controller_ConsoleSixAxis>(
1781 HidController::ConsoleSixAxisSensor);
1782
1783 if (!firmware_settings->IsDeviceManaged()) {
1784 result = console_sixaxis.Activate();
1785 }
1786
1787 if (result.IsSuccess()) {
1788 result = console_sixaxis.Activate(applet_resource_user_id);
1789 }
1724 1790
1725 IPC::ResponseBuilder rb{ctx, 2}; 1791 IPC::ResponseBuilder rb{ctx, 2};
1726 rb.Push(ResultSuccess); 1792 rb.Push(result);
1727} 1793}
1728 1794
1729void IHidServer::StartConsoleSixAxisSensor(HLERequestContext& ctx) { 1795void IHidServer::StartConsoleSixAxisSensor(HLERequestContext& ctx) {
@@ -1770,9 +1836,19 @@ void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) {
1770 IPC::RequestParser rp{ctx}; 1836 IPC::RequestParser rp{ctx};
1771 const auto applet_resource_user_id{rp.Pop<u64>()}; 1837 const auto applet_resource_user_id{rp.Pop<u64>()};
1772 1838
1773 GetResourceManager()->ActivateController(HidController::ConsoleSixAxisSensor); 1839 LOG_INFO(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1774 1840
1775 LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1841 Result result = ResultSuccess;
1842 auto console_sixaxis = GetResourceManager()->GetController<Controller_ConsoleSixAxis>(
1843 HidController::ConsoleSixAxisSensor);
1844
1845 if (!firmware_settings->IsDeviceManaged()) {
1846 result = console_sixaxis.Activate();
1847 }
1848
1849 if (result.IsSuccess()) {
1850 console_sixaxis.Activate(applet_resource_user_id);
1851 }
1776 1852
1777 IPC::ResponseBuilder rb{ctx, 2}; 1853 IPC::ResponseBuilder rb{ctx, 2};
1778 rb.Push(ResultSuccess); 1854 rb.Push(ResultSuccess);
@@ -1837,7 +1913,7 @@ void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) {
1837 // Activate console six axis controller 1913 // Activate console six axis controller
1838 GetResourceManager() 1914 GetResourceManager()
1839 ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) 1915 ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
1840 .ActivateController(); 1916 .Activate();
1841 1917
1842 GetResourceManager() 1918 GetResourceManager()
1843 ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor) 1919 ->GetController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor)
diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h
index 98353b0db..eb2e8e7f4 100644
--- a/src/core/hle/service/hid/hid_server.h
+++ b/src/core/hle/service/hid/hid_server.h
@@ -11,10 +11,12 @@ class System;
11 11
12namespace Service::HID { 12namespace Service::HID {
13class ResourceManager; 13class ResourceManager;
14class HidFirmwareSettings;
14 15
15class IHidServer final : public ServiceFramework<IHidServer> { 16class IHidServer final : public ServiceFramework<IHidServer> {
16public: 17public:
17 explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); 18 explicit IHidServer(Core::System& system_, std::shared_ptr<ResourceManager> resource,
19 std::shared_ptr<HidFirmwareSettings> settings);
18 ~IHidServer() override; 20 ~IHidServer() override;
19 21
20 std::shared_ptr<ResourceManager> GetResourceManager(); 22 std::shared_ptr<ResourceManager> GetResourceManager();
@@ -141,6 +143,7 @@ private:
141 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); 143 void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx);
142 144
143 std::shared_ptr<ResourceManager> resource_manager; 145 std::shared_ptr<ResourceManager> resource_manager;
146 std::shared_ptr<HidFirmwareSettings> firmware_settings;
144}; 147};
145 148
146} // namespace Service::HID 149} // namespace Service::HID
diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp
index 4e462f3b3..d6f42c646 100644
--- a/src/core/hle/service/hid/resource_manager.cpp
+++ b/src/core/hle/service/hid/resource_manager.cpp
@@ -59,8 +59,8 @@ void ResourceManager::Initialize() {
59 MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory); 59 MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory);
60 60
61 // Homebrew doesn't try to activate some controllers, so we activate them by default 61 // Homebrew doesn't try to activate some controllers, so we activate them by default
62 GetController<Controller_NPad>(HidController::NPad).ActivateController(); 62 GetController<Controller_NPad>(HidController::NPad).Activate();
63 GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController(); 63 GetController<Controller_Touchscreen>(HidController::Touchscreen).Activate();
64 64
65 GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); 65 GetController<Controller_Stubbed>(HidController::HomeButton).SetCommonHeaderOffset(0x4C00);
66 GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); 66 GetController<Controller_Stubbed>(HidController::SleepButton).SetCommonHeaderOffset(0x4E00);
@@ -73,14 +73,6 @@ void ResourceManager::Initialize() {
73 is_initialized = true; 73 is_initialized = true;
74} 74}
75 75
76void ResourceManager::ActivateController(HidController controller) {
77 controllers[static_cast<size_t>(controller)]->ActivateController();
78}
79
80void ResourceManager::DeactivateController(HidController controller) {
81 controllers[static_cast<size_t>(controller)]->DeactivateController();
82}
83
84void ResourceManager::UpdateControllers(std::uintptr_t user_data, 76void ResourceManager::UpdateControllers(std::uintptr_t user_data,
85 std::chrono::nanoseconds ns_late) { 77 std::chrono::nanoseconds ns_late) {
86 auto& core_timing = system.CoreTiming(); 78 auto& core_timing = system.CoreTiming();
diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h
index 81b17f9a9..34dbf36bc 100644
--- a/src/core/hle/service/hid/resource_manager.h
+++ b/src/core/hle/service/hid/resource_manager.h
@@ -55,8 +55,6 @@ public:
55 } 55 }
56 56
57 void Initialize(); 57 void Initialize();
58 void ActivateController(HidController controller);
59 void DeactivateController(HidController controller);
60 58
61 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 59 void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
62 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); 60 void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h
index 3da8cc3aa..7fd808f54 100644
--- a/src/core/hle/service/nvnflinger/buffer_item.h
+++ b/src/core/hle/service/nvnflinger/buffer_item.h
@@ -15,7 +15,7 @@
15 15
16namespace Service::android { 16namespace Service::android {
17 17
18struct GraphicBuffer; 18class GraphicBuffer;
19 19
20class BufferItem final { 20class BufferItem final {
21public: 21public:
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
index 51291539d..d91886bed 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp
@@ -5,7 +5,6 @@
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp 5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "core/hle/service/nvdrv/core/nvmap.h"
9#include "core/hle/service/nvnflinger/buffer_item.h" 8#include "core/hle/service/nvnflinger/buffer_item.h"
10#include "core/hle/service/nvnflinger/buffer_queue_consumer.h" 9#include "core/hle/service/nvnflinger/buffer_queue_consumer.h"
11#include "core/hle/service/nvnflinger/buffer_queue_core.h" 10#include "core/hle/service/nvnflinger/buffer_queue_core.h"
@@ -14,9 +13,8 @@
14 13
15namespace Service::android { 14namespace Service::android {
16 15
17BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, 16BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
18 Service::Nvidia::NvCore::NvMap& nvmap_) 17 : core{std::move(core_)}, slots{core->slots} {}
19 : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
20 18
21BufferQueueConsumer::~BufferQueueConsumer() = default; 19BufferQueueConsumer::~BufferQueueConsumer() = default;
22 20
@@ -136,8 +134,6 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
136 134
137 slots[slot].buffer_state = BufferState::Free; 135 slots[slot].buffer_state = BufferState::Free;
138 136
139 nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
140
141 listener = core->connected_producer_listener; 137 listener = core->connected_producer_listener;
142 138
143 LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot); 139 LOG_DEBUG(Service_Nvnflinger, "releasing slot {}", slot);
@@ -175,6 +171,25 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
175 return Status::NoError; 171 return Status::NoError;
176} 172}
177 173
174Status BufferQueueConsumer::Disconnect() {
175 LOG_DEBUG(Service_Nvnflinger, "called");
176
177 std::scoped_lock lock{core->mutex};
178
179 if (core->consumer_listener == nullptr) {
180 LOG_ERROR(Service_Nvnflinger, "no consumer is connected");
181 return Status::BadValue;
182 }
183
184 core->is_abandoned = true;
185 core->consumer_listener = nullptr;
186 core->queue.clear();
187 core->FreeAllBuffersLocked();
188 core->SignalDequeueCondition();
189
190 return Status::NoError;
191}
192
178Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) { 193Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
179 if (out_slot_mask == nullptr) { 194 if (out_slot_mask == nullptr) {
180 LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr"); 195 LOG_ERROR(Service_Nvnflinger, "out_slot_mask may not be nullptr");
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
index 50ed0bb5f..0a61e8dbd 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.h
@@ -13,10 +13,6 @@
13#include "core/hle/service/nvnflinger/buffer_queue_defs.h" 13#include "core/hle/service/nvnflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvnflinger/status.h" 14#include "core/hle/service/nvnflinger/status.h"
15 15
16namespace Service::Nvidia::NvCore {
17class NvMap;
18} // namespace Service::Nvidia::NvCore
19
20namespace Service::android { 16namespace Service::android {
21 17
22class BufferItem; 18class BufferItem;
@@ -25,19 +21,18 @@ class IConsumerListener;
25 21
26class BufferQueueConsumer final { 22class BufferQueueConsumer final {
27public: 23public:
28 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_, 24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
29 Service::Nvidia::NvCore::NvMap& nvmap_);
30 ~BufferQueueConsumer(); 25 ~BufferQueueConsumer();
31 26
32 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present); 27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
33 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence); 28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
34 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app); 29 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
30 Status Disconnect();
35 Status GetReleasedBuffers(u64* out_slot_mask); 31 Status GetReleasedBuffers(u64* out_slot_mask);
36 32
37private: 33private:
38 std::shared_ptr<BufferQueueCore> core; 34 std::shared_ptr<BufferQueueCore> core;
39 BufferQueueDefs::SlotsType& slots; 35 BufferQueueDefs::SlotsType& slots;
40 Service::Nvidia::NvCore::NvMap& nvmap;
41}; 36};
42 37
43} // namespace Service::android 38} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
index ed66f6f5b..4ed5e5978 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp
@@ -14,24 +14,12 @@ BufferQueueCore::BufferQueueCore() = default;
14 14
15BufferQueueCore::~BufferQueueCore() = default; 15BufferQueueCore::~BufferQueueCore() = default;
16 16
17void BufferQueueCore::NotifyShutdown() {
18 std::scoped_lock lock{mutex};
19
20 is_shutting_down = true;
21
22 SignalDequeueCondition();
23}
24
25void BufferQueueCore::SignalDequeueCondition() { 17void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_possible.store(true); 18 dequeue_possible.store(true);
27 dequeue_condition.notify_all(); 19 dequeue_condition.notify_all();
28} 20}
29 21
30bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { 22bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) {
31 if (is_shutting_down) {
32 return false;
33 }
34
35 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); 23 dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); });
36 dequeue_possible.store(false); 24 dequeue_possible.store(false);
37 25
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h
index 9164f08a0..e513d183b 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_core.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h
@@ -34,8 +34,6 @@ public:
34 BufferQueueCore(); 34 BufferQueueCore();
35 ~BufferQueueCore(); 35 ~BufferQueueCore();
36 36
37 void NotifyShutdown();
38
39private: 37private:
40 void SignalDequeueCondition(); 38 void SignalDequeueCondition();
41 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); 39 bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk);
@@ -74,7 +72,6 @@ private:
74 u32 transform_hint{}; 72 u32 transform_hint{};
75 bool is_allocating{}; 73 bool is_allocating{};
76 mutable std::condition_variable_any is_allocating_condition; 74 mutable std::condition_variable_any is_allocating_condition;
77 bool is_shutting_down{};
78}; 75};
79 76
80} // namespace Service::android 77} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
index 6e7a49658..5d8762d25 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp
@@ -13,7 +13,6 @@
13#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
14#include "core/hle/service/hle_ipc.h" 14#include "core/hle/service/hle_ipc.h"
15#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvdrv/core/nvmap.h"
17#include "core/hle/service/nvnflinger/buffer_queue_core.h" 16#include "core/hle/service/nvnflinger/buffer_queue_core.h"
18#include "core/hle/service/nvnflinger/buffer_queue_producer.h" 17#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
19#include "core/hle/service/nvnflinger/consumer_listener.h" 18#include "core/hle/service/nvnflinger/consumer_listener.h"
@@ -533,8 +532,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
533 item.is_droppable = core->dequeue_buffer_cannot_block || async; 532 item.is_droppable = core->dequeue_buffer_cannot_block || async;
534 item.swap_interval = swap_interval; 533 item.swap_interval = swap_interval;
535 534
536 nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true);
537
538 sticky_transform = sticky_transform_; 535 sticky_transform = sticky_transform_;
539 536
540 if (core->queue.empty()) { 537 if (core->queue.empty()) {
@@ -744,19 +741,13 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
744 return Status::NoError; 741 return Status::NoError;
745 } 742 }
746 743
747 // HACK: We are not Android. Remove handle for items in queue, and clear queue.
748 // Allows synchronous destruction of nvmap handles.
749 for (auto& item : core->queue) {
750 nvmap.FreeHandle(item.graphic_buffer->BufferId(), true);
751 }
752 core->queue.clear();
753
754 switch (api) { 744 switch (api) {
755 case NativeWindowApi::Egl: 745 case NativeWindowApi::Egl:
756 case NativeWindowApi::Cpu: 746 case NativeWindowApi::Cpu:
757 case NativeWindowApi::Media: 747 case NativeWindowApi::Media:
758 case NativeWindowApi::Camera: 748 case NativeWindowApi::Camera:
759 if (core->connected_api == api) { 749 if (core->connected_api == api) {
750 core->queue.clear();
760 core->FreeAllBuffersLocked(); 751 core->FreeAllBuffersLocked();
761 core->connected_producer_listener = nullptr; 752 core->connected_producer_listener = nullptr;
762 core->connected_api = NativeWindowApi::NoConnectedApi; 753 core->connected_api = NativeWindowApi::NoConnectedApi;
@@ -785,7 +776,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
785} 776}
786 777
787Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, 778Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
788 const std::shared_ptr<GraphicBuffer>& buffer) { 779 const std::shared_ptr<NvGraphicBuffer>& buffer) {
789 LOG_DEBUG(Service_Nvnflinger, "slot {}", slot); 780 LOG_DEBUG(Service_Nvnflinger, "slot {}", slot);
790 781
791 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { 782 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
@@ -796,7 +787,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
796 787
797 slots[slot] = {}; 788 slots[slot] = {};
798 slots[slot].fence = Fence::NoFence(); 789 slots[slot].fence = Fence::NoFence();
799 slots[slot].graphic_buffer = buffer; 790 slots[slot].graphic_buffer = std::make_shared<GraphicBuffer>(nvmap, buffer);
800 slots[slot].frame_number = 0; 791 slots[slot].frame_number = 0;
801 792
802 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for 793 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
@@ -839,7 +830,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
839 } 830 }
840 case TransactionId::SetPreallocatedBuffer: { 831 case TransactionId::SetPreallocatedBuffer: {
841 const auto slot = parcel_in.Read<s32>(); 832 const auto slot = parcel_in.Read<s32>();
842 const auto buffer = parcel_in.ReadObject<GraphicBuffer>(); 833 const auto buffer = parcel_in.ReadObject<NvGraphicBuffer>();
843 834
844 status = SetPreallocatedBuffer(slot, buffer); 835 status = SetPreallocatedBuffer(slot, buffer);
845 break; 836 break;
@@ -867,7 +858,7 @@ void BufferQueueProducer::Transact(HLERequestContext& ctx, TransactionId code, u
867 858
868 status = RequestBuffer(slot, &buf); 859 status = RequestBuffer(slot, &buf);
869 860
870 parcel_out.WriteFlattenedObject(buf); 861 parcel_out.WriteFlattenedObject<NvGraphicBuffer>(buf.get());
871 break; 862 break;
872 } 863 }
873 case TransactionId::QueueBuffer: { 864 case TransactionId::QueueBuffer: {
diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
index d4201c104..64c17d56c 100644
--- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h
+++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h
@@ -38,6 +38,7 @@ namespace Service::android {
38 38
39class BufferQueueCore; 39class BufferQueueCore;
40class IProducerListener; 40class IProducerListener;
41struct NvGraphicBuffer;
41 42
42class BufferQueueProducer final : public IBinder { 43class BufferQueueProducer final : public IBinder {
43public: 44public:
@@ -65,7 +66,7 @@ public:
65 bool producer_controlled_by_app, QueueBufferOutput* output); 66 bool producer_controlled_by_app, QueueBufferOutput* output);
66 67
67 Status Disconnect(NativeWindowApi api); 68 Status Disconnect(NativeWindowApi api);
68 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer); 69 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<NvGraphicBuffer>& buffer);
69 70
70private: 71private:
71 BufferQueueProducer(const BufferQueueProducer&) = delete; 72 BufferQueueProducer(const BufferQueueProducer&) = delete;
diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h
index d8c9dec3b..d25bca049 100644
--- a/src/core/hle/service/nvnflinger/buffer_slot.h
+++ b/src/core/hle/service/nvnflinger/buffer_slot.h
@@ -13,7 +13,7 @@
13 13
14namespace Service::android { 14namespace Service::android {
15 15
16struct GraphicBuffer; 16class GraphicBuffer;
17 17
18enum class BufferState : u32 { 18enum class BufferState : u32 {
19 Free = 0, 19 Free = 0,
diff --git a/src/core/hle/service/nvnflinger/consumer_base.cpp b/src/core/hle/service/nvnflinger/consumer_base.cpp
index 4dcda8dac..1059e72bf 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.cpp
+++ b/src/core/hle/service/nvnflinger/consumer_base.cpp
@@ -27,6 +27,26 @@ void ConsumerBase::Connect(bool controlled_by_app) {
27 consumer->Connect(shared_from_this(), controlled_by_app); 27 consumer->Connect(shared_from_this(), controlled_by_app);
28} 28}
29 29
30void ConsumerBase::Abandon() {
31 LOG_DEBUG(Service_Nvnflinger, "called");
32
33 std::scoped_lock lock{mutex};
34
35 if (!is_abandoned) {
36 this->AbandonLocked();
37 is_abandoned = true;
38 }
39}
40
41void ConsumerBase::AbandonLocked() {
42 for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
43 this->FreeBufferLocked(i);
44 }
45 // disconnect from the BufferQueue
46 consumer->Disconnect();
47 consumer = nullptr;
48}
49
30void ConsumerBase::FreeBufferLocked(s32 slot_index) { 50void ConsumerBase::FreeBufferLocked(s32 slot_index) {
31 LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index); 51 LOG_DEBUG(Service_Nvnflinger, "slot_index={}", slot_index);
32 52
diff --git a/src/core/hle/service/nvnflinger/consumer_base.h b/src/core/hle/service/nvnflinger/consumer_base.h
index 264829414..ea3e9e97a 100644
--- a/src/core/hle/service/nvnflinger/consumer_base.h
+++ b/src/core/hle/service/nvnflinger/consumer_base.h
@@ -24,6 +24,7 @@ class BufferQueueConsumer;
24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> { 24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
25public: 25public:
26 void Connect(bool controlled_by_app); 26 void Connect(bool controlled_by_app);
27 void Abandon();
27 28
28protected: 29protected:
29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_); 30 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
@@ -34,6 +35,7 @@ protected:
34 void OnBuffersReleased() override; 35 void OnBuffersReleased() override;
35 void OnSidebandStreamChanged() override; 36 void OnSidebandStreamChanged() override;
36 37
38 void AbandonLocked();
37 void FreeBufferLocked(s32 slot_index); 39 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when); 40 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer); 41 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer>& graphic_buffer);
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index 6dc327b8b..d7db24f42 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -166,7 +166,7 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
166}(); 166}();
167 167
168void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { 168void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
169 auto buffer = std::make_shared<android::GraphicBuffer>(); 169 auto buffer = std::make_shared<android::NvGraphicBuffer>();
170 buffer->width = SharedBufferWidth; 170 buffer->width = SharedBufferWidth;
171 buffer->height = SharedBufferHeight; 171 buffer->height = SharedBufferHeight;
172 buffer->stride = SharedBufferBlockLinearStride; 172 buffer->stride = SharedBufferBlockLinearStride;
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index bebb45eae..0745434c5 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -47,7 +47,10 @@ void Nvnflinger::SplitVSync(std::stop_token stop_token) {
47 vsync_signal.Wait(); 47 vsync_signal.Wait();
48 48
49 const auto lock_guard = Lock(); 49 const auto lock_guard = Lock();
50 Compose(); 50
51 if (!is_abandoned) {
52 Compose();
53 }
51 } 54 }
52} 55}
53 56
@@ -98,7 +101,6 @@ Nvnflinger::~Nvnflinger() {
98 } 101 }
99 102
100 ShutdownLayers(); 103 ShutdownLayers();
101 vsync_thread = {};
102 104
103 if (nvdrv) { 105 if (nvdrv) {
104 nvdrv->Close(disp_fd); 106 nvdrv->Close(disp_fd);
@@ -106,12 +108,20 @@ Nvnflinger::~Nvnflinger() {
106} 108}
107 109
108void Nvnflinger::ShutdownLayers() { 110void Nvnflinger::ShutdownLayers() {
109 const auto lock_guard = Lock(); 111 // Abandon consumers.
110 for (auto& display : displays) { 112 {
111 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) { 113 const auto lock_guard = Lock();
112 display.GetLayer(layer).Core().NotifyShutdown(); 114 for (auto& display : displays) {
115 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
116 display.GetLayer(layer).GetConsumer().Abandon();
117 }
113 } 118 }
119
120 is_abandoned = true;
114 } 121 }
122
123 // Join the vsync thread, if it exists.
124 vsync_thread = {};
115} 125}
116 126
117void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 127void Nvnflinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index 959d8b46b..f5d73acdb 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -140,6 +140,8 @@ private:
140 140
141 s32 swap_interval = 1; 141 s32 swap_interval = 1;
142 142
143 bool is_abandoned = false;
144
143 /// Event that handles screen composition. 145 /// Event that handles screen composition.
144 std::shared_ptr<Core::Timing::EventType> multi_composition_event; 146 std::shared_ptr<Core::Timing::EventType> multi_composition_event;
145 std::shared_ptr<Core::Timing::EventType> single_composition_event; 147 std::shared_ptr<Core::Timing::EventType> single_composition_event;
diff --git a/src/core/hle/service/nvnflinger/status.h b/src/core/hle/service/nvnflinger/status.h
index 7af166c40..3fa0fe15b 100644
--- a/src/core/hle/service/nvnflinger/status.h
+++ b/src/core/hle/service/nvnflinger/status.h
@@ -19,7 +19,7 @@ enum class Status : s32 {
19 Busy = -16, 19 Busy = -16,
20 NoInit = -19, 20 NoInit = -19,
21 BadValue = -22, 21 BadValue = -22,
22 InvalidOperation = -37, 22 InvalidOperation = -38,
23 BufferNeedsReallocation = 1, 23 BufferNeedsReallocation = 1,
24 ReleaseAllBuffers = 2, 24 ReleaseAllBuffers = 2,
25}; 25};
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp
new file mode 100644
index 000000000..ce70946ec
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.cpp
@@ -0,0 +1,34 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4#include "core/hle/service/nvdrv/core/nvmap.h"
5#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
6
7namespace Service::android {
8
9static NvGraphicBuffer GetBuffer(std::shared_ptr<NvGraphicBuffer>& buffer) {
10 if (buffer) {
11 return *buffer;
12 } else {
13 return {};
14 }
15}
16
17GraphicBuffer::GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
18 : NvGraphicBuffer(width_, height_, format_, usage_), m_nvmap(nullptr) {}
19
20GraphicBuffer::GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
21 std::shared_ptr<NvGraphicBuffer> buffer)
22 : NvGraphicBuffer(GetBuffer(buffer)), m_nvmap(std::addressof(nvmap)) {
23 if (this->BufferId() > 0) {
24 m_nvmap->DuplicateHandle(this->BufferId(), true);
25 }
26}
27
28GraphicBuffer::~GraphicBuffer() {
29 if (m_nvmap != nullptr && this->BufferId() > 0) {
30 m_nvmap->FreeHandle(this->BufferId(), true);
31 }
32}
33
34} // namespace Service::android
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
index 3eac5cedd..da430aa75 100644
--- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
+++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
@@ -6,16 +6,22 @@
6 6
7#pragma once 7#pragma once
8 8
9#include <memory>
10
9#include "common/common_funcs.h" 11#include "common/common_funcs.h"
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "core/hle/service/nvnflinger/pixel_format.h" 13#include "core/hle/service/nvnflinger/pixel_format.h"
12 14
15namespace Service::Nvidia::NvCore {
16class NvMap;
17} // namespace Service::Nvidia::NvCore
18
13namespace Service::android { 19namespace Service::android {
14 20
15struct GraphicBuffer final { 21struct NvGraphicBuffer {
16 constexpr GraphicBuffer() = default; 22 constexpr NvGraphicBuffer() = default;
17 23
18 constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_) 24 constexpr NvGraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
19 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_}, 25 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
20 usage{static_cast<s32>(usage_)} {} 26 usage{static_cast<s32>(usage_)} {}
21 27
@@ -93,6 +99,17 @@ struct GraphicBuffer final {
93 u32 offset{}; 99 u32 offset{};
94 INSERT_PADDING_WORDS(60); 100 INSERT_PADDING_WORDS(60);
95}; 101};
96static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size"); 102static_assert(sizeof(NvGraphicBuffer) == 0x16C, "NvGraphicBuffer has wrong size");
103
104class GraphicBuffer final : public NvGraphicBuffer {
105public:
106 explicit GraphicBuffer(u32 width, u32 height, PixelFormat format, u32 usage);
107 explicit GraphicBuffer(Service::Nvidia::NvCore::NvMap& nvmap,
108 std::shared_ptr<NvGraphicBuffer> buffer);
109 ~GraphicBuffer();
110
111private:
112 Service::Nvidia::NvCore::NvMap* m_nvmap{};
113};
97 114
98} // namespace Service::android 115} // namespace Service::android
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index f0b5eff8a..d30f49877 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -35,7 +35,7 @@ static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_cont
35 return { 35 return {
36 buffer_queue_core, 36 buffer_queue_core,
37 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap), 37 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
38 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)}; 38 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
39} 39}
40 40
41Display::Display(u64 id, std::string name_, 41Display::Display(u64 id, std::string name_,
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index d0e308124..64e7bad75 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -559,12 +559,12 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
559 const IR::Value& offset, const IR::Value& lod_clamp) { 559 const IR::Value& offset, const IR::Value& lod_clamp) {
560 const auto info{inst.Flags<IR::TextureInstInfo>()}; 560 const auto info{inst.Flags<IR::TextureInstInfo>()};
561 ScopedRegister dpdx, dpdy, coords; 561 ScopedRegister dpdx, dpdy, coords;
562 const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; 562 const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp};
563 if (multi_component) { 563 if (multi_component) {
564 // Allocate this early to avoid aliasing other registers 564 // Allocate this early to avoid aliasing other registers
565 dpdx = ScopedRegister{ctx.reg_alloc}; 565 dpdx = ScopedRegister{ctx.reg_alloc};
566 dpdy = ScopedRegister{ctx.reg_alloc}; 566 dpdy = ScopedRegister{ctx.reg_alloc};
567 if (info.num_derivates >= 3) { 567 if (info.num_derivatives >= 3) {
568 coords = ScopedRegister{ctx.reg_alloc}; 568 coords = ScopedRegister{ctx.reg_alloc};
569 } 569 }
570 } 570 }
@@ -584,7 +584,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
584 dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec, 584 dpdx.reg, derivatives_vec, dpdx.reg, derivatives_vec, dpdy.reg, derivatives_vec,
585 dpdy.reg, derivatives_vec); 585 dpdy.reg, derivatives_vec);
586 Register final_coord; 586 Register final_coord;
587 if (info.num_derivates >= 3) { 587 if (info.num_derivatives >= 3) {
588 ctx.Add("MOV.F {}.z,{}.x;" 588 ctx.Add("MOV.F {}.z,{}.x;"
589 "MOV.F {}.z,{}.y;", 589 "MOV.F {}.z,{}.y;",
590 dpdx.reg, coord_vec, dpdy.reg, coord_vec); 590 dpdx.reg, coord_vec, dpdy.reg, coord_vec);
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index d9872ecc2..6e940bd5a 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -548,15 +548,15 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index,
548 if (sparse_inst) { 548 if (sparse_inst) {
549 throw NotImplementedException("EmitImageGradient Sparse"); 549 throw NotImplementedException("EmitImageGradient Sparse");
550 } 550 }
551 if (!offset.IsEmpty() && info.num_derivates <= 2) { 551 if (!offset.IsEmpty() && info.num_derivatives <= 2) {
552 throw NotImplementedException("EmitImageGradient offset"); 552 throw NotImplementedException("EmitImageGradient offset");
553 } 553 }
554 const auto texture{Texture(ctx, info, index)}; 554 const auto texture{Texture(ctx, info, index)};
555 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)}; 555 const auto texel{ctx.var_alloc.Define(inst, GlslVarType::F32x4)};
556 const bool multi_component{info.num_derivates > 1 || info.has_lod_clamp}; 556 const bool multi_component{info.num_derivatives > 1 || info.has_lod_clamp};
557 const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)}; 557 const auto derivatives_vec{ctx.var_alloc.Consume(derivatives)};
558 if (multi_component) { 558 if (multi_component) {
559 if (info.num_derivates >= 3) { 559 if (info.num_derivatives >= 3) {
560 const auto offset_vec{ctx.var_alloc.Consume(offset)}; 560 const auto offset_vec{ctx.var_alloc.Consume(offset)};
561 ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture, 561 ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture,
562 coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); 562 coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 8decdf399..22ceca19c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -67,22 +67,22 @@ public:
67 } 67 }
68 } 68 }
69 69
70 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates, u32 num_derivates, 70 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives,
71 Id offset, Id lod_clamp) { 71 u32 num_derivatives, Id offset, Id lod_clamp) {
72 if (!Sirit::ValidId(derivates)) { 72 if (!Sirit::ValidId(derivatives)) {
73 throw LogicError("Derivates must be present"); 73 throw LogicError("Derivatives must be present");
74 } 74 }
75 boost::container::static_vector<Id, 3> deriv_x_accum; 75 boost::container::static_vector<Id, 3> deriv_x_accum;
76 boost::container::static_vector<Id, 3> deriv_y_accum; 76 boost::container::static_vector<Id, 3> deriv_y_accum;
77 for (u32 i = 0; i < num_derivates; ++i) { 77 for (u32 i = 0; i < num_derivatives; ++i) {
78 deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2)); 78 deriv_x_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2));
79 deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivates, i * 2 + 1)); 79 deriv_y_accum.push_back(ctx.OpCompositeExtract(ctx.F32[1], derivatives, i * 2 + 1));
80 } 80 }
81 const Id derivates_X{ctx.OpCompositeConstruct( 81 const Id derivatives_X{ctx.OpCompositeConstruct(
82 ctx.F32[num_derivates], std::span{deriv_x_accum.data(), deriv_x_accum.size()})}; 82 ctx.F32[num_derivatives], std::span{deriv_x_accum.data(), deriv_x_accum.size()})};
83 const Id derivates_Y{ctx.OpCompositeConstruct( 83 const Id derivatives_Y{ctx.OpCompositeConstruct(
84 ctx.F32[num_derivates], std::span{deriv_y_accum.data(), deriv_y_accum.size()})}; 84 ctx.F32[num_derivatives], std::span{deriv_y_accum.data(), deriv_y_accum.size()})};
85 Add(spv::ImageOperandsMask::Grad, derivates_X, derivates_Y); 85 Add(spv::ImageOperandsMask::Grad, derivatives_X, derivatives_Y);
86 if (Sirit::ValidId(offset)) { 86 if (Sirit::ValidId(offset)) {
87 Add(spv::ImageOperandsMask::Offset, offset); 87 Add(spv::ImageOperandsMask::Offset, offset);
88 } 88 }
@@ -91,26 +91,26 @@ public:
91 } 91 }
92 } 92 }
93 93
94 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates_1, Id derivates_2, 94 explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivatives_1, Id derivatives_2,
95 Id offset, Id lod_clamp) { 95 Id offset, Id lod_clamp) {
96 if (!Sirit::ValidId(derivates_1) || !Sirit::ValidId(derivates_2)) { 96 if (!Sirit::ValidId(derivatives_1) || !Sirit::ValidId(derivatives_2)) {
97 throw LogicError("Derivates must be present"); 97 throw LogicError("Derivatives must be present");
98 } 98 }
99 boost::container::static_vector<Id, 3> deriv_1_accum{ 99 boost::container::static_vector<Id, 3> deriv_1_accum{
100 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 0), 100 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 0),
101 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 2), 101 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 2),
102 ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 0), 102 ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 0),
103 }; 103 };
104 boost::container::static_vector<Id, 3> deriv_2_accum{ 104 boost::container::static_vector<Id, 3> deriv_2_accum{
105 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 1), 105 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 1),
106 ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 3), 106 ctx.OpCompositeExtract(ctx.F32[1], derivatives_1, 3),
107 ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 1), 107 ctx.OpCompositeExtract(ctx.F32[1], derivatives_2, 1),
108 }; 108 };
109 const Id derivates_id1{ctx.OpCompositeConstruct( 109 const Id derivatives_id1{ctx.OpCompositeConstruct(
110 ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})}; 110 ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})};
111 const Id derivates_id2{ctx.OpCompositeConstruct( 111 const Id derivatives_id2{ctx.OpCompositeConstruct(
112 ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})}; 112 ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})};
113 Add(spv::ImageOperandsMask::Grad, derivates_id1, derivates_id2); 113 Add(spv::ImageOperandsMask::Grad, derivatives_id1, derivatives_id2);
114 if (Sirit::ValidId(offset)) { 114 if (Sirit::ValidId(offset)) {
115 Add(spv::ImageOperandsMask::Offset, offset); 115 Add(spv::ImageOperandsMask::Offset, offset);
116 } 116 }
@@ -548,12 +548,12 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
548} 548}
549 549
550Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 550Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
551 Id derivates, Id offset, Id lod_clamp) { 551 Id derivatives, Id offset, Id lod_clamp) {
552 const auto info{inst->Flags<IR::TextureInstInfo>()}; 552 const auto info{inst->Flags<IR::TextureInstInfo>()};
553 const auto operands = 553 const auto operands =
554 info.num_derivates == 3 554 info.num_derivatives == 3
555 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivates, offset, {}, lod_clamp) 555 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, offset, {}, lod_clamp)
556 : ImageOperands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, offset, 556 : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset,
557 lod_clamp); 557 lod_clamp);
558 return Emit(&EmitContext::OpImageSparseSampleExplicitLod, 558 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
559 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], 559 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index a440b557d..7d34575c8 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -543,7 +543,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
543 const IR::Value& skip_mips); 543 const IR::Value& skip_mips);
544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 544Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 545Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
546 Id derivates, Id offset, Id lod_clamp); 546 Id derivatives, Id offset, Id lod_clamp);
547Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 547Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
548void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); 548void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
549Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index); 549Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index);
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index b7caa4246..49171c470 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -1864,11 +1864,11 @@ Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, Texture
1864 return Inst(op, Flags{info}, handle, coords); 1864 return Inst(op, Flags{info}, handle, coords);
1865} 1865}
1866 1866
1867Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivates, 1867Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivatives,
1868 const Value& offset, const F32& lod_clamp, TextureInstInfo info) { 1868 const Value& offset, const F32& lod_clamp, TextureInstInfo info) {
1869 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient 1869 const Opcode op{handle.IsImmediate() ? Opcode::BoundImageGradient
1870 : Opcode::BindlessImageGradient}; 1870 : Opcode::BindlessImageGradient};
1871 return Inst(op, Flags{info}, handle, coords, derivates, offset, lod_clamp); 1871 return Inst(op, Flags{info}, handle, coords, derivatives, offset, lod_clamp);
1872} 1872}
1873 1873
1874Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { 1874Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) {
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index f3c81dbe1..6c30897f4 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -335,7 +335,7 @@ public:
335 [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, 335 [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset,
336 const U32& lod, const U32& multisampling, TextureInstInfo info); 336 const U32& lod, const U32& multisampling, TextureInstInfo info);
337 [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, 337 [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords,
338 const Value& derivates, const Value& offset, 338 const Value& derivatives, const Value& offset,
339 const F32& lod_clamp, TextureInstInfo info); 339 const F32& lod_clamp, TextureInstInfo info);
340 [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); 340 [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info);
341 void ImageWrite(const Value& handle, const Value& coords, const Value& color, 341 void ImageWrite(const Value& handle, const Value& coords, const Value& color,
diff --git a/src/shader_recompiler/frontend/ir/modifiers.h b/src/shader_recompiler/frontend/ir/modifiers.h
index 1e9e8c8f5..c20c2401f 100644
--- a/src/shader_recompiler/frontend/ir/modifiers.h
+++ b/src/shader_recompiler/frontend/ir/modifiers.h
@@ -40,7 +40,7 @@ union TextureInstInfo {
40 BitField<21, 1, u32> has_lod_clamp; 40 BitField<21, 1, u32> has_lod_clamp;
41 BitField<22, 1, u32> relaxed_precision; 41 BitField<22, 1, u32> relaxed_precision;
42 BitField<23, 2, u32> gather_component; 42 BitField<23, 2, u32> gather_component;
43 BitField<25, 2, u32> num_derivates; 43 BitField<25, 2, u32> num_derivatives;
44 BitField<27, 3, ImageFormat> image_format; 44 BitField<27, 3, ImageFormat> image_format;
45 BitField<30, 1, u32> ndv_is_active; 45 BitField<30, 1, u32> ndv_is_active;
46}; 46};
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
index dd34507bc..4ce3dd0cd 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/texture_gradient.cpp
@@ -59,7 +59,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
59 BitField<51, 3, IR::Pred> sparse_pred; 59 BitField<51, 3, IR::Pred> sparse_pred;
60 BitField<0, 8, IR::Reg> dest_reg; 60 BitField<0, 8, IR::Reg> dest_reg;
61 BitField<8, 8, IR::Reg> coord_reg; 61 BitField<8, 8, IR::Reg> coord_reg;
62 BitField<20, 8, IR::Reg> derivate_reg; 62 BitField<20, 8, IR::Reg> derivative_reg;
63 BitField<28, 3, TextureType> type; 63 BitField<28, 3, TextureType> type;
64 BitField<31, 4, u64> mask; 64 BitField<31, 4, u64> mask;
65 BitField<36, 13, u64> cbuf_offset; 65 BitField<36, 13, u64> cbuf_offset;
@@ -71,7 +71,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
71 } 71 }
72 72
73 IR::Value coords; 73 IR::Value coords;
74 u32 num_derivates{}; 74 u32 num_derivatives{};
75 IR::Reg base_reg{txd.coord_reg}; 75 IR::Reg base_reg{txd.coord_reg};
76 IR::Reg last_reg; 76 IR::Reg last_reg;
77 IR::Value handle; 77 IR::Value handle;
@@ -90,42 +90,42 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
90 switch (txd.type) { 90 switch (txd.type) {
91 case TextureType::_1D: { 91 case TextureType::_1D: {
92 coords = v.F(base_reg); 92 coords = v.F(base_reg);
93 num_derivates = 1; 93 num_derivatives = 1;
94 last_reg = base_reg + 1; 94 last_reg = base_reg + 1;
95 break; 95 break;
96 } 96 }
97 case TextureType::ARRAY_1D: { 97 case TextureType::ARRAY_1D: {
98 last_reg = base_reg + 1; 98 last_reg = base_reg + 1;
99 coords = v.ir.CompositeConstruct(v.F(base_reg), read_array()); 99 coords = v.ir.CompositeConstruct(v.F(base_reg), read_array());
100 num_derivates = 1; 100 num_derivatives = 1;
101 break; 101 break;
102 } 102 }
103 case TextureType::_2D: { 103 case TextureType::_2D: {
104 last_reg = base_reg + 2; 104 last_reg = base_reg + 2;
105 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1)); 105 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1));
106 num_derivates = 2; 106 num_derivatives = 2;
107 break; 107 break;
108 } 108 }
109 case TextureType::ARRAY_2D: { 109 case TextureType::ARRAY_2D: {
110 last_reg = base_reg + 2; 110 last_reg = base_reg + 2;
111 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array()); 111 coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array());
112 num_derivates = 2; 112 num_derivatives = 2;
113 break; 113 break;
114 } 114 }
115 default: 115 default:
116 throw NotImplementedException("Invalid texture type"); 116 throw NotImplementedException("Invalid texture type");
117 } 117 }
118 118
119 const IR::Reg derivate_reg{txd.derivate_reg}; 119 const IR::Reg derivative_reg{txd.derivative_reg};
120 IR::Value derivates; 120 IR::Value derivatives;
121 switch (num_derivates) { 121 switch (num_derivatives) {
122 case 1: { 122 case 1: {
123 derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1)); 123 derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1));
124 break; 124 break;
125 } 125 }
126 case 2: { 126 case 2: {
127 derivates = v.ir.CompositeConstruct(v.F(derivate_reg), v.F(derivate_reg + 1), 127 derivatives = v.ir.CompositeConstruct(v.F(derivative_reg), v.F(derivative_reg + 1),
128 v.F(derivate_reg + 2), v.F(derivate_reg + 3)); 128 v.F(derivative_reg + 2), v.F(derivative_reg + 3));
129 break; 129 break;
130 } 130 }
131 default: 131 default:
@@ -150,9 +150,10 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
150 150
151 IR::TextureInstInfo info{}; 151 IR::TextureInstInfo info{};
152 info.type.Assign(GetType(txd.type)); 152 info.type.Assign(GetType(txd.type));
153 info.num_derivates.Assign(num_derivates); 153 info.num_derivatives.Assign(num_derivatives);
154 info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0); 154 info.has_lod_clamp.Assign(has_lod_clamp ? 1 : 0);
155 const IR::Value sample{v.ir.ImageGradient(handle, coords, derivates, offset, lod_clamp, info)}; 155 const IR::Value sample{
156 v.ir.ImageGradient(handle, coords, derivatives, offset, lod_clamp, info)};
156 157
157 IR::Reg dest_reg{txd.dest_reg}; 158 IR::Reg dest_reg{txd.dest_reg};
158 for (size_t element = 0; element < 4; ++element) { 159 for (size_t element = 0; element < 4; ++element) {
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index f46e55122..ec12c843a 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -428,7 +428,7 @@ void FoldFPAdd32(IR::Inst& inst) {
428 } 428 }
429} 429}
430 430
431bool FoldDerivateYFromCorrection(IR::Inst& inst) { 431bool FoldDerivativeYFromCorrection(IR::Inst& inst) {
432 const IR::Value lhs_value{inst.Arg(0)}; 432 const IR::Value lhs_value{inst.Arg(0)};
433 const IR::Value rhs_value{inst.Arg(1)}; 433 const IR::Value rhs_value{inst.Arg(1)};
434 IR::Inst* const lhs_op{lhs_value.InstRecursive()}; 434 IR::Inst* const lhs_op{lhs_value.InstRecursive()};
@@ -464,7 +464,7 @@ void FoldFPMul32(IR::Inst& inst) {
464 if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) { 464 if (lhs_value.IsImmediate() || rhs_value.IsImmediate()) {
465 return; 465 return;
466 } 466 }
467 if (FoldDerivateYFromCorrection(inst)) { 467 if (FoldDerivativeYFromCorrection(inst)) {
468 return; 468 return;
469 } 469 }
470 IR::Inst* const lhs_op{lhs_value.InstRecursive()}; 470 IR::Inst* const lhs_op{lhs_value.InstRecursive()};
@@ -699,7 +699,7 @@ void FoldFSwizzleAdd(IR::Block& block, IR::Inst& inst) {
699 } 699 }
700} 700}
701 701
702bool FindGradient3DDerivates(std::array<IR::Value, 3>& results, IR::Value coord) { 702bool FindGradient3DDerivatives(std::array<IR::Value, 3>& results, IR::Value coord) {
703 if (coord.IsImmediate()) { 703 if (coord.IsImmediate()) {
704 return false; 704 return false;
705 } 705 }
@@ -834,7 +834,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
834 IR::Inst* const inst2 = coords.InstRecursive(); 834 IR::Inst* const inst2 = coords.InstRecursive();
835 std::array<std::array<IR::Value, 3>, 3> results_matrix; 835 std::array<std::array<IR::Value, 3>, 3> results_matrix;
836 for (size_t i = 0; i < 3; i++) { 836 for (size_t i = 0; i < 3; i++) {
837 if (!FindGradient3DDerivates(results_matrix[i], inst2->Arg(i).Resolve())) { 837 if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) {
838 return; 838 return;
839 } 839 }
840 } 840 }
@@ -852,7 +852,7 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
852 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2], 852 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2],
853 results_matrix[1][1], results_matrix[1][2]); 853 results_matrix[1][1], results_matrix[1][2]);
854 IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]); 854 IR::Value derivatives_2 = ir.CompositeConstruct(results_matrix[2][1], results_matrix[2][2]);
855 info.num_derivates.Assign(3); 855 info.num_derivatives.Assign(3);
856 IR::Value new_gradient_instruction = 856 IR::Value new_gradient_instruction =
857 ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info); 857 ir.ImageGradient(handle, new_coords, derivatives_1, derivatives_2, lod_clamp, info);
858 IR::Inst* const new_inst = new_gradient_instruction.InstRecursive(); 858 IR::Inst* const new_inst = new_gradient_instruction.InstRecursive();
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 081a574e8..f5b10411b 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -1192,11 +1192,6 @@ void BufferCache<P>::UpdateDrawIndirect() {
1192 .size = static_cast<u32>(size), 1192 .size = static_cast<u32>(size),
1193 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)), 1193 .buffer_id = FindBuffer(*cpu_addr, static_cast<u32>(size)),
1194 }; 1194 };
1195 VAddr cpu_addr_start = Common::AlignDown(*cpu_addr, 64);
1196 VAddr cpu_addr_end = Common::AlignUp(*cpu_addr + size, 64);
1197 IntervalType interval{cpu_addr_start, cpu_addr_end};
1198 ClearDownload(interval);
1199 common_ranges.subtract(interval);
1200 }; 1195 };
1201 if (current_draw_indirect->include_count) { 1196 if (current_draw_indirect->include_count) {
1202 update(current_draw_indirect->count_start_address, sizeof(u32), 1197 update(current_draw_indirect->count_start_address, sizeof(u32),
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 02e161270..91f10aec2 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -72,7 +72,7 @@ void Fermi2D::Blit() {
72 UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled"); 72 UNIMPLEMENTED_IF_MSG(regs.clip_enable != 0, "Clipped blit enabled");
73 73
74 const auto& args = regs.pixels_from_memory; 74 const auto& args = regs.pixels_from_memory;
75 constexpr s64 null_derivate = 1ULL << 32; 75 constexpr s64 null_derivative = 1ULL << 32;
76 Surface src = regs.src; 76 Surface src = regs.src;
77 const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); 77 const auto bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format));
78 const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 && 78 const bool delegate_to_gpu = src.width > 512 && src.height > 512 && bytes_per_pixel <= 8 &&
@@ -89,7 +89,7 @@ void Fermi2D::Blit() {
89 .operation = regs.operation, 89 .operation = regs.operation,
90 .filter = args.sample_mode.filter, 90 .filter = args.sample_mode.filter,
91 .must_accelerate = 91 .must_accelerate =
92 args.du_dx != null_derivate || args.dv_dy != null_derivate || delegate_to_gpu, 92 args.du_dx != null_derivative || args.dv_dy != null_derivative || delegate_to_gpu,
93 .dst_x0 = args.dst_x0, 93 .dst_x0 = args.dst_x0,
94 .dst_y0 = args.dst_y0, 94 .dst_y0 = args.dst_y0,
95 .dst_x1 = args.dst_x0 + args.dst_width, 95 .dst_x1 = args.dst_x0 + args.dst_width,
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 32d767d85..592c28ba3 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -268,7 +268,7 @@ size_t Maxwell3D::EstimateIndexBufferSize() {
268 std::numeric_limits<u32>::max()}; 268 std::numeric_limits<u32>::max()};
269 const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); 269 const size_t byte_size = regs.index_buffer.FormatSizeInBytes();
270 const size_t log2_byte_size = Common::Log2Ceil64(byte_size); 270 const size_t log2_byte_size = Common::Log2Ceil64(byte_size);
271 const size_t cap{GetMaxCurrentVertices() * 3 * byte_size}; 271 const size_t cap{GetMaxCurrentVertices() * 4 * byte_size};
272 const size_t lower_cap = 272 const size_t lower_cap =
273 std::min<size_t>(static_cast<size_t>(end_address - start_address), cap); 273 std::min<size_t>(static_cast<size_t>(end_address - start_address), cap);
274 return std::min<size_t>( 274 return std::min<size_t>(
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 44a771d65..af0a453ee 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -559,7 +559,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
559} 559}
560 560
561void GraphicsPipeline::ConfigureTransformFeedbackImpl() const { 561void GraphicsPipeline::ConfigureTransformFeedbackImpl() const {
562 glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), GL_SEPARATE_ATTRIBS); 562 const GLenum buffer_mode =
563 num_xfb_buffers_active == 1 ? GL_INTERLEAVED_ATTRIBS : GL_SEPARATE_ATTRIBS;
564 glTransformFeedbackAttribsNV(num_xfb_attribs, xfb_attribs.data(), buffer_mode);
563} 565}
564 566
565void GraphicsPipeline::GenerateTransformFeedbackState() { 567void GraphicsPipeline::GenerateTransformFeedbackState() {
@@ -567,12 +569,14 @@ void GraphicsPipeline::GenerateTransformFeedbackState() {
567 // when this is required. 569 // when this is required.
568 GLint* cursor{xfb_attribs.data()}; 570 GLint* cursor{xfb_attribs.data()};
569 571
572 num_xfb_buffers_active = 0;
570 for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) { 573 for (size_t feedback = 0; feedback < Maxwell::NumTransformFeedbackBuffers; ++feedback) {
571 const auto& layout = key.xfb_state.layouts[feedback]; 574 const auto& layout = key.xfb_state.layouts[feedback];
572 UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding"); 575 UNIMPLEMENTED_IF_MSG(layout.stride != layout.varying_count * 4, "Stride padding");
573 if (layout.varying_count == 0) { 576 if (layout.varying_count == 0) {
574 continue; 577 continue;
575 } 578 }
579 num_xfb_buffers_active++;
576 580
577 const auto& locations = key.xfb_state.varyings[feedback]; 581 const auto& locations = key.xfb_state.varyings[feedback];
578 std::optional<u32> current_index; 582 std::optional<u32> current_index;
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 74fc9cc3d..2f70c1ae9 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -154,6 +154,7 @@ private:
154 154
155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3; 155 static constexpr std::size_t XFB_ENTRY_STRIDE = 3;
156 GLsizei num_xfb_attribs{}; 156 GLsizei num_xfb_attribs{};
157 u32 num_xfb_buffers_active{};
157 std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{}; 158 std::array<GLint, 128 * XFB_ENTRY_STRIDE * Maxwell::NumTransformFeedbackBuffers> xfb_attribs{};
158 159
159 std::mutex built_mutex; 160 std::mutex built_mutex;