mirror of
https://invent.kde.org/graphics/okular
synced 2024-10-05 23:49:20 +00:00
Improve the evicting algorithm
The miss counter was taken away. Now the algorithm relies on the distance between the tiles and the viewport. Also the visibleRect() and setVisibleRect() methods were removed from TilesManager since we now pass this information to TilesManager::cleanupPixmapMemory()
This commit is contained in:
parent
c13ad1afc3
commit
da54ffdd8e
|
@ -227,11 +227,21 @@ void DocumentPrivate::cleanupPixmapMemory( qulonglong memoryToFree )
|
|||
{
|
||||
if ( memoryToFree > 0 )
|
||||
{
|
||||
// Number of one of the visible pages (to be used by tiles cleanup)
|
||||
int visiblePageNumber = -1;
|
||||
QMap< int, VisiblePageRect * > visibleRects;
|
||||
QVector< Okular::VisiblePageRect * >::const_iterator vIt = m_pageRects.constBegin(), vEnd = m_pageRects.constEnd();
|
||||
for ( ; vIt != vEnd; ++vIt )
|
||||
{
|
||||
visiblePageNumber = (*vIt)->pageNumber;
|
||||
visibleRects.insert( (*vIt)->pageNumber, (*vIt) );
|
||||
}
|
||||
|
||||
// Free memory starting from pages that are farthest from the current one
|
||||
int pagesFreed = 0;
|
||||
while ( memoryToFree > 0 )
|
||||
{
|
||||
AllocatedPixmap * p = searchLowestPriorityUnloadablePixmap( true );
|
||||
AllocatedPixmap * p = searchLowestPriorityPixmap( true, true );
|
||||
if ( !p ) // No pixmap to remove
|
||||
break;
|
||||
|
||||
|
@ -254,34 +264,51 @@ void DocumentPrivate::cleanupPixmapMemory( qulonglong memoryToFree )
|
|||
|
||||
// If we're still on low memory, try to free individual tiles based on
|
||||
// a ranking algorithm
|
||||
QLinkedList< AllocatedPixmap * >::iterator pIt = m_allocatedPixmaps.begin();
|
||||
QLinkedList< AllocatedPixmap * >::iterator pEnd = m_allocatedPixmaps.end();
|
||||
while ( pIt != pEnd && memoryToFree > 0 )
|
||||
|
||||
// Store pages that weren't completely removed
|
||||
QLinkedList< AllocatedPixmap * > pixmapsToKeep;
|
||||
while ( memoryToFree > 0 )
|
||||
{
|
||||
AllocatedPixmap * p = *pIt;
|
||||
AllocatedPixmap * p = searchLowestPriorityPixmap( false, true );
|
||||
if ( !p ) // No pixmap to remove
|
||||
break;
|
||||
|
||||
TilesManager *tilesManager = m_pagesVector.at( p->page )->tilesManager( p->id );
|
||||
if ( tilesManager && tilesManager->totalMemory() > 0 )
|
||||
{
|
||||
tilesManager->cleanupPixmapMemory( memoryToFree );
|
||||
m_allocatedPixmapsTotalMemory -= p->memory;
|
||||
int memoryDiff = -p->memory;
|
||||
p->memory = tilesManager->totalMemory();
|
||||
memoryDiff += p->memory;
|
||||
memoryToFree = qMax( 0, memoryDiff );
|
||||
m_allocatedPixmapsTotalMemory += p->memory;
|
||||
}
|
||||
int memoryDiff = p->memory;
|
||||
NormalizedRect currentViewport;
|
||||
if ( visibleRects.contains( p->page ) )
|
||||
currentViewport = visibleRects[ p->page ]->rect;
|
||||
|
||||
++pIt;
|
||||
// Cleanup non visible tiles based on its dirty state and distance from the viewport
|
||||
tilesManager->cleanupPixmapMemory( memoryToFree, currentViewport, visiblePageNumber );
|
||||
|
||||
p->memory = tilesManager->totalMemory();
|
||||
memoryDiff -= p->memory;
|
||||
memoryToFree = memoryToFree - memoryDiff;
|
||||
m_allocatedPixmapsTotalMemory -= memoryDiff;
|
||||
|
||||
if ( p->memory > 0 )
|
||||
pixmapsToKeep.append( p );
|
||||
else
|
||||
delete p;
|
||||
}
|
||||
else
|
||||
pixmapsToKeep.append( p );
|
||||
}
|
||||
|
||||
m_allocatedPixmaps += pixmapsToKeep;
|
||||
//p--rintf("freeMemory A:[%d -%d = %d] \n", m_allocatedPixmaps.count() + pagesFreed, pagesFreed, m_allocatedPixmaps.count() );
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the next pixmap to evict from cache, or NULL if no suitable pixmap
|
||||
* is found. If thenRemoveIt is set, the pixmap is removed from
|
||||
* m_allocatedPixmaps before returning it */
|
||||
AllocatedPixmap * DocumentPrivate::searchLowestPriorityUnloadablePixmap( bool thenRemoveIt )
|
||||
* if found. If unloadableOnly is set, only unloadable pixmaps are returned. If
|
||||
* thenRemoveIt is set, the pixmap is removed from m_allocatedPixmaps before
|
||||
* returning it
|
||||
*/
|
||||
AllocatedPixmap * DocumentPrivate::searchLowestPriorityPixmap( bool unloadableOnly, bool thenRemoveIt )
|
||||
{
|
||||
QLinkedList< AllocatedPixmap * >::iterator pIt = m_allocatedPixmaps.begin();
|
||||
QLinkedList< AllocatedPixmap * >::iterator pEnd = m_allocatedPixmaps.end();
|
||||
|
@ -294,7 +321,7 @@ AllocatedPixmap * DocumentPrivate::searchLowestPriorityUnloadablePixmap( bool th
|
|||
{
|
||||
const AllocatedPixmap * p = *pIt;
|
||||
const int distance = qAbs( p->page - currentViewportPage );
|
||||
if ( maxDistance < distance && m_observers.value( p->id )->canUnloadPixmap( p->page ) )
|
||||
if ( maxDistance < distance && ( !unloadableOnly || m_observers.value( p->id )->canUnloadPixmap( p->page ) ) )
|
||||
{
|
||||
maxDistance = distance;
|
||||
farthestPixmap = pIt;
|
||||
|
@ -1007,7 +1034,7 @@ void DocumentPrivate::sendGeneratorRequest()
|
|||
int maxDistance = INT_MAX; // Default: No maximum
|
||||
if ( memoryToFree )
|
||||
{
|
||||
AllocatedPixmap *pixmapToReplace = searchLowestPriorityUnloadablePixmap();
|
||||
AllocatedPixmap *pixmapToReplace = searchLowestPriorityPixmap( true );
|
||||
if ( pixmapToReplace )
|
||||
maxDistance = qAbs( pixmapToReplace->page - currentViewportPage );
|
||||
}
|
||||
|
@ -1056,7 +1083,7 @@ void DocumentPrivate::sendGeneratorRequest()
|
|||
const QPixmap *pixmap = r->page()->_o_nearestPixmap( r->id(), r->width(), r->height() );
|
||||
if ( pixmap )
|
||||
{
|
||||
tilesManager = new TilesManager( pixmap->width(), pixmap->height(), r->page()->rotation() );
|
||||
tilesManager = new TilesManager( r->pageNumber(), pixmap->width(), pixmap->height(), r->page()->rotation() );
|
||||
tilesManager->setPixmap( pixmap, NormalizedRect( 0, 0, 1, 1 ) );
|
||||
tilesManager->setWidth( r->width() );
|
||||
tilesManager->setHeight( r->height() );
|
||||
|
@ -1064,7 +1091,7 @@ void DocumentPrivate::sendGeneratorRequest()
|
|||
else
|
||||
{
|
||||
// create new tiles manager
|
||||
tilesManager = new TilesManager( r->width(), r->height(), r->page()->rotation() );
|
||||
tilesManager = new TilesManager( r->pageNumber(), r->width(), r->height(), r->page()->rotation() );
|
||||
}
|
||||
tilesManager->setRequest( r->normalizedRect(), r->width(), r->height() );
|
||||
r->page()->deletePixmap( r->id() );
|
||||
|
@ -1274,7 +1301,19 @@ void DocumentPrivate::refreshPixmaps( int pageNumber )
|
|||
PixmapRequest * p = new PixmapRequest( tmIt.key(), pageNumber, tilesManager->width(), tilesManager->height(), 1, true );
|
||||
|
||||
NormalizedRect tilesRect;
|
||||
QList<Tile> tiles = tilesManager->tilesAt( tilesManager->visibleRect() );
|
||||
|
||||
// Get the visible page rect
|
||||
NormalizedRect visibleRect;
|
||||
QVector< Okular::VisiblePageRect * >::const_iterator vIt = m_pageRects.constBegin(), vEnd = m_pageRects.constEnd();
|
||||
for ( ; vIt != vEnd; ++vIt )
|
||||
{
|
||||
if ( (*vIt)->pageNumber == pageNumber )
|
||||
{
|
||||
visibleRect = (*vIt)->rect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QList<Tile> tiles = tilesManager->tilesAt( visibleRect );
|
||||
QList<Tile>::const_iterator tIt = tiles.constBegin(), tEnd = tiles.constEnd();
|
||||
while ( tIt != tEnd )
|
||||
{
|
||||
|
|
|
@ -98,7 +98,7 @@ class DocumentPrivate
|
|||
qulonglong calculateMemoryToFree();
|
||||
void cleanupPixmapMemory();
|
||||
void cleanupPixmapMemory( qulonglong memoryToFree );
|
||||
AllocatedPixmap * searchLowestPriorityUnloadablePixmap( bool thenRemoveIt = false );
|
||||
AllocatedPixmap * searchLowestPriorityPixmap( bool unloadableOnly = false, bool thenRemoveIt = false );
|
||||
void calculateMaxTextPages();
|
||||
qulonglong getTotalMemory();
|
||||
qulonglong getFreeMemory( qulonglong *freeSwap = 0 );
|
||||
|
|
|
@ -12,18 +12,15 @@
|
|||
#include <QtCore/qmath.h>
|
||||
#include <QList>
|
||||
|
||||
#define MISSCOUNTER_MAX INT_MAX/2
|
||||
#define MISSCOUNTER_MIN INT_MIN/2
|
||||
#define TILES_MAXSIZE 2000000
|
||||
|
||||
using namespace Okular;
|
||||
|
||||
static bool rankedTilesLessThan( Tile *t1, Tile *t2 )
|
||||
{
|
||||
// Order tiles by its dirty state and miss counter. That is: dirty tiles
|
||||
// will be evicted first then tiles with higher miss values.
|
||||
// Order tiles by its dirty state and then by distance from the viewport.
|
||||
if ( t1->dirty == t2->dirty )
|
||||
return t1->miss < t2->miss;
|
||||
return t1->distance < t2->distance;
|
||||
|
||||
return !t1->dirty;
|
||||
}
|
||||
|
@ -48,7 +45,7 @@ class TilesManager::Private
|
|||
void deleteTiles( const Tile &tile );
|
||||
|
||||
void markParentDirty( const Tile &tile );
|
||||
void rankTiles( Tile &tile, QList<Tile*> &rankedTiles );
|
||||
void rankTiles( Tile &tile, QList<Tile*> &rankedTiles, const NormalizedRect &visibleRect, int visiblePageNumber );
|
||||
/**
|
||||
* Since the tile can be large enough to occupy a significant amount of
|
||||
* space, they may be split in more tiles. This operation is performed
|
||||
|
@ -69,6 +66,7 @@ class TilesManager::Private
|
|||
Tile tiles[16];
|
||||
int width;
|
||||
int height;
|
||||
int pageNumber;
|
||||
long totalPixels;
|
||||
Rotation rotation;
|
||||
NormalizedRect visibleRect;
|
||||
|
@ -80,6 +78,7 @@ class TilesManager::Private
|
|||
TilesManager::Private::Private()
|
||||
: width( 0 )
|
||||
, height( 0 )
|
||||
, pageNumber( 0 )
|
||||
, totalPixels( 0 )
|
||||
, rotation( Rotation0 )
|
||||
, requestRect( NormalizedRect() )
|
||||
|
@ -88,9 +87,10 @@ TilesManager::Private::Private()
|
|||
{
|
||||
}
|
||||
|
||||
TilesManager::TilesManager( int width, int height, Rotation rotation )
|
||||
TilesManager::TilesManager( int pageNumber, int width, int height, Rotation rotation )
|
||||
: d( new Private )
|
||||
{
|
||||
d->pageNumber = pageNumber;
|
||||
d->width = width;
|
||||
d->height = height;
|
||||
d->rotation = rotation;
|
||||
|
@ -191,19 +191,6 @@ void TilesManager::Private::markDirty( Tile &tile )
|
|||
}
|
||||
}
|
||||
|
||||
void TilesManager::setVisibleRect( const NormalizedRect &rect )
|
||||
{
|
||||
if ( d->visibleRect == rect )
|
||||
return;
|
||||
|
||||
d->visibleRect = rect;
|
||||
}
|
||||
|
||||
NormalizedRect TilesManager::visibleRect() const
|
||||
{
|
||||
return d->visibleRect;
|
||||
}
|
||||
|
||||
void TilesManager::setPixmap( const QPixmap *pixmap, const NormalizedRect &rect )
|
||||
{
|
||||
if ( !d->requestRect.isNull() )
|
||||
|
@ -368,10 +355,7 @@ QList<Tile> TilesManager::tilesAt( const NormalizedRect &rect, bool allowEmpty )
|
|||
void TilesManager::Private::tilesAt( const NormalizedRect &rect, Tile &tile, QList<Tile> &result, bool allowEmpty )
|
||||
{
|
||||
if ( !tile.rect.intersects( rect ) )
|
||||
{
|
||||
tile.miss = qMin( tile.miss+1, MISSCOUNTER_MAX );
|
||||
return;
|
||||
}
|
||||
|
||||
// split big tiles before the requests are made, otherwise we would end up
|
||||
// requesting huge areas unnecessarily
|
||||
|
@ -379,7 +363,6 @@ void TilesManager::Private::tilesAt( const NormalizedRect &rect, Tile &tile, QLi
|
|||
|
||||
if ( ( allowEmpty && tile.nTiles == 0 ) || ( !allowEmpty && tile.pixmap ) )
|
||||
{
|
||||
tile.miss = qMax( tile.miss-1, MISSCOUNTER_MIN );
|
||||
Tile newTile = tile;
|
||||
if ( rotation != Rotation0 )
|
||||
newTile.rect = TilesManager::toRotatedRect( tile.rect, rotation );
|
||||
|
@ -397,12 +380,12 @@ long TilesManager::totalMemory() const
|
|||
return 4*d->totalPixels;
|
||||
}
|
||||
|
||||
void TilesManager::cleanupPixmapMemory( qulonglong numberOfBytes )
|
||||
void TilesManager::cleanupPixmapMemory( qulonglong numberOfBytes, const NormalizedRect &visibleRect, int visiblePageNumber )
|
||||
{
|
||||
QList<Tile*> rankedTiles;
|
||||
for ( int i = 0; i < 16; ++i )
|
||||
{
|
||||
d->rankTiles( d->tiles[ i ], rankedTiles );
|
||||
d->rankTiles( d->tiles[ i ], rankedTiles, visibleRect, visiblePageNumber );
|
||||
}
|
||||
qSort( rankedTiles.begin(), rankedTiles.end(), rankedTilesLessThan );
|
||||
|
||||
|
@ -413,7 +396,7 @@ void TilesManager::cleanupPixmapMemory( qulonglong numberOfBytes )
|
|||
continue;
|
||||
|
||||
// do not evict visible pixmaps
|
||||
if ( tile->rect.intersects( d->visibleRect ) )
|
||||
if ( tile->rect.intersects( visibleRect ) )
|
||||
continue;
|
||||
|
||||
qulonglong pixels = tile->pixmap->width()*tile->pixmap->height();
|
||||
|
@ -423,7 +406,6 @@ void TilesManager::cleanupPixmapMemory( qulonglong numberOfBytes )
|
|||
else
|
||||
numberOfBytes -= 4*pixels;
|
||||
|
||||
tile->miss = 0;
|
||||
delete tile->pixmap;
|
||||
tile->pixmap = 0;
|
||||
|
||||
|
@ -443,24 +425,43 @@ void TilesManager::Private::markParentDirty( const Tile &tile )
|
|||
}
|
||||
}
|
||||
|
||||
void TilesManager::Private::rankTiles( Tile &tile, QList<Tile*> &rankedTiles )
|
||||
void TilesManager::Private::rankTiles( Tile &tile, QList<Tile*> &rankedTiles, const NormalizedRect &visibleRect, int visiblePageNumber )
|
||||
{
|
||||
if ( tile.parent )
|
||||
tile.miss = qBound( MISSCOUNTER_MIN, tile.miss + tile.parent->miss, MISSCOUNTER_MAX );
|
||||
// If the page is visible, visibleRect is not null.
|
||||
// Otherwise we use the number of one of the visible pages to calculate the
|
||||
// distance.
|
||||
// Note that the current page may be visible and yet its pageNumber is
|
||||
// different from visiblePageNumber. Since we only use this value on hidden
|
||||
// pages, any visible page number will fit.
|
||||
if ( visibleRect.isNull() && visiblePageNumber < 0 )
|
||||
return;
|
||||
|
||||
if ( tile.pixmap )
|
||||
{
|
||||
// Update distance
|
||||
if ( !visibleRect.isNull() )
|
||||
{
|
||||
NormalizedPoint viewportCenter = visibleRect.center();
|
||||
NormalizedPoint tileCenter = tile.rect.center();
|
||||
// Manhattan distance. It's a good and fast approximation.
|
||||
tile.distance = qAbs(viewportCenter.x - tileCenter.x) + qAbs(viewportCenter.y + tileCenter.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For non visible pages only the vertical distance is used
|
||||
if ( pageNumber < visiblePageNumber )
|
||||
tile.distance = 1 - tile.rect.bottom;
|
||||
else
|
||||
tile.distance = tile.rect.top;
|
||||
}
|
||||
rankedTiles.append( &tile );
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0; i < tile.nTiles; ++i )
|
||||
{
|
||||
rankTiles( tile.tiles[ i ], rankedTiles );
|
||||
rankTiles( tile.tiles[ i ], rankedTiles, visibleRect, visiblePageNumber );
|
||||
}
|
||||
|
||||
if ( tile.nTiles > 0 )
|
||||
tile.miss = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -564,10 +565,10 @@ NormalizedRect TilesManager::toRotatedRect( const NormalizedRect &rect, Rotation
|
|||
Tile::Tile()
|
||||
: pixmap( 0 )
|
||||
, dirty ( true )
|
||||
, distance( -1 )
|
||||
, tiles( 0 )
|
||||
, nTiles( 0 )
|
||||
, parent( 0 )
|
||||
, miss( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,11 @@ class OKULAR_EXPORT Tile
|
|||
* (dirty = false), the parent tile is also considered updated.
|
||||
*/
|
||||
bool dirty;
|
||||
/**
|
||||
* Distance between the tile and the viewport.
|
||||
* This is used by the evicting algorithm.
|
||||
*/
|
||||
double distance;
|
||||
|
||||
/**
|
||||
* Children tiles
|
||||
|
@ -67,14 +72,6 @@ class OKULAR_EXPORT Tile
|
|||
int nTiles;
|
||||
|
||||
Tile *parent;
|
||||
|
||||
/**
|
||||
* Hit/miss counter.
|
||||
* Increased whenever the tile is not referenced. Decreased whenever a
|
||||
* reference to the tile is made
|
||||
* Used by the ranking algorithm.
|
||||
*/
|
||||
int miss;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -93,7 +90,7 @@ class OKULAR_EXPORT Tile
|
|||
class OKULAR_EXPORT TilesManager
|
||||
{
|
||||
public:
|
||||
TilesManager( int width, int height, Rotation rotation = Rotation0 );
|
||||
TilesManager( int pageNumber, int width, int height, Rotation rotation = Rotation0 );
|
||||
virtual ~TilesManager();
|
||||
|
||||
/**
|
||||
|
@ -119,7 +116,6 @@ class OKULAR_EXPORT TilesManager
|
|||
*
|
||||
* As to avoid requests of big areas, each traversed tile is checked
|
||||
* for its size and split if necessary.
|
||||
* Also the miss counter is updated for the use in the evicting algorithm.
|
||||
*
|
||||
* @param allowEmpty If false only tiles with a non null pixmap are returned
|
||||
*/
|
||||
|
@ -133,9 +129,11 @@ class OKULAR_EXPORT TilesManager
|
|||
/**
|
||||
* Removes at least @p numberOfBytes bytes worth of tiles (least ranked
|
||||
* tiles are removed first).
|
||||
* Set @p visibleRect to the visible region of the page. Set a
|
||||
* @p visiblePageNumber if the current page is not visible.
|
||||
* Visible tiles are not discarded.
|
||||
*/
|
||||
void cleanupPixmapMemory( qulonglong numberOfBytes = 1 );
|
||||
void cleanupPixmapMemory( qulonglong numberOfBytes, const NormalizedRect &visibleRect, int visiblePageNumber );
|
||||
|
||||
/**
|
||||
* Checks whether a given region has already been requested
|
||||
|
@ -176,17 +174,6 @@ class OKULAR_EXPORT TilesManager
|
|||
*/
|
||||
void markDirty();
|
||||
|
||||
/**
|
||||
* Sets the visible area of the page so tiles in this area will not be
|
||||
* removed in the evicting process.
|
||||
*/
|
||||
void setVisibleRect( const NormalizedRect &rect );
|
||||
|
||||
/**
|
||||
* Returns the visible area of the page
|
||||
*/
|
||||
NormalizedRect visibleRect() const;
|
||||
|
||||
/**
|
||||
* Returns a rotated NormalizedRect given a @p rotation
|
||||
*/
|
||||
|
|
|
@ -4007,9 +4007,6 @@ void PageView::slotRequestVisiblePixmaps( int newValue )
|
|||
QRect intersectionRect = viewportRect.intersect( i->croppedGeometry() );
|
||||
if ( intersectionRect.isEmpty() )
|
||||
{
|
||||
Okular::TilesManager *tilesManager = i->page()->tilesManager( PAGEVIEW_ID );
|
||||
if ( tilesManager )
|
||||
tilesManager->setVisibleRect( Okular::NormalizedRect() );
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -4023,9 +4020,6 @@ void PageView::slotRequestVisiblePixmaps( int newValue )
|
|||
#endif
|
||||
|
||||
Okular::TilesManager *tilesManager = i->page()->tilesManager( PAGEVIEW_ID );
|
||||
if ( tilesManager )
|
||||
tilesManager->setVisibleRect( vItem->rect );
|
||||
|
||||
Okular::NormalizedRect expandedVisibleRect = vItem->rect;
|
||||
if ( tilesManager && Okular::Settings::memoryLevel() != Okular::Settings::EnumMemoryLevel::Low )
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue