[nvnflinger] add GetBufferHistory from sudachi (#82)
Some checks are pending
eden-build / windows (msvc) (push) Waiting to run
eden-build / linux (push) Waiting to run
eden-build / android (push) Waiting to run
eden-build / source (push) Successful in 3m54s

Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: #82

This commit adds a working implementation of the `GetBufferHistory` transaction in `BufferQueueProducer`, removing the previous stub.

Adapted by Jarrod Norwell for Sudachi, this implementation references the behavior in Ryujinx; commit rescued by Maufeat and another Eden teammate from Sudachi's reference, fixed and adapted for Eden usage.

It helps improve compatibility with Unreal Engine 4 titles and others that depend on proper surface history tracking for rendering pipelines, especially with regard to lighting, bloom, and alpha transitions.

Functionality has been tested for stability and does not introduce regressions, though further validation is recommended.
Co-authored-by: Maufeat <maufeat@eden-emu.dev>
Co-committed-by: Maufeat <maufeat@eden-emu.dev>
This commit is contained in:
Maufeat 2025-07-21 07:16:26 +02:00 committed by crueter
parent 12a690e15f
commit be97bf3c1b
Signed by: crueter
GPG key ID: 425ACD2D4830EBC6
6 changed files with 79 additions and 8 deletions

View file

@ -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

View file

@ -10,7 +10,9 @@
namespace Service::android {
BufferQueueCore::BufferQueueCore() = default;
BufferQueueCore::BufferQueueCore() {
history.resize(8);
};
BufferQueueCore::~BufferQueueCore() = default;

View file

@ -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<BufferInfo> history{8};
};
} // namespace Service::android

View file

@ -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<const u8> parcel_data,
std::span<u8> parcel_reply, u32 flags) {
// Values used by BnGraphicBufferProducer onTransact
@ -922,23 +933,49 @@ void BufferQueueProducer::Transact(u32 code, std::span<const u8> 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>(), (s32)core->history.size());
if (buffer_history_count <= 0) {
parcel_out.Write(Status::BadValue);
parcel_out.Write<s32>(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<BufferInfo>(info);
status = Status::None;
break;
}
default:
ASSERT_MSG(false, "Unimplemented TransactionId {}", code);
break;
}
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

View file

@ -86,6 +86,8 @@ private:
s32 current_callback_ticket{};
std::condition_variable_any callback_condition;
u64 position;
Service::Nvidia::NvCore::NvMap& nvmap;
};

View file

@ -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