another try
This commit is contained in:
@@ -1,43 +1,43 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
enum class TransactionId {
|
||||
RequestBuffer = 1,
|
||||
SetBufferCount = 2,
|
||||
DequeueBuffer = 3,
|
||||
DetachBuffer = 4,
|
||||
DetachNextBuffer = 5,
|
||||
AttachBuffer = 6,
|
||||
QueueBuffer = 7,
|
||||
CancelBuffer = 8,
|
||||
Query = 9,
|
||||
Connect = 10,
|
||||
Disconnect = 11,
|
||||
AllocateBuffers = 13,
|
||||
SetPreallocatedBuffer = 14,
|
||||
GetBufferHistory = 17,
|
||||
};
|
||||
|
||||
class IBinder {
|
||||
public:
|
||||
virtual ~IBinder() = default;
|
||||
virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
|
||||
u32 flags) = 0;
|
||||
virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Kernel {
|
||||
class HLERequestContext;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
enum class TransactionId {
|
||||
RequestBuffer = 1,
|
||||
SetBufferCount = 2,
|
||||
DequeueBuffer = 3,
|
||||
DetachBuffer = 4,
|
||||
DetachNextBuffer = 5,
|
||||
AttachBuffer = 6,
|
||||
QueueBuffer = 7,
|
||||
CancelBuffer = 8,
|
||||
Query = 9,
|
||||
Connect = 10,
|
||||
Disconnect = 11,
|
||||
AllocateBuffers = 13,
|
||||
SetPreallocatedBuffer = 14,
|
||||
GetBufferHistory = 17,
|
||||
};
|
||||
|
||||
class IBinder {
|
||||
public:
|
||||
virtual ~IBinder() = default;
|
||||
virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
|
||||
u32 flags) = 0;
|
||||
virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvflinger/ui/fence.h"
|
||||
#include "core/hle/service/nvflinger/window.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class GraphicBuffer;
|
||||
|
||||
class BufferItem final {
|
||||
public:
|
||||
constexpr BufferItem() = default;
|
||||
|
||||
std::shared_ptr<GraphicBuffer> graphic_buffer;
|
||||
Fence fence;
|
||||
Common::Rectangle<s32> crop;
|
||||
NativeWindowTransform transform{};
|
||||
u32 scaling_mode{};
|
||||
s64 timestamp{};
|
||||
bool is_auto_timestamp{};
|
||||
u64 frame_number{};
|
||||
|
||||
// The default value for buf, used to indicate this doesn't correspond to a slot.
|
||||
static constexpr s32 INVALID_BUFFER_SLOT = -1;
|
||||
union {
|
||||
s32 slot{INVALID_BUFFER_SLOT};
|
||||
s32 buf;
|
||||
};
|
||||
|
||||
bool is_droppable{};
|
||||
bool acquire_called{};
|
||||
bool transform_to_display_inverse{};
|
||||
s32 swap_interval{};
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvflinger/ui/fence.h"
|
||||
#include "core/hle/service/nvflinger/window.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class GraphicBuffer;
|
||||
|
||||
class BufferItem final {
|
||||
public:
|
||||
constexpr BufferItem() = default;
|
||||
|
||||
std::shared_ptr<GraphicBuffer> graphic_buffer;
|
||||
Fence fence;
|
||||
Common::Rectangle<s32> crop;
|
||||
NativeWindowTransform transform{};
|
||||
u32 scaling_mode{};
|
||||
s64 timestamp{};
|
||||
bool is_auto_timestamp{};
|
||||
u64 frame_number{};
|
||||
|
||||
// The default value for buf, used to indicate this doesn't correspond to a slot.
|
||||
static constexpr s32 INVALID_BUFFER_SLOT = -1;
|
||||
union {
|
||||
s32 slot{INVALID_BUFFER_SLOT};
|
||||
s32 buf;
|
||||
};
|
||||
|
||||
bool is_droppable{};
|
||||
bool acquire_called{};
|
||||
bool transform_to_display_inverse{};
|
||||
s32 swap_interval{};
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item_consumer.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
|
||||
: ConsumerBase{std::move(consumer_)} {}
|
||||
|
||||
Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
|
||||
bool wait_for_fence) {
|
||||
if (!item) {
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
|
||||
if (status != Status::NoBufferAvailable) {
|
||||
LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if (wait_for_fence) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
item->graphic_buffer = slots[item->slot].graphic_buffer;
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
|
||||
status != Status::NoError) {
|
||||
LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status);
|
||||
}
|
||||
|
||||
if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer);
|
||||
status != Status::NoError) {
|
||||
LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item_consumer.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
|
||||
: ConsumerBase{std::move(consumer_)} {}
|
||||
|
||||
Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
|
||||
bool wait_for_fence) {
|
||||
if (!item) {
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
|
||||
if (status != Status::NoBufferAvailable) {
|
||||
LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if (wait_for_fence) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
item->graphic_buffer = slots[item->slot].graphic_buffer;
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
|
||||
status != Status::NoError) {
|
||||
LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status);
|
||||
}
|
||||
|
||||
if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer);
|
||||
status != Status::NoError) {
|
||||
LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/consumer_base.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferItem;
|
||||
|
||||
class BufferItemConsumer final : public ConsumerBase {
|
||||
public:
|
||||
explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
|
||||
Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
|
||||
bool wait_for_fence = true);
|
||||
Status ReleaseBuffer(const BufferItem& item, Fence& release_fence);
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/consumer_base.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferItem;
|
||||
|
||||
class BufferItemConsumer final : public ConsumerBase {
|
||||
public:
|
||||
explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
|
||||
Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
|
||||
bool wait_for_fence = true);
|
||||
Status ReleaseBuffer(const BufferItem& item, Fence& release_fence);
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,213 +1,213 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_core.h"
|
||||
#include "core/hle/service/nvflinger/producer_listener.h"
|
||||
#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
|
||||
Service::Nvidia::NvCore::NvMap& nvmap_)
|
||||
: core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
|
||||
|
||||
BufferQueueConsumer::~BufferQueueConsumer() = default;
|
||||
|
||||
Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
|
||||
std::chrono::nanoseconds expected_present) {
|
||||
std::scoped_lock lock{core->mutex};
|
||||
|
||||
// Check that the consumer doesn't currently have the maximum number of buffers acquired.
|
||||
const s32 num_acquired_buffers{
|
||||
static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
|
||||
return slot.buffer_state == BufferState::Acquired;
|
||||
}))};
|
||||
|
||||
if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
|
||||
LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
|
||||
num_acquired_buffers, core->max_acquired_buffer_count);
|
||||
return Status::InvalidOperation;
|
||||
}
|
||||
|
||||
// Check if the queue is empty.
|
||||
if (core->queue.empty()) {
|
||||
return Status::NoBufferAvailable;
|
||||
}
|
||||
|
||||
auto front(core->queue.begin());
|
||||
|
||||
// If expected_present is specified, we may not want to return a buffer yet.
|
||||
if (expected_present.count() != 0) {
|
||||
constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
|
||||
|
||||
// The expected_present argument indicates when the buffer is expected to be presented
|
||||
// on-screen.
|
||||
while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
|
||||
const auto& buffer_item{core->queue[1]};
|
||||
|
||||
// If entry[1] is timely, drop entry[0] (and repeat).
|
||||
const auto desired_present = buffer_item.timestamp;
|
||||
if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
|
||||
desired_present > expected_present.count()) {
|
||||
// This buffer is set to display in the near future, or desired_present is garbage.
|
||||
LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
|
||||
expected_present.count());
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
|
||||
expected_present.count(), core->queue.size());
|
||||
|
||||
if (core->StillTracking(*front)) {
|
||||
// Front buffer is still in mSlots, so mark the slot as free
|
||||
slots[front->slot].buffer_state = BufferState::Free;
|
||||
}
|
||||
|
||||
core->queue.erase(front);
|
||||
front = core->queue.begin();
|
||||
}
|
||||
|
||||
// See if the front buffer is ready to be acquired.
|
||||
const auto desired_present = front->timestamp;
|
||||
if (desired_present > expected_present.count() &&
|
||||
desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
|
||||
LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
|
||||
expected_present.count());
|
||||
return Status::PresentLater;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
|
||||
expected_present.count());
|
||||
}
|
||||
|
||||
const auto slot = front->slot;
|
||||
*out_buffer = *front;
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
|
||||
|
||||
// If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
|
||||
// avoid unnecessarily remapping this buffer on the consumer side.
|
||||
if (out_buffer->acquire_called) {
|
||||
out_buffer->graphic_buffer = nullptr;
|
||||
}
|
||||
|
||||
core->queue.erase(front);
|
||||
|
||||
// We might have freed a slot while dropping old buffers, or the producer may be blocked
|
||||
// waiting for the number of buffers in the queue to decrease.
|
||||
core->SignalDequeueCondition();
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) {
|
||||
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot);
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
std::shared_ptr<IProducerListener> listener;
|
||||
{
|
||||
std::scoped_lock lock{core->mutex};
|
||||
|
||||
// If the frame number has changed because the buffer has been reallocated, we can ignore
|
||||
// this ReleaseBuffer for the old buffer.
|
||||
if (frame_number != slots[slot].frame_number) {
|
||||
return Status::StaleBufferSlot;
|
||||
}
|
||||
|
||||
// Make sure this buffer hasn't been queued while acquired by the consumer.
|
||||
auto current(core->queue.begin());
|
||||
while (current != core->queue.end()) {
|
||||
if (current->slot == slot) {
|
||||
LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued",
|
||||
slot);
|
||||
return Status::BadValue;
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
slots[slot].buffer_state = BufferState::Free;
|
||||
|
||||
nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
|
||||
|
||||
listener = core->connected_producer_listener;
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
|
||||
|
||||
core->SignalDequeueCondition();
|
||||
}
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != nullptr) {
|
||||
listener->OnBufferReleased();
|
||||
}
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener,
|
||||
bool controlled_by_app) {
|
||||
if (consumer_listener == nullptr) {
|
||||
LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr");
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
|
||||
|
||||
std::scoped_lock lock{core->mutex};
|
||||
|
||||
if (core->is_abandoned) {
|
||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||
return Status::NoInit;
|
||||
}
|
||||
|
||||
core->consumer_listener = consumer_listener;
|
||||
core->consumer_controlled_by_app = controlled_by_app;
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
|
||||
if (out_slot_mask == nullptr) {
|
||||
LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{core->mutex};
|
||||
|
||||
if (core->is_abandoned) {
|
||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||
return Status::NoInit;
|
||||
}
|
||||
|
||||
u64 mask = 0;
|
||||
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (!slots[s].acquire_called) {
|
||||
mask |= (1ULL << s);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from the mask queued buffers for which acquire has been called, since the consumer
|
||||
// will not receive their buffer addresses and so must retain their cached information
|
||||
auto current(core->queue.begin());
|
||||
while (current != core->queue.end()) {
|
||||
if (current->acquire_called) {
|
||||
mask &= ~(1ULL << current->slot);
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
|
||||
*out_slot_mask = mask;
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_core.h"
|
||||
#include "core/hle/service/nvflinger/producer_listener.h"
|
||||
#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
|
||||
Service::Nvidia::NvCore::NvMap& nvmap_)
|
||||
: core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
|
||||
|
||||
BufferQueueConsumer::~BufferQueueConsumer() = default;
|
||||
|
||||
Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
|
||||
std::chrono::nanoseconds expected_present) {
|
||||
std::scoped_lock lock{core->mutex};
|
||||
|
||||
// Check that the consumer doesn't currently have the maximum number of buffers acquired.
|
||||
const s32 num_acquired_buffers{
|
||||
static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
|
||||
return slot.buffer_state == BufferState::Acquired;
|
||||
}))};
|
||||
|
||||
if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
|
||||
LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
|
||||
num_acquired_buffers, core->max_acquired_buffer_count);
|
||||
return Status::InvalidOperation;
|
||||
}
|
||||
|
||||
// Check if the queue is empty.
|
||||
if (core->queue.empty()) {
|
||||
return Status::NoBufferAvailable;
|
||||
}
|
||||
|
||||
auto front(core->queue.begin());
|
||||
|
||||
// If expected_present is specified, we may not want to return a buffer yet.
|
||||
if (expected_present.count() != 0) {
|
||||
constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
|
||||
|
||||
// The expected_present argument indicates when the buffer is expected to be presented
|
||||
// on-screen.
|
||||
while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
|
||||
const auto& buffer_item{core->queue[1]};
|
||||
|
||||
// If entry[1] is timely, drop entry[0] (and repeat).
|
||||
const auto desired_present = buffer_item.timestamp;
|
||||
if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
|
||||
desired_present > expected_present.count()) {
|
||||
// This buffer is set to display in the near future, or desired_present is garbage.
|
||||
LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
|
||||
expected_present.count());
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
|
||||
expected_present.count(), core->queue.size());
|
||||
|
||||
if (core->StillTracking(*front)) {
|
||||
// Front buffer is still in mSlots, so mark the slot as free
|
||||
slots[front->slot].buffer_state = BufferState::Free;
|
||||
}
|
||||
|
||||
core->queue.erase(front);
|
||||
front = core->queue.begin();
|
||||
}
|
||||
|
||||
// See if the front buffer is ready to be acquired.
|
||||
const auto desired_present = front->timestamp;
|
||||
if (desired_present > expected_present.count() &&
|
||||
desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
|
||||
LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
|
||||
expected_present.count());
|
||||
return Status::PresentLater;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
|
||||
expected_present.count());
|
||||
}
|
||||
|
||||
const auto slot = front->slot;
|
||||
*out_buffer = *front;
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
|
||||
|
||||
// If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
|
||||
// avoid unnecessarily remapping this buffer on the consumer side.
|
||||
if (out_buffer->acquire_called) {
|
||||
out_buffer->graphic_buffer = nullptr;
|
||||
}
|
||||
|
||||
core->queue.erase(front);
|
||||
|
||||
// We might have freed a slot while dropping old buffers, or the producer may be blocked
|
||||
// waiting for the number of buffers in the queue to decrease.
|
||||
core->SignalDequeueCondition();
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) {
|
||||
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot);
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
std::shared_ptr<IProducerListener> listener;
|
||||
{
|
||||
std::scoped_lock lock{core->mutex};
|
||||
|
||||
// If the frame number has changed because the buffer has been reallocated, we can ignore
|
||||
// this ReleaseBuffer for the old buffer.
|
||||
if (frame_number != slots[slot].frame_number) {
|
||||
return Status::StaleBufferSlot;
|
||||
}
|
||||
|
||||
// Make sure this buffer hasn't been queued while acquired by the consumer.
|
||||
auto current(core->queue.begin());
|
||||
while (current != core->queue.end()) {
|
||||
if (current->slot == slot) {
|
||||
LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued",
|
||||
slot);
|
||||
return Status::BadValue;
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
slots[slot].buffer_state = BufferState::Free;
|
||||
|
||||
nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
|
||||
|
||||
listener = core->connected_producer_listener;
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
|
||||
|
||||
core->SignalDequeueCondition();
|
||||
}
|
||||
|
||||
// Call back without lock held
|
||||
if (listener != nullptr) {
|
||||
listener->OnBufferReleased();
|
||||
}
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener,
|
||||
bool controlled_by_app) {
|
||||
if (consumer_listener == nullptr) {
|
||||
LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr");
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
|
||||
|
||||
std::scoped_lock lock{core->mutex};
|
||||
|
||||
if (core->is_abandoned) {
|
||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||
return Status::NoInit;
|
||||
}
|
||||
|
||||
core->consumer_listener = consumer_listener;
|
||||
core->consumer_controlled_by_app = controlled_by_app;
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
|
||||
if (out_slot_mask == nullptr) {
|
||||
LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
|
||||
return Status::BadValue;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{core->mutex};
|
||||
|
||||
if (core->is_abandoned) {
|
||||
LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
|
||||
return Status::NoInit;
|
||||
}
|
||||
|
||||
u64 mask = 0;
|
||||
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
|
||||
if (!slots[s].acquire_called) {
|
||||
mask |= (1ULL << s);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from the mask queued buffers for which acquire has been called, since the consumer
|
||||
// will not receive their buffer addresses and so must retain their cached information
|
||||
auto current(core->queue.begin());
|
||||
while (current != core->queue.end()) {
|
||||
if (current->acquire_called) {
|
||||
mask &= ~(1ULL << current->slot);
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
|
||||
*out_slot_mask = mask;
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_defs.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class NvMap;
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferItem;
|
||||
class BufferQueueCore;
|
||||
class IConsumerListener;
|
||||
|
||||
class BufferQueueConsumer final {
|
||||
public:
|
||||
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
|
||||
Service::Nvidia::NvCore::NvMap& nvmap_);
|
||||
~BufferQueueConsumer();
|
||||
|
||||
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
|
||||
Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
|
||||
Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
|
||||
Status GetReleasedBuffers(u64* out_slot_mask);
|
||||
|
||||
private:
|
||||
std::shared_ptr<BufferQueueCore> core;
|
||||
BufferQueueDefs::SlotsType& slots;
|
||||
Service::Nvidia::NvCore::NvMap& nvmap;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_defs.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class NvMap;
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferItem;
|
||||
class BufferQueueCore;
|
||||
class IConsumerListener;
|
||||
|
||||
class BufferQueueConsumer final {
|
||||
public:
|
||||
explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
|
||||
Service::Nvidia::NvCore::NvMap& nvmap_);
|
||||
~BufferQueueConsumer();
|
||||
|
||||
Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
|
||||
Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
|
||||
Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
|
||||
Status GetReleasedBuffers(u64* out_slot_mask);
|
||||
|
||||
private:
|
||||
std::shared_ptr<BufferQueueCore> core;
|
||||
BufferQueueDefs::SlotsType& slots;
|
||||
Service::Nvidia::NvCore::NvMap& nvmap;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,113 +1,113 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "core/hle/service/nvflinger/buffer_queue_core.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
BufferQueueCore::BufferQueueCore() = default;
|
||||
|
||||
BufferQueueCore::~BufferQueueCore() = default;
|
||||
|
||||
void BufferQueueCore::NotifyShutdown() {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
is_shutting_down = true;
|
||||
|
||||
SignalDequeueCondition();
|
||||
}
|
||||
|
||||
void BufferQueueCore::SignalDequeueCondition() {
|
||||
dequeue_condition.notify_all();
|
||||
}
|
||||
|
||||
bool BufferQueueCore::WaitForDequeueCondition() {
|
||||
if (is_shutting_down) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dequeue_condition.wait(mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
|
||||
// If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
|
||||
if (!use_async_buffer) {
|
||||
return max_acquired_buffer_count;
|
||||
}
|
||||
|
||||
if (dequeue_buffer_cannot_block || async) {
|
||||
return max_acquired_buffer_count + 1;
|
||||
}
|
||||
|
||||
return max_acquired_buffer_count;
|
||||
}
|
||||
|
||||
s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
|
||||
return GetMinUndequeuedBufferCountLocked(async) + 1;
|
||||
}
|
||||
|
||||
s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
|
||||
const auto min_buffer_count = GetMinMaxBufferCountLocked(async);
|
||||
auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count);
|
||||
|
||||
if (override_max_buffer_count != 0) {
|
||||
ASSERT(override_max_buffer_count >= min_buffer_count);
|
||||
max_buffer_count = override_max_buffer_count;
|
||||
}
|
||||
|
||||
// Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed
|
||||
// need to have their slots preserved.
|
||||
for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
|
||||
const auto state = slots[slot].buffer_state;
|
||||
if (state == BufferState::Queued || state == BufferState::Dequeued) {
|
||||
max_buffer_count = slot + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return max_buffer_count;
|
||||
}
|
||||
|
||||
s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
|
||||
return static_cast<s32>(std::count_if(slots.begin(), slots.end(),
|
||||
[](const auto& slot) { return slot.is_preallocated; }));
|
||||
}
|
||||
|
||||
void BufferQueueCore::FreeBufferLocked(s32 slot) {
|
||||
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
|
||||
|
||||
slots[slot].graphic_buffer.reset();
|
||||
|
||||
slots[slot].buffer_state = BufferState::Free;
|
||||
slots[slot].frame_number = UINT32_MAX;
|
||||
slots[slot].acquire_called = false;
|
||||
slots[slot].fence = Fence::NoFence();
|
||||
}
|
||||
|
||||
void BufferQueueCore::FreeAllBuffersLocked() {
|
||||
buffer_has_been_queued = false;
|
||||
|
||||
for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
|
||||
FreeBufferLocked(slot);
|
||||
}
|
||||
}
|
||||
|
||||
bool BufferQueueCore::StillTracking(const BufferItem& item) const {
|
||||
const BufferSlot& slot = slots[item.slot];
|
||||
|
||||
return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer);
|
||||
}
|
||||
|
||||
void BufferQueueCore::WaitWhileAllocatingLocked() const {
|
||||
while (is_allocating) {
|
||||
is_allocating_condition.wait(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "core/hle/service/nvflinger/buffer_queue_core.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
BufferQueueCore::BufferQueueCore() = default;
|
||||
|
||||
BufferQueueCore::~BufferQueueCore() = default;
|
||||
|
||||
void BufferQueueCore::NotifyShutdown() {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
is_shutting_down = true;
|
||||
|
||||
SignalDequeueCondition();
|
||||
}
|
||||
|
||||
void BufferQueueCore::SignalDequeueCondition() {
|
||||
dequeue_condition.notify_all();
|
||||
}
|
||||
|
||||
bool BufferQueueCore::WaitForDequeueCondition() {
|
||||
if (is_shutting_down) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dequeue_condition.wait(mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
|
||||
// If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
|
||||
if (!use_async_buffer) {
|
||||
return max_acquired_buffer_count;
|
||||
}
|
||||
|
||||
if (dequeue_buffer_cannot_block || async) {
|
||||
return max_acquired_buffer_count + 1;
|
||||
}
|
||||
|
||||
return max_acquired_buffer_count;
|
||||
}
|
||||
|
||||
s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
|
||||
return GetMinUndequeuedBufferCountLocked(async) + 1;
|
||||
}
|
||||
|
||||
s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
|
||||
const auto min_buffer_count = GetMinMaxBufferCountLocked(async);
|
||||
auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count);
|
||||
|
||||
if (override_max_buffer_count != 0) {
|
||||
ASSERT(override_max_buffer_count >= min_buffer_count);
|
||||
max_buffer_count = override_max_buffer_count;
|
||||
}
|
||||
|
||||
// Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed
|
||||
// need to have their slots preserved.
|
||||
for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
|
||||
const auto state = slots[slot].buffer_state;
|
||||
if (state == BufferState::Queued || state == BufferState::Dequeued) {
|
||||
max_buffer_count = slot + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return max_buffer_count;
|
||||
}
|
||||
|
||||
s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
|
||||
return static_cast<s32>(std::count_if(slots.begin(), slots.end(),
|
||||
[](const auto& slot) { return slot.is_preallocated; }));
|
||||
}
|
||||
|
||||
void BufferQueueCore::FreeBufferLocked(s32 slot) {
|
||||
LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
|
||||
|
||||
slots[slot].graphic_buffer.reset();
|
||||
|
||||
slots[slot].buffer_state = BufferState::Free;
|
||||
slots[slot].frame_number = UINT32_MAX;
|
||||
slots[slot].acquire_called = false;
|
||||
slots[slot].fence = Fence::NoFence();
|
||||
}
|
||||
|
||||
void BufferQueueCore::FreeAllBuffersLocked() {
|
||||
buffer_has_been_queued = false;
|
||||
|
||||
for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
|
||||
FreeBufferLocked(slot);
|
||||
}
|
||||
}
|
||||
|
||||
bool BufferQueueCore::StillTracking(const BufferItem& item) const {
|
||||
const BufferSlot& slot = slots[item.slot];
|
||||
|
||||
return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer);
|
||||
}
|
||||
|
||||
void BufferQueueCore::WaitWhileAllocatingLocked() const {
|
||||
while (is_allocating) {
|
||||
is_allocating_condition.wait(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,79 +1,79 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/service/nvflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_defs.h"
|
||||
#include "core/hle/service/nvflinger/pixel_format.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
#include "core/hle/service/nvflinger/window.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class IConsumerListener;
|
||||
class IProducerListener;
|
||||
|
||||
class BufferQueueCore final {
|
||||
friend class BufferQueueProducer;
|
||||
friend class BufferQueueConsumer;
|
||||
|
||||
public:
|
||||
static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
|
||||
|
||||
BufferQueueCore();
|
||||
~BufferQueueCore();
|
||||
|
||||
void NotifyShutdown();
|
||||
|
||||
private:
|
||||
void SignalDequeueCondition();
|
||||
bool WaitForDequeueCondition();
|
||||
|
||||
s32 GetMinUndequeuedBufferCountLocked(bool async) const;
|
||||
s32 GetMinMaxBufferCountLocked(bool async) const;
|
||||
s32 GetMaxBufferCountLocked(bool async) const;
|
||||
s32 GetPreallocatedBufferCountLocked() const;
|
||||
void FreeBufferLocked(s32 slot);
|
||||
void FreeAllBuffersLocked();
|
||||
bool StillTracking(const BufferItem& item) const;
|
||||
void WaitWhileAllocatingLocked() const;
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex;
|
||||
bool is_abandoned{};
|
||||
bool consumer_controlled_by_app{};
|
||||
std::shared_ptr<IConsumerListener> consumer_listener;
|
||||
u32 consumer_usage_bit{};
|
||||
NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi};
|
||||
std::shared_ptr<IProducerListener> connected_producer_listener;
|
||||
BufferQueueDefs::SlotsType slots{};
|
||||
std::vector<BufferItem> queue;
|
||||
s32 override_max_buffer_count{};
|
||||
mutable std::condition_variable_any dequeue_condition;
|
||||
const bool use_async_buffer{}; // This is always disabled on HOS
|
||||
bool dequeue_buffer_cannot_block{};
|
||||
PixelFormat default_buffer_format{PixelFormat::Rgba8888};
|
||||
u32 default_width{1};
|
||||
u32 default_height{1};
|
||||
s32 default_max_buffer_count{2};
|
||||
const s32 max_acquired_buffer_count{}; // This is always zero on HOS
|
||||
bool buffer_has_been_queued{};
|
||||
u64 frame_counter{};
|
||||
u32 transform_hint{};
|
||||
bool is_allocating{};
|
||||
mutable std::condition_variable_any is_allocating_condition;
|
||||
bool is_shutting_down{};
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/service/nvflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_defs.h"
|
||||
#include "core/hle/service/nvflinger/pixel_format.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
#include "core/hle/service/nvflinger/window.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class IConsumerListener;
|
||||
class IProducerListener;
|
||||
|
||||
class BufferQueueCore final {
|
||||
friend class BufferQueueProducer;
|
||||
friend class BufferQueueConsumer;
|
||||
|
||||
public:
|
||||
static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
|
||||
|
||||
BufferQueueCore();
|
||||
~BufferQueueCore();
|
||||
|
||||
void NotifyShutdown();
|
||||
|
||||
private:
|
||||
void SignalDequeueCondition();
|
||||
bool WaitForDequeueCondition();
|
||||
|
||||
s32 GetMinUndequeuedBufferCountLocked(bool async) const;
|
||||
s32 GetMinMaxBufferCountLocked(bool async) const;
|
||||
s32 GetMaxBufferCountLocked(bool async) const;
|
||||
s32 GetPreallocatedBufferCountLocked() const;
|
||||
void FreeBufferLocked(s32 slot);
|
||||
void FreeAllBuffersLocked();
|
||||
bool StillTracking(const BufferItem& item) const;
|
||||
void WaitWhileAllocatingLocked() const;
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex;
|
||||
bool is_abandoned{};
|
||||
bool consumer_controlled_by_app{};
|
||||
std::shared_ptr<IConsumerListener> consumer_listener;
|
||||
u32 consumer_usage_bit{};
|
||||
NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi};
|
||||
std::shared_ptr<IProducerListener> connected_producer_listener;
|
||||
BufferQueueDefs::SlotsType slots{};
|
||||
std::vector<BufferItem> queue;
|
||||
s32 override_max_buffer_count{};
|
||||
mutable std::condition_variable_any dequeue_condition;
|
||||
const bool use_async_buffer{}; // This is always disabled on HOS
|
||||
bool dequeue_buffer_cannot_block{};
|
||||
PixelFormat default_buffer_format{PixelFormat::Rgba8888};
|
||||
u32 default_width{1};
|
||||
u32 default_height{1};
|
||||
s32 default_max_buffer_count{2};
|
||||
const s32 max_acquired_buffer_count{}; // This is always zero on HOS
|
||||
bool buffer_has_been_queued{};
|
||||
u64 frame_counter{};
|
||||
u32 transform_hint{};
|
||||
bool is_allocating{};
|
||||
mutable std::condition_variable_any is_allocating_condition;
|
||||
bool is_shutting_down{};
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/buffer_slot.h"
|
||||
|
||||
namespace Service::android::BufferQueueDefs {
|
||||
|
||||
// BufferQueue will keep track of at most this value of buffers.
|
||||
constexpr s32 NUM_BUFFER_SLOTS = 64;
|
||||
|
||||
using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>;
|
||||
|
||||
} // namespace Service::android::BufferQueueDefs
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/buffer_slot.h"
|
||||
|
||||
namespace Service::android::BufferQueueDefs {
|
||||
|
||||
// BufferQueue will keep track of at most this value of buffers.
|
||||
constexpr s32 NUM_BUFFER_SLOTS = 64;
|
||||
|
||||
using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>;
|
||||
|
||||
} // namespace Service::android::BufferQueueDefs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,89 +1,89 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvflinger/binder.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_defs.h"
|
||||
#include "core/hle/service/nvflinger/buffer_slot.h"
|
||||
#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
|
||||
#include "core/hle/service/nvflinger/pixel_format.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
#include "core/hle/service/nvflinger/window.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
class ServiceContext;
|
||||
} // namespace Service::KernelHelpers
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class NvMap;
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferQueueCore;
|
||||
class IProducerListener;
|
||||
|
||||
class BufferQueueProducer final : public IBinder {
|
||||
public:
|
||||
explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
|
||||
std::shared_ptr<BufferQueueCore> buffer_queue_core_,
|
||||
Service::Nvidia::NvCore::NvMap& nvmap_);
|
||||
~BufferQueueProducer();
|
||||
|
||||
void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
|
||||
|
||||
Kernel::KReadableEvent& GetNativeHandle() override;
|
||||
|
||||
public:
|
||||
Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
|
||||
Status SetBufferCount(s32 buffer_count);
|
||||
Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
|
||||
u32 height, PixelFormat format, u32 usage);
|
||||
Status DetachBuffer(s32 slot);
|
||||
Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
|
||||
Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
|
||||
Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
|
||||
void CancelBuffer(s32 slot, const Fence& fence);
|
||||
Status Query(NativeWindow what, s32* out_value);
|
||||
Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
|
||||
bool producer_controlled_by_app, QueueBufferOutput* output);
|
||||
|
||||
Status Disconnect(NativeWindowApi api);
|
||||
Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
|
||||
|
||||
private:
|
||||
BufferQueueProducer(const BufferQueueProducer&) = delete;
|
||||
|
||||
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
|
||||
|
||||
Kernel::KEvent* buffer_wait_event{};
|
||||
Service::KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
std::shared_ptr<BufferQueueCore> core;
|
||||
BufferQueueDefs::SlotsType& slots;
|
||||
u32 sticky_transform{};
|
||||
std::mutex callback_mutex;
|
||||
s32 next_callback_ticket{};
|
||||
s32 current_callback_ticket{};
|
||||
std::condition_variable_any callback_condition;
|
||||
|
||||
Service::Nvidia::NvCore::NvMap& nvmap;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "core/hle/service/nvflinger/binder.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_defs.h"
|
||||
#include "core/hle/service/nvflinger/buffer_slot.h"
|
||||
#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
|
||||
#include "core/hle/service/nvflinger/pixel_format.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
#include "core/hle/service/nvflinger/window.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
class KEvent;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
class ServiceContext;
|
||||
} // namespace Service::KernelHelpers
|
||||
|
||||
namespace Service::Nvidia::NvCore {
|
||||
class NvMap;
|
||||
} // namespace Service::Nvidia::NvCore
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferQueueCore;
|
||||
class IProducerListener;
|
||||
|
||||
class BufferQueueProducer final : public IBinder {
|
||||
public:
|
||||
explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
|
||||
std::shared_ptr<BufferQueueCore> buffer_queue_core_,
|
||||
Service::Nvidia::NvCore::NvMap& nvmap_);
|
||||
~BufferQueueProducer();
|
||||
|
||||
void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
|
||||
|
||||
Kernel::KReadableEvent& GetNativeHandle() override;
|
||||
|
||||
public:
|
||||
Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
|
||||
Status SetBufferCount(s32 buffer_count);
|
||||
Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
|
||||
u32 height, PixelFormat format, u32 usage);
|
||||
Status DetachBuffer(s32 slot);
|
||||
Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
|
||||
Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
|
||||
Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
|
||||
void CancelBuffer(s32 slot, const Fence& fence);
|
||||
Status Query(NativeWindow what, s32* out_value);
|
||||
Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
|
||||
bool producer_controlled_by_app, QueueBufferOutput* output);
|
||||
|
||||
Status Disconnect(NativeWindowApi api);
|
||||
Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
|
||||
|
||||
private:
|
||||
BufferQueueProducer(const BufferQueueProducer&) = delete;
|
||||
|
||||
Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
|
||||
|
||||
Kernel::KEvent* buffer_wait_event{};
|
||||
Service::KernelHelpers::ServiceContext& service_context;
|
||||
|
||||
std::shared_ptr<BufferQueueCore> core;
|
||||
BufferQueueDefs::SlotsType& slots;
|
||||
u32 sticky_transform{};
|
||||
std::mutex callback_mutex;
|
||||
s32 next_callback_ticket{};
|
||||
s32 current_callback_ticket{};
|
||||
std::condition_variable_any callback_condition;
|
||||
|
||||
Service::Nvidia::NvCore::NvMap& nvmap;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/ui/fence.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class GraphicBuffer;
|
||||
|
||||
enum class BufferState : u32 {
|
||||
Free = 0,
|
||||
Dequeued = 1,
|
||||
Queued = 2,
|
||||
Acquired = 3,
|
||||
};
|
||||
|
||||
struct BufferSlot final {
|
||||
constexpr BufferSlot() = default;
|
||||
|
||||
std::shared_ptr<GraphicBuffer> graphic_buffer;
|
||||
BufferState buffer_state{BufferState::Free};
|
||||
bool request_buffer_called{};
|
||||
u64 frame_number{};
|
||||
Fence fence;
|
||||
bool acquire_called{};
|
||||
bool attached_by_consumer{};
|
||||
bool is_preallocated{};
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/ui/fence.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class GraphicBuffer;
|
||||
|
||||
enum class BufferState : u32 {
|
||||
Free = 0,
|
||||
Dequeued = 1,
|
||||
Queued = 2,
|
||||
Acquired = 3,
|
||||
};
|
||||
|
||||
struct BufferSlot final {
|
||||
constexpr BufferSlot() = default;
|
||||
|
||||
std::shared_ptr<GraphicBuffer> graphic_buffer;
|
||||
BufferState buffer_state{BufferState::Free};
|
||||
bool request_buffer_called{};
|
||||
u64 frame_number{};
|
||||
Fence fence;
|
||||
bool acquire_called{};
|
||||
bool attached_by_consumer{};
|
||||
bool is_preallocated{};
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
enum class BufferTransformFlags : u32 {
|
||||
/// No transform flags are set
|
||||
Unset = 0x00,
|
||||
/// Flip source image horizontally (around the vertical axis)
|
||||
FlipH = 0x01,
|
||||
/// Flip source image vertically (around the horizontal axis)
|
||||
FlipV = 0x02,
|
||||
/// Rotate source image 90 degrees clockwise
|
||||
Rotate90 = 0x04,
|
||||
/// Rotate source image 180 degrees
|
||||
Rotate180 = 0x03,
|
||||
/// Rotate source image 270 degrees clockwise
|
||||
Rotate270 = 0x07,
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
enum class BufferTransformFlags : u32 {
|
||||
/// No transform flags are set
|
||||
Unset = 0x00,
|
||||
/// Flip source image horizontally (around the vertical axis)
|
||||
FlipH = 0x01,
|
||||
/// Flip source image vertically (around the horizontal axis)
|
||||
FlipV = 0x02,
|
||||
/// Rotate source image 90 degrees clockwise
|
||||
Rotate90 = 0x04,
|
||||
/// Rotate source image 180 degrees
|
||||
Rotate180 = 0x03,
|
||||
/// Rotate source image 270 degrees clockwise
|
||||
Rotate270 = 0x07,
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,133 +1,133 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_core.h"
|
||||
#include "core/hle/service/nvflinger/consumer_base.h"
|
||||
#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
|
||||
: consumer{std::move(consumer_)} {}
|
||||
|
||||
ConsumerBase::~ConsumerBase() {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
|
||||
}
|
||||
|
||||
void ConsumerBase::Connect(bool controlled_by_app) {
|
||||
consumer->Connect(shared_from_this(), controlled_by_app);
|
||||
}
|
||||
|
||||
void ConsumerBase::FreeBufferLocked(s32 slot_index) {
|
||||
LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index);
|
||||
|
||||
slots[slot_index].graphic_buffer = nullptr;
|
||||
slots[slot_index].fence = Fence::NoFence();
|
||||
slots[slot_index].frame_number = 0;
|
||||
}
|
||||
|
||||
void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
|
||||
LOG_DEBUG(Service_NVFlinger, "called");
|
||||
}
|
||||
|
||||
void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
|
||||
LOG_DEBUG(Service_NVFlinger, "called");
|
||||
}
|
||||
|
||||
void ConsumerBase::OnBuffersReleased() {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "called");
|
||||
|
||||
if (is_abandoned) {
|
||||
// Nothing to do if we're already abandoned.
|
||||
return;
|
||||
}
|
||||
|
||||
u64 mask = 0;
|
||||
consumer->GetReleasedBuffers(&mask);
|
||||
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
|
||||
if (mask & (1ULL << i)) {
|
||||
FreeBufferLocked(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConsumerBase::OnSidebandStreamChanged() {}
|
||||
|
||||
Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
|
||||
Status err = consumer->AcquireBuffer(item, present_when);
|
||||
if (err != Status::NoError) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (item->graphic_buffer != nullptr) {
|
||||
slots[item->slot].graphic_buffer = item->graphic_buffer;
|
||||
}
|
||||
|
||||
slots[item->slot].frame_number = item->frame_number;
|
||||
slots[item->slot].fence = item->fence;
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot);
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
|
||||
const std::shared_ptr<GraphicBuffer> graphic_buffer,
|
||||
const Fence& fence) {
|
||||
LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
|
||||
|
||||
// If consumer no longer tracks this graphic_buffer, we can safely
|
||||
// drop this fence, as it will never be received by the producer.
|
||||
|
||||
if (!StillTracking(slot, graphic_buffer)) {
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
slots[slot].fence = fence;
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status ConsumerBase::ReleaseBufferLocked(s32 slot,
|
||||
const std::shared_ptr<GraphicBuffer> graphic_buffer) {
|
||||
// If consumer no longer tracks this graphic_buffer (we received a new
|
||||
// buffer on the same slot), the buffer producer is definitely no longer
|
||||
// tracking it.
|
||||
|
||||
if (!StillTracking(slot, graphic_buffer)) {
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
|
||||
Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence);
|
||||
if (err == Status::StaleBufferSlot) {
|
||||
FreeBufferLocked(slot);
|
||||
}
|
||||
|
||||
slots[slot].fence = Fence::NoFence();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool ConsumerBase::StillTracking(s32 slot,
|
||||
const std::shared_ptr<GraphicBuffer> graphic_buffer) const {
|
||||
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (slots[slot].graphic_buffer != nullptr &&
|
||||
slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle());
|
||||
}
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_core.h"
|
||||
#include "core/hle/service/nvflinger/consumer_base.h"
|
||||
#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
|
||||
: consumer{std::move(consumer_)} {}
|
||||
|
||||
ConsumerBase::~ConsumerBase() {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
|
||||
}
|
||||
|
||||
void ConsumerBase::Connect(bool controlled_by_app) {
|
||||
consumer->Connect(shared_from_this(), controlled_by_app);
|
||||
}
|
||||
|
||||
void ConsumerBase::FreeBufferLocked(s32 slot_index) {
|
||||
LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index);
|
||||
|
||||
slots[slot_index].graphic_buffer = nullptr;
|
||||
slots[slot_index].fence = Fence::NoFence();
|
||||
slots[slot_index].frame_number = 0;
|
||||
}
|
||||
|
||||
void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
|
||||
LOG_DEBUG(Service_NVFlinger, "called");
|
||||
}
|
||||
|
||||
void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
|
||||
LOG_DEBUG(Service_NVFlinger, "called");
|
||||
}
|
||||
|
||||
void ConsumerBase::OnBuffersReleased() {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "called");
|
||||
|
||||
if (is_abandoned) {
|
||||
// Nothing to do if we're already abandoned.
|
||||
return;
|
||||
}
|
||||
|
||||
u64 mask = 0;
|
||||
consumer->GetReleasedBuffers(&mask);
|
||||
for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
|
||||
if (mask & (1ULL << i)) {
|
||||
FreeBufferLocked(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConsumerBase::OnSidebandStreamChanged() {}
|
||||
|
||||
Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
|
||||
Status err = consumer->AcquireBuffer(item, present_when);
|
||||
if (err != Status::NoError) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (item->graphic_buffer != nullptr) {
|
||||
slots[item->slot].graphic_buffer = item->graphic_buffer;
|
||||
}
|
||||
|
||||
slots[item->slot].frame_number = item->frame_number;
|
||||
slots[item->slot].fence = item->fence;
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot);
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
|
||||
const std::shared_ptr<GraphicBuffer> graphic_buffer,
|
||||
const Fence& fence) {
|
||||
LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
|
||||
|
||||
// If consumer no longer tracks this graphic_buffer, we can safely
|
||||
// drop this fence, as it will never be received by the producer.
|
||||
|
||||
if (!StillTracking(slot, graphic_buffer)) {
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
slots[slot].fence = fence;
|
||||
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
Status ConsumerBase::ReleaseBufferLocked(s32 slot,
|
||||
const std::shared_ptr<GraphicBuffer> graphic_buffer) {
|
||||
// If consumer no longer tracks this graphic_buffer (we received a new
|
||||
// buffer on the same slot), the buffer producer is definitely no longer
|
||||
// tracking it.
|
||||
|
||||
if (!StillTracking(slot, graphic_buffer)) {
|
||||
return Status::NoError;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
|
||||
Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence);
|
||||
if (err == Status::StaleBufferSlot) {
|
||||
FreeBufferLocked(slot);
|
||||
}
|
||||
|
||||
slots[slot].fence = Fence::NoFence();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
bool ConsumerBase::StillTracking(s32 slot,
|
||||
const std::shared_ptr<GraphicBuffer> graphic_buffer) const {
|
||||
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (slots[slot].graphic_buffer != nullptr &&
|
||||
slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle());
|
||||
}
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_defs.h"
|
||||
#include "core/hle/service/nvflinger/consumer_listener.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferItem;
|
||||
class BufferQueueConsumer;
|
||||
|
||||
class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
|
||||
public:
|
||||
void Connect(bool controlled_by_app);
|
||||
|
||||
protected:
|
||||
explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
|
||||
virtual ~ConsumerBase();
|
||||
|
||||
virtual void OnFrameAvailable(const BufferItem& item) override;
|
||||
virtual void OnFrameReplaced(const BufferItem& item) override;
|
||||
virtual void OnBuffersReleased() override;
|
||||
virtual void OnSidebandStreamChanged() override;
|
||||
|
||||
void FreeBufferLocked(s32 slot_index);
|
||||
Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
|
||||
Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
|
||||
bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
|
||||
Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,
|
||||
const Fence& fence);
|
||||
|
||||
struct Slot final {
|
||||
std::shared_ptr<GraphicBuffer> graphic_buffer;
|
||||
Fence fence;
|
||||
u64 frame_number{};
|
||||
};
|
||||
|
||||
protected:
|
||||
std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots;
|
||||
|
||||
bool is_abandoned{};
|
||||
|
||||
std::unique_ptr<BufferQueueConsumer> consumer;
|
||||
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_defs.h"
|
||||
#include "core/hle/service/nvflinger/consumer_listener.h"
|
||||
#include "core/hle/service/nvflinger/status.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferItem;
|
||||
class BufferQueueConsumer;
|
||||
|
||||
class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
|
||||
public:
|
||||
void Connect(bool controlled_by_app);
|
||||
|
||||
protected:
|
||||
explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
|
||||
virtual ~ConsumerBase();
|
||||
|
||||
virtual void OnFrameAvailable(const BufferItem& item) override;
|
||||
virtual void OnFrameReplaced(const BufferItem& item) override;
|
||||
virtual void OnBuffersReleased() override;
|
||||
virtual void OnSidebandStreamChanged() override;
|
||||
|
||||
void FreeBufferLocked(s32 slot_index);
|
||||
Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
|
||||
Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
|
||||
bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
|
||||
Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,
|
||||
const Fence& fence);
|
||||
|
||||
struct Slot final {
|
||||
std::shared_ptr<GraphicBuffer> graphic_buffer;
|
||||
Fence fence;
|
||||
u64 frame_number{};
|
||||
};
|
||||
|
||||
protected:
|
||||
std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots;
|
||||
|
||||
bool is_abandoned{};
|
||||
|
||||
std::unique_ptr<BufferQueueConsumer> consumer;
|
||||
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferItem;
|
||||
|
||||
/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
|
||||
/// that the consumer may wish to react to.
|
||||
class IConsumerListener {
|
||||
public:
|
||||
IConsumerListener() = default;
|
||||
virtual ~IConsumerListener() = default;
|
||||
|
||||
virtual void OnFrameAvailable(const BufferItem& item) = 0;
|
||||
virtual void OnFrameReplaced(const BufferItem& item) = 0;
|
||||
virtual void OnBuffersReleased() = 0;
|
||||
virtual void OnSidebandStreamChanged() = 0;
|
||||
};
|
||||
|
||||
}; // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class BufferItem;
|
||||
|
||||
/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
|
||||
/// that the consumer may wish to react to.
|
||||
class IConsumerListener {
|
||||
public:
|
||||
IConsumerListener() = default;
|
||||
virtual ~IConsumerListener() = default;
|
||||
|
||||
virtual void OnFrameAvailable(const BufferItem& item) = 0;
|
||||
virtual void OnFrameReplaced(const BufferItem& item) = 0;
|
||||
virtual void OnBuffersReleased() = 0;
|
||||
virtual void OnSidebandStreamChanged() = 0;
|
||||
};
|
||||
|
||||
}; // namespace Service::android
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp
|
||||
|
||||
#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
|
||||
#include "core/hle/service/nvflinger/parcel.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
QueueBufferInput::QueueBufferInput(Parcel& parcel) {
|
||||
parcel.ReadFlattened(*this);
|
||||
}
|
||||
|
||||
QueueBufferOutput::QueueBufferOutput() = default;
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp
|
||||
|
||||
#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
|
||||
#include "core/hle/service/nvflinger/parcel.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
QueueBufferInput::QueueBufferInput(Parcel& parcel) {
|
||||
parcel.ReadFlattened(*this);
|
||||
}
|
||||
|
||||
QueueBufferOutput::QueueBufferOutput() = default;
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvflinger/ui/fence.h"
|
||||
#include "core/hle/service/nvflinger/window.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class Parcel;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct QueueBufferInput final {
|
||||
explicit QueueBufferInput(Parcel& parcel);
|
||||
|
||||
void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
|
||||
NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
|
||||
u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const {
|
||||
*timestamp_ = timestamp;
|
||||
*is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp);
|
||||
*crop_ = crop;
|
||||
*scaling_mode_ = scaling_mode;
|
||||
*transform_ = transform;
|
||||
*sticky_transform_ = sticky_transform;
|
||||
*async_ = static_cast<bool>(async);
|
||||
*swap_interval_ = swap_interval;
|
||||
*fence_ = fence;
|
||||
}
|
||||
|
||||
private:
|
||||
s64 timestamp{};
|
||||
s32 is_auto_timestamp{};
|
||||
Common::Rectangle<s32> crop{};
|
||||
NativeWindowScalingMode scaling_mode{};
|
||||
NativeWindowTransform transform{};
|
||||
u32 sticky_transform{};
|
||||
s32 async{};
|
||||
s32 swap_interval{};
|
||||
Fence fence{};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size");
|
||||
|
||||
struct QueueBufferOutput final {
|
||||
QueueBufferOutput();
|
||||
|
||||
void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const {
|
||||
*width_ = width;
|
||||
*height_ = height;
|
||||
*transform_hint_ = transform_hint;
|
||||
*num_pending_buffers_ = num_pending_buffers;
|
||||
}
|
||||
|
||||
void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) {
|
||||
width = width_;
|
||||
height = height_;
|
||||
transform_hint = transform_hint_;
|
||||
num_pending_buffers = num_pending_buffers_;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 width{};
|
||||
u32 height{};
|
||||
u32 transform_hint{};
|
||||
u32 num_pending_buffers{};
|
||||
};
|
||||
static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size");
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvflinger/ui/fence.h"
|
||||
#include "core/hle/service/nvflinger/window.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class Parcel;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct QueueBufferInput final {
|
||||
explicit QueueBufferInput(Parcel& parcel);
|
||||
|
||||
void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
|
||||
NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
|
||||
u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const {
|
||||
*timestamp_ = timestamp;
|
||||
*is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp);
|
||||
*crop_ = crop;
|
||||
*scaling_mode_ = scaling_mode;
|
||||
*transform_ = transform;
|
||||
*sticky_transform_ = sticky_transform;
|
||||
*async_ = static_cast<bool>(async);
|
||||
*swap_interval_ = swap_interval;
|
||||
*fence_ = fence;
|
||||
}
|
||||
|
||||
private:
|
||||
s64 timestamp{};
|
||||
s32 is_auto_timestamp{};
|
||||
Common::Rectangle<s32> crop{};
|
||||
NativeWindowScalingMode scaling_mode{};
|
||||
NativeWindowTransform transform{};
|
||||
u32 sticky_transform{};
|
||||
s32 async{};
|
||||
s32 swap_interval{};
|
||||
Fence fence{};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size");
|
||||
|
||||
struct QueueBufferOutput final {
|
||||
QueueBufferOutput();
|
||||
|
||||
void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const {
|
||||
*width_ = width;
|
||||
*height_ = height;
|
||||
*transform_hint_ = transform_hint;
|
||||
*num_pending_buffers_ = num_pending_buffers;
|
||||
}
|
||||
|
||||
void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) {
|
||||
width = width_;
|
||||
height = height_;
|
||||
transform_hint = transform_hint_;
|
||||
num_pending_buffers = num_pending_buffers_;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 width{};
|
||||
u32 height{};
|
||||
u32 transform_hint{};
|
||||
u32 num_pending_buffers{};
|
||||
};
|
||||
static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size");
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
|
||||
: service_context(system_, "HosBinderDriverServer") {}
|
||||
|
||||
HosBinderDriverServer::~HosBinderDriverServer() {}
|
||||
|
||||
u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
|
||||
std::scoped_lock lk{lock};
|
||||
|
||||
last_id++;
|
||||
|
||||
producers[last_id] = std::move(binder);
|
||||
|
||||
return last_id;
|
||||
}
|
||||
|
||||
android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
|
||||
std::scoped_lock lk{lock};
|
||||
|
||||
if (auto search = producers.find(id); search != producers.end()) {
|
||||
return search->second.get();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
|
||||
: service_context(system_, "HosBinderDriverServer") {}
|
||||
|
||||
HosBinderDriverServer::~HosBinderDriverServer() {}
|
||||
|
||||
u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
|
||||
std::scoped_lock lk{lock};
|
||||
|
||||
last_id++;
|
||||
|
||||
producers[last_id] = std::move(binder);
|
||||
|
||||
return last_id;
|
||||
}
|
||||
|
||||
android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
|
||||
std::scoped_lock lk{lock};
|
||||
|
||||
if (auto search = producers.find(id); search != producers.end()) {
|
||||
return search->second.get();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvflinger/binder.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
class HosBinderDriverServer final {
|
||||
public:
|
||||
explicit HosBinderDriverServer(Core::System& system_);
|
||||
~HosBinderDriverServer();
|
||||
|
||||
u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
|
||||
|
||||
android::IBinder* TryGetProducer(u64 id);
|
||||
|
||||
private:
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
|
||||
std::mutex lock;
|
||||
u64 last_id{};
|
||||
};
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
#include "core/hle/service/nvflinger/binder.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
class HosBinderDriverServer final {
|
||||
public:
|
||||
explicit HosBinderDriverServer(Core::System& system_);
|
||||
~HosBinderDriverServer();
|
||||
|
||||
u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
|
||||
|
||||
android::IBinder* TryGetProducer(u64 id);
|
||||
|
||||
private:
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
|
||||
std::mutex lock;
|
||||
u64 last_id{};
|
||||
};
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -1,336 +1,336 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item_consumer.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_core.h"
|
||||
#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
|
||||
#include "core/hle/service/vi/display/vi_display.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
#include "video_core/host1x/syncpoint_manager.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60};
|
||||
|
||||
void NVFlinger::SplitVSync(std::stop_token stop_token) {
|
||||
system.RegisterHostThread();
|
||||
std::string name = "VSyncThread";
|
||||
MicroProfileOnThreadCreate(name.c_str());
|
||||
|
||||
// Cleanup
|
||||
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
|
||||
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
vsync_signal.wait(false);
|
||||
vsync_signal.store(false);
|
||||
|
||||
guard->lock();
|
||||
|
||||
Compose();
|
||||
|
||||
guard->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
|
||||
: system(system_), service_context(system_, "nvflinger"),
|
||||
hos_binder_driver_server(hos_binder_driver_server_) {
|
||||
displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
|
||||
displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
|
||||
displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
|
||||
displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
|
||||
displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
|
||||
guard = std::make_shared<std::mutex>();
|
||||
|
||||
// Schedule the screen composition events
|
||||
multi_composition_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition",
|
||||
[this](std::uintptr_t, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
vsync_signal.store(true);
|
||||
vsync_signal.notify_all();
|
||||
return std::chrono::nanoseconds(GetNextTicks());
|
||||
});
|
||||
|
||||
single_composition_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition",
|
||||
[this](std::uintptr_t, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto lock_guard = Lock();
|
||||
Compose();
|
||||
|
||||
return std::chrono::nanoseconds(GetNextTicks());
|
||||
});
|
||||
|
||||
if (system.IsMulticore()) {
|
||||
system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event);
|
||||
vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
|
||||
} else {
|
||||
system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event);
|
||||
}
|
||||
}
|
||||
|
||||
NVFlinger::~NVFlinger() {
|
||||
if (system.IsMulticore()) {
|
||||
system.CoreTiming().UnscheduleEvent(multi_composition_event, {});
|
||||
vsync_thread.request_stop();
|
||||
vsync_signal.store(true);
|
||||
vsync_signal.notify_all();
|
||||
} else {
|
||||
system.CoreTiming().UnscheduleEvent(single_composition_event, {});
|
||||
}
|
||||
|
||||
ShutdownLayers();
|
||||
|
||||
if (nvdrv) {
|
||||
nvdrv->Close(disp_fd);
|
||||
}
|
||||
}
|
||||
|
||||
void NVFlinger::ShutdownLayers() {
|
||||
for (auto& display : displays) {
|
||||
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
|
||||
display.GetLayer(layer).Core().NotifyShutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
||||
nvdrv = std::move(instance);
|
||||
disp_fd = nvdrv->Open("/dev/nvdisp_disp0");
|
||||
}
|
||||
|
||||
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
|
||||
const auto lock_guard = Lock();
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
|
||||
|
||||
const auto itr =
|
||||
std::find_if(displays.begin(), displays.end(),
|
||||
[&](const VI::Display& display) { return display.GetName() == name; });
|
||||
|
||||
if (itr == displays.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return itr->GetID();
|
||||
}
|
||||
|
||||
bool NVFlinger::CloseDisplay(u64 display_id) {
|
||||
const auto lock_guard = Lock();
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
display->Reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
||||
const auto lock_guard = Lock();
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const u64 layer_id = next_layer_id++;
|
||||
CreateLayerAtId(*display, layer_id);
|
||||
return layer_id;
|
||||
}
|
||||
|
||||
void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
|
||||
const auto buffer_id = next_buffer_queue_id++;
|
||||
display.CreateLayer(layer_id, buffer_id, nvdrv->container);
|
||||
}
|
||||
|
||||
void NVFlinger::CloseLayer(u64 layer_id) {
|
||||
const auto lock_guard = Lock();
|
||||
|
||||
for (auto& display : displays) {
|
||||
display.CloseLayer(layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
|
||||
const auto lock_guard = Lock();
|
||||
const auto* const layer = FindOrCreateLayer(display_id, layer_id);
|
||||
|
||||
if (layer == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return layer->GetBinderId();
|
||||
}
|
||||
|
||||
ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) {
|
||||
const auto lock_guard = Lock();
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return VI::ResultNotFound;
|
||||
}
|
||||
|
||||
return display->GetVSyncEvent();
|
||||
}
|
||||
|
||||
VI::Display* NVFlinger::FindDisplay(u64 display_id) {
|
||||
const auto itr =
|
||||
std::find_if(displays.begin(), displays.end(),
|
||||
[&](const VI::Display& display) { return display.GetID() == display_id; });
|
||||
|
||||
if (itr == displays.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*itr;
|
||||
}
|
||||
|
||||
const VI::Display* NVFlinger::FindDisplay(u64 display_id) const {
|
||||
const auto itr =
|
||||
std::find_if(displays.begin(), displays.end(),
|
||||
[&](const VI::Display& display) { return display.GetID() == display_id; });
|
||||
|
||||
if (itr == displays.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*itr;
|
||||
}
|
||||
|
||||
VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) {
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return display->FindLayer(layer_id);
|
||||
}
|
||||
|
||||
const VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const {
|
||||
const auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return display->FindLayer(layer_id);
|
||||
}
|
||||
|
||||
VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* layer = display->FindLayer(layer_id);
|
||||
|
||||
if (layer == nullptr) {
|
||||
LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
|
||||
CreateLayerAtId(*display, layer_id);
|
||||
return display->FindLayer(layer_id);
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
void NVFlinger::Compose() {
|
||||
for (auto& display : displays) {
|
||||
// Trigger vsync for this display at the end of drawing
|
||||
SCOPE_EXIT({ display.SignalVSyncEvent(); });
|
||||
|
||||
// Don't do anything for displays without layers.
|
||||
if (!display.HasLayers())
|
||||
continue;
|
||||
|
||||
// TODO(Subv): Support more than 1 layer.
|
||||
VI::Layer& layer = display.GetLayer(0);
|
||||
|
||||
android::BufferItem buffer{};
|
||||
const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
|
||||
|
||||
if (status != android::Status::NoError) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& igbp_buffer = *buffer.graphic_buffer;
|
||||
|
||||
if (!system.IsPoweredOn()) {
|
||||
return; // We are likely shutting down
|
||||
}
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
// on which display we're drawing (Default, Internal, External, etc)
|
||||
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
|
||||
ASSERT(nvdisp);
|
||||
|
||||
guard->unlock();
|
||||
Common::Rectangle<int> crop_rect{
|
||||
static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
|
||||
static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
|
||||
|
||||
nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
|
||||
igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
|
||||
static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect,
|
||||
buffer.fence.fences, buffer.fence.num_fences);
|
||||
|
||||
MicroProfileFlip();
|
||||
guard->lock();
|
||||
|
||||
swap_interval = buffer.swap_interval;
|
||||
|
||||
auto fence = android::Fence::NoFence();
|
||||
layer.GetConsumer().ReleaseBuffer(buffer, fence);
|
||||
}
|
||||
}
|
||||
|
||||
s64 NVFlinger::GetNextTicks() const {
|
||||
static constexpr s64 max_hertz = 120LL;
|
||||
|
||||
const auto& settings = Settings::values;
|
||||
auto speed_scale = 1.f;
|
||||
if (settings.use_multi_core.GetValue()) {
|
||||
if (settings.use_speed_limit.GetValue()) {
|
||||
// Scales the speed based on speed_limit setting on MC. SC is handled by
|
||||
// SpeedLimiter::DoSpeedLimiting.
|
||||
speed_scale = 100.f / settings.speed_limit.GetValue();
|
||||
} else {
|
||||
// Run at unlocked framerate.
|
||||
speed_scale = 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz);
|
||||
|
||||
return static_cast<s64>(speed_scale * static_cast<float>(next_ticks));
|
||||
}
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
#include "core/hle/service/nvflinger/buffer_item_consumer.h"
|
||||
#include "core/hle/service/nvflinger/buffer_queue_core.h"
|
||||
#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
|
||||
#include "core/hle/service/nvflinger/nvflinger.h"
|
||||
#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
|
||||
#include "core/hle/service/vi/display/vi_display.h"
|
||||
#include "core/hle/service/vi/layer/vi_layer.h"
|
||||
#include "core/hle/service/vi/vi_results.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
#include "video_core/host1x/syncpoint_manager.h"
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60};
|
||||
|
||||
void NVFlinger::SplitVSync(std::stop_token stop_token) {
|
||||
system.RegisterHostThread();
|
||||
std::string name = "VSyncThread";
|
||||
MicroProfileOnThreadCreate(name.c_str());
|
||||
|
||||
// Cleanup
|
||||
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
|
||||
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
|
||||
while (!stop_token.stop_requested()) {
|
||||
vsync_signal.wait(false);
|
||||
vsync_signal.store(false);
|
||||
|
||||
guard->lock();
|
||||
|
||||
Compose();
|
||||
|
||||
guard->unlock();
|
||||
}
|
||||
}
|
||||
|
||||
NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
|
||||
: system(system_), service_context(system_, "nvflinger"),
|
||||
hos_binder_driver_server(hos_binder_driver_server_) {
|
||||
displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
|
||||
displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
|
||||
displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
|
||||
displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
|
||||
displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
|
||||
guard = std::make_shared<std::mutex>();
|
||||
|
||||
// Schedule the screen composition events
|
||||
multi_composition_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition",
|
||||
[this](std::uintptr_t, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
vsync_signal.store(true);
|
||||
vsync_signal.notify_all();
|
||||
return std::chrono::nanoseconds(GetNextTicks());
|
||||
});
|
||||
|
||||
single_composition_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition",
|
||||
[this](std::uintptr_t, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto lock_guard = Lock();
|
||||
Compose();
|
||||
|
||||
return std::chrono::nanoseconds(GetNextTicks());
|
||||
});
|
||||
|
||||
if (system.IsMulticore()) {
|
||||
system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event);
|
||||
vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
|
||||
} else {
|
||||
system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event);
|
||||
}
|
||||
}
|
||||
|
||||
NVFlinger::~NVFlinger() {
|
||||
if (system.IsMulticore()) {
|
||||
system.CoreTiming().UnscheduleEvent(multi_composition_event, {});
|
||||
vsync_thread.request_stop();
|
||||
vsync_signal.store(true);
|
||||
vsync_signal.notify_all();
|
||||
} else {
|
||||
system.CoreTiming().UnscheduleEvent(single_composition_event, {});
|
||||
}
|
||||
|
||||
ShutdownLayers();
|
||||
|
||||
if (nvdrv) {
|
||||
nvdrv->Close(disp_fd);
|
||||
}
|
||||
}
|
||||
|
||||
void NVFlinger::ShutdownLayers() {
|
||||
for (auto& display : displays) {
|
||||
for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
|
||||
display.GetLayer(layer).Core().NotifyShutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
|
||||
nvdrv = std::move(instance);
|
||||
disp_fd = nvdrv->Open("/dev/nvdisp_disp0");
|
||||
}
|
||||
|
||||
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
|
||||
const auto lock_guard = Lock();
|
||||
|
||||
LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
|
||||
|
||||
const auto itr =
|
||||
std::find_if(displays.begin(), displays.end(),
|
||||
[&](const VI::Display& display) { return display.GetName() == name; });
|
||||
|
||||
if (itr == displays.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return itr->GetID();
|
||||
}
|
||||
|
||||
bool NVFlinger::CloseDisplay(u64 display_id) {
|
||||
const auto lock_guard = Lock();
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
display->Reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
||||
const auto lock_guard = Lock();
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const u64 layer_id = next_layer_id++;
|
||||
CreateLayerAtId(*display, layer_id);
|
||||
return layer_id;
|
||||
}
|
||||
|
||||
void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
|
||||
const auto buffer_id = next_buffer_queue_id++;
|
||||
display.CreateLayer(layer_id, buffer_id, nvdrv->container);
|
||||
}
|
||||
|
||||
void NVFlinger::CloseLayer(u64 layer_id) {
|
||||
const auto lock_guard = Lock();
|
||||
|
||||
for (auto& display : displays) {
|
||||
display.CloseLayer(layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
|
||||
const auto lock_guard = Lock();
|
||||
const auto* const layer = FindOrCreateLayer(display_id, layer_id);
|
||||
|
||||
if (layer == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return layer->GetBinderId();
|
||||
}
|
||||
|
||||
ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) {
|
||||
const auto lock_guard = Lock();
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return VI::ResultNotFound;
|
||||
}
|
||||
|
||||
return display->GetVSyncEvent();
|
||||
}
|
||||
|
||||
VI::Display* NVFlinger::FindDisplay(u64 display_id) {
|
||||
const auto itr =
|
||||
std::find_if(displays.begin(), displays.end(),
|
||||
[&](const VI::Display& display) { return display.GetID() == display_id; });
|
||||
|
||||
if (itr == displays.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*itr;
|
||||
}
|
||||
|
||||
const VI::Display* NVFlinger::FindDisplay(u64 display_id) const {
|
||||
const auto itr =
|
||||
std::find_if(displays.begin(), displays.end(),
|
||||
[&](const VI::Display& display) { return display.GetID() == display_id; });
|
||||
|
||||
if (itr == displays.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &*itr;
|
||||
}
|
||||
|
||||
VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) {
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return display->FindLayer(layer_id);
|
||||
}
|
||||
|
||||
const VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const {
|
||||
const auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return display->FindLayer(layer_id);
|
||||
}
|
||||
|
||||
VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
|
||||
auto* const display = FindDisplay(display_id);
|
||||
|
||||
if (display == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* layer = display->FindLayer(layer_id);
|
||||
|
||||
if (layer == nullptr) {
|
||||
LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
|
||||
CreateLayerAtId(*display, layer_id);
|
||||
return display->FindLayer(layer_id);
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
void NVFlinger::Compose() {
|
||||
for (auto& display : displays) {
|
||||
// Trigger vsync for this display at the end of drawing
|
||||
SCOPE_EXIT({ display.SignalVSyncEvent(); });
|
||||
|
||||
// Don't do anything for displays without layers.
|
||||
if (!display.HasLayers())
|
||||
continue;
|
||||
|
||||
// TODO(Subv): Support more than 1 layer.
|
||||
VI::Layer& layer = display.GetLayer(0);
|
||||
|
||||
android::BufferItem buffer{};
|
||||
const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
|
||||
|
||||
if (status != android::Status::NoError) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& igbp_buffer = *buffer.graphic_buffer;
|
||||
|
||||
if (!system.IsPoweredOn()) {
|
||||
return; // We are likely shutting down
|
||||
}
|
||||
|
||||
// Now send the buffer to the GPU for drawing.
|
||||
// TODO(Subv): Support more than just disp0. The display device selection is probably based
|
||||
// on which display we're drawing (Default, Internal, External, etc)
|
||||
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
|
||||
ASSERT(nvdisp);
|
||||
|
||||
guard->unlock();
|
||||
Common::Rectangle<int> crop_rect{
|
||||
static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
|
||||
static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
|
||||
|
||||
nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
|
||||
igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
|
||||
static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect,
|
||||
buffer.fence.fences, buffer.fence.num_fences);
|
||||
|
||||
MicroProfileFlip();
|
||||
guard->lock();
|
||||
|
||||
swap_interval = buffer.swap_interval;
|
||||
|
||||
auto fence = android::Fence::NoFence();
|
||||
layer.GetConsumer().ReleaseBuffer(buffer, fence);
|
||||
}
|
||||
}
|
||||
|
||||
s64 NVFlinger::GetNextTicks() const {
|
||||
static constexpr s64 max_hertz = 120LL;
|
||||
|
||||
const auto& settings = Settings::values;
|
||||
auto speed_scale = 1.f;
|
||||
if (settings.use_multi_core.GetValue()) {
|
||||
if (settings.use_speed_limit.GetValue()) {
|
||||
// Scales the speed based on speed_limit setting on MC. SC is handled by
|
||||
// SpeedLimiter::DoSpeedLimiting.
|
||||
speed_scale = 100.f / settings.speed_limit.GetValue();
|
||||
} else {
|
||||
// Run at unlocked framerate.
|
||||
speed_scale = 0.01f;
|
||||
}
|
||||
}
|
||||
|
||||
const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz);
|
||||
|
||||
return static_cast<s64>(speed_scale * static_cast<float>(next_ticks));
|
||||
}
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -1,154 +1,154 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Common {
|
||||
class Event;
|
||||
} // namespace Common
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
struct EventType;
|
||||
} // namespace Core::Timing
|
||||
|
||||
namespace Kernel {
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::Nvidia {
|
||||
class Module;
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
namespace Service::VI {
|
||||
class Display;
|
||||
class Layer;
|
||||
} // namespace Service::VI
|
||||
|
||||
namespace Service::android {
|
||||
class BufferQueueCore;
|
||||
class BufferQueueProducer;
|
||||
} // namespace Service::android
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
class NVFlinger final {
|
||||
public:
|
||||
explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
|
||||
~NVFlinger();
|
||||
|
||||
void ShutdownLayers();
|
||||
|
||||
/// Sets the NVDrv module instance to use to send buffers to the GPU.
|
||||
void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
|
||||
|
||||
/// Opens the specified display and returns the ID.
|
||||
///
|
||||
/// If an invalid display name is provided, then an empty optional is returned.
|
||||
[[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
|
||||
|
||||
/// Closes the specified display by its ID.
|
||||
///
|
||||
/// Returns false if an invalid display ID is provided.
|
||||
[[nodiscard]] bool CloseDisplay(u64 display_id);
|
||||
|
||||
/// Creates a layer on the specified display and returns the layer ID.
|
||||
///
|
||||
/// If an invalid display ID is specified, then an empty optional is returned.
|
||||
[[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
|
||||
|
||||
/// Closes a layer on all displays for the given layer ID.
|
||||
void CloseLayer(u64 layer_id);
|
||||
|
||||
/// Finds the buffer queue ID of the specified layer in the specified display.
|
||||
///
|
||||
/// If an invalid display ID or layer ID is provided, then an empty optional is returned.
|
||||
[[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Gets the vsync event for the specified display.
|
||||
///
|
||||
/// If an invalid display ID is provided, then VI::ResultNotFound is returned.
|
||||
/// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned.
|
||||
[[nodiscard]] ResultVal<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id);
|
||||
|
||||
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
|
||||
/// finished.
|
||||
void Compose();
|
||||
|
||||
[[nodiscard]] s64 GetNextTicks() const;
|
||||
|
||||
private:
|
||||
struct Layer {
|
||||
std::unique_ptr<android::BufferQueueCore> core;
|
||||
std::unique_ptr<android::BufferQueueProducer> producer;
|
||||
};
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
|
||||
return std::unique_lock{*guard};
|
||||
}
|
||||
|
||||
/// Finds the display identified by the specified ID.
|
||||
[[nodiscard]] VI::Display* FindDisplay(u64 display_id);
|
||||
|
||||
/// Finds the display identified by the specified ID.
|
||||
[[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display.
|
||||
[[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display.
|
||||
[[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display,
|
||||
/// or creates the layer if it is not found.
|
||||
/// To be used when the system expects the specified ID to already exist.
|
||||
[[nodiscard]] VI::Layer* FindOrCreateLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Creates a layer with the specified layer ID in the desired display.
|
||||
void CreateLayerAtId(VI::Display& display, u64 layer_id);
|
||||
|
||||
void SplitVSync(std::stop_token stop_token);
|
||||
|
||||
std::shared_ptr<Nvidia::Module> nvdrv;
|
||||
s32 disp_fd;
|
||||
|
||||
std::list<VI::Display> displays;
|
||||
|
||||
/// Id to use for the next layer that is created, this counter is shared among all displays.
|
||||
u64 next_layer_id = 1;
|
||||
/// Id to use for the next buffer queue that is created, this counter is shared among all
|
||||
/// layers.
|
||||
u32 next_buffer_queue_id = 1;
|
||||
|
||||
u32 swap_interval = 1;
|
||||
|
||||
/// Event that handles screen composition.
|
||||
std::shared_ptr<Core::Timing::EventType> multi_composition_event;
|
||||
std::shared_ptr<Core::Timing::EventType> single_composition_event;
|
||||
|
||||
std::shared_ptr<std::mutex> guard;
|
||||
|
||||
Core::System& system;
|
||||
|
||||
std::atomic<bool> vsync_signal;
|
||||
|
||||
std::jthread vsync_thread;
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
HosBinderDriverServer& hos_binder_driver_server;
|
||||
};
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Common {
|
||||
class Event;
|
||||
} // namespace Common
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
struct EventType;
|
||||
} // namespace Core::Timing
|
||||
|
||||
namespace Kernel {
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::Nvidia {
|
||||
class Module;
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
namespace Service::VI {
|
||||
class Display;
|
||||
class Layer;
|
||||
} // namespace Service::VI
|
||||
|
||||
namespace Service::android {
|
||||
class BufferQueueCore;
|
||||
class BufferQueueProducer;
|
||||
} // namespace Service::android
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
class NVFlinger final {
|
||||
public:
|
||||
explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
|
||||
~NVFlinger();
|
||||
|
||||
void ShutdownLayers();
|
||||
|
||||
/// Sets the NVDrv module instance to use to send buffers to the GPU.
|
||||
void SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance);
|
||||
|
||||
/// Opens the specified display and returns the ID.
|
||||
///
|
||||
/// If an invalid display name is provided, then an empty optional is returned.
|
||||
[[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name);
|
||||
|
||||
/// Closes the specified display by its ID.
|
||||
///
|
||||
/// Returns false if an invalid display ID is provided.
|
||||
[[nodiscard]] bool CloseDisplay(u64 display_id);
|
||||
|
||||
/// Creates a layer on the specified display and returns the layer ID.
|
||||
///
|
||||
/// If an invalid display ID is specified, then an empty optional is returned.
|
||||
[[nodiscard]] std::optional<u64> CreateLayer(u64 display_id);
|
||||
|
||||
/// Closes a layer on all displays for the given layer ID.
|
||||
void CloseLayer(u64 layer_id);
|
||||
|
||||
/// Finds the buffer queue ID of the specified layer in the specified display.
|
||||
///
|
||||
/// If an invalid display ID or layer ID is provided, then an empty optional is returned.
|
||||
[[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Gets the vsync event for the specified display.
|
||||
///
|
||||
/// If an invalid display ID is provided, then VI::ResultNotFound is returned.
|
||||
/// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned.
|
||||
[[nodiscard]] ResultVal<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id);
|
||||
|
||||
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
|
||||
/// finished.
|
||||
void Compose();
|
||||
|
||||
[[nodiscard]] s64 GetNextTicks() const;
|
||||
|
||||
private:
|
||||
struct Layer {
|
||||
std::unique_ptr<android::BufferQueueCore> core;
|
||||
std::unique_ptr<android::BufferQueueProducer> producer;
|
||||
};
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
|
||||
return std::unique_lock{*guard};
|
||||
}
|
||||
|
||||
/// Finds the display identified by the specified ID.
|
||||
[[nodiscard]] VI::Display* FindDisplay(u64 display_id);
|
||||
|
||||
/// Finds the display identified by the specified ID.
|
||||
[[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const;
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display.
|
||||
[[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display.
|
||||
[[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
|
||||
|
||||
/// Finds the layer identified by the specified ID in the desired display,
|
||||
/// or creates the layer if it is not found.
|
||||
/// To be used when the system expects the specified ID to already exist.
|
||||
[[nodiscard]] VI::Layer* FindOrCreateLayer(u64 display_id, u64 layer_id);
|
||||
|
||||
/// Creates a layer with the specified layer ID in the desired display.
|
||||
void CreateLayerAtId(VI::Display& display, u64 layer_id);
|
||||
|
||||
void SplitVSync(std::stop_token stop_token);
|
||||
|
||||
std::shared_ptr<Nvidia::Module> nvdrv;
|
||||
s32 disp_fd;
|
||||
|
||||
std::list<VI::Display> displays;
|
||||
|
||||
/// Id to use for the next layer that is created, this counter is shared among all displays.
|
||||
u64 next_layer_id = 1;
|
||||
/// Id to use for the next buffer queue that is created, this counter is shared among all
|
||||
/// layers.
|
||||
u32 next_buffer_queue_id = 1;
|
||||
|
||||
u32 swap_interval = 1;
|
||||
|
||||
/// Event that handles screen composition.
|
||||
std::shared_ptr<Core::Timing::EventType> multi_composition_event;
|
||||
std::shared_ptr<Core::Timing::EventType> single_composition_event;
|
||||
|
||||
std::shared_ptr<std::mutex> guard;
|
||||
|
||||
Core::System& system;
|
||||
|
||||
std::atomic<bool> vsync_signal;
|
||||
|
||||
std::jthread vsync_thread;
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
HosBinderDriverServer& hos_binder_driver_server;
|
||||
};
|
||||
|
||||
} // namespace Service::NVFlinger
|
||||
|
||||
@@ -1,172 +1,172 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class Parcel final {
|
||||
public:
|
||||
static constexpr std::size_t DefaultBufferSize = 0x40;
|
||||
|
||||
Parcel() : buffer(DefaultBufferSize) {}
|
||||
|
||||
template <typename T>
|
||||
explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
|
||||
Write(out_data);
|
||||
}
|
||||
|
||||
explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) {
|
||||
DeserializeHeader();
|
||||
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Read(T& val) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
ASSERT(read_index + sizeof(T) <= buffer.size());
|
||||
|
||||
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
|
||||
read_index += sizeof(T);
|
||||
read_index = Common::AlignUp(read_index, 4);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Read() {
|
||||
T val;
|
||||
Read(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ReadFlattened(T& val) {
|
||||
const auto flattened_size = Read<s64>();
|
||||
ASSERT(sizeof(T) == flattened_size);
|
||||
Read(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ReadFlattened() {
|
||||
T val;
|
||||
ReadFlattened(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ReadUnaligned() {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
ASSERT(read_index + sizeof(T) <= buffer.size());
|
||||
|
||||
T val;
|
||||
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
|
||||
read_index += sizeof(T);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const std::shared_ptr<T> ReadObject() {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
|
||||
const auto is_valid{Read<bool>()};
|
||||
|
||||
if (is_valid) {
|
||||
auto result = std::make_shared<T>();
|
||||
ReadFlattened(*result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::u16string ReadInterfaceToken() {
|
||||
[[maybe_unused]] const u32 unknown = Read<u32>();
|
||||
const u32 length = Read<u32>();
|
||||
|
||||
std::u16string token;
|
||||
token.reserve(length + 1);
|
||||
|
||||
for (u32 ch = 0; ch < length + 1; ++ch) {
|
||||
token.push_back(ReadUnaligned<u16>());
|
||||
}
|
||||
|
||||
read_index = Common::AlignUp(read_index, 4);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Write(const T& val) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
|
||||
if (buffer.size() < write_index + sizeof(T)) {
|
||||
buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
|
||||
}
|
||||
|
||||
std::memcpy(buffer.data() + write_index, &val, sizeof(T));
|
||||
write_index += sizeof(T);
|
||||
write_index = Common::AlignUp(write_index, 4);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void WriteObject(const T* ptr) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
|
||||
if (!ptr) {
|
||||
Write<u32>(0);
|
||||
return;
|
||||
}
|
||||
|
||||
Write<u32>(1);
|
||||
Write<s64>(sizeof(T));
|
||||
Write(*ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void WriteObject(const std::shared_ptr<T> ptr) {
|
||||
WriteObject(ptr.get());
|
||||
}
|
||||
|
||||
void DeserializeHeader() {
|
||||
ASSERT(buffer.size() > sizeof(Header));
|
||||
|
||||
Header header{};
|
||||
std::memcpy(&header, buffer.data(), sizeof(Header));
|
||||
|
||||
read_index = header.data_offset;
|
||||
}
|
||||
|
||||
std::vector<u8> Serialize() const {
|
||||
ASSERT(read_index == 0);
|
||||
|
||||
Header header{};
|
||||
header.data_size = static_cast<u32>(write_index - sizeof(Header));
|
||||
header.data_offset = sizeof(Header);
|
||||
header.objects_size = 4;
|
||||
header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
|
||||
std::memcpy(buffer.data(), &header, sizeof(Header));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Header {
|
||||
u32 data_size;
|
||||
u32 data_offset;
|
||||
u32 objects_size;
|
||||
u32 objects_offset;
|
||||
};
|
||||
static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
|
||||
|
||||
mutable std::vector<u8> buffer;
|
||||
std::size_t read_index = 0;
|
||||
std::size_t write_index = sizeof(Header);
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class Parcel final {
|
||||
public:
|
||||
static constexpr std::size_t DefaultBufferSize = 0x40;
|
||||
|
||||
Parcel() : buffer(DefaultBufferSize) {}
|
||||
|
||||
template <typename T>
|
||||
explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
|
||||
Write(out_data);
|
||||
}
|
||||
|
||||
explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) {
|
||||
DeserializeHeader();
|
||||
[[maybe_unused]] const std::u16string token = ReadInterfaceToken();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Read(T& val) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
ASSERT(read_index + sizeof(T) <= buffer.size());
|
||||
|
||||
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
|
||||
read_index += sizeof(T);
|
||||
read_index = Common::AlignUp(read_index, 4);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Read() {
|
||||
T val;
|
||||
Read(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ReadFlattened(T& val) {
|
||||
const auto flattened_size = Read<s64>();
|
||||
ASSERT(sizeof(T) == flattened_size);
|
||||
Read(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ReadFlattened() {
|
||||
T val;
|
||||
ReadFlattened(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ReadUnaligned() {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
ASSERT(read_index + sizeof(T) <= buffer.size());
|
||||
|
||||
T val;
|
||||
std::memcpy(&val, buffer.data() + read_index, sizeof(T));
|
||||
read_index += sizeof(T);
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const std::shared_ptr<T> ReadObject() {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
|
||||
const auto is_valid{Read<bool>()};
|
||||
|
||||
if (is_valid) {
|
||||
auto result = std::make_shared<T>();
|
||||
ReadFlattened(*result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::u16string ReadInterfaceToken() {
|
||||
[[maybe_unused]] const u32 unknown = Read<u32>();
|
||||
const u32 length = Read<u32>();
|
||||
|
||||
std::u16string token;
|
||||
token.reserve(length + 1);
|
||||
|
||||
for (u32 ch = 0; ch < length + 1; ++ch) {
|
||||
token.push_back(ReadUnaligned<u16>());
|
||||
}
|
||||
|
||||
read_index = Common::AlignUp(read_index, 4);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Write(const T& val) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
|
||||
if (buffer.size() < write_index + sizeof(T)) {
|
||||
buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
|
||||
}
|
||||
|
||||
std::memcpy(buffer.data() + write_index, &val, sizeof(T));
|
||||
write_index += sizeof(T);
|
||||
write_index = Common::AlignUp(write_index, 4);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void WriteObject(const T* ptr) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
|
||||
|
||||
if (!ptr) {
|
||||
Write<u32>(0);
|
||||
return;
|
||||
}
|
||||
|
||||
Write<u32>(1);
|
||||
Write<s64>(sizeof(T));
|
||||
Write(*ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void WriteObject(const std::shared_ptr<T> ptr) {
|
||||
WriteObject(ptr.get());
|
||||
}
|
||||
|
||||
void DeserializeHeader() {
|
||||
ASSERT(buffer.size() > sizeof(Header));
|
||||
|
||||
Header header{};
|
||||
std::memcpy(&header, buffer.data(), sizeof(Header));
|
||||
|
||||
read_index = header.data_offset;
|
||||
}
|
||||
|
||||
std::vector<u8> Serialize() const {
|
||||
ASSERT(read_index == 0);
|
||||
|
||||
Header header{};
|
||||
header.data_size = static_cast<u32>(write_index - sizeof(Header));
|
||||
header.data_offset = sizeof(Header);
|
||||
header.objects_size = 4;
|
||||
header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
|
||||
std::memcpy(buffer.data(), &header, sizeof(Header));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Header {
|
||||
u32 data_size;
|
||||
u32 data_offset;
|
||||
u32 objects_size;
|
||||
u32 objects_offset;
|
||||
};
|
||||
static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
|
||||
|
||||
mutable std::vector<u8> buffer;
|
||||
std::size_t read_index = 0;
|
||||
std::size_t write_index = sizeof(Header);
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
enum class PixelFormat : u32 {
|
||||
NoFormat = 0,
|
||||
Rgba8888 = 1,
|
||||
Rgbx8888 = 2,
|
||||
Rgb888 = 3,
|
||||
Rgb565 = 4,
|
||||
Bgra8888 = 5,
|
||||
Rgba5551 = 6,
|
||||
Rgba4444 = 7,
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
enum class PixelFormat : u32 {
|
||||
NoFormat = 0,
|
||||
Rgba8888 = 1,
|
||||
Rgbx8888 = 2,
|
||||
Rgb888 = 3,
|
||||
Rgb565 = 4,
|
||||
Bgra8888 = 5,
|
||||
Rgba5551 = 6,
|
||||
Rgba4444 = 7,
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class IProducerListener {
|
||||
public:
|
||||
virtual void OnBufferReleased() = 0;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class IProducerListener {
|
||||
public:
|
||||
virtual void OnBufferReleased() = 0;
|
||||
};
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
enum class Status : s32 {
|
||||
None = 0,
|
||||
NoError = 0,
|
||||
StaleBufferSlot = 1,
|
||||
NoBufferAvailable = 2,
|
||||
PresentLater = 3,
|
||||
WouldBlock = -11,
|
||||
NoMemory = -12,
|
||||
Busy = -16,
|
||||
NoInit = -19,
|
||||
BadValue = -22,
|
||||
InvalidOperation = -37,
|
||||
BufferNeedsReallocation = 1,
|
||||
ReleaseAllBuffers = 2,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(Status);
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
enum class Status : s32 {
|
||||
None = 0,
|
||||
NoError = 0,
|
||||
StaleBufferSlot = 1,
|
||||
NoBufferAvailable = 2,
|
||||
PresentLater = 3,
|
||||
WouldBlock = -11,
|
||||
NoMemory = -12,
|
||||
Busy = -16,
|
||||
NoInit = -19,
|
||||
BadValue = -22,
|
||||
InvalidOperation = -37,
|
||||
BufferNeedsReallocation = 1,
|
||||
ReleaseAllBuffers = 2,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(Status);
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class Fence {
|
||||
public:
|
||||
constexpr Fence() = default;
|
||||
|
||||
static constexpr Fence NoFence() {
|
||||
Fence fence;
|
||||
fence.fences[0].id = -1;
|
||||
return fence;
|
||||
}
|
||||
|
||||
public:
|
||||
u32 num_fences{};
|
||||
std::array<Service::Nvidia::NvFence, 4> fences{};
|
||||
};
|
||||
static_assert(sizeof(Fence) == 36, "Fence has wrong size");
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class Fence {
|
||||
public:
|
||||
constexpr Fence() = default;
|
||||
|
||||
static constexpr Fence NoFence() {
|
||||
Fence fence;
|
||||
fence.fences[0].id = -1;
|
||||
return fence;
|
||||
}
|
||||
|
||||
public:
|
||||
u32 num_fences{};
|
||||
std::array<Service::Nvidia::NvFence, 4> fences{};
|
||||
};
|
||||
static_assert(sizeof(Fence) == 36, "Fence has wrong size");
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,100 +1,100 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2007 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/pixel_format.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class GraphicBuffer final {
|
||||
public:
|
||||
constexpr GraphicBuffer() = default;
|
||||
|
||||
constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
|
||||
: width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
|
||||
usage{static_cast<s32>(usage_)} {}
|
||||
|
||||
constexpr u32 Width() const {
|
||||
return static_cast<u32>(width);
|
||||
}
|
||||
|
||||
constexpr u32 Height() const {
|
||||
return static_cast<u32>(height);
|
||||
}
|
||||
|
||||
constexpr u32 Stride() const {
|
||||
return static_cast<u32>(stride);
|
||||
}
|
||||
|
||||
constexpr u32 Usage() const {
|
||||
return static_cast<u32>(usage);
|
||||
}
|
||||
|
||||
constexpr PixelFormat Format() const {
|
||||
return format;
|
||||
}
|
||||
|
||||
constexpr u32 BufferId() const {
|
||||
return buffer_id;
|
||||
}
|
||||
|
||||
constexpr PixelFormat ExternalFormat() const {
|
||||
return external_format;
|
||||
}
|
||||
|
||||
constexpr u32 Handle() const {
|
||||
return handle;
|
||||
}
|
||||
|
||||
constexpr u32 Offset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_,
|
||||
u32 usage_) const {
|
||||
if (static_cast<s32>(width_) != width) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (static_cast<s32>(height_) != height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (format_ != format) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((static_cast<u32>(usage) & usage_) != usage_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 magic{};
|
||||
s32 width{};
|
||||
s32 height{};
|
||||
s32 stride{};
|
||||
PixelFormat format{};
|
||||
s32 usage{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 index{};
|
||||
INSERT_PADDING_WORDS(3);
|
||||
u32 buffer_id{};
|
||||
INSERT_PADDING_WORDS(6);
|
||||
PixelFormat external_format{};
|
||||
INSERT_PADDING_WORDS(10);
|
||||
u32 handle{};
|
||||
u32 offset{};
|
||||
INSERT_PADDING_WORDS(60);
|
||||
};
|
||||
static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2007 The Android Open Source Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Parts of this implementation were based on:
|
||||
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvflinger/pixel_format.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
class GraphicBuffer final {
|
||||
public:
|
||||
constexpr GraphicBuffer() = default;
|
||||
|
||||
constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
|
||||
: width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
|
||||
usage{static_cast<s32>(usage_)} {}
|
||||
|
||||
constexpr u32 Width() const {
|
||||
return static_cast<u32>(width);
|
||||
}
|
||||
|
||||
constexpr u32 Height() const {
|
||||
return static_cast<u32>(height);
|
||||
}
|
||||
|
||||
constexpr u32 Stride() const {
|
||||
return static_cast<u32>(stride);
|
||||
}
|
||||
|
||||
constexpr u32 Usage() const {
|
||||
return static_cast<u32>(usage);
|
||||
}
|
||||
|
||||
constexpr PixelFormat Format() const {
|
||||
return format;
|
||||
}
|
||||
|
||||
constexpr u32 BufferId() const {
|
||||
return buffer_id;
|
||||
}
|
||||
|
||||
constexpr PixelFormat ExternalFormat() const {
|
||||
return external_format;
|
||||
}
|
||||
|
||||
constexpr u32 Handle() const {
|
||||
return handle;
|
||||
}
|
||||
|
||||
constexpr u32 Offset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_,
|
||||
u32 usage_) const {
|
||||
if (static_cast<s32>(width_) != width) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (static_cast<s32>(height_) != height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (format_ != format) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((static_cast<u32>(usage) & usage_) != usage_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 magic{};
|
||||
s32 width{};
|
||||
s32 height{};
|
||||
s32 stride{};
|
||||
PixelFormat format{};
|
||||
s32 usage{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32 index{};
|
||||
INSERT_PADDING_WORDS(3);
|
||||
u32 buffer_id{};
|
||||
INSERT_PADDING_WORDS(6);
|
||||
PixelFormat external_format{};
|
||||
INSERT_PADDING_WORDS(10);
|
||||
u32 handle{};
|
||||
u32 offset{};
|
||||
INSERT_PADDING_WORDS(60);
|
||||
};
|
||||
static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
/// Attributes queryable with Query
|
||||
enum class NativeWindow : s32 {
|
||||
Width = 0,
|
||||
Height = 1,
|
||||
Format = 2,
|
||||
MinUndequeedBuffers = 3,
|
||||
QueuesToWindowComposer = 4,
|
||||
ConcreteType = 5,
|
||||
DefaultWidth = 6,
|
||||
DefaultHeight = 7,
|
||||
TransformHint = 8,
|
||||
ConsumerRunningBehind = 9,
|
||||
ConsumerUsageBits = 10,
|
||||
StickyTransform = 11,
|
||||
DefaultDataSpace = 12,
|
||||
BufferAge = 13,
|
||||
};
|
||||
|
||||
/// Parameter for Connect/Disconnect
|
||||
enum class NativeWindowApi : s32 {
|
||||
NoConnectedApi = 0,
|
||||
Egl = 1,
|
||||
Cpu = 2,
|
||||
Media = 3,
|
||||
Camera = 4,
|
||||
};
|
||||
|
||||
/// Scaling mode parameter for QueueBuffer
|
||||
enum class NativeWindowScalingMode : s32 {
|
||||
Freeze = 0,
|
||||
ScaleToWindow = 1,
|
||||
ScaleCrop = 2,
|
||||
NoScaleCrop = 3,
|
||||
};
|
||||
|
||||
/// Transform parameter for QueueBuffer
|
||||
enum class NativeWindowTransform : u32 {
|
||||
None = 0x0,
|
||||
InverseDisplay = 0x08,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform);
|
||||
|
||||
} // namespace Service::android
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Service::android {
|
||||
|
||||
/// Attributes queryable with Query
|
||||
enum class NativeWindow : s32 {
|
||||
Width = 0,
|
||||
Height = 1,
|
||||
Format = 2,
|
||||
MinUndequeedBuffers = 3,
|
||||
QueuesToWindowComposer = 4,
|
||||
ConcreteType = 5,
|
||||
DefaultWidth = 6,
|
||||
DefaultHeight = 7,
|
||||
TransformHint = 8,
|
||||
ConsumerRunningBehind = 9,
|
||||
ConsumerUsageBits = 10,
|
||||
StickyTransform = 11,
|
||||
DefaultDataSpace = 12,
|
||||
BufferAge = 13,
|
||||
};
|
||||
|
||||
/// Parameter for Connect/Disconnect
|
||||
enum class NativeWindowApi : s32 {
|
||||
NoConnectedApi = 0,
|
||||
Egl = 1,
|
||||
Cpu = 2,
|
||||
Media = 3,
|
||||
Camera = 4,
|
||||
};
|
||||
|
||||
/// Scaling mode parameter for QueueBuffer
|
||||
enum class NativeWindowScalingMode : s32 {
|
||||
Freeze = 0,
|
||||
ScaleToWindow = 1,
|
||||
ScaleCrop = 2,
|
||||
NoScaleCrop = 3,
|
||||
};
|
||||
|
||||
/// Transform parameter for QueueBuffer
|
||||
enum class NativeWindowTransform : u32 {
|
||||
None = 0x0,
|
||||
InverseDisplay = 0x08,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform);
|
||||
|
||||
} // namespace Service::android
|
||||
|
||||
Reference in New Issue
Block a user