Merge lp:~mspacek/qpdfview/cachezoom into lp:qpdfview
- cachezoom
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 1635 | ||||
Proposed branch: | lp:~mspacek/qpdfview/cachezoom | ||||
Merge into: | lp:qpdfview | ||||
Diff against target: |
208 lines (+53/-16) 4 files modified
sources/pageitem.cpp (+11/-4) sources/pageitem.h (+1/-1) sources/tileitem.cpp (+36/-10) sources/tileitem.h (+5/-1) |
||||
To merge this branch: | bzr merge lp:~mspacek/qpdfview/cachezoom | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Adam Reichold | Approve | ||
Review via email:
|
Commit message
Description of the change
Here is my naive implementation of caching. It uses a string as the cache key instead of the tile. The key is just a bunch of concatenated values of page number, rotation, inversion, tile left, right, width, and height. Not sure if these are the best parameters to use to ensure unique keys.
No pixmap is ever removed from the cache any more. This only happens (I presume) when the cache hist its size limit and starts removing the entries accessed the furthest ago in time.
This doesn't deal at all with annotations or forms. I haven't thought about that at all, so this almost certainly needs more work.
"(I would suggest defining a struct with a comparison operator in the global header for that.)"
I'm very inexperienced when it comes to C++, so I'm not sure what that means.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
Aren't the tiles created and destroyed every time there's a changing in tiling (like from 1x1 to 2x2)? If so, when don't you then lose access to previously cached pixmaps? Wouldn't it be better to use the document as part of the key, instead?
I tried doing this (haven't tried making a record yet), but it doesn't compile. I get this error:
/usr/include/
sources/
/usr/include/
/usr/include/
/usr/include/
/usr/include/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello,
Am 19.06.2014 00:55, schrieb Martin Spacek:
> Aren't the tiles created and destroyed every time there's a changing in tiling (like from 1x1 to 2x2)? If so, when don't you then lose access to previously cached pixmaps? Wouldn't it be better to use the document as part of the key, instead?
Yes, you are right. Tiles might be destroyed if there is a change in
tiling, i.e. we will reuse as many TileItems and create new ones if the
number of tiles increases and destroy some when the number of tiles
decreases. And so yes, a better candidate for a key is the page object,
i.e. the "m_page" member of type "Model::Page*".
> I tried doing this (haven't tried making a record yet), but it doesn't compile. I get this error:
>
> /usr/include/
> sources/
> /usr/include/
> /usr/include/
> /usr/include/
> /usr/include/
>
This looks like you tried to use "qobject_cast" on a class that is no
subtype of QObject and hence has no "Q_OBJECT" macro (meaning no meta
type associated with it). The solution would be to use a normal C++ cast
like "static_cast" if you statically know the correct type or
"dynamic_cast" which is decided at runtime like "qobject_cast" just a
bit slower, c.f. [1] and [2] if you're interested in the background.
Best regards, Adam.
[1] http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
Hi Adam,
Thanks for that. Yup, using the page seems to work. Now I no longer get cache collisions when multiple files with the same page size are open (although that was pretty amusing :)). The branch has been updated.
I still need to switch to using a struct, and then start thinking about annots and forms.
- 1582. By Martin Spacek <email address hidden>
-
Remove commented out lines
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello again,
yeah, cache collisions can probably give a interesting twist to the more boring documents... Otherwise it seems to start to come together and I made some smaller inline comments.
Concerning the cache key: I would really prefer a custom struct since that could also be used to simplify the code in "RenderTask" and especially in "TileItem:
Best regards, Adam.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
Hi Adam,
Yes, that all sounds good. I'm not sure how much of it I can do for now, or even how much of it I'm capable of doing, but when I'm feeling up to it (or feel like procrastinating), I'll give it a shot.
Just a note that I've noticed, only once so far, a cache collision between a page of an open document and that of another previously open document. This is while using the latest version of this branch, which uses the m_page as part of the key (and no longer uses the page number). Is it possible that once in a while the same page object is reused across documents, or is allocated the same memory address as a previously cached one?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello Martin,
Am 21.06.2014 04:25, schrieb Martin Spacek:
> Hi Adam,
>
> Yes, that all sounds good. I'm not sure how much of it I can do for now, or even how much of it I'm capable of doing, but when I'm feeling up to it (or feel like procrastinating), I'll give it a shot.
Just take your time, there is no pressure involved. (It was deliberate
to set the bug report into "In Progress" but without a target milestone.)
> Just a note that I've noticed, only once so far, a cache collision between a page of an open document and that of another previously open document. This is while using the latest version of this branch, which uses the m_page as part of the key (and no longer uses the page number). Is it possible that once in a while the same page object is reused across documents, or is allocated the same memory address as a previously cached one?
>
Yes this is possible, even though the default memory allocators reuse
storage locations in a FIFO manner, it is possible for a page object to
get allocated at the same address as another.
(Especially since the allocators tend to use several pools for
allocations of different sizes, but a "Model::Page" object for the same
plug-in will have the same size make use of the same pool increasing the
probability of address reuse.)
But since this depends on the actual implementation in question,
probably glibc's malloc, and I am not an expert on any of them, I feel
hard to pressure to numerically estimate the probability.
With trunk, this is impossible since "TileItem" removes itself from the
cache upon destruction. The best solution IMHO would be to key on the
"PageItem*" instead of "Model::Page*" and remove all entries with that
address in the destructor of "PageItem". This would also fit well
together with the fact, the "PageItem" will have to do the same
operation when adding an annotation or after hiding the form or
annotation overlay, i.e. "PageItem" will need a
remove-
Best regards, Adam.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello,
if you merge from trunk (revision 1593), you'll notice that I packaged the relevant parameter into a strcuture with ordering relations defined. I though that since this all boiler plate code, it might help you to concentrate on the actual functionality.
Best regards, Adam.
- 1583. By Martin Spacek <email address hidden>
-
Merge from trunk
- 1584. By Martin Spacek <email address hidden>
-
No need to include model.h, clearer variable name
- 1585. By Martin Spacek <email address hidden>
-
Rename getPixmapKey -> pixmapKey, make it private and const
- 1586. By Martin Spacek <email address hidden>
-
Use PageItem instead of Page as cache key
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
OK, I've merged from the latest trunk. The key taps into the RenderParam structure now for various bits of info. Do the operators you added to the struct allow it to be used more directly as a key, instead of having to check each of its fields individually? I tried doing so, but couldn't get it to work. I kept getting call type errors for qHash. Also, shouldn't m_rect be part of the key? Something like:
QPair< PageItem*, RenderParam, QRect > TileItem:
I've also tried to address the inline comments regarding private and const. Hope I did it right.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
For the current revision of this branch, I'm noticing cache collisions on page refresh. Do I maybe need to do something in the PageItem destructor?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello again,
Am 23.06.2014 15:44, schrieb Martin Spacek:
> For the current revision of this branch, I'm noticing cache
> collisions on page refresh. Do I maybe need to do something in the
> PageItem destructor?
>
yes, within the PageItem destructor, you need to find all entries
which belong to this page and remove them so they can't be reused.
This also implies that the PageItem pointer needs to be recoverable
from the cache key. (And as said earlier, you will need to do the same
thing whenever annotations or form fields change.)
Best regards, Adam.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello again,
Am 23.06.2014 14:04, schrieb Martin Spacek:
> OK, I've merged from the latest trunk. The key taps into the
> RenderParam structure now for various bits of info. Do the
> operators you added to the struct allow it to be used more directly
> as a key, instead of having to check each of its fields
> individually? I tried doing so, but couldn't get it to work. I kept
> getting call type errors for qHash. Also, shouldn't m_rect be part
> of the key? Something like:
>
> QPair< PageItem*, RenderParam, QRect > TileItem:
The comparison operators do not help. I assumed that QCache was based
on an ordered associative container like QMap, but it uses hashing,
i.e. QHash and hence we have to provide a hash function for our data
structure. We therefore need to provide an implemementation of "qHash"
[1] for our key type, which should be something like
QPair< PageItem*, QPair< RenderParam, QRect > >
or
struct CacheKey
{
PageItem* pageItem;
RenderParam renderParam;
QRect rect;
};
where the first one could make direct use of "qHash(QPair)" if we have
a "qHash" implementation for the partial keys, but we actually only
need to provide "qHash(CacheKey)", maybe using the implementations in
the Qt header "qhash.h" [2] as a guide. (In the end, this will be
effectively the same as using a QString like you currently do to
obtain a hashable key, but to me it doesn't look right to use
"sprintf" for this kind of thing.)
And yes, the tile/rect should be part of the key but I left it out of
RenderParameter so that I can use a single instance of it stored in
PageItem rather than several copies in each TileItem.
> I've also tried to address the inline comments regarding private
> and const. Hope I did it right.
>
Yes, looks fine now. (The include for "model.h" seems unnecessary?)
Best regards, Adam.
[1] http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello again,
from what we discussed so far, I think the most efficient cache key type would be
QPair< PageItem*, uint >
with the second member being set to a "qHash-like" value created by hashing the RenderParam and the QRect. This way, we don't store any information in the cache the we don't need to recover for deleting all entries associated with a given PageItem instance.
Best regards, Adam.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
"Forgot something as it won't work like that since this would still be
the key and not the hash, i.e. we would have to collision-free w.r.t.
the second member and that seems are rather harsh requirement, so back
to "struct CacheKey" I'd say. Sorry for my thinking going off the rails..."
Hm, this is starting to sound a bit complicated. OK, so a CacheKey struct should be defined in global.h like this:
struct CacheKey
{
PageItem* pageItem;
RenderParam renderParam;
QRect rect;
};
and it needs to be given a qHash method so that QCache knows how to hash it?
How does this solve the problem of removing from the QCache all the keys associated with a specific PageItem that we want to destroy?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello,
Am 23.06.2014 22:48, schrieb Martin Spacek:
> "Forgot something as it won't work like that since this would still
> be the key and not the hash, i.e. we would have to collision-free
> w.r.t. the second member and that seems are rather harsh
> requirement, so back to "struct CacheKey" I'd say. Sorry for my
> thinking going off the rails..."
I should take less and think more before doing it...
> Hm, this is starting to sound a bit complicated. OK, so a CacheKey
> struct should be defined in global.h like this:
>
> struct CacheKey { PageItem* pageItem; RenderParam renderParam;
> QRect rect; };
>
> and it needs to be given a qHash method so that QCache knows how to
> hash it?
Yes, this sounds about right. (Even though I would probably declare
"struct CacheKey {...};" and "qHash(const CacheKey&);" in "tileitem.h"
and implement the hashing function in "tileitem.cpp".)
But this is mainly a size optimization as to not store a textual
representation of these parameter but the parameter itself.
> How does this solve the problem of removing from the QCache all the
> keys associated with a specific PageItem that we want to destroy?
It doesn't. ;-) As I said, all this is a size (and hence to a degree
speed) optimization. You can definitely keep using "QPair< PageItem*,
QString >" until everything works functionally.
If you intend to continue using "QCache", they I don't think there is an
alternative to enumerating "QCache::keys()" and testing for "pageItem ==
this" in the PageItem destructor.
(If you want this to be faster, you'd need an ordered container like
QMap, where you can lexicographically order w.r.t. "CacheKey:
and then the rest, so that "upperBound(
"lowerBound(
"this" but you would have to reinvent all the LRU caching stuff.)
I'd suggest putting this into something like "PageItem:
since you'll then just need to call this when changing annotations or
form fields as well. But especially for large caches with a lot of
entries, it will probably be rather slow...
Best regards, Adam.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
OK. I was thinking another fast way to deal with invalidating a PageItem in the cache would be to keep a second, separate mapping of PageItems to cache keys. That way, all you need to do is a quick look up of the keys associated with the PageItem to be destroyed, and remove those keys from the cache. Of course, this extra mapping would take a bit more memory, but not nearly as much as the cache itself, because it's only storing keys, not pixmaps. If this was Python, I could throw something together in a few of minutes :)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello Martin,
Am 24.06.2014 20:21, schrieb Martin Spacek:
> OK. I was thinking another fast way to deal with invalidating a PageItem in the cache would be to keep a second, separate mapping of PageItems to cache keys. That way, all you need to do is a quick look up of the keys associated with the PageItem to be destroyed, and remove those keys from the cache. Of course, this extra mapping would take a bit more memory, but not nearly as much as the cache itself, because it's only storing keys, not pixmaps. If this was Python, I could throw something together in a few of minutes :)
>
Yes, you could try to keep a separate list of all partial keys which
belong to a given PageItem instance, but then you would not notice when
a cache key is evicted by the cache and would potentially keep a lot
stale keys around which do not belong to the cache anymore. (Or you
would have to wrap the QPixmap inside a separate object whose destructor
tells your second list that it was destroyed and can be removed from
list. It just keeps getting better...)
Btw., I don't think C/C++ is the main problem, is rather trying to
encode a rather specific set of requirements into a rather general data
structure like QCache have only the cache key type to maneuver with. As
I said, the look-up part would become rather simple by using an ordered
map like QMap, but then you would need to duplicate the LRU logic (and
also penalize read access somewhat).
All in all, I'd also say that you are only a few minutes away from a
working if not optimal solution, just add "PageItem:
enumerating "TileItem:
pointer part of the key fits. Call this in "PageItem:
when the annotation and form field overlays are hidden as well when an
annotation is added and you should have a basic working program. Then
you have all the time in the world to work on optimizing it. (And you
have something to do performance comparisons against...)
Best regards, Adam.
- 1592. By Martin Spacek <email address hidden>
-
Merge from trunk
- 1593. By Martin Spacek <email address hidden>
-
Merge from trunk
- 1594. By Martin Spacek <email address hidden>
-
Merge from trunk
- 1595. By Martin Spacek <email address hidden>
-
Merge from lp:~adamreichold/qpdfview/cachezoom
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
Hi Adam,
Thanks for that! I've merged it in and pushed it back up to LP. Obviously, I haven't worked on this much lately. I only have a vague idea of what you did, and so far it seems to work. Can you remind me if there's anything else that needs to be done? Does it just require some longer-term testing before merging into trunk?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Hello Martin,
I more or less just implemented what we talked about: When a PageItem is destroyed or changes internally (e.g. via a form field), all associated cache entries are searched and removed.
Basically, I would say this is feature complete and just needs more testing (which it will probably not see without being merged to trunk and hence being available via the dailydebs). But I would also like to have some sort of performance metric on how heavy the more involved cache key really is. A more compact representation of the cache key would be further bonus. (QDataStream does a lot of coding to keep its output platform-
Best regards, Adam.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
OK, I'll keep running off this branch, see if I find any problems. So far, I haven't found any.
The other potential feature I think you mentioned before is a way to avoid having to iterate over all cache keys in TileItem:
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Am 23.07.2014 um 07:50 schrieb Martin Spacek:
> OK, I'll keep running off this branch, see if I find any problems. So far, I haven't found any.
>
> The other potential feature I think you mentioned before is a way to avoid having to iterate over all cache keys in TileItem:
From simple time measurements with this code and considering that this
is not done during normal drawing, I'd say the effort isn't worth it.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Adam Reichold (adamreichold) wrote : | # |
Ok, since you did not seem to have found any more issues with this, I'll merge it into trunk so it receives more widespread testing. Thanks again for your contribution!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Martin Spacek (mspacek) wrote : | # |
Great, thanks! Yup, still haven't encountered any problems.
Preview Diff
1 | === modified file 'sources/pageitem.cpp' |
2 | --- sources/pageitem.cpp 2014-06-23 16:07:02 +0000 |
3 | +++ sources/pageitem.cpp 2014-07-21 20:51:01 +0000 |
4 | @@ -94,6 +94,8 @@ |
5 | hideAnnotationOverlay(false); |
6 | hideFormFieldOverlay(false); |
7 | |
8 | + TileItem::dropCachedPixmaps(this); |
9 | + |
10 | qDeleteAll(m_links); |
11 | qDeleteAll(m_annotations); |
12 | qDeleteAll(m_formFields); |
13 | @@ -208,7 +210,7 @@ |
14 | } |
15 | |
16 | |
17 | -void PageItem::refresh(bool keepObsoletePixmaps) |
18 | +void PageItem::refresh(bool keepObsoletePixmaps, bool dropCachedPixmaps) |
19 | { |
20 | if(!s_settings->pageItem().useTiling()) |
21 | { |
22 | @@ -222,6 +224,11 @@ |
23 | } |
24 | } |
25 | |
26 | + if(dropCachedPixmaps) |
27 | + { |
28 | + TileItem::dropCachedPixmaps(this); |
29 | + } |
30 | + |
31 | update(); |
32 | } |
33 | |
34 | @@ -703,7 +710,7 @@ |
35 | m_annotations.append(annotation); |
36 | connect(annotation, SIGNAL(wasModified()), SIGNAL(wasModified())); |
37 | |
38 | - refresh(); |
39 | + refresh(false, true); |
40 | emit wasModified(); |
41 | |
42 | showAnnotationOverlay(annotation); |
43 | @@ -728,7 +735,7 @@ |
44 | |
45 | annotation->deleteLater(); |
46 | |
47 | - refresh(); |
48 | + refresh(false, true); |
49 | emit wasModified(); |
50 | } |
51 | } |
52 | @@ -798,7 +805,7 @@ |
53 | } |
54 | } |
55 | |
56 | - refresh(); |
57 | + refresh(false, true); |
58 | } |
59 | } |
60 | |
61 | |
62 | === modified file 'sources/pageitem.h' |
63 | --- sources/pageitem.h 2014-06-23 16:07:02 +0000 |
64 | +++ sources/pageitem.h 2014-07-21 20:51:01 +0000 |
65 | @@ -105,7 +105,7 @@ |
66 | void wasModified(); |
67 | |
68 | public slots: |
69 | - void refresh(bool keepObsoletePixmaps = false); |
70 | + void refresh(bool keepObsoletePixmaps = false, bool dropCachedPixmaps = false); |
71 | |
72 | int startRender(bool prefetch = false); |
73 | void cancelRender(); |
74 | |
75 | === modified file 'sources/tileitem.cpp' |
76 | --- sources/tileitem.cpp 2014-06-23 16:07:02 +0000 |
77 | +++ sources/tileitem.cpp 2014-07-21 20:51:01 +0000 |
78 | @@ -33,7 +33,7 @@ |
79 | |
80 | Settings* TileItem::s_settings = 0; |
81 | |
82 | -QCache< TileItem*, QPixmap > TileItem::s_cache; |
83 | +QCache< TileItem::CacheKey, QPixmap > TileItem::s_cache; |
84 | |
85 | TileItem::TileItem(QObject* parent) : QObject(parent), |
86 | m_rect(), |
87 | @@ -59,8 +59,17 @@ |
88 | { |
89 | m_renderTask->cancel(true); |
90 | m_renderTask->wait(); |
91 | +} |
92 | |
93 | - s_cache.remove(this); |
94 | +void TileItem::dropCachedPixmaps(PageItem* page) |
95 | +{ |
96 | + foreach(CacheKey key, s_cache.keys()) |
97 | + { |
98 | + if(key.first == page) |
99 | + { |
100 | + s_cache.remove(key); |
101 | + } |
102 | + } |
103 | } |
104 | |
105 | void TileItem::paint(QPainter* painter, const QPointF& topLeft) |
106 | @@ -105,9 +114,9 @@ |
107 | { |
108 | if(keepObsoletePixmaps && s_settings->pageItem().keepObsoletePixmaps()) |
109 | { |
110 | - if(s_cache.contains(this)) |
111 | + if(s_cache.contains(cacheKey())) |
112 | { |
113 | - m_obsoletePixmap = *s_cache.object(this); |
114 | + m_obsoletePixmap = *s_cache.object(cacheKey()); |
115 | } |
116 | } |
117 | else |
118 | @@ -119,12 +128,11 @@ |
119 | |
120 | m_pixmapError = false; |
121 | m_pixmap = QPixmap(); |
122 | - s_cache.remove(this); |
123 | } |
124 | |
125 | int TileItem::startRender(bool prefetch) |
126 | { |
127 | - if(m_pixmapError || m_renderTask->isRunning() || (prefetch && s_cache.contains(this))) |
128 | + if(m_pixmapError || m_renderTask->isRunning() || (prefetch && s_cache.contains(cacheKey()))) |
129 | { |
130 | return 0; |
131 | } |
132 | @@ -184,7 +192,7 @@ |
133 | if(prefetch && !m_renderTask->wasCanceledForcibly()) |
134 | { |
135 | int cost = image.width() * image.height() * image.depth() / 8; |
136 | - s_cache.insert(this, new QPixmap(QPixmap::fromImage(image)), cost); |
137 | + s_cache.insert(cacheKey(), new QPixmap(QPixmap::fromImage(image)), cost); |
138 | } |
139 | else if(!m_renderTask->wasCanceled()) |
140 | { |
141 | @@ -197,13 +205,31 @@ |
142 | return qobject_cast< PageItem* >(parent()); |
143 | } |
144 | |
145 | +TileItem::CacheKey TileItem::cacheKey() const |
146 | +{ |
147 | + PageItem* page = parentPage(); |
148 | + QByteArray key; |
149 | + |
150 | + QDataStream(&key, QIODevice::WriteOnly) |
151 | + << page->m_renderParam.resolution.resolutionX |
152 | + << page->m_renderParam.resolution.resolutionY |
153 | + << page->m_renderParam.scaleFactor |
154 | + << page->m_renderParam.rotation |
155 | + << page->m_renderParam.invertColors |
156 | + << m_rect; |
157 | + |
158 | + return qMakePair(page, key); |
159 | +} |
160 | + |
161 | QPixmap TileItem::takePixmap() |
162 | { |
163 | QPixmap pixmap; |
164 | |
165 | - if(s_cache.contains(this)) |
166 | + if(s_cache.contains(cacheKey())) |
167 | { |
168 | - pixmap = *s_cache.object(this); |
169 | + pixmap = *s_cache.object(cacheKey()); |
170 | + |
171 | + m_obsoletePixmap = QPixmap(); |
172 | } |
173 | else |
174 | { |
175 | @@ -213,7 +239,7 @@ |
176 | m_pixmap = QPixmap(); |
177 | |
178 | int cost = pixmap.width() * pixmap.height() * pixmap.depth() / 8; |
179 | - s_cache.insert(this, new QPixmap(pixmap), cost); |
180 | + s_cache.insert(cacheKey(), new QPixmap(pixmap), cost); |
181 | } |
182 | else |
183 | { |
184 | |
185 | === modified file 'sources/tileitem.h' |
186 | --- sources/tileitem.h 2014-06-25 11:59:57 +0000 |
187 | +++ sources/tileitem.h 2014-07-21 20:51:01 +0000 |
188 | @@ -49,6 +49,8 @@ |
189 | inline void dropPixmap() { m_pixmap = QPixmap(); } |
190 | inline void dropObsoletePixmap() { m_obsoletePixmap = QPixmap(); } |
191 | |
192 | + static void dropCachedPixmaps(PageItem* page); |
193 | + |
194 | void paint(QPainter* painter, const QPointF& topLeft); |
195 | |
196 | public slots: |
197 | @@ -70,9 +72,11 @@ |
198 | |
199 | static Settings* s_settings; |
200 | |
201 | - static QCache< TileItem*, QPixmap > s_cache; |
202 | + typedef QPair< PageItem*, QByteArray > CacheKey; |
203 | + static QCache< CacheKey, QPixmap > s_cache; |
204 | |
205 | PageItem* parentPage() const; |
206 | + CacheKey cacheKey() const; |
207 | |
208 | QRect m_rect; |
209 |
Hello Martin,
thanks for attacking that problem. However, leaving aside the annotation resp. forms problem, I don't think that keying the index and rendering parameter (in whatever form) is a suitable key because it is not unique: The cache is a static variable and is therefore shared through the whole application, so if you open two documents with equal render parameters and page sizes (e.g. both A4), the pages with the same indices will occupy the same cache entries. And more subtle, if you open two instances of the same document, even their source (e.g. file path or something) will be the same but you could have added an annotation in one instance but not the other. So I think keying at least on the pointer (and invalidating on destruction, so that a newly created object cannot take a stale cache entry) is necessary.
Best regards, Adam.
P.S. The suggestion is really just what I would have tried, e.g. define
struct RenderParamters
{
qreal m_resolutionX;
qreal m_resolutionY;
...
bool operator<(const RenderParameters& that) const { /* lexicographical comparison or hashing or something */ }
};
and the use QPair< TileItem*, RenderParameters > as a cache key...