forked from suyu/suyu
Merge pull request #8169 from merryhime/scoped_lock
Replace lock_guard with scoped_lock
This commit is contained in:
commit
04efd729d6
29 changed files with 105 additions and 105 deletions
|
@ -149,7 +149,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unmap(size_t virtual_offset, size_t length) {
|
void Unmap(size_t virtual_offset, size_t length) {
|
||||||
std::lock_guard lock{placeholder_mutex};
|
std::scoped_lock lock{placeholder_mutex};
|
||||||
|
|
||||||
// Unmap until there are no more placeholders
|
// Unmap until there are no more placeholders
|
||||||
while (UnmapOnePlaceholder(virtual_offset, length)) {
|
while (UnmapOnePlaceholder(virtual_offset, length)) {
|
||||||
|
@ -169,7 +169,7 @@ public:
|
||||||
}
|
}
|
||||||
const size_t virtual_end = virtual_offset + length;
|
const size_t virtual_end = virtual_offset + length;
|
||||||
|
|
||||||
std::lock_guard lock{placeholder_mutex};
|
std::scoped_lock lock{placeholder_mutex};
|
||||||
auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
|
auto [it, end] = placeholders.equal_range({virtual_offset, virtual_end});
|
||||||
while (it != end) {
|
while (it != end) {
|
||||||
const size_t offset = std::max(it->lower(), virtual_offset);
|
const size_t offset = std::max(it->lower(), virtual_offset);
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace Common {
|
||||||
class Event {
|
class Event {
|
||||||
public:
|
public:
|
||||||
void Set() {
|
void Set() {
|
||||||
std::lock_guard lk{mutex};
|
std::scoped_lock lk{mutex};
|
||||||
if (!is_set) {
|
if (!is_set) {
|
||||||
is_set = true;
|
is_set = true;
|
||||||
condvar.notify_one();
|
condvar.notify_one();
|
||||||
|
|
|
@ -52,7 +52,7 @@ public:
|
||||||
// line before cv.wait
|
// line before cv.wait
|
||||||
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
|
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
|
||||||
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
|
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
|
||||||
std::lock_guard lock{cv_mutex};
|
std::scoped_lock lock{cv_mutex};
|
||||||
cv.notify_one();
|
cv.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ public:
|
||||||
|
|
||||||
template <typename Arg>
|
template <typename Arg>
|
||||||
void Push(Arg&& t) {
|
void Push(Arg&& t) {
|
||||||
std::lock_guard lock{write_lock};
|
std::scoped_lock lock{write_lock};
|
||||||
spsc_queue.Push(t);
|
spsc_queue.Push(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ struct KernelCore::Impl {
|
||||||
// Close all open server ports.
|
// Close all open server ports.
|
||||||
std::unordered_set<KServerPort*> server_ports_;
|
std::unordered_set<KServerPort*> server_ports_;
|
||||||
{
|
{
|
||||||
std::lock_guard lk(server_ports_lock);
|
std::scoped_lock lk{server_ports_lock};
|
||||||
server_ports_ = server_ports;
|
server_ports_ = server_ports;
|
||||||
server_ports.clear();
|
server_ports.clear();
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ struct KernelCore::Impl {
|
||||||
|
|
||||||
// Close kernel objects that were not freed on shutdown
|
// Close kernel objects that were not freed on shutdown
|
||||||
{
|
{
|
||||||
std::lock_guard lk(registered_in_use_objects_lock);
|
std::scoped_lock lk{registered_in_use_objects_lock};
|
||||||
if (registered_in_use_objects.size()) {
|
if (registered_in_use_objects.size()) {
|
||||||
for (auto& object : registered_in_use_objects) {
|
for (auto& object : registered_in_use_objects) {
|
||||||
object->Close();
|
object->Close();
|
||||||
|
@ -178,7 +178,7 @@ struct KernelCore::Impl {
|
||||||
|
|
||||||
// Track kernel objects that were not freed on shutdown
|
// Track kernel objects that were not freed on shutdown
|
||||||
{
|
{
|
||||||
std::lock_guard lk(registered_objects_lock);
|
std::scoped_lock lk{registered_objects_lock};
|
||||||
if (registered_objects.size()) {
|
if (registered_objects.size()) {
|
||||||
LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
|
LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
|
||||||
registered_objects.size());
|
registered_objects.size());
|
||||||
|
@ -660,7 +660,7 @@ struct KernelCore::Impl {
|
||||||
|
|
||||||
KClientPort* port = &search->second(system.ServiceManager(), system);
|
KClientPort* port = &search->second(system.ServiceManager(), system);
|
||||||
{
|
{
|
||||||
std::lock_guard lk(server_ports_lock);
|
std::scoped_lock lk{server_ports_lock};
|
||||||
server_ports.insert(&port->GetParent()->GetServerPort());
|
server_ports.insert(&port->GetParent()->GetServerPort());
|
||||||
}
|
}
|
||||||
return port;
|
return port;
|
||||||
|
@ -929,22 +929,22 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::RegisterKernelObject(KAutoObject* object) {
|
void KernelCore::RegisterKernelObject(KAutoObject* object) {
|
||||||
std::lock_guard lk(impl->registered_objects_lock);
|
std::scoped_lock lk{impl->registered_objects_lock};
|
||||||
impl->registered_objects.insert(object);
|
impl->registered_objects.insert(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::UnregisterKernelObject(KAutoObject* object) {
|
void KernelCore::UnregisterKernelObject(KAutoObject* object) {
|
||||||
std::lock_guard lk(impl->registered_objects_lock);
|
std::scoped_lock lk{impl->registered_objects_lock};
|
||||||
impl->registered_objects.erase(object);
|
impl->registered_objects.erase(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::RegisterInUseObject(KAutoObject* object) {
|
void KernelCore::RegisterInUseObject(KAutoObject* object) {
|
||||||
std::lock_guard lk(impl->registered_in_use_objects_lock);
|
std::scoped_lock lk{impl->registered_in_use_objects_lock};
|
||||||
impl->registered_in_use_objects.insert(object);
|
impl->registered_in_use_objects.insert(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelCore::UnregisterInUseObject(KAutoObject* object) {
|
void KernelCore::UnregisterInUseObject(KAutoObject* object) {
|
||||||
std::lock_guard lk(impl->registered_in_use_objects_lock);
|
std::scoped_lock lk{impl->registered_in_use_objects_lock};
|
||||||
impl->registered_in_use_objects.erase(object);
|
impl->registered_in_use_objects.erase(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
|
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
if (nanoseconds > 0) {
|
if (nanoseconds > 0) {
|
||||||
ASSERT(thread);
|
ASSERT(thread);
|
||||||
ASSERT(thread->GetState() != ThreadState::Runnable);
|
ASSERT(thread->GetState() != ThreadState::Runnable);
|
||||||
|
@ -35,7 +35,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeManager::UnscheduleTimeEvent(KThread* thread) {
|
void TimeManager::UnscheduleTimeEvent(KThread* thread) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
system.CoreTiming().UnscheduleEvent(time_manager_event_type,
|
system.CoreTiming().UnscheduleEvent(time_manager_event_type,
|
||||||
reinterpret_cast<uintptr_t>(thread));
|
reinterpret_cast<uintptr_t>(thread));
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,7 +318,7 @@ void Controller_NPad::OnRelease() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
|
void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
auto& controller = GetControllerFromNpadIdType(npad_id);
|
auto& controller = GetControllerFromNpadIdType(npad_id);
|
||||||
const auto controller_type = controller.device->GetNpadStyleIndex();
|
const auto controller_type = controller.device->GetNpadStyleIndex();
|
||||||
if (!controller.device->IsConnected()) {
|
if (!controller.device->IsConnected()) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco
|
||||||
return Status::BadValue;
|
return Status::BadValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock{mutex};
|
||||||
|
|
||||||
if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
|
if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
|
||||||
if (status != Status::NoBufferAvailable) {
|
if (status != Status::NoBufferAvailable) {
|
||||||
|
@ -40,7 +40,7 @@ Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseco
|
||||||
}
|
}
|
||||||
|
|
||||||
Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
|
Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock{mutex};
|
||||||
|
|
||||||
if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
|
if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
|
||||||
status != Status::NoError) {
|
status != Status::NoError) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ BufferQueueConsumer::~BufferQueueConsumer() = default;
|
||||||
|
|
||||||
Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
|
Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
|
||||||
std::chrono::nanoseconds expected_present) {
|
std::chrono::nanoseconds expected_present) {
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
// Check that the consumer doesn't currently have the maximum number of buffers acquired.
|
// Check that the consumer doesn't currently have the maximum number of buffers acquired.
|
||||||
const s32 num_acquired_buffers{
|
const s32 num_acquired_buffers{
|
||||||
|
@ -120,7 +120,7 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc
|
||||||
|
|
||||||
std::shared_ptr<IProducerListener> listener;
|
std::shared_ptr<IProducerListener> listener;
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
// If the frame number has changed because the buffer has been reallocated, we can ignore
|
// If the frame number has changed because the buffer has been reallocated, we can ignore
|
||||||
// this ReleaseBuffer for the old buffer.
|
// this ReleaseBuffer for the old buffer.
|
||||||
|
@ -180,7 +180,7 @@ Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_
|
||||||
|
|
||||||
LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
|
LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
|
||||||
|
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||||
|
@ -199,7 +199,7 @@ Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
|
||||||
return Status::BadValue;
|
return Status::BadValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||||
|
|
|
@ -15,7 +15,7 @@ BufferQueueCore::BufferQueueCore() = default;
|
||||||
BufferQueueCore::~BufferQueueCore() = default;
|
BufferQueueCore::~BufferQueueCore() = default;
|
||||||
|
|
||||||
void BufferQueueCore::NotifyShutdown() {
|
void BufferQueueCore::NotifyShutdown() {
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock{mutex};
|
||||||
|
|
||||||
is_shutting_down = true;
|
is_shutting_down = true;
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ BufferQueueProducer::~BufferQueueProducer() {
|
||||||
Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
|
Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
|
||||||
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
|
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
|
||||||
|
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||||
|
@ -65,7 +65,7 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
|
||||||
|
|
||||||
std::shared_ptr<IConsumerListener> listener;
|
std::shared_ptr<IConsumerListener> listener;
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
core->WaitWhileAllocatingLocked();
|
core->WaitWhileAllocatingLocked();
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
|
@ -236,7 +236,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
|
||||||
Status return_flags = Status::NoError;
|
Status return_flags = Status::NoError;
|
||||||
bool attached_by_consumer = false;
|
bool attached_by_consumer = false;
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
core->WaitWhileAllocatingLocked();
|
core->WaitWhileAllocatingLocked();
|
||||||
|
|
||||||
if (format == PixelFormat::NoFormat) {
|
if (format == PixelFormat::NoFormat) {
|
||||||
|
@ -295,7 +295,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||||
|
@ -320,7 +320,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool
|
||||||
Status BufferQueueProducer::DetachBuffer(s32 slot) {
|
Status BufferQueueProducer::DetachBuffer(s32 slot) {
|
||||||
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
|
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
|
||||||
|
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||||
|
@ -356,7 +356,7 @@ Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out
|
||||||
return Status::BadValue;
|
return Status::BadValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
core->WaitWhileAllocatingLocked();
|
core->WaitWhileAllocatingLocked();
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
|
@ -399,7 +399,7 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot,
|
||||||
return Status::BadValue;
|
return Status::BadValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
core->WaitWhileAllocatingLocked();
|
core->WaitWhileAllocatingLocked();
|
||||||
|
|
||||||
Status return_flags = Status::NoError;
|
Status return_flags = Status::NoError;
|
||||||
|
@ -460,7 +460,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
|
||||||
BufferItem item;
|
BufferItem item;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||||
|
@ -576,7 +576,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
|
||||||
// Call back without the main BufferQueue lock held, but with the callback lock held so we can
|
// Call back without the main BufferQueue lock held, but with the callback lock held so we can
|
||||||
// ensure that callbacks occur in order
|
// ensure that callbacks occur in order
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(callback_mutex);
|
std::scoped_lock lock{callback_mutex};
|
||||||
while (callback_ticket != current_callback_ticket) {
|
while (callback_ticket != current_callback_ticket) {
|
||||||
callback_condition.wait(callback_mutex);
|
callback_condition.wait(callback_mutex);
|
||||||
}
|
}
|
||||||
|
@ -597,7 +597,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
|
||||||
void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
|
void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
|
||||||
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
|
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
|
||||||
|
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
if (core->is_abandoned) {
|
if (core->is_abandoned) {
|
||||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||||
|
@ -623,7 +623,7 @@ void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
|
Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
if (out_value == nullptr) {
|
if (out_value == nullptr) {
|
||||||
LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
|
LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
|
||||||
|
@ -673,7 +673,7 @@ Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
|
||||||
Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
|
Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
|
||||||
NativeWindowApi api, bool producer_controlled_by_app,
|
NativeWindowApi api, bool producer_controlled_by_app,
|
||||||
QueueBufferOutput* output) {
|
QueueBufferOutput* output) {
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
|
LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
|
||||||
producer_controlled_by_app);
|
producer_controlled_by_app);
|
||||||
|
@ -730,7 +730,7 @@ Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
|
||||||
std::shared_ptr<IConsumerListener> listener;
|
std::shared_ptr<IConsumerListener> listener;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
core->WaitWhileAllocatingLocked();
|
core->WaitWhileAllocatingLocked();
|
||||||
|
|
||||||
|
@ -780,7 +780,7 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
|
||||||
return Status::BadValue;
|
return Status::BadValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::scoped_lock lock(core->mutex);
|
std::scoped_lock lock{core->mutex};
|
||||||
|
|
||||||
slots[slot] = {};
|
slots[slot] = {};
|
||||||
slots[slot].graphic_buffer = buffer;
|
slots[slot].graphic_buffer = buffer;
|
||||||
|
|
|
@ -18,7 +18,7 @@ ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
|
||||||
: consumer{std::move(consumer_)} {}
|
: consumer{std::move(consumer_)} {}
|
||||||
|
|
||||||
ConsumerBase::~ConsumerBase() {
|
ConsumerBase::~ConsumerBase() {
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock{mutex};
|
||||||
|
|
||||||
ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
|
ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsumerBase::OnBuffersReleased() {
|
void ConsumerBase::OnBuffersReleased() {
|
||||||
std::scoped_lock lock(mutex);
|
std::scoped_lock lock{mutex};
|
||||||
|
|
||||||
LOG_DEBUG(Service_NVFlinger, "called");
|
LOG_DEBUG(Service_NVFlinger, "called");
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
|
||||||
HosBinderDriverServer::~HosBinderDriverServer() {}
|
HosBinderDriverServer::~HosBinderDriverServer() {}
|
||||||
|
|
||||||
u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
|
u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
|
||||||
std::lock_guard lk{lock};
|
std::scoped_lock lk{lock};
|
||||||
|
|
||||||
last_id++;
|
last_id++;
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&&
|
||||||
}
|
}
|
||||||
|
|
||||||
android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
|
android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
|
||||||
std::lock_guard lk{lock};
|
std::scoped_lock lk{lock};
|
||||||
|
|
||||||
if (auto search = producers.find(id); search != producers.end()) {
|
if (auto search = producers.find(id); search != producers.end()) {
|
||||||
return search->second.get();
|
return search->second.get();
|
||||||
|
|
|
@ -53,13 +53,13 @@ PerfStats::~PerfStats() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerfStats::BeginSystemFrame() {
|
void PerfStats::BeginSystemFrame() {
|
||||||
std::lock_guard lock{object_mutex};
|
std::scoped_lock lock{object_mutex};
|
||||||
|
|
||||||
frame_begin = Clock::now();
|
frame_begin = Clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PerfStats::EndSystemFrame() {
|
void PerfStats::EndSystemFrame() {
|
||||||
std::lock_guard lock{object_mutex};
|
std::scoped_lock lock{object_mutex};
|
||||||
|
|
||||||
auto frame_end = Clock::now();
|
auto frame_end = Clock::now();
|
||||||
const auto frame_time = frame_end - frame_begin;
|
const auto frame_time = frame_end - frame_begin;
|
||||||
|
@ -79,7 +79,7 @@ void PerfStats::EndGameFrame() {
|
||||||
}
|
}
|
||||||
|
|
||||||
double PerfStats::GetMeanFrametime() const {
|
double PerfStats::GetMeanFrametime() const {
|
||||||
std::lock_guard lock{object_mutex};
|
std::scoped_lock lock{object_mutex};
|
||||||
|
|
||||||
if (current_index <= IgnoreFrames) {
|
if (current_index <= IgnoreFrames) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -91,7 +91,7 @@ double PerfStats::GetMeanFrametime() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
|
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
|
||||||
std::lock_guard lock{object_mutex};
|
std::scoped_lock lock{object_mutex};
|
||||||
|
|
||||||
const auto now = Clock::now();
|
const auto now = Clock::now();
|
||||||
// Walltime elapsed since stats were reset
|
// Walltime elapsed since stats were reset
|
||||||
|
@ -120,7 +120,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
|
||||||
}
|
}
|
||||||
|
|
||||||
double PerfStats::GetLastFrameTimeScale() const {
|
double PerfStats::GetLastFrameTimeScale() const {
|
||||||
std::lock_guard lock{object_mutex};
|
std::scoped_lock lock{object_mutex};
|
||||||
|
|
||||||
constexpr double FRAME_LENGTH = 1.0 / 60;
|
constexpr double FRAME_LENGTH = 1.0 / 60;
|
||||||
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
|
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
|
||||||
|
|
|
@ -80,7 +80,7 @@ bool Freezer::IsActive() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Freezer::Clear() {
|
void Freezer::Clear() {
|
||||||
std::lock_guard lock{entries_mutex};
|
std::scoped_lock lock{entries_mutex};
|
||||||
|
|
||||||
LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
|
LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ void Freezer::Clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Freezer::Freeze(VAddr address, u32 width) {
|
u64 Freezer::Freeze(VAddr address, u32 width) {
|
||||||
std::lock_guard lock{entries_mutex};
|
std::scoped_lock lock{entries_mutex};
|
||||||
|
|
||||||
const auto current_value = MemoryReadWidth(memory, width, address);
|
const auto current_value = MemoryReadWidth(memory, width, address);
|
||||||
entries.push_back({address, width, current_value});
|
entries.push_back({address, width, current_value});
|
||||||
|
@ -101,7 +101,7 @@ u64 Freezer::Freeze(VAddr address, u32 width) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Freezer::Unfreeze(VAddr address) {
|
void Freezer::Unfreeze(VAddr address) {
|
||||||
std::lock_guard lock{entries_mutex};
|
std::scoped_lock lock{entries_mutex};
|
||||||
|
|
||||||
LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
|
LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
|
||||||
|
|
||||||
|
@ -109,13 +109,13 @@ void Freezer::Unfreeze(VAddr address) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Freezer::IsFrozen(VAddr address) const {
|
bool Freezer::IsFrozen(VAddr address) const {
|
||||||
std::lock_guard lock{entries_mutex};
|
std::scoped_lock lock{entries_mutex};
|
||||||
|
|
||||||
return FindEntry(address) != entries.cend();
|
return FindEntry(address) != entries.cend();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Freezer::SetFrozenValue(VAddr address, u64 value) {
|
void Freezer::SetFrozenValue(VAddr address, u64 value) {
|
||||||
std::lock_guard lock{entries_mutex};
|
std::scoped_lock lock{entries_mutex};
|
||||||
|
|
||||||
const auto iter = FindEntry(address);
|
const auto iter = FindEntry(address);
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
|
std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
|
||||||
std::lock_guard lock{entries_mutex};
|
std::scoped_lock lock{entries_mutex};
|
||||||
|
|
||||||
const auto iter = FindEntry(address);
|
const auto iter = FindEntry(address);
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Freezer::Entry> Freezer::GetEntries() const {
|
std::vector<Freezer::Entry> Freezer::GetEntries() const {
|
||||||
std::lock_guard lock{entries_mutex};
|
std::scoped_lock lock{entries_mutex};
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard lock{entries_mutex};
|
std::scoped_lock lock{entries_mutex};
|
||||||
|
|
||||||
for (const auto& entry : entries) {
|
for (const auto& entry : entries) {
|
||||||
LOG_DEBUG(Common_Memory,
|
LOG_DEBUG(Common_Memory,
|
||||||
|
@ -178,7 +178,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Freezer::FillEntryReads() {
|
void Freezer::FillEntryReads() {
|
||||||
std::lock_guard lock{entries_mutex};
|
std::scoped_lock lock{entries_mutex};
|
||||||
|
|
||||||
LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
|
LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ public:
|
||||||
|
|
||||||
bool UpdateMotion(SDL_ControllerSensorEvent event) {
|
bool UpdateMotion(SDL_ControllerSensorEvent event) {
|
||||||
constexpr float gravity_constant = 9.80665f;
|
constexpr float gravity_constant = 9.80665f;
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const u64 time_difference = event.timestamp - last_motion_update;
|
const u64 time_difference = event.timestamp - last_motion_update;
|
||||||
last_motion_update = event.timestamp;
|
last_motion_update = event.timestamp;
|
||||||
switch (event.sensor) {
|
switch (event.sensor) {
|
||||||
|
@ -241,7 +241,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
|
std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickByGUID(const std::string& guid, int port) {
|
||||||
std::lock_guard lock{joystick_map_mutex};
|
std::scoped_lock lock{joystick_map_mutex};
|
||||||
const auto it = joystick_map.find(guid);
|
const auto it = joystick_map.find(guid);
|
||||||
|
|
||||||
if (it != joystick_map.end()) {
|
if (it != joystick_map.end()) {
|
||||||
|
@ -263,7 +263,7 @@ std::shared_ptr<SDLJoystick> SDLDriver::GetSDLJoystickBySDLID(SDL_JoystickID sdl
|
||||||
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
|
auto sdl_joystick = SDL_JoystickFromInstanceID(sdl_id);
|
||||||
const std::string guid = GetGUID(sdl_joystick);
|
const std::string guid = GetGUID(sdl_joystick);
|
||||||
|
|
||||||
std::lock_guard lock{joystick_map_mutex};
|
std::scoped_lock lock{joystick_map_mutex};
|
||||||
const auto map_it = joystick_map.find(guid);
|
const auto map_it = joystick_map.find(guid);
|
||||||
|
|
||||||
if (map_it == joystick_map.end()) {
|
if (map_it == joystick_map.end()) {
|
||||||
|
@ -297,7 +297,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
|
||||||
|
|
||||||
const std::string guid = GetGUID(sdl_joystick);
|
const std::string guid = GetGUID(sdl_joystick);
|
||||||
|
|
||||||
std::lock_guard lock{joystick_map_mutex};
|
std::scoped_lock lock{joystick_map_mutex};
|
||||||
if (joystick_map.find(guid) == joystick_map.end()) {
|
if (joystick_map.find(guid) == joystick_map.end()) {
|
||||||
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
|
auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
|
||||||
PreSetController(joystick->GetPadIdentifier());
|
PreSetController(joystick->GetPadIdentifier());
|
||||||
|
@ -326,7 +326,7 @@ void SDLDriver::InitJoystick(int joystick_index) {
|
||||||
void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
|
void SDLDriver::CloseJoystick(SDL_Joystick* sdl_joystick) {
|
||||||
const std::string guid = GetGUID(sdl_joystick);
|
const std::string guid = GetGUID(sdl_joystick);
|
||||||
|
|
||||||
std::lock_guard lock{joystick_map_mutex};
|
std::scoped_lock lock{joystick_map_mutex};
|
||||||
// This call to guid is safe since the joystick is guaranteed to be in the map
|
// This call to guid is safe since the joystick is guaranteed to be in the map
|
||||||
const auto& joystick_guid_list = joystick_map[guid];
|
const auto& joystick_guid_list = joystick_map[guid];
|
||||||
const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
|
const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
|
||||||
|
@ -392,7 +392,7 @@ void SDLDriver::HandleGameControllerEvent(const SDL_Event& event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLDriver::CloseJoysticks() {
|
void SDLDriver::CloseJoysticks() {
|
||||||
std::lock_guard lock{joystick_map_mutex};
|
std::scoped_lock lock{joystick_map_mutex};
|
||||||
joystick_map.clear();
|
joystick_map.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,37 +8,37 @@
|
||||||
namespace InputCommon {
|
namespace InputCommon {
|
||||||
|
|
||||||
void InputEngine::PreSetController(const PadIdentifier& identifier) {
|
void InputEngine::PreSetController(const PadIdentifier& identifier) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
controller_list.try_emplace(identifier);
|
controller_list.try_emplace(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
|
void InputEngine::PreSetButton(const PadIdentifier& identifier, int button) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
ControllerData& controller = controller_list.at(identifier);
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
controller.buttons.try_emplace(button, false);
|
controller.buttons.try_emplace(button, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
|
void InputEngine::PreSetHatButton(const PadIdentifier& identifier, int button) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
ControllerData& controller = controller_list.at(identifier);
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
controller.hat_buttons.try_emplace(button, u8{0});
|
controller.hat_buttons.try_emplace(button, u8{0});
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
|
void InputEngine::PreSetAxis(const PadIdentifier& identifier, int axis) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
ControllerData& controller = controller_list.at(identifier);
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
controller.axes.try_emplace(axis, 0.0f);
|
controller.axes.try_emplace(axis, 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
|
void InputEngine::PreSetMotion(const PadIdentifier& identifier, int motion) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
ControllerData& controller = controller_list.at(identifier);
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
controller.motions.try_emplace(motion);
|
controller.motions.try_emplace(motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
|
void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool value) {
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
ControllerData& controller = controller_list.at(identifier);
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
if (!configuring) {
|
if (!configuring) {
|
||||||
controller.buttons.insert_or_assign(button, value);
|
controller.buttons.insert_or_assign(button, value);
|
||||||
|
@ -49,7 +49,7 @@ void InputEngine::SetButton(const PadIdentifier& identifier, int button, bool va
|
||||||
|
|
||||||
void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
|
void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 value) {
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
ControllerData& controller = controller_list.at(identifier);
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
if (!configuring) {
|
if (!configuring) {
|
||||||
controller.hat_buttons.insert_or_assign(button, value);
|
controller.hat_buttons.insert_or_assign(button, value);
|
||||||
|
@ -60,7 +60,7 @@ void InputEngine::SetHatButton(const PadIdentifier& identifier, int button, u8 v
|
||||||
|
|
||||||
void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
|
void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value) {
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
ControllerData& controller = controller_list.at(identifier);
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
if (!configuring) {
|
if (!configuring) {
|
||||||
controller.axes.insert_or_assign(axis, value);
|
controller.axes.insert_or_assign(axis, value);
|
||||||
|
@ -71,7 +71,7 @@ void InputEngine::SetAxis(const PadIdentifier& identifier, int axis, f32 value)
|
||||||
|
|
||||||
void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
|
void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value) {
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
ControllerData& controller = controller_list.at(identifier);
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
if (!configuring) {
|
if (!configuring) {
|
||||||
controller.battery = value;
|
controller.battery = value;
|
||||||
|
@ -82,7 +82,7 @@ void InputEngine::SetBattery(const PadIdentifier& identifier, Common::Input::Bat
|
||||||
|
|
||||||
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
|
void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value) {
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
ControllerData& controller = controller_list.at(identifier);
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
if (!configuring) {
|
if (!configuring) {
|
||||||
controller.motions.insert_or_assign(motion, value);
|
controller.motions.insert_or_assign(motion, value);
|
||||||
|
@ -92,7 +92,7 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const auto controller_iter = controller_list.find(identifier);
|
const auto controller_iter = controller_list.find(identifier);
|
||||||
if (controller_iter == controller_list.cend()) {
|
if (controller_iter == controller_list.cend()) {
|
||||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||||
|
@ -109,7 +109,7 @@ bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
|
bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 direction) const {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const auto controller_iter = controller_list.find(identifier);
|
const auto controller_iter = controller_list.find(identifier);
|
||||||
if (controller_iter == controller_list.cend()) {
|
if (controller_iter == controller_list.cend()) {
|
||||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||||
|
@ -126,7 +126,7 @@ bool InputEngine::GetHatButton(const PadIdentifier& identifier, int button, u8 d
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
|
f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const auto controller_iter = controller_list.find(identifier);
|
const auto controller_iter = controller_list.find(identifier);
|
||||||
if (controller_iter == controller_list.cend()) {
|
if (controller_iter == controller_list.cend()) {
|
||||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||||
|
@ -143,7 +143,7 @@ f32 InputEngine::GetAxis(const PadIdentifier& identifier, int axis) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identifier) const {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const auto controller_iter = controller_list.find(identifier);
|
const auto controller_iter = controller_list.find(identifier);
|
||||||
if (controller_iter == controller_list.cend()) {
|
if (controller_iter == controller_list.cend()) {
|
||||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||||
|
@ -155,7 +155,7 @@ Common::Input::BatteryLevel InputEngine::GetBattery(const PadIdentifier& identif
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
|
BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion) const {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const auto controller_iter = controller_list.find(identifier);
|
const auto controller_iter = controller_list.find(identifier);
|
||||||
if (controller_iter == controller_list.cend()) {
|
if (controller_iter == controller_list.cend()) {
|
||||||
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||||
|
@ -186,7 +186,7 @@ void InputEngine::ResetAnalogState() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
|
void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value) {
|
||||||
std::lock_guard lock{mutex_callback};
|
std::scoped_lock lock{mutex_callback};
|
||||||
for (const auto& poller_pair : callback_list) {
|
for (const auto& poller_pair : callback_list) {
|
||||||
const InputIdentifier& poller = poller_pair.second;
|
const InputIdentifier& poller = poller_pair.second;
|
||||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
|
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Button, button)) {
|
||||||
|
@ -214,7 +214,7 @@ void InputEngine::TriggerOnButtonChange(const PadIdentifier& identifier, int but
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
|
void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int button, u8 value) {
|
||||||
std::lock_guard lock{mutex_callback};
|
std::scoped_lock lock{mutex_callback};
|
||||||
for (const auto& poller_pair : callback_list) {
|
for (const auto& poller_pair : callback_list) {
|
||||||
const InputIdentifier& poller = poller_pair.second;
|
const InputIdentifier& poller = poller_pair.second;
|
||||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
|
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::HatButton, button)) {
|
||||||
|
@ -243,7 +243,7 @@ void InputEngine::TriggerOnHatButtonChange(const PadIdentifier& identifier, int
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
|
void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis, f32 value) {
|
||||||
std::lock_guard lock{mutex_callback};
|
std::scoped_lock lock{mutex_callback};
|
||||||
for (const auto& poller_pair : callback_list) {
|
for (const auto& poller_pair : callback_list) {
|
||||||
const InputIdentifier& poller = poller_pair.second;
|
const InputIdentifier& poller = poller_pair.second;
|
||||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
|
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Analog, axis)) {
|
||||||
|
@ -270,7 +270,7 @@ void InputEngine::TriggerOnAxisChange(const PadIdentifier& identifier, int axis,
|
||||||
|
|
||||||
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||||
[[maybe_unused]] Common::Input::BatteryLevel value) {
|
[[maybe_unused]] Common::Input::BatteryLevel value) {
|
||||||
std::lock_guard lock{mutex_callback};
|
std::scoped_lock lock{mutex_callback};
|
||||||
for (const auto& poller_pair : callback_list) {
|
for (const auto& poller_pair : callback_list) {
|
||||||
const InputIdentifier& poller = poller_pair.second;
|
const InputIdentifier& poller = poller_pair.second;
|
||||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
|
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Battery, 0)) {
|
||||||
|
@ -284,7 +284,7 @@ void InputEngine::TriggerOnBatteryChange(const PadIdentifier& identifier,
|
||||||
|
|
||||||
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||||
const BasicMotion& value) {
|
const BasicMotion& value) {
|
||||||
std::lock_guard lock{mutex_callback};
|
std::scoped_lock lock{mutex_callback};
|
||||||
for (const auto& poller_pair : callback_list) {
|
for (const auto& poller_pair : callback_list) {
|
||||||
const InputIdentifier& poller = poller_pair.second;
|
const InputIdentifier& poller = poller_pair.second;
|
||||||
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
|
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Motion, motion)) {
|
||||||
|
@ -346,18 +346,18 @@ const std::string& InputEngine::GetEngineName() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
int InputEngine::SetCallback(InputIdentifier input_identifier) {
|
int InputEngine::SetCallback(InputIdentifier input_identifier) {
|
||||||
std::lock_guard lock{mutex_callback};
|
std::scoped_lock lock{mutex_callback};
|
||||||
callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
|
callback_list.insert_or_assign(last_callback_key, std::move(input_identifier));
|
||||||
return last_callback_key++;
|
return last_callback_key++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::SetMappingCallback(MappingCallback callback) {
|
void InputEngine::SetMappingCallback(MappingCallback callback) {
|
||||||
std::lock_guard lock{mutex_callback};
|
std::scoped_lock lock{mutex_callback};
|
||||||
mapping_callback = std::move(callback);
|
mapping_callback = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputEngine::DeleteCallback(int key) {
|
void InputEngine::DeleteCallback(int key) {
|
||||||
std::lock_guard lock{mutex_callback};
|
std::scoped_lock lock{mutex_callback};
|
||||||
const auto& iterator = callback_list.find(key);
|
const auto& iterator = callback_list.find(key);
|
||||||
if (iterator == callback_list.end()) {
|
if (iterator == callback_list.end()) {
|
||||||
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
|
LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
|
||||||
|
|
|
@ -230,7 +230,7 @@ struct GPU::Impl {
|
||||||
void IncrementSyncPoint(u32 syncpoint_id) {
|
void IncrementSyncPoint(u32 syncpoint_id) {
|
||||||
auto& syncpoint = syncpoints.at(syncpoint_id);
|
auto& syncpoint = syncpoints.at(syncpoint_id);
|
||||||
syncpoint++;
|
syncpoint++;
|
||||||
std::lock_guard lock{sync_mutex};
|
std::scoped_lock lock{sync_mutex};
|
||||||
sync_cv.notify_all();
|
sync_cv.notify_all();
|
||||||
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
|
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
|
||||||
if (!interrupt.empty()) {
|
if (!interrupt.empty()) {
|
||||||
|
@ -252,7 +252,7 @@ struct GPU::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) {
|
void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value) {
|
||||||
std::lock_guard lock{sync_mutex};
|
std::scoped_lock lock{sync_mutex};
|
||||||
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
|
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
|
||||||
bool contains = std::any_of(interrupt.begin(), interrupt.end(),
|
bool contains = std::any_of(interrupt.begin(), interrupt.end(),
|
||||||
[value](u32 in_value) { return in_value == value; });
|
[value](u32 in_value) { return in_value == value; });
|
||||||
|
@ -263,7 +263,7 @@ struct GPU::Impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) {
|
[[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value) {
|
||||||
std::lock_guard lock{sync_mutex};
|
std::scoped_lock lock{sync_mutex};
|
||||||
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
|
auto& interrupt = syncpt_interrupts.at(syncpoint_id);
|
||||||
const auto iter =
|
const auto iter =
|
||||||
std::find_if(interrupt.begin(), interrupt.end(),
|
std::find_if(interrupt.begin(), interrupt.end(),
|
||||||
|
|
|
@ -56,7 +56,7 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
|
||||||
if (next.block) {
|
if (next.block) {
|
||||||
// We have to lock the write_lock to ensure that the condition_variable wait not get a
|
// We have to lock the write_lock to ensure that the condition_variable wait not get a
|
||||||
// race between the check and the lock itself.
|
// race between the check and the lock itself.
|
||||||
std::lock_guard lk(state.write_lock);
|
std::scoped_lock lk{state.write_lock};
|
||||||
state.cv.notify_all();
|
state.cv.notify_all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,7 +253,7 @@ GraphicsPipeline::GraphicsPipeline(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (in_parallel) {
|
if (in_parallel) {
|
||||||
std::lock_guard lock{built_mutex};
|
std::scoped_lock lock{built_mutex};
|
||||||
built_fence.Create();
|
built_fence.Create();
|
||||||
// Flush this context to ensure compilation commands and fence are in the GPU pipe.
|
// Flush this context to ensure compilation commands and fence are in the GPU pipe.
|
||||||
glFlush();
|
glFlush();
|
||||||
|
|
|
@ -258,7 +258,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||||
[this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
|
[this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
|
||||||
ctx->pools.ReleaseContents();
|
ctx->pools.ReleaseContents();
|
||||||
auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
|
auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
|
||||||
std::lock_guard lock{state.mutex};
|
std::scoped_lock lock{state.mutex};
|
||||||
if (pipeline) {
|
if (pipeline) {
|
||||||
compute_cache.emplace(key, std::move(pipeline));
|
compute_cache.emplace(key, std::move(pipeline));
|
||||||
}
|
}
|
||||||
|
@ -280,7 +280,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
|
||||||
}
|
}
|
||||||
ctx->pools.ReleaseContents();
|
ctx->pools.ReleaseContents();
|
||||||
auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
|
auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
|
||||||
std::lock_guard lock{state.mutex};
|
std::scoped_lock lock{state.mutex};
|
||||||
if (pipeline) {
|
if (pipeline) {
|
||||||
graphics_cache.emplace(key, std::move(pipeline));
|
graphics_cache.emplace(key, std::move(pipeline));
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ void PipelineStatistics::Collect(VkPipeline pipeline) {
|
||||||
stage_stats.basic_block_count = GetUint64(statistic);
|
stage_stats.basic_block_count = GetUint64(statistic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
collected_stats.push_back(stage_stats);
|
collected_stats.push_back(stage_stats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ void PipelineStatistics::Report() const {
|
||||||
double num{};
|
double num{};
|
||||||
Stats total;
|
Stats total;
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
for (const Stats& stats : collected_stats) {
|
for (const Stats& stats : collected_stats) {
|
||||||
total.code_size += stats.code_size;
|
total.code_size += stats.code_size;
|
||||||
total.register_count += stats.register_count;
|
total.register_count += stats.register_count;
|
||||||
|
|
|
@ -77,7 +77,7 @@ ComputePipeline::ComputePipeline(const Device& device_, DescriptorPool& descript
|
||||||
if (pipeline_statistics) {
|
if (pipeline_statistics) {
|
||||||
pipeline_statistics->Collect(*pipeline);
|
pipeline_statistics->Collect(*pipeline);
|
||||||
}
|
}
|
||||||
std::lock_guard lock{build_mutex};
|
std::scoped_lock lock{build_mutex};
|
||||||
is_built = true;
|
is_built = true;
|
||||||
build_condvar.notify_one();
|
build_condvar.notify_one();
|
||||||
if (shader_notify) {
|
if (shader_notify) {
|
||||||
|
|
|
@ -258,7 +258,7 @@ GraphicsPipeline::GraphicsPipeline(
|
||||||
pipeline_statistics->Collect(*pipeline);
|
pipeline_statistics->Collect(*pipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard lock{build_mutex};
|
std::scoped_lock lock{build_mutex};
|
||||||
is_built = true;
|
is_built = true;
|
||||||
build_condvar.notify_one();
|
build_condvar.notify_one();
|
||||||
if (shader_notify) {
|
if (shader_notify) {
|
||||||
|
|
|
@ -404,7 +404,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||||
workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
|
workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
|
||||||
ShaderPools pools;
|
ShaderPools pools;
|
||||||
auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
|
auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)};
|
||||||
std::lock_guard lock{state.mutex};
|
std::scoped_lock lock{state.mutex};
|
||||||
if (pipeline) {
|
if (pipeline) {
|
||||||
compute_cache.emplace(key, std::move(pipeline));
|
compute_cache.emplace(key, std::move(pipeline));
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||||
auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
|
auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs),
|
||||||
state.statistics.get(), false)};
|
state.statistics.get(), false)};
|
||||||
|
|
||||||
std::lock_guard lock{state.mutex};
|
std::scoped_lock lock{state.mutex};
|
||||||
graphics_cache.emplace(key, std::move(pipeline));
|
graphics_cache.emplace(key, std::move(pipeline));
|
||||||
++state.built;
|
++state.built;
|
||||||
if (state.has_loaded) {
|
if (state.has_loaded) {
|
||||||
|
|
|
@ -36,7 +36,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat
|
||||||
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
|
RenderPassCache::RenderPassCache(const Device& device_) : device{&device_} {}
|
||||||
|
|
||||||
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
VkRenderPass RenderPassCache::Get(const RenderPassKey& key) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const auto [pair, is_new] = cache.try_emplace(key);
|
const auto [pair, is_new] = cache.try_emplace(key);
|
||||||
if (!is_new) {
|
if (!is_new) {
|
||||||
return *pair->second;
|
return *pair->second;
|
||||||
|
|
|
@ -73,7 +73,7 @@ void VKScheduler::DispatchWork() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::lock_guard lock{work_mutex};
|
std::scoped_lock lock{work_mutex};
|
||||||
work_queue.push(std::move(chunk));
|
work_queue.push(std::move(chunk));
|
||||||
}
|
}
|
||||||
work_cv.notify_one();
|
work_cv.notify_one();
|
||||||
|
@ -157,7 +157,7 @@ void VKScheduler::WorkerThread(std::stop_token stop_token) {
|
||||||
if (has_submit) {
|
if (has_submit) {
|
||||||
AllocateWorkerCommandBuffer();
|
AllocateWorkerCommandBuffer();
|
||||||
}
|
}
|
||||||
std::lock_guard reserve_lock{reserve_mutex};
|
std::scoped_lock reserve_lock{reserve_mutex};
|
||||||
chunk_reserve.push_back(std::move(work));
|
chunk_reserve.push_back(std::move(work));
|
||||||
} while (!stop_token.stop_requested());
|
} while (!stop_token.stop_requested());
|
||||||
}
|
}
|
||||||
|
@ -282,7 +282,7 @@ void VKScheduler::EndRenderPass() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKScheduler::AcquireNewChunk() {
|
void VKScheduler::AcquireNewChunk() {
|
||||||
std::lock_guard lock{reserve_mutex};
|
std::scoped_lock lock{reserve_mutex};
|
||||||
if (chunk_reserve.empty()) {
|
if (chunk_reserve.empty()) {
|
||||||
chunk = std::make_unique<CommandChunk>();
|
chunk = std::make_unique<CommandChunk>();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -25,7 +25,7 @@ void ShaderCache::InvalidateRegion(VAddr addr, size_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderCache::OnCPUWrite(VAddr addr, size_t size) {
|
void ShaderCache::OnCPUWrite(VAddr addr, size_t size) {
|
||||||
std::lock_guard lock{invalidation_mutex};
|
std::scoped_lock lock{invalidation_mutex};
|
||||||
InvalidatePagesInRegion(addr, size);
|
InvalidatePagesInRegion(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ constexpr std::size_t TIMEOUT_SECONDS = 30;
|
||||||
struct Client::Impl {
|
struct Client::Impl {
|
||||||
Impl(std::string host, std::string username, std::string token)
|
Impl(std::string host, std::string username, std::string token)
|
||||||
: host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
|
: host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
|
||||||
std::lock_guard lock{jwt_cache.mutex};
|
std::scoped_lock lock{jwt_cache.mutex};
|
||||||
if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
|
if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
|
||||||
jwt = jwt_cache.jwt;
|
jwt = jwt_cache.jwt;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ struct Client::Impl {
|
||||||
if (result.result_code != WebResult::Code::Success) {
|
if (result.result_code != WebResult::Code::Success) {
|
||||||
LOG_ERROR(WebService, "UpdateJWT failed");
|
LOG_ERROR(WebService, "UpdateJWT failed");
|
||||||
} else {
|
} else {
|
||||||
std::lock_guard lock{jwt_cache.mutex};
|
std::scoped_lock lock{jwt_cache.mutex};
|
||||||
jwt_cache.username = username;
|
jwt_cache.username = username;
|
||||||
jwt_cache.token = token;
|
jwt_cache.token = token;
|
||||||
jwt_cache.jwt = jwt = result.returned_data;
|
jwt_cache.jwt = jwt = result.returned_data;
|
||||||
|
|
|
@ -39,7 +39,7 @@ void ControllerNavigation::TriggerButton(Settings::NativeButton::Values native_b
|
||||||
}
|
}
|
||||||
|
|
||||||
void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
|
void ControllerNavigation::ControllerUpdateEvent(Core::HID::ControllerTriggerType type) {
|
||||||
std::lock_guard lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
if (!Settings::values.controller_navigation) {
|
if (!Settings::values.controller_navigation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue