[0] - http://websvn.kde.org/?view=revision&revision=1181593
Saturday, October 2, 2010
Krita: panning, race conditions and other bug fixes
[0] - http://websvn.kde.org/?view=revision&revision=1181593
Sunday, September 12, 2010
Krita: Free Scrolling and Masks improvements
[1] - http://websvn.kde.org/?view=revision&revision=1174365
Tuesday, June 22, 2010
Krita: transactions, swapping and new fastBitBlt for tiled data manager
It was almost two weeks ago when I blogged last time. It was very tough those weeks because I had to merge two processes at the same time: preparations for an examination at the university and coding on Krita. But nevertheless, I'm glad announce much progress in my project for Krita!
Transactions refactoring
I've finally commited my patchset for transactions in Krita [1]. So now there is almost nothing common between Qt's QUndoCommand and Krita's KisTransaction (from the public side :)). It was needed to allow pooler (and upcoming swapper) threads to work efficiently. Now every transaction has the beginning and the end. So when you end a stroke on the canvas you finish a transaction as well. The pooler recognizes this signal and starts doing his routine job to optimize your next stroke (while you are still thinking about it ;)).
There are big changes from a developer point of view as well. Now you can create a transaction in a stack instead of a heap, that makes the code much simplier. And the most curious artefact of Krita's past was removed. There was a "global variable" in every KisDoc2 that used to disable saving undo information while loading the document. This variable had to be checked thoughout all the code of Krita: in every tool, in every action. Now it has gone. New tile engine does initial document loading in a cheap and fast way.
If you are interested in details of the new transactions system you can read a design document here. Examples are given!
Region() interface for paint devices
After finishing with transaction I continued my work on preparation to Filter API redesign. I've added a feature to a paint device for getting a QRegion of non-transparent (non-default) pixels. Of course it is based on tiles, and the region is rounded by 64. It is intended to be used as optimization of filter's application. You can read the details in Filter API Discussion Notes, that happend between Cyrille Berger and me in Deventer.
FastBitBlt interface for paint devices
This is part of Filter API redesign as well. The interface simply shares tiles between different paint devices. It allows us to create clones of devices really fast! It is intended to be used in layer merging subsystem and during filter application. There are limitation in this interface as well: it cannot be used when paint devices are shifted and when their color spaces do not coincide.
Framework for tile compression
After some discussion with Cyrille and his experiments with LZF algorithm, I started implementing tile compression framework inside the tile engine (Cyrille has already added nessecary features into KoStore to allow delegating compression to it). I'm going to kill two rabbits with one shoot, by doing this. The first "rabbit": Krita will be able to work with various formats of saved tiles. It means that we will be able to modernize the tile format without losing back compatibility. And the second: this framework will be the first stone in the swapper subsystem's building!
Thursday, June 10, 2010
Krita: New Transaction System For Review
Wednesday, May 19, 2010
Krita: canvas will not flicker anymore!
The bug was caused by a race condition between Image and UI threads. The latter one tried to read the data from the merged projection while the former one was preparing it. I have fixed this by splitting the update process into two parts. The first part reads data from the image. It is executed in the context of the Image thread. Then the thread saves update information got from the canvas to the external structure called KisUpdateInformation and passes it to the UI thread, that continues updating doing rescaling and other slow operations.
This patch touches much code in canvas subsystem, so there will be a short delay for reviewing and testing the patch. And if you want to see it in trunk faster, you can help testing it! [0]
There are some technical details of implementation in our wiki [1]
[0] - http://dimula73.narod.ru/canvas-split-single-v2.patch
[1] - http://wiki.koffice.org/index.php?title=Krita/KisCanvas2_Update_Split_Reasoning
Friday, August 21, 2009
GSoC Krita tile engine wrap-up
The summer has almost gone, GSoC has finished..
I had two aims for my project: the first was to make a new, good, able to swap and compress tile engine for Krita and the second was to make a mipmapping subsystem. They are in trunk now, but they both are in testing state.
Features
Let's take a look what features do they suggest:
Tile engine
I had to refactor most of tile-subsystem code. New tiles can be easily shared, that helps much during saving undo information. Speaking strictly, now we don't have to save this undo information just before painting. This is done in deferred way, in a separate thread. It means that new engine has a very good perspective on multiprocessor systems.
Unfortunately i didn't manage to finish swapper thread in time. Too much time was spent on the second task. That is one of the resons why this engine is still in a "testing" state.
Mipmapping
This task was quite difficult to perform. Adding new canvas caching and prescaling engine caused much code of KisPrescaledProjection to be refactored. So i spent most of the time on that. Now mipmapping stuff itself is finished, but much of Krita merging and threading code must be revised. We've faced with many paint artefacts caused by race conditions in a merging mechanism. That is why at the moment i'm working on that part of Krita. After solving all the issues with threading mipmapping will be a very good option for users to optimize their zooming.
How to activate
As i said these features are being tested for now. This means they are not turned on by default, but if you are interested, you can always try them out!
New tile engine activation HOWTO:
This feature needs recompilation of Krita.
1) svn co svn://anonsvn.kde.org/home/kde/trunk/koffice
2) cd koffice
3) .. edit a file ./krita/image/CMakeLists.txt ..
.. change a line ..
set(USE_TILESYSTEM 1)
.. to ..
set(USE_TILESYSTEM 3)
4) make -j3 install
Mipmapping activation HOWTO:
For mipmapping you needn't recompile anything :)
Just set an option in your kritarc configuration file
useMipmapping=true
This file is usually stored in ~/.kde4/share/config/kritarc
Wednesday, July 22, 2009
We are in trunk!
Just checkout a trunk and switch engine in krita/image/CMakeLists.txt file
To activate a new one set USE_TILESYSTEM to '3'
Tuesday, July 21, 2009
Mipmapping for Krita's canvas subsys
Pros:
+ we merge only small images, not fullsized big ones
+ as a consequence, filter layers and masks are applied much
faster
Cons:
will never be used at all.
- complexity of the system: alignment between layers
- probable artefacts caused by merging after scaling
Pros:
+ not so much overhead as we create only one pyramid for a view
+ zooming works fast thanks to the pyramid
+ no scale-merge artefacts.
+ no problems with alignment
Cons:
- no help to filter layers
->updateCanvasProjection(rect)
->preScale()
->resizePrescaledImage(size)
These methods fit for image pyramid very well:
preScale() would request scaled image from pyramid.
It would look like:
QPainter gc(m_prescaledQImage);
...
m_imagePyramid.drawScaledImage(gc, ..., ..., scale);
Here is a mockup of an interface of a KisImagePyramidClass:
class KisImagePyramid {
public:
void updateCanvasProjection(QRect rect);
/**
* Here are some problems with scaleX/scaleY
* as for image pyramid they should be equal
*/
void drawScaledImage(QPainter gc,
QPointF topLeftScaled /* target topLeft point */,
QRect rectUnscaled /* source rect in image */,
qreal scale);
private:
QVectorm_pyramid;
KisProjectionCache /* or QImage */ m_original;
QThreadPool m_pyramidUpdater;
};
What do you think about this idea? Maybe i've missed something?
As always, comments are welcome! =)
PS:
There are some points where i'm in doubt now:
3) KisPresceledProjection::Private::prescaledQImage vs
KisPresceledProjection::Private::prescaledQPixmap?
What is the difference and where are they mostly used?
Saturday, June 13, 2009
The Memento Manager started his work
Here are the links to test:
http://websvn.kde.org/?view=rev&revision=981347
or
svn co svn://anonsvn.kde.org/home/kde/branches/work/koffice-feature-tiles-ng/
To run a test:
cd krita/image/tiles3/tests/dm_consistancy_test/
qmake
make
./dm_consistancy_test |less
PS:
s/dm_consistancy_test/dm_consistency_test :)
Next time =)
Monday, June 8, 2009
Krita: Memento story
The main goal of refactoring current KisMemento-system is to reject the idea of “ensuringTileMementoed” inside data-manager (DM) class. I think DM should know nothing about internals of memento-mechanism. From some point of view mementos should work in a way like SVN or Git does. DM should know just how to “commit” a newly created revision into repository (KisMementoManager class) and “checkout“ some previously created one. And no ensureTileMementoed() needed.
Well, that was an idea. Here are details about implementation.

Once upon a time there was a Memento Manager. He was very pedantic and knew history of every citizen (tile) in his town. He stored it in a list that was called m_revisionsList. It consisted of smaller lists of KisMementos which stored citizen's accomodation (m_col, m_row) and state (m_tileData). How did he know everybody's history? It's simple! They said it to him themselves! Every time a tile wanted to write some data (KisTile::lockForWrite called) it checked whether there were some other users of the tile-data (TD). If there were some, it got new piece of tile-data and registered it in a Memento Manager's office (KisMementoManager ::registerTileData(td, col, row)) saying: “Hey, doc! I've got a new data!”. The Memento Manager saved this TD in a temporary list (m_indexList) called “index” (do you remember “index” term in Git?) and incremented KisTileData::m_refCount counter. By this time TD structure had only one COW-user (it was our KisTile object), so all lockForWrite() calls wouldn't cause any COWing. But TD-object itself couldn't be freely deleted from memory and corrupt m_indexList's pointres, because The Manager held it's m_refCount counter.
Time flied and DM decided to make a commit. He said about it to The Memento Manager (KisMementoManager::commit()) and the latter one appended m_indexList into the end of m_revisionsList acquiring all items of index for COW'ing (incrementing KisTileData::m_usersCount). From this moment all call's to KisTile::lockForWrite() would cause activation of COW. So tile-datas stored in m_revisionsList were untouched.
When DM would want to restore some revision it would get a bunch of tiles, committed by last commit, would search their's parents and restore them.
=)
* Deletion of tiles should be done in the same way.
* The thing I haven't thought over properly is searching parents of mementos.
If you have some comments or ideas or objections you are welcome to say them here or in the maillist! =)
Monday, June 1, 2009
TledDataManager first tests
http://websvn.kde.org/branches/work/koffice-feature-tiles-ng/
At the moment it can't be fully attached to krita, it doesn't have mementoes and so on.. But it's able to perform tests.. But we shall speak about them a bit later.
First thing to discuss is implementation. The thing i don't like in current engine is that all functions of datamanager are done in the only class (hash table, tile walkers, parts of memento mechanism are all places into KisTiledDataManager). I tried to split those things and got three classes (not counting memento subsys.): KisTiledDataManager itself, KisTileHashTable and KisTileProcessor. Let's look at them.
Let's perceed to KisTileProcessor class. I wanted to have a unified object that could be applied to tiles in unified way. More than that it could be done in threaded way. And i have done it. Done it for read/write operations inside data manager. These classes are KisTileReadWrite{,Planar}Processor. They work, they read and write perfectly! And that is corroborated by the last two tests in .../test_tiles_cow/ folder. The problem is, object oriented approach creates a great overhead in time. And that is disappoiningly corroborated by tests in image/tiles3/tests/dm_perfomance_test. On uniprocessor PC tile-processors work 20% slower than old data-manager. I'm thinking over this problem now... Much time is spent in constructors, destructors, shared-ptrs... If someone is interested, i could give him a callgrind.out...
And the most interesting thing about KisTileProcessor is that there is NO gain in multithreading. *Absolutely nothing!*. I tried it on T7250. Most of the time both cores are stalled. They are simply waiting for memory request to finish.
So by this moment i decided to perform read/write in non-threaded way. But i want to leave processors inside data-manager so as a great number of operations could be done with them.
Well, thinking...
PS:
HOW TO RUN TESTS
There are two tests:
image/tiles3/tests/test_tiles_cow/
image/tiles3/tests/dm_perfomance_test/
Both of them are qmake projects, so the only thing you should do:
$ cd [whatever you want]
$ qmake
$ make
$ ./[executable]
If it won't compile, check include paths in [folder_name].pro
=)
Sunday, May 10, 2009
First step of tile engine
The three classes mentioned earlier are committed to http://websvn.kde.org/branches/work/koffice-feature-tiles-ng/. New tiles engine called "tiles3" ;). Some details are changed, but the idea remained the same. Of course you can't build krita with this engine =), but you can test some features of it with a simple test suite located in krita/image/tiles3/tests/test_tiles_cow/. The list of "features" is quite short yet. Atm it simply creates a few tiles and shares tile data between them with CopyOnWrite mechanism.
And there are a few issues i have already faced with. The main is deadlocks. There can be a plenty of situations when the tilesystem will be stuck in the lock forever. And the worst thing we can't use "Ostrich algorithm" suggested by Tannenbaum! =). At the moment i see the only solution for this problem: locking rules.
So "the first great locking rule" sounds like:
E.g. if you want to copy some data from one tile to another (and they both share the same tiledata) you should acquire the source first, and only then acquire the destination.
Here is kind of "variations table". Imagine tile1 has already acquired some lock, but tile2 wants to acquire it too (both use the same tiledata).
A guess there are many rules to be discovered. But i hope they i'll be able to encapsulate them inside iterators.Friday, May 8, 2009
Krita's tilesystem project idea
I'll begin with the lowest level: KisTile. New KisTile class will be an abstraction of a tile that doesn't store the actual data of the image itself (actual data is stored in tile-data object, that can be shared by several tiles). Tile-class will encapsulate locking and copy-on-write mechanisms.

Let's imagine the situation when we need to access some tile for read.- We call tile->acquireForRead
- It acquires tile->m_tileData->m_RWLock for read
- Checks whether tile->m_tileData->m_state==NORMAL
a) if not calls tileDataStore->getTileDataToMemory - Allows user to read tile->m_tileData->m_data
Access for write:
- We call tile::acquireForWrite
- It acquires tile->m_tileData->m_RWLock for write
- Checks whether m_refCount==1 (we are the only user of tile-data)
a) m_refCount--
b) if not, COW. tile->m_tileData = tileDataStore->allocNewTileData() - Checks whether tile->m_tileData->m_state==NORMAL
a) if not calls tileDataStore->getTileDataToMemory() - Allows user to read tile->m_tileData->m_data
Creating new tile object (KisTile::KisTile):
- tile->m_tileData = tileDataStore::getDefaultTileData()
defaultTileData object is shared by all tiles, so it has m_refCount field higher than 1, so it'll be replaced by a new tile-date on the first write request.


