Kaydet (Commit) 9236370b authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl Kaydeden (comit) Miklos Vajna

android: use locking to make tile redraw more predictable

CopyOnWriteList is a good thread safe container to store tiles,
however any change to the list makes a internal copy of the
underlaying array which contains the changes. The effect of this
is that this changes aren't immediately shown or only partially
in the other (UI) thread. So they are sometimes partially drawn or
drawn with a delay. This replaces the CopyOnWriteList with a
simple thread unsafe ArrayList and introduces Read/Write locking
to all ArrayList operations. Read operations don't lock, only
a write operation locks access.

Change-Id: I5783c6cde96360a6fd47faa801eec35e4debb792
üst 6ab77b35
...@@ -7,88 +7,106 @@ import android.graphics.RectF; ...@@ -7,88 +7,106 @@ import android.graphics.RectF;
import android.graphics.Region; import android.graphics.Region;
import android.util.Log; import android.util.Log;
import org.libreoffice.LOEvent;
import org.libreoffice.LOKitShell; import org.libreoffice.LOKitShell;
import org.libreoffice.TileIdentifier; import org.libreoffice.TileIdentifier;
import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.FloatUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public abstract class ComposedTileLayer extends Layer implements ComponentCallbacks2 { public abstract class ComposedTileLayer extends Layer implements ComponentCallbacks2 {
private static final String LOGTAG = ComposedTileLayer.class.getSimpleName(); private static final String LOGTAG = ComposedTileLayer.class.getSimpleName();
protected final List<SubTile> tiles = new CopyOnWriteArrayList<SubTile>(); protected final List<SubTile> tiles = new ArrayList<SubTile>();
protected final IntSize tileSize; protected final IntSize tileSize;
protected RectF currentViewport = new RectF(); protected RectF currentViewport = new RectF();
protected float currentZoom; protected float currentZoom;
private final ReadWriteLock tilesReadWriteLock = new ReentrantReadWriteLock();
private final Lock tilesReadLock = tilesReadWriteLock.readLock();
private final Lock tilesWriteLock = tilesReadWriteLock.writeLock();
public ComposedTileLayer(Context context) { public ComposedTileLayer(Context context) {
context.registerComponentCallbacks(this); context.registerComponentCallbacks(this);
this.tileSize = new IntSize(256, 256); this.tileSize = new IntSize(256, 256);
} }
public void invalidate() { public void invalidate() {
tilesReadLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
tile.invalidate(); tile.invalidate();
} }
tilesReadLock.unlock();
} }
@Override @Override
public void beginTransaction() { public void beginTransaction() {
super.beginTransaction(); super.beginTransaction();
tilesReadLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
tile.beginTransaction(); tile.beginTransaction();
} }
tilesReadLock.unlock();
} }
@Override @Override
public void endTransaction() { public void endTransaction() {
tilesReadLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
tile.endTransaction(); tile.endTransaction();
} }
tilesReadLock.unlock();
super.endTransaction(); super.endTransaction();
} }
@Override @Override
public void draw(RenderContext context) { public void draw(RenderContext context) {
tilesReadLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
if (RectF.intersects(tile.getBounds(context), context.viewport)) { if (RectF.intersects(tile.getBounds(context), context.viewport)) {
tile.draw(context); tile.draw(context);
} }
} }
tilesReadLock.unlock();
} }
@Override @Override
protected void performUpdates(RenderContext context) { protected void performUpdates(RenderContext context) {
super.performUpdates(context); super.performUpdates(context);
tilesReadLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
tile.beginTransaction(); tile.beginTransaction();
tile.refreshTileMetrics(); tile.refreshTileMetrics();
tile.endTransaction(); tile.endTransaction();
tile.performUpdates(context); tile.performUpdates(context);
} }
tilesReadLock.unlock();
} }
@Override @Override
public Region getValidRegion(RenderContext context) { public Region getValidRegion(RenderContext context) {
Region validRegion = new Region(); Region validRegion = new Region();
tilesReadLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
validRegion.op(tile.getValidRegion(context), Region.Op.UNION); validRegion.op(tile.getValidRegion(context), Region.Op.UNION);
} }
tilesReadLock.unlock();
return validRegion; return validRegion;
} }
@Override @Override
public void setResolution(float newResolution) { public void setResolution(float newResolution) {
super.setResolution(newResolution); super.setResolution(newResolution);
tilesReadLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
tile.setResolution(newResolution); tile.setResolution(newResolution);
} }
tilesReadLock.unlock();
} }
protected RectF roundToTileSize(RectF input, IntSize tileSize) { protected RectF roundToTileSize(RectF input, IntSize tileSize) {
...@@ -144,6 +162,20 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba ...@@ -144,6 +162,20 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
protected abstract int getTilePriority(); protected abstract int getTilePriority();
private boolean containsTilesMatching(float x, float y, float currentZoom) {
tilesReadLock.lock();
try {
for (SubTile tile : tiles) {
if (tile.id.x == x && tile.id.y == y && tile.id.zoom == currentZoom) {
return true;
}
}
return false;
} finally {
tilesReadLock.unlock();
}
}
private void addNewTiles(RectF pageRect) { private void addNewTiles(RectF pageRect) {
for (float y = currentViewport.top; y < currentViewport.bottom; y += tileSize.height) { for (float y = currentViewport.top; y < currentViewport.bottom; y += tileSize.height) {
if (y > pageRect.height()) { if (y > pageRect.height()) {
...@@ -153,13 +185,7 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba ...@@ -153,13 +185,7 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
if (x > pageRect.width()) { if (x > pageRect.width()) {
continue; continue;
} }
boolean contains = false; if (!containsTilesMatching(x, y, currentZoom)) {
for (SubTile tile : tiles) {
if (tile.id.x == x && tile.id.y == y && tile.id.zoom == currentZoom) {
contains = true;
}
}
if (!contains) {
TileIdentifier tileId = new TileIdentifier((int) x, (int) y, currentZoom, tileSize); TileIdentifier tileId = new TileIdentifier((int) x, (int) y, currentZoom, tileSize);
LOKitShell.sendTileRequestEvent(this, tileId, true, getTilePriority()); LOKitShell.sendTileRequestEvent(this, tileId, true, getTilePriority());
} }
...@@ -169,6 +195,7 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba ...@@ -169,6 +195,7 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
private void clearMarkedTiles() { private void clearMarkedTiles() {
List<SubTile> tilesToRemove = new ArrayList<SubTile>(); List<SubTile> tilesToRemove = new ArrayList<SubTile>();
tilesWriteLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
if (tile.markedForRemoval) { if (tile.markedForRemoval) {
tile.destroy(); tile.destroy();
...@@ -176,9 +203,11 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba ...@@ -176,9 +203,11 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
} }
} }
tiles.removeAll(tilesToRemove); tiles.removeAll(tilesToRemove);
tilesWriteLock.unlock();
} }
private void markTiles() { private void markTiles() {
tilesReadLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
if (FloatUtils.fuzzyEquals(tile.id.zoom, currentZoom)) { if (FloatUtils.fuzzyEquals(tile.id.zoom, currentZoom)) {
RectF tileRect = tile.id.getRectF(); RectF tileRect = tile.id.getRectF();
...@@ -189,16 +218,21 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba ...@@ -189,16 +218,21 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
tile.markForRemoval(); tile.markForRemoval();
} }
} }
tilesReadLock.unlock();
} }
public void clearAndReset() { public void clearAndReset() {
tilesWriteLock.lock();
tiles.clear(); tiles.clear();
tilesWriteLock.unlock();
currentViewport = new RectF(); currentViewport = new RectF();
} }
public void addTile(SubTile tile) { public void addTile(SubTile tile) {
tile.beginTransaction(); tile.beginTransaction();
tilesWriteLock.lock();
tiles.add(tile); tiles.add(tile);
tilesWriteLock.unlock();
} }
public boolean isStillValid(TileIdentifier tileId) { public boolean isStillValid(TileIdentifier tileId) {
...@@ -210,12 +244,13 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba ...@@ -210,12 +244,13 @@ public abstract class ComposedTileLayer extends Layer implements ComponentCallba
*/ */
public void invalidateTiles(List<SubTile> tilesToInvalidate, RectF cssRect) { public void invalidateTiles(List<SubTile> tilesToInvalidate, RectF cssRect) {
RectF zoomedRect = RectUtils.scale(cssRect, currentZoom); RectF zoomedRect = RectUtils.scale(cssRect, currentZoom);
tilesReadLock.lock();
for (SubTile tile : tiles) { for (SubTile tile : tiles) {
if (!tile.markedForRemoval && RectF.intersects(zoomedRect, tile.id.getRectF())) { if (!tile.markedForRemoval && RectF.intersects(zoomedRect, tile.id.getRectF())) {
tilesToInvalidate.add(tile); tilesToInvalidate.add(tile);
} }
} }
tilesReadLock.unlock();
} }
@Override @Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment