summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp49
-rw-r--r--src/core/hle/kernel/hle_ipc.h34
-rw-r--r--src/core/hle/kernel/thread.cpp21
-rw-r--r--src/core/hle/kernel/thread.h2
-rw-r--r--src/core/hle/kernel/wait_object.cpp3
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp25
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h6
-rw-r--r--src/core/hle/service/service.cpp3
-rw-r--r--src/core/hle/service/vi/vi.cpp30
-rw-r--r--src/yuzu/debugger/wait_tree.cpp6
10 files changed, 128 insertions, 51 deletions
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index d9faf4b53..293756790 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -7,6 +7,7 @@
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/handle_table.h" 11#include "core/hle/kernel/handle_table.h"
11#include "core/hle/kernel/hle_ipc.h" 12#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/kernel/kernel.h" 13#include "core/hle/kernel/kernel.h"
@@ -26,6 +27,32 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
26 boost::range::remove_erase(connected_sessions, server_session); 27 boost::range::remove_erase(connected_sessions, server_session);
27} 28}
28 29
30SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
31 const std::string& reason, u64 timeout,
32 WakeupCallback&& callback) {
33
34 // Put the client thread to sleep until the wait event is signaled or the timeout expires.
35 thread->wakeup_callback =
36 [context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread,
37 SharedPtr<WaitObject> object, size_t index) mutable -> bool {
38 ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT);
39 callback(thread, context, reason);
40 context.WriteToOutgoingCommandBuffer(*thread);
41 return true;
42 };
43
44 auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
45 thread->status = THREADSTATUS_WAIT_HLE_EVENT;
46 thread->wait_objects = {event};
47 event->AddWaitingThread(thread);
48
49 if (timeout > 0) {
50 thread->WakeAfterDelay(timeout);
51 }
52
53 return event;
54}
55
29HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session) 56HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
30 : server_session(std::move(server_session)) { 57 : server_session(std::move(server_session)) {
31 cmd_buf[0] = 0; 58 cmd_buf[0] = 0;
@@ -35,7 +62,7 @@ HLERequestContext::~HLERequestContext() = default;
35 62
36void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) { 63void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
37 IPC::RequestParser rp(src_cmdbuf); 64 IPC::RequestParser rp(src_cmdbuf);
38 command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); 65 command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
39 66
40 if (command_header->type == IPC::CommandType::Close) { 67 if (command_header->type == IPC::CommandType::Close) {
41 // Close does not populate the rest of the IPC header 68 // Close does not populate the rest of the IPC header
@@ -45,7 +72,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
45 // If handle descriptor is present, add size of it 72 // If handle descriptor is present, add size of it
46 if (command_header->enable_handle_descriptor) { 73 if (command_header->enable_handle_descriptor) {
47 handle_descriptor_header = 74 handle_descriptor_header =
48 std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>()); 75 std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
49 if (handle_descriptor_header->send_current_pid) { 76 if (handle_descriptor_header->send_current_pid) {
50 rp.Skip(2, false); 77 rp.Skip(2, false);
51 } 78 }
@@ -88,7 +115,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
88 // All outgoing domain messages have the domain header, if only incoming has it 115 // All outgoing domain messages have the domain header, if only incoming has it
89 if (incoming || domain_message_header) { 116 if (incoming || domain_message_header) {
90 domain_message_header = 117 domain_message_header =
91 std::make_unique<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>()); 118 std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
92 } else { 119 } else {
93 if (Session()->IsDomain()) 120 if (Session()->IsDomain())
94 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); 121 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
@@ -96,7 +123,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
96 } 123 }
97 124
98 data_payload_header = 125 data_payload_header =
99 std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>()); 126 std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
100 127
101 data_payload_offset = rp.GetCurrentOffset(); 128 data_payload_offset = rp.GetCurrentOffset();
102 129
@@ -159,8 +186,11 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
159 return RESULT_SUCCESS; 186 return RESULT_SUCCESS;
160} 187}
161 188
162ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, 189ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
163 HandleTable& dst_table) { 190 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
191 Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
192 dst_cmdbuf.size() * sizeof(u32));
193
164 // The header was already built in the internal command buffer. Attempt to parse it to verify 194 // The header was already built in the internal command buffer. Attempt to parse it to verify
165 // the integrity and then copy it over to the target command buffer. 195 // the integrity and then copy it over to the target command buffer.
166 ParseCommandBuffer(cmd_buf.data(), false); 196 ParseCommandBuffer(cmd_buf.data(), false);
@@ -171,7 +201,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
171 if (domain_message_header) 201 if (domain_message_header)
172 size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); 202 size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32);
173 203
174 std::copy_n(cmd_buf.begin(), size, dst_cmdbuf); 204 std::copy_n(cmd_buf.begin(), size, dst_cmdbuf.data());
175 205
176 if (command_header->enable_handle_descriptor) { 206 if (command_header->enable_handle_descriptor) {
177 ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(), 207 ASSERT_MSG(!move_objects.empty() || !copy_objects.empty(),
@@ -213,6 +243,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
213 dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size()); 243 dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size());
214 } 244 }
215 } 245 }
246
247 // Copy the translated command buffer back into the thread's command buffer area.
248 Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
249 dst_cmdbuf.size() * sizeof(u32));
250
216 return RESULT_SUCCESS; 251 return RESULT_SUCCESS;
217} 252}
218 253
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index b5631b773..8b35da4c9 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <string>
9#include <vector> 10#include <vector>
10#include <boost/container/small_vector.hpp> 11#include <boost/container/small_vector.hpp>
11#include "common/common_types.h" 12#include "common/common_types.h"
@@ -13,6 +14,7 @@
13#include "core/hle/ipc.h" 14#include "core/hle/ipc.h"
14#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
15#include "core/hle/kernel/server_session.h" 16#include "core/hle/kernel/server_session.h"
17#include "core/hle/kernel/thread.h"
16 18
17namespace Service { 19namespace Service {
18class ServiceFrameworkBase; 20class ServiceFrameworkBase;
@@ -24,6 +26,7 @@ class Domain;
24class HandleTable; 26class HandleTable;
25class HLERequestContext; 27class HLERequestContext;
26class Process; 28class Process;
29class Event;
27 30
28/** 31/**
29 * Interface implemented by HLE Session handlers. 32 * Interface implemented by HLE Session handlers.
@@ -102,14 +105,31 @@ public:
102 return server_session; 105 return server_session;
103 } 106 }
104 107
108 using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context,
109 ThreadWakeupReason reason)>;
110
111 /**
112 * Puts the specified guest thread to sleep until the returned event is signaled or until the
113 * specified timeout expires.
114 * @param thread Thread to be put to sleep.
115 * @param reason Reason for pausing the thread, to be used for debugging purposes.
116 * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
117 * invoked with a Timeout reason.
118 * @param callback Callback to be invoked when the thread is resumed. This callback must write
119 * the entire command response once again, regardless of the state of it before this function
120 * was called.
121 * @returns Event that when signaled will resume the thread and call the callback function.
122 */
123 SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
124 u64 timeout, WakeupCallback&& callback);
125
105 void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming); 126 void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
106 127
107 /// Populates this context with data from the requesting process/thread. 128 /// Populates this context with data from the requesting process/thread.
108 ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, 129 ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
109 HandleTable& src_table); 130 HandleTable& src_table);
110 /// Writes data from this context back to the requesting process/thread. 131 /// Writes data from this context back to the requesting process/thread.
111 ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, 132 ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
112 HandleTable& dst_table);
113 133
114 u32_le GetCommand() const { 134 u32_le GetCommand() const {
115 return command; 135 return command;
@@ -139,7 +159,7 @@ public:
139 return buffer_c_desciptors; 159 return buffer_c_desciptors;
140 } 160 }
141 161
142 const std::unique_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const { 162 const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
143 return domain_message_header; 163 return domain_message_header;
144 } 164 }
145 165
@@ -212,10 +232,10 @@ private:
212 boost::container::small_vector<SharedPtr<Object>, 8> copy_objects; 232 boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
213 boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects; 233 boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
214 234
215 std::unique_ptr<IPC::CommandHeader> command_header; 235 std::shared_ptr<IPC::CommandHeader> command_header;
216 std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header; 236 std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header;
217 std::unique_ptr<IPC::DataPayloadHeader> data_payload_header; 237 std::shared_ptr<IPC::DataPayloadHeader> data_payload_header;
218 std::unique_ptr<IPC::DomainMessageHeader> domain_message_header; 238 std::shared_ptr<IPC::DomainMessageHeader> domain_message_header;
219 std::vector<IPC::BufferDescriptorX> buffer_x_desciptors; 239 std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
220 std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors; 240 std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
221 std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors; 241 std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index a39c53db5..145f50887 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -55,16 +55,6 @@ inline static u32 const NewThreadId() {
55Thread::Thread() {} 55Thread::Thread() {}
56Thread::~Thread() {} 56Thread::~Thread() {}
57 57
58/**
59 * Check if the specified thread is waiting on the specified address to be arbitrated
60 * @param thread The thread to test
61 * @param wait_address The address to test against
62 * @return True if the thread is waiting, false otherwise
63 */
64static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) {
65 return thread->status == THREADSTATUS_WAIT_ARB && wait_address == thread->wait_address;
66}
67
68void Thread::Stop() { 58void Thread::Stop() {
69 // Cancel any outstanding wakeup events for this thread 59 // Cancel any outstanding wakeup events for this thread
70 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); 60 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
@@ -102,12 +92,6 @@ void WaitCurrentThread_Sleep() {
102 thread->status = THREADSTATUS_WAIT_SLEEP; 92 thread->status = THREADSTATUS_WAIT_SLEEP;
103} 93}
104 94
105void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) {
106 Thread* thread = GetCurrentThread();
107 thread->wait_address = wait_address;
108 thread->status = THREADSTATUS_WAIT_ARB;
109}
110
111void ExitCurrentThread() { 95void ExitCurrentThread() {
112 Thread* thread = GetCurrentThread(); 96 Thread* thread = GetCurrentThread();
113 thread->Stop(); 97 thread->Stop();
@@ -129,7 +113,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
129 bool resume = true; 113 bool resume = true;
130 114
131 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || 115 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
132 thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { 116 thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
117 thread->status == THREADSTATUS_WAIT_HLE_EVENT) {
133 118
134 // Remove the thread from each of its waiting objects' waitlists 119 // Remove the thread from each of its waiting objects' waitlists
135 for (auto& object : thread->wait_objects) 120 for (auto& object : thread->wait_objects)
@@ -163,7 +148,7 @@ void Thread::ResumeFromWait() {
163 switch (status) { 148 switch (status) {
164 case THREADSTATUS_WAIT_SYNCH_ALL: 149 case THREADSTATUS_WAIT_SYNCH_ALL:
165 case THREADSTATUS_WAIT_SYNCH_ANY: 150 case THREADSTATUS_WAIT_SYNCH_ANY:
166 case THREADSTATUS_WAIT_ARB: 151 case THREADSTATUS_WAIT_HLE_EVENT:
167 case THREADSTATUS_WAIT_SLEEP: 152 case THREADSTATUS_WAIT_SLEEP:
168 case THREADSTATUS_WAIT_IPC: 153 case THREADSTATUS_WAIT_IPC:
169 break; 154 break;
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 4fd2fc2f8..dbf47e269 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -38,7 +38,7 @@ enum ThreadProcessorId : s32 {
38enum ThreadStatus { 38enum ThreadStatus {
39 THREADSTATUS_RUNNING, ///< Currently running 39 THREADSTATUS_RUNNING, ///< Currently running
40 THREADSTATUS_READY, ///< Ready to run 40 THREADSTATUS_READY, ///< Ready to run
41 THREADSTATUS_WAIT_ARB, ///< Waiting on an address arbiter 41 THREADSTATUS_WAIT_HLE_EVENT, ///< Waiting for hle event to finish
42 THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC 42 THREADSTATUS_WAIT_SLEEP, ///< Waiting due to a SleepThread SVC
43 THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request 43 THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
44 THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false 44 THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index ec147b84c..b08ac72c1 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -39,7 +39,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
39 for (const auto& thread : waiting_threads) { 39 for (const auto& thread : waiting_threads) {
40 // The list of waiting threads must not contain threads that are not waiting to be awakened. 40 // The list of waiting threads must not contain threads that are not waiting to be awakened.
41 ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || 41 ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
42 thread->status == THREADSTATUS_WAIT_SYNCH_ALL, 42 thread->status == THREADSTATUS_WAIT_SYNCH_ALL ||
43 thread->status == THREADSTATUS_WAIT_HLE_EVENT,
43 "Inconsistent thread statuses in waiting_threads"); 44 "Inconsistent thread statuses in waiting_threads");
44 45
45 if (thread->current_priority >= candidate_priority) 46 if (thread->current_priority >= candidate_priority)
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index e2c25048b..e4ff2e267 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -26,24 +26,30 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
26 LOG_WARNING(Service, "Adding graphics buffer %u", slot); 26 LOG_WARNING(Service, "Adding graphics buffer %u", slot);
27 27
28 queue.emplace_back(buffer); 28 queue.emplace_back(buffer);
29
30 if (buffer_wait_event) {
31 buffer_wait_event->Signal();
32 }
29} 33}
30 34
31u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) { 35boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
32 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) { 36 auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
33 // Only consider free buffers. Buffers become free once again after they've been Acquired 37 // Only consider free buffers. Buffers become free once again after they've been Acquired
34 // and Released by the compositor, see the NVFlinger::Compose method. 38 // and Released by the compositor, see the NVFlinger::Compose method.
35 if (buffer.status != Buffer::Status::Free) 39 if (buffer.status != Buffer::Status::Free) {
36 return false; 40 return false;
41 }
37 42
38 // Make sure that the parameters match. 43 // Make sure that the parameters match.
39 return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height; 44 return buffer.igbp_buffer.width == width && buffer.igbp_buffer.height == height;
40 }); 45 });
46
41 if (itr == queue.end()) { 47 if (itr == queue.end()) {
42 LOG_CRITICAL(Service_NVDRV, "no free buffers for pixel_format=%d, width=%d, height=%d", 48 return boost::none;
43 pixel_format, width, height);
44 itr = queue.begin();
45 } 49 }
46 50
51 buffer_wait_event = nullptr;
52
47 itr->status = Buffer::Status::Dequeued; 53 itr->status = Buffer::Status::Dequeued;
48 return itr->slot; 54 return itr->slot;
49} 55}
@@ -81,6 +87,10 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
81 ASSERT(itr != queue.end()); 87 ASSERT(itr != queue.end());
82 ASSERT(itr->status == Buffer::Status::Acquired); 88 ASSERT(itr->status == Buffer::Status::Acquired);
83 itr->status = Buffer::Status::Free; 89 itr->status = Buffer::Status::Free;
90
91 if (buffer_wait_event) {
92 buffer_wait_event->Signal();
93 }
84} 94}
85 95
86u32 BufferQueue::Query(QueryType type) { 96u32 BufferQueue::Query(QueryType type) {
@@ -96,5 +106,10 @@ u32 BufferQueue::Query(QueryType type) {
96 return 0; 106 return 0;
97} 107}
98 108
109void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event) {
110 ASSERT_MSG(!buffer_wait_event, "buffer_wait_event only supports a single waiting thread!");
111 buffer_wait_event = std::move(wait_event);
112}
113
99} // namespace NVFlinger 114} // namespace NVFlinger
100} // namespace Service 115} // namespace Service
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index ef9732769..686eadca7 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -69,12 +69,13 @@ public:
69 }; 69 };
70 70
71 void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer); 71 void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
72 u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height); 72 boost::optional<u32> DequeueBuffer(u32 width, u32 height);
73 const IGBPBuffer& RequestBuffer(u32 slot) const; 73 const IGBPBuffer& RequestBuffer(u32 slot) const;
74 void QueueBuffer(u32 slot, BufferTransformFlags transform); 74 void QueueBuffer(u32 slot, BufferTransformFlags transform);
75 boost::optional<const Buffer&> AcquireBuffer(); 75 boost::optional<const Buffer&> AcquireBuffer();
76 void ReleaseBuffer(u32 slot); 76 void ReleaseBuffer(u32 slot);
77 u32 Query(QueryType type); 77 u32 Query(QueryType type);
78 void SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_event);
78 79
79 u32 GetId() const { 80 u32 GetId() const {
80 return id; 81 return id;
@@ -90,6 +91,9 @@ private:
90 91
91 std::vector<Buffer> queue; 92 std::vector<Buffer> queue;
92 Kernel::SharedPtr<Kernel::Event> native_handle; 93 Kernel::SharedPtr<Kernel::Event> native_handle;
94
95 /// Used to signal waiting thread when no buffers are available
96 Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
93}; 97};
94 98
95} // namespace NVFlinger 99} // namespace NVFlinger
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 8818b0f0f..a1ca8a033 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -152,8 +152,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
152 UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType()); 152 UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType());
153 } 153 }
154 154
155 u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress()); 155 context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread());
156 context.WriteToOutgoingCommandBuffer(cmd_buf, *Core::CurrentProcess(), Kernel::g_handle_table);
157 156
158 return RESULT_SUCCESS; 157 return RESULT_SUCCESS;
159} 158}
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 0aa621dfe..7b6453447 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -486,12 +486,30 @@ private:
486 ctx.WriteBuffer(response.Serialize()); 486 ctx.WriteBuffer(response.Serialize());
487 } else if (transaction == TransactionId::DequeueBuffer) { 487 } else if (transaction == TransactionId::DequeueBuffer) {
488 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; 488 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
489 489 const u32 width{request.data.width};
490 u32 slot = buffer_queue->DequeueBuffer(request.data.pixel_format, request.data.width, 490 const u32 height{request.data.height};
491 request.data.height); 491 boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
492 492
493 IGBPDequeueBufferResponseParcel response{slot}; 493 if (slot != boost::none) {
494 ctx.WriteBuffer(response.Serialize()); 494 // Buffer is available
495 IGBPDequeueBufferResponseParcel response{*slot};
496 ctx.WriteBuffer(response.Serialize());
497 } else {
498 // Wait the current thread until a buffer becomes available
499 auto wait_event = ctx.SleepClientThread(
500 Kernel::GetCurrentThread(), "IHOSBinderDriver::DequeueBuffer", -1,
501 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
502 ThreadWakeupReason reason) {
503 // Repeat TransactParcel DequeueBuffer when a buffer is available
504 auto buffer_queue = nv_flinger->GetBufferQueue(id);
505 boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
506 IGBPDequeueBufferResponseParcel response{*slot};
507 ctx.WriteBuffer(response.Serialize());
508 IPC::ResponseBuilder rb{ctx, 2};
509 rb.Push(RESULT_SUCCESS);
510 });
511 buffer_queue->SetBufferWaitEvent(std::move(wait_event));
512 }
495 } else if (transaction == TransactionId::RequestBuffer) { 513 } else if (transaction == TransactionId::RequestBuffer) {
496 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; 514 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
497 515
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index 7a62f57b5..cae2864e5 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -150,8 +150,8 @@ QString WaitTreeThread::GetText() const {
150 case THREADSTATUS_READY: 150 case THREADSTATUS_READY:
151 status = tr("ready"); 151 status = tr("ready");
152 break; 152 break;
153 case THREADSTATUS_WAIT_ARB: 153 case THREADSTATUS_WAIT_HLE_EVENT:
154 status = tr("waiting for address 0x%1").arg(thread.wait_address, 8, 16, QLatin1Char('0')); 154 status = tr("waiting for HLE return");
155 break; 155 break;
156 case THREADSTATUS_WAIT_SLEEP: 156 case THREADSTATUS_WAIT_SLEEP:
157 status = tr("sleeping"); 157 status = tr("sleeping");
@@ -180,7 +180,7 @@ QColor WaitTreeThread::GetColor() const {
180 return QColor(Qt::GlobalColor::darkGreen); 180 return QColor(Qt::GlobalColor::darkGreen);
181 case THREADSTATUS_READY: 181 case THREADSTATUS_READY:
182 return QColor(Qt::GlobalColor::darkBlue); 182 return QColor(Qt::GlobalColor::darkBlue);
183 case THREADSTATUS_WAIT_ARB: 183 case THREADSTATUS_WAIT_HLE_EVENT:
184 return QColor(Qt::GlobalColor::darkRed); 184 return QColor(Qt::GlobalColor::darkRed);
185 case THREADSTATUS_WAIT_SLEEP: 185 case THREADSTATUS_WAIT_SLEEP:
186 return QColor(Qt::GlobalColor::darkYellow); 186 return QColor(Qt::GlobalColor::darkYellow);