source-engine/engine/LoadScreenUpdate.cpp

240 lines
7.6 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: To accomplish X360 TCR 22, we need to call present ever 66msec
// at least during loading screens. This amazing hack will do it
// by overriding the allocator to tick it every so often.
//
// $NoKeywords: $
//===========================================================================//
#include "LoadScreenUpdate.h"
#include "tier0/memalloc.h"
#include "tier1/delegates.h"
#include "tier0/threadtools.h"
#include "tier2/tier2.h"
#include "materialsystem/imaterialsystem.h"
#include "tier0/dbg.h"
#ifdef _X360
#define LOADING_UPDATE_INTERVAL 0.015f
#define UNINITIALIZED_LAST_TIME -1000.0f
//-----------------------------------------------------------------------------
// Used to tick the loading screen every so often
//-----------------------------------------------------------------------------
class CLoaderMemAlloc : public IMemAlloc
{
// Methods of IMemAlloc
public:
virtual void *Alloc( size_t nSize );
virtual void *Realloc( void *pMem, size_t nSize );
virtual void Free( void *pMem );
DELEGATE_TO_OBJECT_2( void *, Expand_NoLongerSupported, void *, size_t, m_pMemAlloc );
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
virtual void Free( void *pMem, const char *pFileName, int nLine );
DELEGATE_TO_OBJECT_4( void*, Expand_NoLongerSupported, void *, size_t, const char *, int, m_pMemAlloc );
DELEGATE_TO_OBJECT_1( size_t, GetSize, void *, m_pMemAlloc );
DELEGATE_TO_OBJECT_2V( PushAllocDbgInfo, const char *, int, m_pMemAlloc );
DELEGATE_TO_OBJECT_0V( PopAllocDbgInfo, m_pMemAlloc );
DELEGATE_TO_OBJECT_1( long, CrtSetBreakAlloc, long, m_pMemAlloc );
DELEGATE_TO_OBJECT_2( int, CrtSetReportMode, int, int, m_pMemAlloc );
DELEGATE_TO_OBJECT_1( int, CrtIsValidHeapPointer, const void *, m_pMemAlloc );
DELEGATE_TO_OBJECT_3( int, CrtIsValidPointer, const void *, unsigned int, int, m_pMemAlloc );
DELEGATE_TO_OBJECT_0( int, CrtCheckMemory, m_pMemAlloc );
DELEGATE_TO_OBJECT_1( int, CrtSetDbgFlag, int, m_pMemAlloc );
DELEGATE_TO_OBJECT_1V( CrtMemCheckpoint, _CrtMemState *, m_pMemAlloc );
DELEGATE_TO_OBJECT_0V( DumpStats, m_pMemAlloc );
DELEGATE_TO_OBJECT_1V( DumpStatsFileBase, const char *, m_pMemAlloc );
DELEGATE_TO_OBJECT_2( void*, CrtSetReportFile, int, void*, m_pMemAlloc );
DELEGATE_TO_OBJECT_1( void*, CrtSetReportHook, void*, m_pMemAlloc );
DELEGATE_TO_OBJECT_5( int, CrtDbgReport, int, const char *, int, const char *, const char *, m_pMemAlloc );
DELEGATE_TO_OBJECT_0( int, heapchk, m_pMemAlloc );
DELEGATE_TO_OBJECT_0( bool, IsDebugHeap, m_pMemAlloc );
DELEGATE_TO_OBJECT_2V( GetActualDbgInfo, const char *&, int &, m_pMemAlloc );
DELEGATE_TO_OBJECT_5V( RegisterAllocation, const char *, int, size_t, size_t, unsigned, m_pMemAlloc );
DELEGATE_TO_OBJECT_5V( RegisterDeallocation, const char *, int, size_t, size_t, unsigned, m_pMemAlloc );
DELEGATE_TO_OBJECT_0( int, GetVersion, m_pMemAlloc );
DELEGATE_TO_OBJECT_0V( CompactHeap, m_pMemAlloc );
DELEGATE_TO_OBJECT_1( MemAllocFailHandler_t, SetAllocFailHandler, MemAllocFailHandler_t, m_pMemAlloc );
DELEGATE_TO_OBJECT_1V( DumpBlockStats, void *, m_pMemAlloc );
#if defined( _MEMTEST )
DELEGATE_TO_OBJECT_2V( SetStatsExtraInfo, const char *, const char *, m_pMemAlloc );
#endif
DELEGATE_TO_OBJECT_0(size_t, MemoryAllocFailed, m_pMemAlloc );
virtual uint32 GetDebugInfoSize() { return 0; }
virtual void SaveDebugInfo( void *pvDebugInfo ) { }
virtual void RestoreDebugInfo( const void *pvDebugInfo ) {}
virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine ) {}
// Other public methods
public:
CLoaderMemAlloc();
void Start( MaterialNonInteractiveMode_t mode );
void Stop();
// Check if we need to call swap. Do so if necessary.
void CheckSwap( );
private:
IMemAlloc *m_pMemAlloc;
float m_flLastUpdateTime;
bool m_bInSwap;
};
//-----------------------------------------------------------------------------
// Activate, deactivate loadermemalloc
//-----------------------------------------------------------------------------
static CLoaderMemAlloc s_LoaderMemAlloc;
void BeginLoadingUpdates( MaterialNonInteractiveMode_t mode )
{
if ( IsX360() )
{
s_LoaderMemAlloc.Start( mode );
}
}
void RefreshScreenIfNecessary()
{
if ( IsX360() )
{
s_LoaderMemAlloc.CheckSwap();
}
}
void EndLoadingUpdates()
{
if ( IsX360() )
{
s_LoaderMemAlloc.Stop();
}
}
static uintp LoadLibraryThreadFunc(void *pParam)
{
RefreshScreenIfNecessary();
return 15;
}
//-----------------------------------------------------------------------------
// Used to tick the loading screen every so often
//-----------------------------------------------------------------------------
CLoaderMemAlloc::CLoaderMemAlloc()
{
m_pMemAlloc = 0;
}
void CLoaderMemAlloc::Start( MaterialNonInteractiveMode_t mode )
{
if ( m_pMemAlloc || ( mode == MATERIAL_NON_INTERACTIVE_MODE_NONE ) )
return;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->EnableNonInteractiveMode( mode );
if ( mode == MATERIAL_NON_INTERACTIVE_MODE_STARTUP )
{
SetThreadedLoadLibraryFunc( LoadLibraryThreadFunc );
}
// NOTE: This is necessary to avoid a one-frame black flash
// since Present is what copies the back buffer into the temp buffer
if ( mode == MATERIAL_NON_INTERACTIVE_MODE_LEVEL_LOAD )
{
extern void V_RenderVGuiOnly( void );
V_RenderVGuiOnly();
}
m_flLastUpdateTime = UNINITIALIZED_LAST_TIME;
m_bInSwap = false;
m_pMemAlloc = g_pMemAlloc;
g_pMemAlloc = this;
}
void CLoaderMemAlloc::Stop()
{
if ( !m_pMemAlloc )
return;
g_pMemAlloc = m_pMemAlloc;
m_pMemAlloc = 0;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->EnableNonInteractiveMode( MATERIAL_NON_INTERACTIVE_MODE_NONE );
SetThreadedLoadLibraryFunc( NULL );
}
//-----------------------------------------------------------------------------
// Check if we need to call swap. Do so if necessary.
//-----------------------------------------------------------------------------
void CLoaderMemAlloc::CheckSwap( )
{
if ( !m_pMemAlloc )
return;
float t = Plat_FloatTime();
float dt = t - m_flLastUpdateTime;
if ( dt >= LOADING_UPDATE_INTERVAL )
{
if ( ThreadInMainThread() && !m_bInSwap && !g_pMaterialSystem->IsInFrame() )
{
m_bInSwap = true;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
pRenderContext->RefreshFrontBufferNonInteractive();
m_bInSwap = false;
// NOTE: It is necessary to re-read time, since Refresh
// may block, and if it does, it'll force a refresh every allocation
// if we don't resample time after the block
m_flLastUpdateTime = Plat_FloatTime();
}
}
}
//-----------------------------------------------------------------------------
// Hook allocations, render when appropriate
//-----------------------------------------------------------------------------
void *CLoaderMemAlloc::Alloc( size_t nSize )
{
CheckSwap();
return m_pMemAlloc->Alloc( nSize );
}
void *CLoaderMemAlloc::Realloc( void *pMem, size_t nSize )
{
CheckSwap();
return m_pMemAlloc->Realloc( pMem, nSize );
}
void CLoaderMemAlloc::Free( void *pMem )
{
CheckSwap();
m_pMemAlloc->Free( pMem );
}
void *CLoaderMemAlloc::Alloc( size_t nSize, const char *pFileName, int nLine )
{
CheckSwap();
return m_pMemAlloc->Alloc( nSize, pFileName, nLine );
}
void *CLoaderMemAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine )
{
CheckSwap();
return m_pMemAlloc->Realloc( pMem, nSize, pFileName, nLine );
}
void CLoaderMemAlloc::Free( void *pMem, const char *pFileName, int nLine )
{
CheckSwap();
m_pMemAlloc->Free( pMem, pFileName, nLine );
}
#endif // _X360