From 8a5ec974a197790466ca98f8a54c351147b42175 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Fri, 1 Feb 2013 01:37:46 +0100 Subject: [PATCH] - reuse element Tag arrays - use previous elements' RenderInstructions when tags and zoom match --- .../oscim/database/oscimap/MapDatabase.java | 23 +++- src/org/oscim/theme/MatchingCacheKey.java | 83 ++++++------ src/org/oscim/theme/RenderTheme.java | 119 +++++++++++++----- 3 files changed, 145 insertions(+), 80 deletions(-) diff --git a/src/org/oscim/database/oscimap/MapDatabase.java b/src/org/oscim/database/oscimap/MapDatabase.java index 26543fb8..fd610c34 100644 --- a/src/org/oscim/database/oscimap/MapDatabase.java +++ b/src/org/oscim/database/oscimap/MapDatabase.java @@ -226,6 +226,8 @@ public class MapDatabase implements IMapDatabase { mOpenFile = true; + initDecorder(); + return OpenResult.SUCCESS; } @@ -305,7 +307,19 @@ public class MapDatabase implements IMapDatabase { private short[] mIndices = new short[10]; private Tag[] mTmpTags = new Tag[20]; - private float[] mTmpCoords = new float[MAX_WAY_COORDS]; + private float[] mTmpCoords; + + private Tag[][] mElementTags; + + private void initDecorder() { + // reusable tag set + Tag[][] tags = new Tag[10][]; + for (int i = 0; i < 10; i++) + tags[i] = new Tag[i + 1]; + mElementTags = tags; + + mTmpCoords = new float[MAX_WAY_COORDS]; + } private boolean decode() throws IOException { @@ -503,8 +517,13 @@ public class MapDatabase implements IMapDatabase { if (cnt == 0) { Log.d(TAG, "got no TAG!"); } + Tag[] tags; + + if (cnt < 11) + tags = mElementTags[cnt - 1]; + else + tags = new Tag[cnt]; - Tag[] tags = new Tag[cnt]; for (int i = 0; i < cnt; i++) tags[i] = tmp[i]; diff --git a/src/org/oscim/theme/MatchingCacheKey.java b/src/org/oscim/theme/MatchingCacheKey.java index 85fc6a7d..24e2b1b4 100644 --- a/src/org/oscim/theme/MatchingCacheKey.java +++ b/src/org/oscim/theme/MatchingCacheKey.java @@ -17,84 +17,73 @@ package org.oscim.theme; import org.oscim.core.Tag; class MatchingCacheKey { - int mHashCodeValue; + int mHash; Tag[] mTags; MatchingCacheKey() { } - MatchingCacheKey(Tag[] tags) { - mTags = tags; - mHashCodeValue = calculateHashCode(); - } - MatchingCacheKey(MatchingCacheKey key) { - mTags = key.mTags; - mHashCodeValue = key.mHashCodeValue; + // need to clone tags as they belong to MapDatabase + mTags = key.mTags.clone(); + mHash = key.mHash; } - void set(Tag[] tags) { - mTags = tags; + // set temporary values for comparison + boolean set(Tag[] tags, MatchingCacheKey compare) { + int length = tags.length; + + if (compare != null && length == mTags.length) { + int i = 0; + for (; i < length; i++) { + Tag t1 = tags[i]; + Tag t2 = compare.mTags[i]; + + if (!(t1 == t2 || (t1.key == t2.key && t1.value == t2.value))) + break; + } + if (i == length) + return true; + } int result = 7; + for (int i = 0; i < length; i++) + result = 31 * result + tags[i].hashCode(); - for (int i = 0, n = mTags.length; i < n; i++) { - if (mTags[i] == null) - break; - result = 31 * result + mTags[i].hashCode(); - } - result = 31 * result; + mHash = 31 * result; + mTags = tags; - mHashCodeValue = result; + return false; } @Override public boolean equals(Object obj) { - if (this == obj) { + if (this == obj) return true; - } MatchingCacheKey other = (MatchingCacheKey) obj; - if (mTags == null) { - return (other.mTags == null); - } else if (other.mTags == null) - return false; + // if (mTags == null) { + // return (other.mTags == null); + // } else if (other.mTags == null) + // return false; int length = mTags.length; - if (length != other.mTags.length) { + if (length != other.mTags.length) return false; - } for (int i = 0; i < length; i++) { - if (mTags[i] == other.mTags[i]) - continue; - if (mTags[i].key == other.mTags[i].key && mTags[i].value == other.mTags[i].value) - continue; + Tag t1 = mTags[i]; + Tag t2 = other.mTags[i]; - return false; + if (!(t1 == t2 || (t1.key == t2.key && t1.value == t2.value))) + return false; } return true; } @Override public int hashCode() { - return mHashCodeValue; - } - - /** - * @return the hash code of this object. - */ - private int calculateHashCode() { - int result = 7; - - for (int i = 0, n = mTags.length; i < n; i++) { - if (mTags[i] == null) // FIXME - break; - result = 31 * result + mTags[i].hashCode(); - } - result = 31 * result; - - return result; + return mHash; } } diff --git a/src/org/oscim/theme/RenderTheme.java b/src/org/oscim/theme/RenderTheme.java index 0d3f300a..4536a4e9 100644 --- a/src/org/oscim/theme/RenderTheme.java +++ b/src/org/oscim/theme/RenderTheme.java @@ -28,7 +28,7 @@ import android.graphics.Color; * A RenderTheme defines how ways and nodes are drawn. */ public class RenderTheme { - //private final static String TAG = RenderTheme.class.getName(); + private final static String TAG = RenderTheme.class.getName(); private static final int MATCHING_CACHE_SIZE = 512; private static final int RENDER_THEME_VERSION = 1; @@ -96,10 +96,15 @@ public class RenderTheme { private ArrayList mAreaInstructionList = new ArrayList(4); private ArrayList mNodeInstructionList = new ArrayList(4); + private RenderInstructionItem mPrevAreaItem; + private RenderInstructionItem mPrevWayItem; + private RenderInstructionItem mPrevNodeItem; + class RenderInstructionItem { RenderInstructionItem next; int zoom; RenderInstruction[] list; + MatchingCacheKey key; } RenderTheme(int mapBackground, float baseStrokeWidth, float baseTextSize) { @@ -171,18 +176,29 @@ public class RenderTheme { RenderInstructionItem ri = null; synchronized (mNodesCache) { - MatchingCacheKey cacheKey = mNodeCacheKey; //new MatchingCacheKey(tags); - cacheKey.set(tags); - boolean found = mNodesCache.containsKey(cacheKey); int zoomMask = 1 << zoomLevel; - if (found) { - ris = mNodesCache.get(cacheKey); + MatchingCacheKey cacheKey = mNodeCacheKey; + if (mPrevNodeItem == null || (mPrevNodeItem.zoom & zoomMask) == 0) { + // previous instructions zoom does not match + cacheKey.set(tags, null); + } else { + // compare if tags match previous instructions + if (cacheKey.set(tags, mPrevNodeItem.key)) + ri = mPrevNodeItem; + } - for (ri = ris; ri != null; ri = ri.next) - if ((ri.zoom & zoomMask) != 0) - // cache hit - break; + if (ri == null) { + boolean found = mNodesCache.containsKey(cacheKey); + + if (found) { + ris = mNodesCache.get(cacheKey); + + for (ri = ris; ri != null; ri = ri.next) + if ((ri.zoom & zoomMask) != 0) + // cache hit + break; + } } if (ri == null) { @@ -234,16 +250,23 @@ public class RenderTheme { matches.toArray(ri.list); } // attach this list to the one found for MatchingKey - if (ris != null) + if (ris != null) { + ri.next = ris.next; + ri.key = ris.key; ris.next = ri; - else - mNodesCache.put(new MatchingCacheKey(cacheKey), ri); + } else { + ri.key = new MatchingCacheKey(cacheKey); + mNodesCache.put(ri.key, ri); + } } } } + if (ri.list != null) render(renderCallback, ri.list, tags); + mPrevNodeItem = ri; + return ri.list; } @@ -272,32 +295,54 @@ public class RenderTheme { // list of renderinsctruction items in cache RenderInstructionItem ris = null; + RenderInstructionItem prevInstructions = null; + // the item matching tags and zoomlevel RenderInstructionItem ri = null; + + // temporary matching instructions list List matches; LRUCache matchingCache; MatchingCacheKey cacheKey; - if (closed) { + if (closed) matchingCache = mAreaCache; - cacheKey = mAreaCacheKey; - matches = mAreaInstructionList; - } else { + else matchingCache = mWayCache; - cacheKey = mWayCacheKey; - matches = mWayInstructionList; - } + int zoomMask = 1 << zoomLevel; synchronized (matchingCache) { - cacheKey.set(tags); - ris = matchingCache.get(cacheKey); + if (closed) { + cacheKey = mAreaCacheKey; + matches = mAreaInstructionList; + prevInstructions = mPrevAreaItem; + } else { + cacheKey = mWayCacheKey; + matches = mWayInstructionList; + prevInstructions = mPrevWayItem; + } - for (ri = ris; ri != null; ri = ri.next) - if ((ri.zoom & zoomMask) != 0) - // cache hit - break; + if (prevInstructions == null || (prevInstructions.zoom & zoomMask) == 0) { + // previous instructions zoom does not match + cacheKey.set(tags, null); + } else { + // compare if tags match previous instructions + if (cacheKey.set(tags, prevInstructions.key)) { + //Log.d(TAG, "same as previous " + Arrays.deepToString(tags)); + ri = prevInstructions; + } + } + + if (ri == null) { + ris = matchingCache.get(cacheKey); + + for (ri = ris; ri != null; ri = ri.next) + if ((ri.zoom & zoomMask) != 0) + // cache hit + break; + } if (ri == null) { // cache miss @@ -341,10 +386,14 @@ public class RenderTheme { if (ri != null) { // we found a same matchting list on another zoomlevel ri.zoom |= zoomMask; - //Log.d(TAG, " same instructions " + size + " " + Arrays.deepToString(tags)); + //Log.d(TAG, + // zoomLevel + " same instructions " + size + " " + // + Arrays.deepToString(tags)); } else { - //Log.d(TAG, " new instructions " + size + " " + Arrays.deepToString(tags)); + //Log.d(TAG, + // zoomLevel + " new instructions " + size + " " + // + Arrays.deepToString(tags)); ri = new RenderInstructionItem(); ri.zoom = zoomMask; @@ -356,18 +405,26 @@ public class RenderTheme { // attach this list to the one found for MatchingKey if (ris != null) { + ri.next = ris.next; + ri.key = ris.key; ris.next = ri; - } - else { - matchingCache.put(new MatchingCacheKey(cacheKey), ri); + } else { + ri.key = new MatchingCacheKey(cacheKey); + matchingCache.put(ri.key, ri); } } } + if (closed) + mPrevAreaItem = ri; + else + mPrevWayItem = ri; } + if (render && ri.list != null) { for (int i = 0, n = ri.list.length; i < n; i++) ri.list[i].renderWay(renderCallback, tags); } + return ri.list; }