Commit 292c6ff5 authored by Bart Coppens's avatar Bart Coppens
Browse files

Port the newer tiles swap code from 1.6 to trunk (the one without madvise and...

Port the newer tiles swap code from 1.6 to trunk (the one without madvise and lesser crashing on huge images), and fixed a crash (should look into why that works on 1.6).
On another happy note: this is my first KDE4-Krita2 commit \o/ :-)

svn path=/trunk/koffice/; revision=544688
parent b546b0cf
......@@ -153,13 +153,13 @@ public:
/**
* Get a read-only pointer to the specified pixel.
*/
inline const quint8* pixel(qint32 x, qint32 y)
inline const quint8* pixel(qint32 x, qint32 y) KDE_DEPRECATED
{ return ACTUAL_DATAMGR::pixel(x, y); }
/**
* Get a read-write pointer to the specified pixel.
*/
inline quint8* writablePixel(qint32 x, qint32 y)
inline quint8* writablePixel(qint32 x, qint32 y) KDE_DEPRECATED
{ return ACTUAL_DATAMGR::writablePixel(x, y); }
/**
......
......@@ -646,8 +646,11 @@ void KisPaintDevice::convertTo(KisColorSpace * dstColorSpace, qint32 renderingIn
qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
columns = qMin(columns, columnsRemaining);
const quint8 *srcData = pixel(column, row);
quint8 *dstData = dst.writablePixel(column, row);
KisHLineIteratorPixel srcIt = createHLineIterator(column, row, columns, false);
KisHLineIteratorPixel dstIt = dst.createHLineIterator(column, row, columns, true);
const quint8 *srcData = srcIt.rawData();
quint8 *dstData = dstIt.rawData();
m_colorSpace->convertPixelsTo(srcData, dstData, dstColorSpace, columns, renderingIntent);
......@@ -1049,7 +1052,8 @@ bool KisPaintDevice::pixel(qint32 x, qint32 y, KisColor * kc)
KisColor KisPaintDevice::colorAt(qint32 x, qint32 y)
{
return KisColor(m_datamanager->pixel(x - m_x, y - m_y), m_colorSpace);
KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true);
return KisColor(iter.rawData(), m_colorSpace);
}
bool KisPaintDevice::setPixel(qint32 x, qint32 y, const QColor& c, quint8 opacity)
......
......@@ -234,12 +234,12 @@ public:
/**
* Get a read-only pointer to pixel (x, y).
*/
const quint8* pixel(qint32 x, qint32 y);
const quint8* pixel(qint32 x, qint32 y) KDE_DEPRECATED;
/**
* Get a read-write pointer to pixel (x, y).
*/
quint8* writablePixel(qint32 x, qint32 y);
quint8* writablePixel(qint32 x, qint32 y) KDE_DEPRECATED;
/**
* Converts the paint device to a different colorspace
......
......@@ -202,12 +202,13 @@ void KisPainter::bitBlt(qint32 dx, qint32 dy,
qint32 columns = qMin(numContiguousDstColumns, numContiguousSrcColumns);
columns = qMin(columns, columnsRemaining);
const quint8 *srcData = srcdev->pixel(srcX, srcY);
qint32 srcRowStride = srcdev->rowStride(srcX, srcY);
KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
const quint8 *srcData = srcIt.rawData();
quint8 *dstData = m_device->writablePixel(dstX, dstY);
qint32 dstRowStride = m_device->rowStride(dstX, dstY);
KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
quint8 *dstData = dstIt.rawData();
m_colorSpace->bitBlt(dstData,
dstRowStride,
......@@ -307,14 +308,17 @@ void KisPainter::bltSelection(qint32 dx, qint32 dy,
columns = qMin(numContiguousSelColumns, columns);
columns = qMin(columns, columnsRemaining);
quint8 *dstData = m_device->writablePixel(dstX, dstY);
qint32 dstRowStride = m_device->rowStride(dstX, dstY);
KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
quint8 *dstData = dstIt.rawData();
const quint8 *srcData = srcdev->pixel(srcX, srcY);
qint32 srcRowStride = srcdev->rowStride(srcX, srcY);
KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
const quint8 *srcData = srcIt.rawData();
const quint8 *selData = seldev->pixel(dstX, dstY);
qint32 selRowStride = seldev->rowStride(dstX, dstY);
KisHLineIteratorPixel selIt = seldev->createHLineIterator(dstX, dstY, columns, false);
const quint8 *selData = selIt.rawData();
m_colorSpace->bitBlt(dstData,
dstRowStride,
......
......@@ -53,6 +53,11 @@ KisTile::KisTile(const KisTile& rhs, qint32 col, qint32 row)
allocate();
// If we ensure it's loaded, we won't need to modify the tiles with addReader and so,
// which modify the constness much more explicitly than this one, that only does it
// implicitly (that is, it has a non-const copy of the pointer tile somewhere)
KisTileManager::instance()->ensureTileLoaded(&rhs);
assert(rhs.m_data);
if (rhs.m_data) {
memcpy(m_data, rhs.m_data, WIDTH * HEIGHT * m_pixelSize * sizeof(quint8));
}
......@@ -76,6 +81,9 @@ KisTile::KisTile(const KisTile& rhs)
allocate();
// Ditto as above
KisTileManager::instance()->ensureTileLoaded(&rhs);
assert(rhs.m_data);
if (rhs.m_data) {
memcpy(m_data, rhs.m_data, WIDTH * HEIGHT * m_pixelSize * sizeof(quint8));
}
......@@ -112,17 +120,18 @@ void KisTile::setNext(KisTile *n)
quint8 *KisTile::data(qint32 x, qint32 y ) const
{
//Q_ASSERT(m_data != 0);
Q_ASSERT(m_data != 0);
//addReader(); [!] const!
if (m_data == 0) return 0;
//removeReader(); [!]
return m_data + m_pixelSize * ( y * WIDTH + x );
}
void KisTile::setData(const quint8 *pixel)
{
quint8 *dst = m_data;
addReader();
quint8 *dst = m_data;
for(int i=0; i <WIDTH * HEIGHT;i++)
{
memcpy(dst, pixel, m_pixelSize);
......@@ -139,6 +148,7 @@ void KisTile::addReader()
kDebug(41000) << m_nReadlock << endl;
assert(0);
}
assert(m_data);
}
void KisTile::removeReader()
......
......@@ -55,7 +55,7 @@ public:
void setNext(KisTile *);
KisTile *getNext() const { return m_nextTile; }
/// Functions that are needed for locking the tiles into memory for caching
void addReader();
void removeReader();
......
......@@ -350,11 +350,12 @@ void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, quint8 c
QRect clearTileRect = clearRect & tileRect;
tile->addReader();
if (clearTileRect == tileRect) {
// Clear whole tile
tile->addReader();
memset(tile->data(), clearValue, KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize);
tile->removeReader();
} else {
quint32 rowsRemaining = clearTileRect.height();
......@@ -366,6 +367,7 @@ void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, quint8 c
--rowsRemaining;
}
}
tile->removeReader();
}
}
}
......@@ -455,11 +457,13 @@ void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, const qu
if (clearTileRect == tileRect) {
// Clear whole tile
tile->addReader();
memcpy(tile->data(), clearPixelData, KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize);
tile->removeReader();
} else {
quint32 rowsRemaining = clearTileRect.height();
tile->addReader();
quint8 *dst = tile->data(clearTileRect.x() - tileRect.x(), clearTileRect.y() - tileRect.y());
while (rowsRemaining > 0) {
......@@ -467,6 +471,7 @@ void KisTiledDataManager::clear(qint32 x, qint32 y, qint32 w, qint32 h, const qu
dst += rowStride;
--rowsRemaining;
}
tile->removeReader();
}
}
}
......@@ -826,6 +831,13 @@ KisTile *KisTiledDataManager::getOldTile(qint32 col, qint32 row, KisTile *def)
quint8* KisTiledDataManager::pixelPtr(qint32 x, qint32 y, bool writable)
{
// Ahem, this is a bit not as good. The point is, this function needs the tile data,
// but it might be swapped out. This code swaps it in, but at function exit it might
// be swapped out again! THIS MAKES THE RETURNED POINTER QUITE VOLATILE
return pixelPtrSafe(x, y, writable) -> data();
}
KisTileDataWrapperSP KisTiledDataManager::pixelPtrSafe(qint32 x, qint32 y, bool writable) {
qint32 row = yToRow(y);
qint32 col = xToCol(x);
......@@ -836,7 +848,7 @@ quint8* KisTiledDataManager::pixelPtr(qint32 x, qint32 y, bool writable)
KisTile *tile = getTile(col, row, writable);
return tile->data() + offset;
return KisTileDataWrapperSP(new KisTileDataWrapper(tile, offset));
}
const quint8* KisTiledDataManager::pixel(qint32 x, qint32 y)
......@@ -887,7 +899,8 @@ void KisTiledDataManager::readBytes(quint8 * data,
qint32 columns = qMin(numContiguousSrcColumns, columnsRemaining);
const quint8 *srcData = pixel(srcX, srcY);
KisTileDataWrapperSP tileData = pixelPtrSafe(srcX, srcY, false);
const quint8 *srcData = tileData -> data();
qint32 srcRowStride = rowStride(srcX, srcY);
quint8 *dstData = data + ((dstX + (dstY * w)) * m_pixelSize);
......@@ -945,7 +958,9 @@ void KisTiledDataManager::writeBytes(const quint8 * bytes,
qint32 columns = qMin(numContiguousdstColumns, columnsRemaining);
quint8 *dstData = writablePixel(dstX, dstY);
//quint8 *dstData = writablePixel(dstX, dstY);
KisTileDataWrapperSP tileData = pixelPtrSafe(dstX, dstY, true);
quint8 *dstData = tileData->data();
qint32 dstRowStride = rowStride(dstX, dstY);
const quint8 *srcData = bytes + ((srcX + (srcY * w)) * m_pixelSize);
......@@ -1013,3 +1028,13 @@ qint32 KisTiledDataManager::numTiles(void) const
return m_numTiles;
}
KisTileDataWrapper::KisTileDataWrapper(KisTile* tile, qint32 offset)
: m_tile(tile), m_offset(offset)
{
m_tile->addReader();
}
KisTileDataWrapper::~KisTileDataWrapper()
{
m_tile->removeReader();
}
......@@ -37,6 +37,17 @@ typedef KSharedPtr<KisDataManager> KisDataManagerSP;
class KisTiledIterator;
class KoStore;
class KisTileDataWrapper : public KShared {
KisTile* m_tile;
qint32 m_offset;
public:
KisTileDataWrapper(KisTile* tile, qint32 offset);
virtual ~KisTileDataWrapper();
quint8* data() const { return m_tile->data() + m_offset; }
};
typedef KSharedPtr<KisTileDataWrapper> KisTileDataWrapperSP;
/**
* KisTiledDataManager implements the interface that KisDataManager defines
*
......@@ -186,6 +197,7 @@ private:
qint32 yToRow(qint32 y) const;
void getContiguousColumnsAndRows(qint32 x, qint32 y, qint32 *columns, qint32 *rows);
quint8* pixelPtr(qint32 x, qint32 y, bool writable);
KisTileDataWrapperSP pixelPtrSafe(qint32 x, qint32 y, bool writable);
};
......
/*
* Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
* Copyright (c) 2005-2006 Bart Coppens <kde@bartcoppens.be>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -18,8 +18,8 @@
#include <kdebug.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
......@@ -28,6 +28,7 @@
#include <QMutex>
#include <QThread>
#include <qfile.h>
#include <kstaticdeleter.h>
#include <kglobal.h>
......@@ -67,11 +68,11 @@ KisTileManager::KisTileManager() {
KConfig * cfg = KGlobal::config();
cfg->setGroup("");
m_maxInMem = cfg->readEntry("maxtilesinmem", 500);
m_maxInMem = cfg->readEntry("maxtilesinmem", 4000);
m_swappiness = cfg->readEntry("swappiness", 100);
m_tileSize = KisTile::WIDTH * KisTile::HEIGHT;
m_freeLists.reserve(8);
m_freeLists.resize(8);
counter = 0;
......@@ -90,8 +91,6 @@ KisTileManager::~KisTileManager() {
FreeList::iterator end = (*listsIt).end();
while (it != end) {
// munmap it
munmap((*it)->pointer, (*it)->size);
delete *it;
++it;
}
......@@ -108,11 +107,11 @@ KisTileManager::~KisTileManager() {
delete [] m_poolPixelSizes;
delete [] m_pools;
//m_poolMutex->unlock();
//delete m_poolMutex;
m_poolMutex->unlock();
delete m_poolMutex;
//m_swapMutex->unlock();
//delete m_swapMutex;
m_swapMutex->unlock();
delete m_swapMutex;
}
......@@ -133,7 +132,9 @@ void KisTileManager::registerTile(KisTile* tile)
TileInfo* info = new TileInfo();
info->tile = tile;
info->inMem = true;
info->filePos = -1;
info->mmapped = false;
info->onFile = false;
info->filePos = 0;
info->size = tile->WIDTH * tile->HEIGHT * tile->m_pixelSize;
info->fsize = 0; // the size in the file
info->validNode = true;
......@@ -163,19 +164,25 @@ void KisTileManager::deregisterTile(KisTile* tile) {
TileInfo* info = m_tileMap[tile];
if (info->filePos >= 0) { // It is mmapped
if (info->onFile) { // It was once mmapped
// To freelist
FreeInfo* freeInfo = new FreeInfo();
freeInfo->pointer = tile->m_data;
freeInfo->filePos = info->filePos;
freeInfo->size = info->fsize;
int pixelSize = (info->size / m_tileSize);
if (m_freeLists.capacity() <= pixelSize)
uint pixelSize = (info->size / m_tileSize);
// It is still mmapped?
if (info->mmapped) {
// munmap it
munmap(info->tile->m_data, info->size);
m_bytesInMem -= info->size;
m_currentInMem--;
}
if (m_freeLists.size() <= static_cast<int>(pixelSize))
m_freeLists.resize(pixelSize + 1);
m_freeLists[pixelSize].push_back(freeInfo);
madvise(info->tile->m_data, info->fsize, MADV_DONTNEED);
// the KisTile will attempt to delete its data. This is of course silly when
// it was mmapped. So change the m_data to NULL, which is safe to delete
tile->m_data = 0;
......@@ -199,7 +206,7 @@ void KisTileManager::deregisterTile(KisTile* tile) {
m_swapMutex->unlock();
}
void KisTileManager::ensureTileLoaded(KisTile* tile)
void KisTileManager::ensureTileLoaded(const KisTile* tile)
{
m_swapMutex->lock();
......@@ -217,7 +224,7 @@ void KisTileManager::ensureTileLoaded(KisTile* tile)
m_swapMutex->unlock();
}
void KisTileManager::maySwapTile(KisTile* tile)
void KisTileManager::maySwapTile(const KisTile* tile)
{
m_swapMutex->lock();
......@@ -236,15 +243,23 @@ void KisTileManager::fromSwap(TileInfo* info)
{
m_swapMutex->lock();
//Q_ASSERT(!info->inMem);
if (info->inMem) return;
doSwapping();
// ### check return value!
madvise(info->tile->m_data, info->size, MADV_WILLNEED);
Q_ASSERT(info->onFile);
Q_ASSERT(!info->mmapped);
if (!kritaMmap(info->tile->m_data, 0, info->size, PROT_READ | PROT_WRITE, MAP_SHARED,
m_tempFile.handle(), info->filePos)) {
kWarning() << "fromSwap failed!" << endl;
m_swapMutex->unlock();
return;
}
info->inMem = true;
info->mmapped = true;
m_currentInMem++;
m_bytesInMem += info->size;
......@@ -252,34 +267,36 @@ void KisTileManager::fromSwap(TileInfo* info)
}
void KisTileManager::toSwap(TileInfo* info) {
m_swapMutex->lock();
//Q_ASSERT(info->inMem);
if (!info || !info->inMem) return;
if (info->filePos < 0) {
KisTile *tile = info->tile;
if (!info->onFile) {
// This tile is not yet in the file. Save it there
// ### check return values of mmap functions!
KisTile *tile = info->tile;
quint8* data = 0;
int pixelSize = (info->size / m_tileSize);
if (m_freeLists.capacity() > pixelSize) {
uint pixelSize = (info->size / m_tileSize);
bool foundFree = false;
if (m_freeLists.size() > static_cast<int>(pixelSize)) {
if (!m_freeLists[pixelSize].empty()) {
// found one
FreeList::iterator it = m_freeLists[pixelSize].begin();
data = (*it)->pointer;
info->filePos = (*it)->filePos;
info->fsize = (*it)->size;
delete *it;
m_freeLists[pixelSize].erase(it);
foundFree = true;
}
}
if (data == 0) { // No position found or free, create a new
if (!foundFree) { // No position found or free, create a new
long pagesize = sysconf(_SC_PAGESIZE);
int newsize = m_fileSize + info->size;
off_t newsize = m_fileSize + info->size;
newsize = newsize + newsize % pagesize;
if (ftruncate(m_tempFile.handle(), newsize)) {
......@@ -308,43 +325,33 @@ void KisTileManager::toSwap(TileInfo* info) {
return;
}
data = (quint8*) mmap(0, info->size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
m_tempFile.handle(), m_fileSize);
// Same here for warning and GUI
if (data == (quint8*)-1) {
kWarning(DBG_AREA_TILES) << "mmap failed: errno is " << errno << "; we're probably going to crash very soon now...\n";
// Try to ignore what happened and carry on, but unlikely that we'll get
// much further, since the file resizing went OK and this is memory-related...
if (errno == ENOMEM) {
kWarning(DBG_AREA_TILES) << "mmap failed with E NOMEM! This means that "
<< "either there are no more memory mappings available for Krita, "
<< "or that there is no more memory available!" << endl;
}
kWarning(DBG_AREA_TILES) << "Trying to continue anyway (no guarantees)" << endl;
kWarning(DBG_AREA_TILES) << "Will try to avoid using the swap any further" << endl;
kDebug(DBG_AREA_TILES) << "Failed mmap info: "
<< "tried mapping " << info->size << " bytes"
<< "to a " << m_fileSize << " bytes file" << endl;
printInfo();
m_swapForbidden = true;
m_swapMutex->unlock();
return;
}
info->fsize = info->size;
info->filePos = m_fileSize;
m_fileSize = newsize;
}
madvise(data, info->fsize, MADV_WILLNEED);
memcpy(data, tile->m_data, info->size);
madvise(data, info->fsize, MADV_DONTNEED);
//memcpy(data, tile->m_data, info->size);
QFile* file = m_tempFile.file();
if(!file) {
kWarning() << "Opening the file as QFile failed" << endl;
m_swapForbidden = true;
m_swapMutex->unlock();
return;
}
if(!file->at(info->filePos)) {
kWarning() << "Seek to position FAILED!: " << info->filePos << endl;
m_swapForbidden = true;
m_swapMutex->unlock();
return;
}
if (file->writeBlock(reinterpret_cast<const char *>(tile->m_data), info->size) == -1) {
kWarning() << "Write to file FAILED!: " << info->filePos << endl;
m_swapForbidden = true;
m_swapMutex->unlock();
return;
}
m_poolMutex->lock();
if (isPoolTile(tile->m_data, tile->m_pixelSize))
......@@ -353,12 +360,20 @@ void KisTileManager::toSwap(TileInfo* info) {
delete[] tile->m_data;
m_poolMutex->unlock();
tile->m_data = data;
tile->m_data = 0;
} else {
madvise(info->tile->m_data, info->fsize, MADV_DONTNEED);
//madvise(info->tile->m_data, info->fsize, MADV_DONTNEED);
Q_ASSERT(info->mmapped);
// munmap it
munmap(tile->m_data, info->size);
tile->m_data = 0;
}
info->inMem = false;
info->mmapped = false;
info->onFile = true;
m_currentInMem--;
m_bytesInMem -= info->size;
......@@ -376,9 +391,9 @@ void KisTileManager::doSwapping()
#if 1 // enable this to enable swapping
quint32 count = qMin(m_swappableList.size(), (int)m_swappiness);
quint32 count = qMin(m_swappableList.size(), static_cast<int>(m_swappiness));
for (quint32 i = 0; i < count; i++) {
for (quint32 i = 0; i < count && !m_swapForbidden; i++) {
toSwap(m_swappableList.front());
m_swappableList.front()->validNode = false;
m_swappableList.pop_front();
......@@ -395,7 +410,7 @@ void KisTileManager::printInfo()
kDebug(DBG_AREA_TILES) << m_currentInMem << " out of " << m_tileMap.size() << " tiles in memory\n";
kDebug(DBG_AREA_TILES) << m_swappableList.size() << " elements in the swapable list\n";
kDebug(DBG_AREA_TILES) << "Freelists information\n";
for (int i = 0; i < m_freeLists.count(); i++) {
for (int i = 0; i < m_freeLists.size(); i++) {
if ( ! m_freeLists[i].empty() ) {
kDebug(DBG_AREA_TILES) << m_freeLists[i].size()
<< " elements in the freelist for pixelsize " << i << "\n";
......@@ -408,6 +423,8 @@ void KisTileManager::printInfo()
<< ", pixelSize: " << m_poolPixelSizes[i] << endl;
}
}
if (m_swapForbidden)
kDebug(DBG_AREA_TILES) << "Something was wrong with the swap, see above for details" << endl;
kDebug(DBG_AREA_TILES) << endl;
}
......@@ -502,3 +519,35 @@ void KisTileManager::configChanged() {
doSwapping();
m_swapMutex->unlock();
}
bool KisTileManager::kritaMmap(quint8*& result, void *start, size_t length,
int prot, int flags, int fd, off_t offset) {
result = (quint8*) mmap(start, length, prot, flags, fd, offset);
// Same here for warning and GUI
if (result == (quint8*)-1) {
kWarning(DBG_AREA_TILES) << "mmap failed: errno is " << errno << "; we're probably going to crash very soon now...\n";
// Try to ignore what happened and carry on, but unlikely that we'll get
// much further, since the file resizing went OK and this is memory-related...
if (errno == ENOMEM) {