[nvnflinger] add GetBufferHistory from sudachi (#82)
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:
parent
12a690e15f
commit
be97bf3c1b
6 changed files with 79 additions and 8 deletions
|
@ -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
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
|
||||
namespace Service::android {
|
||||
|
||||
BufferQueueCore::BufferQueueCore() = default;
|
||||
BufferQueueCore::BufferQueueCore() {
|
||||
history.resize(8);
|
||||
};
|
||||
|
||||
BufferQueueCore::~BufferQueueCore() = default;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -86,6 +86,8 @@ private:
|
|||
s32 current_callback_ticket{};
|
||||
std::condition_variable_any callback_condition;
|
||||
|
||||
u64 position;
|
||||
|
||||
Service::Nvidia::NvCore::NvMap& nvmap;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue