steamcompmgr: Refactor img wait to handle multiple image waits at once
Refactors the image wait thread to handle multiple image waits at once.
This commit is contained in:
parent
abdc97e796
commit
528eb7ede7
2 changed files with 275 additions and 112 deletions
|
@ -72,6 +72,7 @@
|
|||
#include <signal.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <X11/Xmu/CurUtil.h>
|
||||
#include "waitable.h"
|
||||
|
||||
#include "steamcompmgr_shared.hpp"
|
||||
|
||||
|
@ -103,6 +104,7 @@
|
|||
|
||||
|
||||
static LogScope xwm_log("xwm");
|
||||
LogScope g_WaitableLog("waitable");
|
||||
|
||||
bool g_bWasPartialComposite = false;
|
||||
|
||||
|
@ -706,7 +708,9 @@ struct ignore {
|
|||
unsigned long sequence;
|
||||
};
|
||||
|
||||
struct commit_t
|
||||
gamescope::CAsyncWaiter g_ImageWaiter{ "gamescope_img" };
|
||||
|
||||
struct commit_t : public gamescope::IWaitable
|
||||
{
|
||||
commit_t()
|
||||
{
|
||||
|
@ -758,6 +762,46 @@ struct commit_t
|
|||
uint64_t desired_present_time = 0;
|
||||
uint64_t earliest_present_time = 0;
|
||||
uint64_t present_margin = 0;
|
||||
|
||||
// For waitable:
|
||||
int GetFD() final
|
||||
{
|
||||
return m_nCommitFence;
|
||||
}
|
||||
|
||||
void OnPollIn() final
|
||||
{
|
||||
gpuvis_trace_end_ctx_printf( commitID, "wait fence" );
|
||||
|
||||
g_ImageWaiter.RemoveWaitable( this );
|
||||
close( m_nCommitFence );
|
||||
m_nCommitFence = -1;
|
||||
|
||||
uint64_t frametime;
|
||||
if ( m_bMangoNudge )
|
||||
{
|
||||
uint64_t now = get_time_in_nanos();
|
||||
static uint64_t lastFrameTime = now;
|
||||
frametime = now - lastFrameTime;
|
||||
lastFrameTime = now;
|
||||
}
|
||||
|
||||
// TODO: Move this so it's called in the main loop.
|
||||
// Instead of looping over all the windows like before.
|
||||
// When we get the new IWaitable stuff in there.
|
||||
{
|
||||
std::unique_lock< std::mutex > lock( pDoneCommits->listCommitsDoneLock );
|
||||
pDoneCommits->listCommitsDone.push_back( CommitDoneEntry_t{ commitID, desired_present_time } );
|
||||
}
|
||||
|
||||
if ( m_bMangoNudge )
|
||||
mangoapp_update( frametime, uint64_t(~0ull), uint64_t(~0ull) );
|
||||
|
||||
nudge_steamcompmgr();
|
||||
}
|
||||
int m_nCommitFence = -1;
|
||||
bool m_bMangoNudge = false;
|
||||
CommitDoneList_t *pDoneCommits = nullptr; // I hate this
|
||||
};
|
||||
|
||||
static std::vector<pollfd> pollfds;
|
||||
|
@ -1068,89 +1112,19 @@ private:
|
|||
int count = 0;
|
||||
};
|
||||
|
||||
struct WaitListEntry_t
|
||||
static void
|
||||
dispatch_nudge( int fd )
|
||||
{
|
||||
CommitDoneList_t *doneCommits;
|
||||
int fence;
|
||||
// Josh: Whether or not to nudge mangoapp that we got
|
||||
// a frame as soon as we know this commit is done.
|
||||
// This could technically be out of date if we change windows
|
||||
// but for a max couple frames of inaccuracy when switching windows
|
||||
// compared to being all over the place from handling in the
|
||||
// steamcompmgr thread in handle_done_commits, it is worth it.
|
||||
bool mangoapp_nudge;
|
||||
uint64_t commitID;
|
||||
uint64_t desiredPresentTime;
|
||||
};
|
||||
|
||||
sem waitListSem;
|
||||
std::mutex waitListLock;
|
||||
std::vector< WaitListEntry_t > waitList;
|
||||
|
||||
bool imageWaitThreadRun = true;
|
||||
|
||||
void imageWaitThreadMain( void )
|
||||
{
|
||||
pthread_setname_np( pthread_self(), "gamescope-img" );
|
||||
|
||||
wait:
|
||||
waitListSem.wait();
|
||||
|
||||
if ( imageWaitThreadRun == false )
|
||||
for (;;)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool bFound = false;
|
||||
WaitListEntry_t entry;
|
||||
|
||||
retry:
|
||||
static char buf[1024];
|
||||
if ( read( fd, buf, sizeof(buf) ) < 0 )
|
||||
{
|
||||
std::unique_lock< std::mutex > lock( waitListLock );
|
||||
|
||||
if( waitList.empty() )
|
||||
{
|
||||
goto wait;
|
||||
if ( errno != EAGAIN )
|
||||
xwm_log.errorf_errno(" steamcompmgr: dispatch_nudge: read failed" );
|
||||
break;
|
||||
}
|
||||
|
||||
entry = waitList[ 0 ];
|
||||
bFound = true;
|
||||
waitList.erase( waitList.begin() );
|
||||
}
|
||||
|
||||
assert( bFound == true );
|
||||
|
||||
gpuvis_trace_begin_ctx_printf( entry.commitID, "wait fence" );
|
||||
struct pollfd fd = { entry.fence, POLLIN, 0 };
|
||||
int ret = poll( &fd, 1, 100 );
|
||||
if ( ret < 0 )
|
||||
{
|
||||
xwm_log.errorf_errno( "failed to poll fence FD" );
|
||||
}
|
||||
gpuvis_trace_end_ctx_printf( entry.commitID, "wait fence" );
|
||||
|
||||
close( entry.fence );
|
||||
|
||||
uint64_t frametime;
|
||||
if ( entry.mangoapp_nudge )
|
||||
{
|
||||
uint64_t now = get_time_in_nanos();
|
||||
static uint64_t lastFrameTime = now;
|
||||
frametime = now - lastFrameTime;
|
||||
lastFrameTime = now;
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock< std::mutex > lock( entry.doneCommits->listCommitsDoneLock );
|
||||
entry.doneCommits->listCommitsDone.push_back( CommitDoneEntry_t{ entry.commitID, entry.desiredPresentTime } );
|
||||
}
|
||||
|
||||
nudge_steamcompmgr();
|
||||
|
||||
if ( entry.mangoapp_nudge )
|
||||
mangoapp_update( frametime, uint64_t(~0ull), uint64_t(~0ull) );
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
sem statsThreadSem;
|
||||
|
@ -6196,8 +6170,7 @@ steamcompmgr_exit(void)
|
|||
g_HeldCommits[ HELD_COMMIT_BASE ] = nullptr;
|
||||
g_HeldCommits[ HELD_COMMIT_FADE ] = nullptr;
|
||||
|
||||
imageWaitThreadRun = false;
|
||||
waitListSem.signal();
|
||||
g_ImageWaiter.Shutdown();
|
||||
|
||||
if ( statsThreadRun == true )
|
||||
{
|
||||
|
@ -6623,20 +6596,12 @@ void update_wayland_res(CommitDoneList_t *doneCommits, steamcompmgr_win_t *w, Re
|
|||
|
||||
gpuvis_trace_printf( "pushing wait for commit %lu win %lx", newCommit->commitID, w->type == steamcompmgr_win_type_t::XWAYLAND ? w->xwayland().id : 0 );
|
||||
{
|
||||
std::unique_lock< std::mutex > lock( waitListLock );
|
||||
WaitListEntry_t entry
|
||||
{
|
||||
.doneCommits = doneCommits,
|
||||
.fence = fence,
|
||||
.mangoapp_nudge = mango_nudge,
|
||||
.commitID = newCommit->commitID,
|
||||
.desiredPresentTime = newCommit->desired_present_time,
|
||||
};
|
||||
waitList.push_back( entry );
|
||||
}
|
||||
newCommit->m_nCommitFence = fence;
|
||||
newCommit->m_bMangoNudge = mango_nudge;
|
||||
newCommit->pDoneCommits = doneCommits;
|
||||
|
||||
// Wake up commit wait thread if chilling
|
||||
waitListSem.signal();
|
||||
g_ImageWaiter.AddWaitable( newCommit.get(), EPOLLIN );
|
||||
}
|
||||
|
||||
w->commit_queue.push_back( std::move(newCommit) );
|
||||
}
|
||||
|
@ -7032,21 +6997,6 @@ dispatch_vblank( int fd )
|
|||
return vblank;
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_nudge( int fd )
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
static char buf[1024];
|
||||
if ( read( fd, buf, sizeof(buf) ) < 0 )
|
||||
{
|
||||
if ( errno != EAGAIN )
|
||||
xwm_log.errorf_errno(" steamcompmgr: dispatch_nudge: read failed" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct rgba_t
|
||||
{
|
||||
uint8_t r,g,b,a;
|
||||
|
@ -7738,9 +7688,6 @@ steamcompmgr_main(int argc, char **argv)
|
|||
spawn_client( &argv[ subCommandArg ] );
|
||||
}
|
||||
|
||||
std::thread imageWaitThread( imageWaitThreadMain );
|
||||
imageWaitThread.detach();
|
||||
|
||||
// EVENT_VBLANK
|
||||
pollfds.push_back(pollfd {
|
||||
.fd = vblankFD,
|
||||
|
|
216
src/waitable.h
Normal file
216
src/waitable.h
Normal file
|
@ -0,0 +1,216 @@
|
|||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <stdint.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
extern LogScope g_WaitableLog;
|
||||
|
||||
namespace gamescope
|
||||
{
|
||||
class IWaitable
|
||||
{
|
||||
public:
|
||||
virtual ~IWaitable() {}
|
||||
|
||||
virtual int GetFD() { return -1; }
|
||||
|
||||
virtual void OnPollIn() {}
|
||||
virtual void OnPollOut() {}
|
||||
virtual void OnPollHangUp() {}
|
||||
|
||||
void HandleEvents( uint32_t nEvents )
|
||||
{
|
||||
if ( nEvents & EPOLLIN )
|
||||
this->OnPollIn();
|
||||
if ( nEvents & EPOLLOUT )
|
||||
this->OnPollOut();
|
||||
if ( nEvents & EPOLLHUP )
|
||||
this->OnPollHangUp();
|
||||
}
|
||||
};
|
||||
|
||||
class CNudgeWaitable final : public IWaitable
|
||||
{
|
||||
public:
|
||||
CNudgeWaitable()
|
||||
{
|
||||
if ( pipe2( m_nFDs, O_CLOEXEC | O_NONBLOCK ) != 0 )
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
~CNudgeWaitable()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
for ( int i = 0; i < 2; i++ )
|
||||
{
|
||||
if ( m_nFDs[i] >= 0 )
|
||||
close( m_nFDs[i] );
|
||||
}
|
||||
}
|
||||
|
||||
void Drain()
|
||||
{
|
||||
if ( m_nFDs[0] < 0 )
|
||||
return;
|
||||
|
||||
char buf[1024];
|
||||
for (;;)
|
||||
{
|
||||
if ( read( m_nFDs[0], buf, sizeof( buf ) ) < 0 )
|
||||
{
|
||||
if ( errno != EAGAIN )
|
||||
g_WaitableLog.errorf_errno( "Failed to drain CNudgeWaitable" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Nudge()
|
||||
{
|
||||
return write( m_nFDs[1], "\n", 1 ) >= 0;
|
||||
}
|
||||
|
||||
int GetFD() final { return m_nFDs[0]; }
|
||||
private:
|
||||
int m_nFDs[2] = { -1, -1 };
|
||||
};
|
||||
|
||||
template <size_t MaxEvents = 1024>
|
||||
class CWaiter
|
||||
{
|
||||
public:
|
||||
CWaiter()
|
||||
: m_nEpollFD{ epoll_create1( 0 ) }
|
||||
{
|
||||
AddWaitable( &m_NudgeWaitable, EPOLLIN );
|
||||
}
|
||||
|
||||
~CWaiter()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
if ( !m_bRunning )
|
||||
return;
|
||||
|
||||
Nudge();
|
||||
m_bRunning = false;
|
||||
|
||||
if ( m_nEpollFD >= 0 )
|
||||
{
|
||||
close( m_nEpollFD );
|
||||
m_nEpollFD = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool AddWaitable( IWaitable *pWaitable, uint32_t nEvents = EPOLLIN | EPOLLOUT | EPOLLHUP )
|
||||
{
|
||||
epoll_event event =
|
||||
{
|
||||
.events = nEvents,
|
||||
.data =
|
||||
{
|
||||
.ptr = reinterpret_cast<void *>( pWaitable ),
|
||||
},
|
||||
};
|
||||
|
||||
if ( epoll_ctl( m_nEpollFD, EPOLL_CTL_ADD, pWaitable->GetFD(), &event ) != 0 )
|
||||
{
|
||||
g_WaitableLog.errorf_errno( "Failed to add waitable" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveWaitable( IWaitable *pWaitable )
|
||||
{
|
||||
epoll_ctl( m_nEpollFD, EPOLL_CTL_DEL, pWaitable->GetFD(), nullptr );
|
||||
}
|
||||
|
||||
void PollEvents()
|
||||
{
|
||||
epoll_event events[MaxEvents];
|
||||
|
||||
int nEventCount = epoll_wait( m_nEpollFD, events, MaxEvents, -1);
|
||||
|
||||
if ( !m_bRunning )
|
||||
return;
|
||||
|
||||
if ( nEventCount < 0 )
|
||||
{
|
||||
g_WaitableLog.errorf_errno( "Failed to epoll_wait in CAsyncWaiter" );
|
||||
return;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < nEventCount; i++ )
|
||||
{
|
||||
epoll_event &event = events[i];
|
||||
|
||||
IWaitable *pWaitable = reinterpret_cast<IWaitable *>(event.data.ptr);
|
||||
pWaitable->HandleEvents( event.events );
|
||||
}
|
||||
}
|
||||
|
||||
bool Nudge()
|
||||
{
|
||||
return m_NudgeWaitable.Nudge();
|
||||
}
|
||||
|
||||
bool IsRunning()
|
||||
{
|
||||
return m_bRunning;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<bool> m_bRunning = { true };
|
||||
CNudgeWaitable m_NudgeWaitable;
|
||||
|
||||
int m_nEpollFD = -1;
|
||||
};
|
||||
|
||||
template <size_t MaxEvents = 1024>
|
||||
class CAsyncWaiter : public CWaiter<MaxEvents>
|
||||
{
|
||||
public:
|
||||
CAsyncWaiter( const char *pszThreadName )
|
||||
: m_Thread{ [cWaiter = this, cName = pszThreadName](){ cWaiter->WaiterThreadFunc(cName); } }
|
||||
{
|
||||
}
|
||||
|
||||
~CAsyncWaiter()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
CWaiter<MaxEvents>::Shutdown();
|
||||
|
||||
if ( m_Thread.joinable() )
|
||||
m_Thread.join();
|
||||
}
|
||||
|
||||
void WaiterThreadFunc( const char *pszThreadName )
|
||||
{
|
||||
pthread_setname_np( pthread_self(), pszThreadName );
|
||||
|
||||
while ( this->IsRunning() )
|
||||
CWaiter<MaxEvents>::PollEvents();
|
||||
}
|
||||
private:
|
||||
std::thread m_Thread;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in a new issue