@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-License-Identifier: GPL-3.0-or-later
@ -530,11 +533,6 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
item . is_droppable = core - > dequeue_buffer_cannot_block | | async ;
item . is_droppable = core - > dequeue_buffer_cannot_block | | async ;
item . swap_interval = swap_interval ;
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_ ;
sticky_transform = sticky_transform_ ;
if ( core - > queue . empty ( ) ) {
if ( core - > queue . empty ( ) ) {
@ -551,6 +549,15 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
// mark it as freed
// mark it as freed
if ( core - > StillTracking ( * front ) ) {
if ( core - > StillTracking ( * front ) ) {
slots [ front - > slot ] . buffer_state = BufferState : : Free ;
slots [ front - > slot ] . buffer_state = BufferState : : Free ;
// Mark tracked buffer history records as free
for ( auto & buffer_history_record : core - > buffer_history ) {
if ( buffer_history_record . frame_number = = front - > frame_number ) {
buffer_history_record . state = BufferState : : Free ;
break ;
}
}
// Reset the frame number of the freed buffer so that it is the first in line to
// Reset the frame number of the freed buffer so that it is the first in line to
// be dequeued again
// be dequeued again
slots [ front - > slot ] . frame_number = 0 ;
slots [ front - > slot ] . frame_number = 0 ;
@ -564,6 +571,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
}
}
}
}
core - > PushHistory ( core - > frame_counter , slots [ slot ] . queue_time , slots [ slot ] . presentation_time , BufferState : : Queued ) ;
core - > buffer_has_been_queued = true ;
core - > buffer_has_been_queued = true ;
core - > SignalDequeueCondition ( ) ;
core - > SignalDequeueCondition ( ) ;
output - > Inflate ( core - > default_width , core - > default_height , core - > transform_hint ,
output - > Inflate ( core - > default_width , core - > default_height , core - > transform_hint ,
@ -938,33 +946,46 @@ void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data,
break ;
break ;
}
}
case TransactionId : : 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 ( ) ) ;
LOG_DEBUG ( Service_Nvnflinger , " called, transaction=GetBufferHistory " ) ;
if ( buffer_history_count < = 0 ) {
const s32 request = parcel_in . Read < s32 > ( ) ;
if ( request < = 0 ) {
parcel_out . Write ( Status : : BadValue ) ;
parcel_out . Write ( Status : : BadValue ) ;
parcel_out . Write < s32 > ( 0 ) ;
parcel_out . Write < s32 > ( 0 ) ;
status = Status : : None ;
break ;
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 - - ;
constexpr u32 history_max = BufferQueueCore : : BUFFER_HISTORY_SIZE ;
std : : array < BufferHistoryInfo , history_max > buffer_history_snapshot { } ;
s32 valid_index { } ;
{
std : : scoped_lock lk ( core - > mutex ) ;
const u32 current_history_pos = core - > buffer_history_pos ;
u32 index_reversed { } ;
for ( u32 i = 0 ; i < history_max ; + + i ) {
// Wrap values backwards e.g. 7, 6, 5, etc. in the range of 0-7
index_reversed = ( current_history_pos + history_max - i ) % history_max ;
const auto & current_history_buffer = core - > buffer_history [ index_reversed ] ;
// Here we use the frame number as a terminator.
// Because a buffer without frame_number is not considered complete
if ( current_history_buffer . frame_number = = 0 ) {
break ;
}
buffer_history_snapshot [ valid_index ] = current_history_buffer ;
+ + valid_index ;
}
}
}
const s32 limit = std : : min ( request , valid_index ) ;
parcel_out . Write ( Status : : NoError ) ;
parcel_out . Write ( Status : : NoError ) ;
parcel_out . Write ( buffer_history_count ) ;
parcel_out . WriteFlattenedObject < BufferInfo > ( info ) ;
status = Status : : None ;
parcel_out . Write < s32 > ( limit ) ;
for ( s32 i = 0 ; i < limit ; + + i ) {
parcel_out . Write ( buffer_history_snapshot [ i ] ) ;
}
break ;
break ;
}
}
default :
default :
@ -972,9 +993,7 @@ void BufferQueueProducer::Transact(u32 code, std::span<const u8> parcel_data,
break ;
break ;
}
}
if ( status ! = Status : : None ) {
parcel_out . Write ( status ) ;
}
parcel_out . Write ( status ) ;
const auto serialized = parcel_out . Serialize ( ) ;
const auto serialized = parcel_out . Serialize ( ) ;
std : : memcpy ( parcel_reply . data ( ) , serialized . data ( ) ,
std : : memcpy ( parcel_reply . data ( ) , serialized . data ( ) ,