diff --git a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp index 3bc23aa976..91ba35aef5 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_consumer.cpp @@ -101,6 +101,14 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, // slot to the producer, it will wait for the fence to pass. We should fix this // by properly waiting for the fence in the BufferItemConsumer. // slots[slot].fence = Fence::NoFence(); + + const auto target_frame_number = slots[slot].frame_number; + for (size_t i = 0; i < core->history.size(); i++) { + if (core->history[i].frame_number == target_frame_number) { + core->history[i].state = BufferState::Acquired; + break; + } + } } // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp index 5d8c861fa7..30095b0f73 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.cpp @@ -10,7 +10,9 @@ namespace Service::android { -BufferQueueCore::BufferQueueCore() = default; +BufferQueueCore::BufferQueueCore() { + history.resize(8); +}; BufferQueueCore::~BufferQueueCore() = default; diff --git a/src/core/hle/service/nvnflinger/buffer_queue_core.h b/src/core/hle/service/nvnflinger/buffer_queue_core.h index e513d183bf..341634352b 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_core.h @@ -21,6 +21,25 @@ namespace Service::android { +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif +struct BufferInfo { + u64 frame_number; + s64 queue_time; + s64 presentation_time{}; + BufferState state{BufferState::Free}; +} +#if defined(__GNUC__) || defined(__clang__) +__attribute__((packed)) +#endif +; +#ifdef _MSC_VER +#pragma pack(pop) +#endif +static_assert(sizeof(BufferInfo) == 0x1C, + "BufferInfo is an invalid size"); + class IConsumerListener; class IProducerListener; @@ -72,6 +91,8 @@ private: u32 transform_hint{}; bool is_allocating{}; mutable std::condition_variable_any is_allocating_condition; + + std::vector history{8}; }; } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp index 9e5091eebd..4317aee1c4 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.cpp @@ -512,6 +512,8 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, slots[slot].buffer_state = BufferState::Queued; ++core->frame_counter; slots[slot].frame_number = core->frame_counter; + slots[slot].queue_time = timestamp; + slots[slot].presentation_time = 0; item.acquire_called = slots[slot].acquire_called; item.graphic_buffer = slots[slot].graphic_buffer; @@ -528,6 +530,11 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input, item.is_droppable = core->dequeue_buffer_cannot_block || async; item.swap_interval = swap_interval; + position = (position + 1) % 8; + core->history[position] = {.frame_number = core->frame_counter, + .queue_time = slots[slot].queue_time, + .state = BufferState::Queued}; + sticky_transform = sticky_transform_; if (core->queue.empty()) { @@ -803,6 +810,10 @@ Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot, return Status::NoError; } +Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) { + return &buffer_wait_event->GetReadableEvent(); +} + void BufferQueueProducer::Transact(u32 code, std::span parcel_data, std::span parcel_reply, u32 flags) { // Values used by BnGraphicBufferProducer onTransact @@ -922,23 +933,49 @@ void BufferQueueProducer::Transact(u32 code, std::span parcel_data, status = SetBufferCount(buffer_count); break; } - case TransactionId::GetBufferHistory: - LOG_WARNING(Service_Nvnflinger, "(STUBBED) called, transaction=GetBufferHistory"); + case TransactionId::GetBufferHistory: { + LOG_WARNING(Service_Nvnflinger, "called, transaction=GetBufferHistory"); + + std::scoped_lock lock{core->mutex}; + + auto buffer_history_count = std::min(parcel_in.Read(), (s32)core->history.size()); + + if (buffer_history_count <= 0) { + parcel_out.Write(Status::BadValue); + parcel_out.Write(0); + status = Status::None; + break; + } + + auto info = new BufferInfo[buffer_history_count]; + auto pos = position; + for (int i = 0; i < buffer_history_count; i++) { + info[i] = core->history[(pos - i) % core->history.size()]; + LOG_WARNING(Service_Nvnflinger, "frame_number={}, state={}", + core->history[(pos - i) % core->history.size()].frame_number, + (u32)core->history[(pos - i) % core->history.size()].state); + pos--; + } + + parcel_out.Write(Status::NoError); + parcel_out.Write(buffer_history_count); + parcel_out.WriteFlattenedObject(info); + status = Status::None; break; + } default: ASSERT_MSG(false, "Unimplemented TransactionId {}", code); break; } - parcel_out.Write(status); + if (status != Status::None) { + parcel_out.Write(status); + } const auto serialized = parcel_out.Serialize(); std::memcpy(parcel_reply.data(), serialized.data(), std::min(parcel_reply.size(), serialized.size())); } -Kernel::KReadableEvent* BufferQueueProducer::GetNativeHandle(u32 type_id) { - return &buffer_wait_event->GetReadableEvent(); -} } // namespace Service::android diff --git a/src/core/hle/service/nvnflinger/buffer_queue_producer.h b/src/core/hle/service/nvnflinger/buffer_queue_producer.h index 048523514c..28195cd3c5 100644 --- a/src/core/hle/service/nvnflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvnflinger/buffer_queue_producer.h @@ -86,6 +86,8 @@ private: s32 current_callback_ticket{}; std::condition_variable_any callback_condition; + u64 position; + Service::Nvidia::NvCore::NvMap& nvmap; }; diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h index 37daca78b1..5b5cbb6fbd 100644 --- a/src/core/hle/service/nvnflinger/buffer_slot.h +++ b/src/core/hle/service/nvnflinger/buffer_slot.h @@ -15,7 +15,7 @@ namespace Service::android { class GraphicBuffer; -enum class BufferState : u32 { +enum class BufferState : s32 { Free = 0, Dequeued = 1, Queued = 2, @@ -34,6 +34,7 @@ struct BufferSlot final { bool needs_cleanup_on_release{}; bool attached_by_consumer{}; bool is_preallocated{}; + s64 queue_time{}, presentation_time{}; }; } // namespace Service::android