update tile rendering code
parent
6c72f8052f
commit
218567c7f2
2
pom.xml
2
pom.xml
|
@ -55,7 +55,7 @@
|
|||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>42.1.4</version>
|
||||
<version>42.2.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -100,10 +100,6 @@ Max y value for blocks to render (in Mapblocks)
|
|||
Min y value for blocks to render (in Mapblocks)
|
||||
* Default: **-1** (Equals -16 blocks)
|
||||
|
||||
### tilerenderer.processes
|
||||
Number of processes to allow for rendering
|
||||
* Default: **24**
|
||||
|
||||
### tilerenderer.updateinterval
|
||||
Update interval to check for new tiles (in seconds)
|
||||
* Default: **20**
|
||||
|
|
|
@ -9,12 +9,15 @@ import java.io.ByteArrayOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import com.google.common.util.concurrent.Striped;
|
||||
import io.rudin.minetest.tileserver.config.TileServerConfig;
|
||||
import org.jooq.Condition;
|
||||
import org.jooq.DSLContext;
|
||||
|
@ -42,10 +45,22 @@ public class TileRenderer {
|
|||
this.cfg = cfg;
|
||||
|
||||
this.yCondition = BLOCKS.POSY.between(cfg.tilesMinY(), cfg.tilesMaxY());
|
||||
this.lock = Striped.lazyWeakReadWriteLock(50);
|
||||
|
||||
ImageIO.setUseCache(false);
|
||||
}
|
||||
|
||||
|
||||
private String getKey(int x, int y, int z){
|
||||
return x + "/" + y + "/" + z;
|
||||
}
|
||||
|
||||
private final Striped<ReadWriteLock> lock;
|
||||
|
||||
private ReadWriteLock getLock(int x, int y, int z){
|
||||
return lock.get(getKey(x,y,z));
|
||||
}
|
||||
|
||||
private final Condition yCondition;
|
||||
|
||||
private final TileServerConfig cfg;
|
||||
|
@ -119,7 +134,17 @@ public class TileRenderer {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render the actual image to a bufferedImage
|
||||
* @param tileX
|
||||
* @param tileY
|
||||
* @param zoom
|
||||
* @return
|
||||
* @throws IllegalArgumentException
|
||||
* @throws DataFormatException
|
||||
* @throws IOException
|
||||
* @throws ExecutionException
|
||||
*/
|
||||
public BufferedImage renderImage(int tileX, int tileY, int zoom) throws IllegalArgumentException, DataFormatException, IOException, ExecutionException {
|
||||
|
||||
//Check if binary cached, use cached version for rendering
|
||||
|
@ -128,165 +153,185 @@ public class TileRenderer {
|
|||
return ImageIO.read(new ByteArrayInputStream(data));
|
||||
}
|
||||
|
||||
final int HALF_TILE_PIXEL_SIZE = CoordinateResolver.TILE_PIXEL_SIZE / 2;
|
||||
ReadWriteLock lock = getLock(tileX, tileY, zoom);
|
||||
Lock writeLock = lock.writeLock();
|
||||
//writeLock.lock();
|
||||
|
||||
try {
|
||||
|
||||
//re-check in critical section
|
||||
/*
|
||||
if (cache.has(tileX, tileY, zoom)) {
|
||||
byte[] data = cache.get(tileX, tileY, zoom);
|
||||
return ImageIO.read(new ByteArrayInputStream(data));
|
||||
}
|
||||
*/
|
||||
|
||||
final int HALF_TILE_PIXEL_SIZE = CoordinateResolver.TILE_PIXEL_SIZE / 2;
|
||||
|
||||
|
||||
if (zoom > DEFAULT_BLOCK_ZOOM) {
|
||||
//TODO: needed anymore?
|
||||
//Zoom in
|
||||
if (zoom > DEFAULT_BLOCK_ZOOM) {
|
||||
//TODO: needed anymore?
|
||||
//Zoom in
|
||||
|
||||
int nextZoom = zoom - 1;
|
||||
int offsetNextZoomX = Math.abs(tileX % 2);
|
||||
int offsetNextZoomY = Math.abs(tileY % 2);
|
||||
int nextZoom = zoom - 1;
|
||||
int offsetNextZoomX = Math.abs(tileX % 2);
|
||||
int offsetNextZoomY = Math.abs(tileY % 2);
|
||||
|
||||
int nextZoomX = (tileX - offsetNextZoomX) / 2;
|
||||
int nextZoomY = (tileY - offsetNextZoomY) / 2;
|
||||
int nextZoomX = (tileX - offsetNextZoomX) / 2;
|
||||
int nextZoomY = (tileY - offsetNextZoomY) / 2;
|
||||
|
||||
BufferedImage tile = createTile();
|
||||
Graphics2D graphics = tile.createGraphics();
|
||||
BufferedImage tile = createTile();
|
||||
Graphics2D graphics = tile.createGraphics();
|
||||
|
||||
//Get all quadrants
|
||||
BufferedImage image = renderImage(nextZoomX, nextZoomY, nextZoom);
|
||||
|
||||
BufferedImage quadrant = image.getSubimage(
|
||||
HALF_TILE_PIXEL_SIZE * offsetNextZoomX,
|
||||
HALF_TILE_PIXEL_SIZE * offsetNextZoomY,
|
||||
HALF_TILE_PIXEL_SIZE,
|
||||
HALF_TILE_PIXEL_SIZE
|
||||
);
|
||||
|
||||
Image scaledInstance = quadrant.getScaledInstance(
|
||||
CoordinateResolver.TILE_PIXEL_SIZE,
|
||||
CoordinateResolver.TILE_PIXEL_SIZE,
|
||||
Image.SCALE_FAST
|
||||
);
|
||||
|
||||
graphics.drawImage(scaledInstance, 0, 0, CoordinateResolver.TILE_PIXEL_SIZE, CoordinateResolver.TILE_PIXEL_SIZE, null);
|
||||
//Get all quadrants
|
||||
BufferedImage image = renderImage(nextZoomX, nextZoomY, nextZoom);
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream(12000);
|
||||
ImageIO.write(tile, "png", output);
|
||||
BufferedImage quadrant = image.getSubimage(
|
||||
HALF_TILE_PIXEL_SIZE * offsetNextZoomX,
|
||||
HALF_TILE_PIXEL_SIZE * offsetNextZoomY,
|
||||
HALF_TILE_PIXEL_SIZE,
|
||||
HALF_TILE_PIXEL_SIZE
|
||||
);
|
||||
|
||||
byte[] data = output.toByteArray();
|
||||
Image scaledInstance = quadrant.getScaledInstance(
|
||||
CoordinateResolver.TILE_PIXEL_SIZE,
|
||||
CoordinateResolver.TILE_PIXEL_SIZE,
|
||||
Image.SCALE_FAST
|
||||
);
|
||||
|
||||
cache.put(tileX, tileY, zoom, data);
|
||||
graphics.drawImage(scaledInstance, 0, 0, CoordinateResolver.TILE_PIXEL_SIZE, CoordinateResolver.TILE_PIXEL_SIZE, null);
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream(12000);
|
||||
ImageIO.write(tile, "png", output);
|
||||
|
||||
byte[] data = output.toByteArray();
|
||||
|
||||
cache.put(tileX, tileY, zoom, data);
|
||||
|
||||
|
||||
return tile;
|
||||
return tile;
|
||||
|
||||
|
||||
} else if (zoom < DEFAULT_BLOCK_ZOOM) {
|
||||
//Zoom out
|
||||
} else if (zoom < DEFAULT_BLOCK_ZOOM) {
|
||||
//Zoom out
|
||||
|
||||
|
||||
BufferedImage tile = createTile();
|
||||
//Pack 4 tiles from higher zoom into 1 tile
|
||||
BufferedImage tile = createTile();
|
||||
//Pack 4 tiles from higher zoom into 1 tile
|
||||
|
||||
int nextZoom = zoom + 1;
|
||||
int nextZoomX = tileX * 2;
|
||||
int nextZoomY = tileY * 2;
|
||||
int nextZoom = zoom + 1;
|
||||
int nextZoomX = tileX * 2;
|
||||
int nextZoomY = tileY * 2;
|
||||
|
||||
BufferedImage upperLeft = renderImage(nextZoomX, nextZoomY, nextZoom);
|
||||
BufferedImage upperRightImage = renderImage(nextZoomX+1, nextZoomY, nextZoom);
|
||||
BufferedImage lowerLeftImage = renderImage(nextZoomX, nextZoomY+1, nextZoom);
|
||||
BufferedImage lowerRightImage = renderImage(nextZoomX+1, nextZoomY+1, nextZoom);
|
||||
BufferedImage upperLeft = renderImage(nextZoomX, nextZoomY, nextZoom);
|
||||
BufferedImage upperRightImage = renderImage(nextZoomX + 1, nextZoomY, nextZoom);
|
||||
BufferedImage lowerLeftImage = renderImage(nextZoomX, nextZoomY + 1, nextZoom);
|
||||
BufferedImage lowerRightImage = renderImage(nextZoomX + 1, nextZoomY + 1, nextZoom);
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Graphics2D graphics = tile.createGraphics();
|
||||
|
||||
Image upperLeftScaledInstance = upperLeft.getScaledInstance(HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, Image.SCALE_FAST);
|
||||
graphics.drawImage(upperLeftScaledInstance, 0, 0, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, null);
|
||||
|
||||
Image upperRightScaledInstance = upperRightImage.getScaledInstance(HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, Image.SCALE_FAST);
|
||||
graphics.drawImage(upperRightScaledInstance, HALF_TILE_PIXEL_SIZE, 0, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, null);
|
||||
|
||||
Image lowerLeftScaledInstance = lowerLeftImage.getScaledInstance(HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, Image.SCALE_FAST);
|
||||
graphics.drawImage(lowerLeftScaledInstance, 0, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, null);
|
||||
|
||||
Image lowerRightScaledInstance = lowerRightImage.getScaledInstance(HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, Image.SCALE_FAST);
|
||||
graphics.drawImage(lowerRightScaledInstance, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, null);
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream(12000);
|
||||
ImageIO.write(tile, "png", output);
|
||||
|
||||
byte[] data = output.toByteArray();
|
||||
|
||||
long diff = System.currentTimeMillis() - start;
|
||||
|
||||
logger.debug("Timings of cross-stitched tile X={} Y={} Zoom={}: render={} ms", tileX, tileY, zoom, diff);
|
||||
|
||||
cache.put(tileX, tileY, zoom, data);
|
||||
|
||||
|
||||
return tile;
|
||||
|
||||
}
|
||||
|
||||
//Default zoom (13 == 1 mapblock with 16px wide blocks)
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
Graphics2D graphics = tile.createGraphics();
|
||||
MapBlockCoordinateInfo coordinateInfo = CoordinateResolver.fromTile(tileX, tileY, zoom);
|
||||
|
||||
Image upperLeftScaledInstance = upperLeft.getScaledInstance(HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, Image.SCALE_FAST);
|
||||
graphics.drawImage(upperLeftScaledInstance, 0, 0, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, null);
|
||||
//16x16 mapblocks on a tile
|
||||
BufferedImage image = createTile();
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
|
||||
Image upperRightScaledInstance = upperRightImage.getScaledInstance(HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, Image.SCALE_FAST);
|
||||
graphics.drawImage(upperRightScaledInstance, HALF_TILE_PIXEL_SIZE, 0, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, null);
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fillRect(0, 0, CoordinateResolver.TILE_PIXEL_SIZE, CoordinateResolver.TILE_PIXEL_SIZE);
|
||||
|
||||
Image lowerLeftScaledInstance = lowerLeftImage.getScaledInstance(HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, Image.SCALE_FAST);
|
||||
graphics.drawImage(lowerLeftScaledInstance, 0, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, null);
|
||||
|
||||
Image lowerRightScaledInstance = lowerRightImage.getScaledInstance(HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, Image.SCALE_FAST);
|
||||
graphics.drawImage(lowerRightScaledInstance, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, HALF_TILE_PIXEL_SIZE, null);
|
||||
int mapblockX = coordinateInfo.x;
|
||||
int mapblockZ = coordinateInfo.z;
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
long timingImageSetup = now - start;
|
||||
start = now;
|
||||
|
||||
|
||||
Integer count = ctx
|
||||
.select(DSL.count())
|
||||
.from(BLOCKS)
|
||||
.where(
|
||||
BLOCKS.POSX.eq(mapblockX)
|
||||
.and(BLOCKS.POSZ.eq(mapblockZ))
|
||||
.and(yCondition)
|
||||
)
|
||||
.fetchOne(DSL.count());
|
||||
|
||||
now = System.currentTimeMillis();
|
||||
long timingZeroCountCheck = now - start;
|
||||
start = now;
|
||||
|
||||
long timingRender = 0;
|
||||
|
||||
if (count > 0) {
|
||||
|
||||
|
||||
blockRenderer.render(mapblockX, mapblockZ, graphics, 16);
|
||||
|
||||
now = System.currentTimeMillis();
|
||||
timingRender = now - start;
|
||||
start = now;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream(12000);
|
||||
ImageIO.write(tile, "png", output);
|
||||
ImageIO.write(image, "png", output);
|
||||
|
||||
byte[] data = output.toByteArray();
|
||||
|
||||
long diff = System.currentTimeMillis() - start;
|
||||
now = System.currentTimeMillis();
|
||||
long timingBufferOutput = now - start;
|
||||
|
||||
logger.debug("Timings of cross-stitched tile X={} Y={} Zoom={}: render={} ms", tileX, tileY, zoom, diff);
|
||||
|
||||
logger.debug("Timings of tile X={} Y={} Zoom={}: setup={} ms, zeroCheck={} ms, render={} ms, output={} ms",
|
||||
tileX, tileY, zoom,
|
||||
timingImageSetup, timingZeroCountCheck, timingRender, timingBufferOutput
|
||||
);
|
||||
|
||||
cache.put(tileX, tileY, zoom, data);
|
||||
|
||||
|
||||
return tile;
|
||||
return image;
|
||||
|
||||
} finally {
|
||||
//writeLock.unlock();
|
||||
|
||||
}
|
||||
|
||||
//Default zoom (13 == 1 mapblock with 16px wide blocks)
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
MapBlockCoordinateInfo coordinateInfo = CoordinateResolver.fromTile(tileX, tileY, zoom);
|
||||
|
||||
//16x16 mapblocks on a tile
|
||||
BufferedImage image = createTile();
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fillRect(0, 0, CoordinateResolver.TILE_PIXEL_SIZE, CoordinateResolver.TILE_PIXEL_SIZE);
|
||||
|
||||
|
||||
int mapblockX = coordinateInfo.x;
|
||||
int mapblockZ = coordinateInfo.z;
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
long timingImageSetup = now - start;
|
||||
start = now;
|
||||
|
||||
|
||||
Integer count = ctx
|
||||
.select(DSL.count())
|
||||
.from(BLOCKS)
|
||||
.where(
|
||||
BLOCKS.POSX.eq(mapblockX)
|
||||
.and(BLOCKS.POSZ.eq(mapblockZ))
|
||||
.and(yCondition)
|
||||
)
|
||||
.fetchOne(DSL.count());
|
||||
|
||||
now = System.currentTimeMillis();
|
||||
long timingZeroCountCheck = now - start;
|
||||
start = now;
|
||||
|
||||
long timingRender = 0;
|
||||
|
||||
if (count > 0) {
|
||||
|
||||
|
||||
blockRenderer.render(mapblockX, mapblockZ, graphics, 16);
|
||||
|
||||
now = System.currentTimeMillis();
|
||||
timingRender = now - start;
|
||||
start = now;
|
||||
}
|
||||
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream(12000);
|
||||
ImageIO.write(image, "png", output);
|
||||
|
||||
byte[] data = output.toByteArray();
|
||||
|
||||
now = System.currentTimeMillis();
|
||||
long timingBufferOutput = now - start;
|
||||
|
||||
|
||||
logger.debug("Timings of tile X={} Y={} Zoom={}: setup={} ms, zeroCheck={} ms, render={} ms, output={} ms",
|
||||
tileX, tileY, zoom,
|
||||
timingImageSetup, timingZeroCountCheck, timingRender, timingBufferOutput
|
||||
);
|
||||
|
||||
cache.put(tileX, tileY, zoom, data);
|
||||
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,10 +30,6 @@ public interface TileServerConfig extends Config {
|
|||
@DefaultValue("false")
|
||||
boolean tilerendererEnableInitialRendering();
|
||||
|
||||
@Key("tilerenderer.processes")
|
||||
@DefaultValue("24")
|
||||
int tilerendererProcesses();
|
||||
|
||||
@Key("tilerenderer.updateinterval")
|
||||
@DefaultValue("20")
|
||||
int tilerendererUpdateInterval();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.rudin.minetest.tileserver.job;
|
||||
|
||||
import com.google.common.util.concurrent.Striped;
|
||||
import io.rudin.minetest.tileserver.TileRenderer;
|
||||
import io.rudin.minetest.tileserver.config.TileServerConfig;
|
||||
import io.rudin.minetest.tileserver.util.CoordinateResolver;
|
||||
|
@ -13,6 +14,8 @@ import org.slf4j.LoggerFactory;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
||||
import static io.rudin.minetest.tileserver.blockdb.tables.Blocks.BLOCKS;
|
||||
|
||||
@Singleton
|
||||
|
@ -35,90 +38,97 @@ public class InitialTileRendererJob implements Runnable {
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
logger.info("Starting initial tilerendering");
|
||||
try {
|
||||
logger.info("Starting initial tilerendering");
|
||||
|
||||
Record2<Integer, Integer> minMaxRecords = ctx
|
||||
.select(DSL.max(BLOCKS.POSX), DSL.min(BLOCKS.POSX))
|
||||
.from(BLOCKS)
|
||||
.where(yCondition)
|
||||
.fetchOne();
|
||||
|
||||
Integer maxX = minMaxRecords.get(DSL.max(BLOCKS.POSX));
|
||||
Integer minX = minMaxRecords.get(DSL.min(BLOCKS.POSX));
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
int xStripes = maxX - minX;
|
||||
long byteCount = 0;
|
||||
int errorCount = 0;
|
||||
long lastProgressUpdate = 0;
|
||||
int tileCount = 0;
|
||||
|
||||
logger.info("X-Stripe count: {} (from {} to {})", xStripes, minX, maxX);
|
||||
|
||||
for (int posx = minX; posx <= maxX; posx++){
|
||||
|
||||
double xProgress = (posx - minX) / (double)xStripes;
|
||||
|
||||
minMaxRecords = ctx
|
||||
.select(DSL.max(BLOCKS.POSZ), DSL.min(BLOCKS.POSZ))
|
||||
Record2<Integer, Integer> minMaxRecords = ctx
|
||||
.select(DSL.max(BLOCKS.POSX), DSL.min(BLOCKS.POSX))
|
||||
.from(BLOCKS)
|
||||
.where(yCondition)
|
||||
.and(BLOCKS.POSX.eq(posx))
|
||||
.fetchOne();
|
||||
|
||||
Integer maxZ = minMaxRecords.get(DSL.max(BLOCKS.POSZ));
|
||||
Integer minZ = minMaxRecords.get(DSL.min(BLOCKS.POSZ));
|
||||
Integer maxX = minMaxRecords.get(DSL.max(BLOCKS.POSX));
|
||||
Integer minX = minMaxRecords.get(DSL.min(BLOCKS.POSX));
|
||||
|
||||
int zCount = maxZ - minZ;
|
||||
long start = System.currentTimeMillis();
|
||||
int xStripes = maxX - minX;
|
||||
long byteCount = 0;
|
||||
int errorCount = 0;
|
||||
long lastProgressUpdate = 0;
|
||||
int tileCount = 0;
|
||||
|
||||
for (int posz = minZ; posz <= maxZ; posz++){
|
||||
logger.info("X-Stripe count: {} (from {} to {})", xStripes, minX, maxX);
|
||||
|
||||
double zProgress = (posz - minZ) / (double)zCount;
|
||||
for (int posx = minX; posx <= maxX; posx++) {
|
||||
|
||||
CoordinateResolver.TileInfo tileInfo = CoordinateResolver.fromCoordinates(posx, posz);
|
||||
double xProgress = (posx - minX) / (double) xStripes;
|
||||
|
||||
try {
|
||||
byte[] data = renderer.render(tileInfo.x, tileInfo.x, CoordinateResolver.ONE_TO_ONE_ZOOM);
|
||||
byteCount += data.length;
|
||||
tileCount++;
|
||||
minMaxRecords = ctx
|
||||
.select(DSL.max(BLOCKS.POSZ), DSL.min(BLOCKS.POSZ))
|
||||
.from(BLOCKS)
|
||||
.where(yCondition)
|
||||
.and(BLOCKS.POSX.eq(posx))
|
||||
.fetchOne();
|
||||
|
||||
} catch (Exception e){
|
||||
errorCount++;
|
||||
logger.error("run", e);
|
||||
Integer maxZ = minMaxRecords.get(DSL.max(BLOCKS.POSZ));
|
||||
Integer minZ = minMaxRecords.get(DSL.min(BLOCKS.POSZ));
|
||||
|
||||
if (errorCount > 100){
|
||||
logger.error("Error-count: {}, bailing out!", errorCount);
|
||||
return;
|
||||
}
|
||||
if (maxZ == null || minZ == null){
|
||||
continue; //empty column
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
long diff = now - lastProgressUpdate;
|
||||
int zCount = maxZ - minZ;
|
||||
|
||||
if (diff > 5000){
|
||||
//Report every 5 seconds
|
||||
lastProgressUpdate = now;
|
||||
for (int posz = minZ; posz <= maxZ; posz++) {
|
||||
|
||||
logger.info("Initial rendering status: x-progress({}%) z-progress({}%) tiles({}) data({} MB) posx({}) posz({})",
|
||||
Math.floor(xProgress*100), Math.floor(zProgress*100), tileCount, Math.floor(byteCount/1000)/1000,
|
||||
posx, posz
|
||||
);
|
||||
double zProgress = (posz - minZ) / (double) zCount;
|
||||
|
||||
CoordinateResolver.TileInfo tileInfo = CoordinateResolver.fromCoordinates(posx, posz);
|
||||
|
||||
try {
|
||||
byte [] data = renderer.render(tileInfo.x, tileInfo.y, CoordinateResolver.ONE_TO_ONE_ZOOM);
|
||||
byteCount += data.length;
|
||||
tileCount++;
|
||||
|
||||
} catch (Exception e) {
|
||||
errorCount++;
|
||||
logger.error("run", e);
|
||||
|
||||
if (errorCount > 100) {
|
||||
logger.error("Error-count: {}, bailing out!", errorCount);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
long diff = now - lastProgressUpdate;
|
||||
|
||||
if (diff > 5000) {
|
||||
//Report every 5 seconds
|
||||
lastProgressUpdate = now;
|
||||
|
||||
logger.info("Initial rendering status: x-progress({}%) z-progress({}%) tiles({}) data({} MB) posx({}) posz({})",
|
||||
Math.floor(xProgress * 100), Math.floor(zProgress * 100), tileCount, Math.floor(byteCount / 1000) / 1000,
|
||||
posx, posz
|
||||
);
|
||||
|
||||
//Explicit gc()
|
||||
//Runtime.getRuntime().gc();
|
||||
}
|
||||
|
||||
//Explicit gc()
|
||||
Runtime.getRuntime().gc();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
long renderTime = System.currentTimeMillis() - start;
|
||||
|
||||
logger.info("Initial rendering completed in {} seconds ({} MB, {} tiles)",
|
||||
renderTime, Math.floor(byteCount / 1000) / 1000, tileCount
|
||||
);
|
||||
|
||||
} catch (Exception e){
|
||||
logger.error("run", e);
|
||||
}
|
||||
|
||||
long renderTime = System.currentTimeMillis() - start;
|
||||
|
||||
logger.info("Initial rendering completed in {} seconds ({} MB, {} tiles)",
|
||||
renderTime, Math.floor(byteCount/1000)/1000, tileCount
|
||||
);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ public class UpdateChangedTilesJob implements Runnable {
|
|||
.limit(LIMIT)
|
||||
.fetch();
|
||||
|
||||
if (blocks.size() == LIMIT){
|
||||
if (blocks.size() == LIMIT) {
|
||||
logger.warn("Got max-blocks ({}) from update-queue", LIMIT);
|
||||
}
|
||||
|
||||
|
@ -105,11 +105,11 @@ public class UpdateChangedTilesJob implements Runnable {
|
|||
|
||||
List<String> updatedTileKeys = new ArrayList<>();
|
||||
|
||||
for (BlocksRecord record: blocks) {
|
||||
for (BlocksRecord record : blocks) {
|
||||
Integer x = record.getPosx();
|
||||
Integer z = record.getPosz();
|
||||
|
||||
if (record.getMtime() > latestTimestamp){
|
||||
if (record.getMtime() > latestTimestamp) {
|
||||
//Update timestamp
|
||||
latestTimestamp = record.getMtime();
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ public class UpdateChangedTilesJob implements Runnable {
|
|||
TileInfo tileInfo = CoordinateResolver.fromCoordinates(x, z);
|
||||
|
||||
//remove all tiles in every zoom
|
||||
for (int i=CoordinateResolver.MAX_ZOOM; i>=CoordinateResolver.MIN_ZOOM; i--) {
|
||||
for (int i = CoordinateResolver.MAX_ZOOM; i >= CoordinateResolver.MIN_ZOOM; i--) {
|
||||
TileInfo zoomedTile = tileInfo.toZoom(i);
|
||||
String tileKey = getTileKey(zoomedTile);
|
||||
|
||||
|
@ -140,6 +140,8 @@ public class UpdateChangedTilesJob implements Runnable {
|
|||
|
||||
}
|
||||
|
||||
} catch(Exception e){
|
||||
logger.error("tile-updater", e);
|
||||
|
||||
} finally {
|
||||
running = false;
|
||||
|
|
|
@ -24,7 +24,6 @@ public class TileRoute implements Route {
|
|||
@Inject
|
||||
public TileRoute(TileRenderer renderer, TileServerConfig cfg, TileCache cache) {
|
||||
this.renderer = renderer;
|
||||
this.executorService = Executors.newFixedThreadPool(cfg.tilerendererProcesses());
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
|
@ -32,8 +31,6 @@ public class TileRoute implements Route {
|
|||
|
||||
private final TileCache cache;
|
||||
|
||||
private final ExecutorService executorService;
|
||||
|
||||
@Override
|
||||
public Object handle(Request req, Response res) throws Exception {
|
||||
res.header("Content-Type", "image/png");
|
||||
|
@ -49,10 +46,7 @@ public class TileRoute implements Route {
|
|||
|
||||
logger.debug("Rendering tile @ {}/{} zoom: {}", x,y,z);
|
||||
|
||||
//dispatch rendering to fixed pool
|
||||
Future<byte[]> future = executorService.submit(() -> renderer.render(x, y, z));
|
||||
|
||||
return future.get();
|
||||
return renderer.render(x, y, z);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import javax.inject.Inject;
|
|||
import javax.inject.Singleton;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
|
@ -29,10 +30,9 @@ public class DatabaseTileCache implements TileCache {
|
|||
private static final Logger logger = LoggerFactory.getLogger(DatabaseTileCache.class);
|
||||
|
||||
@Inject
|
||||
public DatabaseTileCache(@TileDB DSLContext ctx, ExecutorService executor){
|
||||
public DatabaseTileCache(@TileDB DSLContext ctx){
|
||||
this.ctx = ctx;
|
||||
this.lock = Striped.lazyWeakReadWriteLock(50);
|
||||
this.executor = executor;
|
||||
|
||||
this.cache = CacheBuilder
|
||||
.newBuilder()
|
||||
|
@ -45,16 +45,14 @@ public class DatabaseTileCache implements TileCache {
|
|||
|
||||
private final Cache<String, byte[]> cache;
|
||||
|
||||
private final ExecutorService executor;
|
||||
|
||||
private final Striped<ReadWriteLock> lock;
|
||||
|
||||
private final DSLContext ctx;
|
||||
|
||||
private String getKey(int x, int y, int z){
|
||||
return x + "/" + y + "/" + z;
|
||||
}
|
||||
|
||||
private final Striped<ReadWriteLock> lock;
|
||||
|
||||
private ReadWriteLock getLock(int x, int y, int z){
|
||||
return lock.get(getKey(x,y,z));
|
||||
}
|
||||
|
@ -63,68 +61,52 @@ public class DatabaseTileCache implements TileCache {
|
|||
public void put(int x, int y, int z, byte[] data) {
|
||||
|
||||
final String key = getKey(x,y,z);
|
||||
|
||||
cache.put(key, data);
|
||||
|
||||
ReadWriteLock lock = getLock(x, y, z);
|
||||
Lock writeLock = lock.writeLock();
|
||||
writeLock.lock();
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
TilesRecord record = ctx.newRecord(TILES);
|
||||
record.setTile(data);
|
||||
record.setX(x);
|
||||
record.setY(y);
|
||||
record.setZ(z);
|
||||
record.setMtime(now);
|
||||
|
||||
AsyncTileInsertJob job = new AsyncTileInsertJob(record, lock);
|
||||
executor.submit(job);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Async insert job
|
||||
*/
|
||||
public class AsyncTileInsertJob implements Runnable {
|
||||
|
||||
AsyncTileInsertJob(TilesRecord record, ReadWriteLock lock){
|
||||
this.record = record;
|
||||
this.lock = lock;
|
||||
}
|
||||
|
||||
private final ReadWriteLock lock;
|
||||
|
||||
private final TilesRecord record;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Lock writeLock = lock.writeLock();
|
||||
writeLock.lock();
|
||||
try {
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
//re-check in critical section
|
||||
if (has(record.getX(),record.getY(),record.getZ(), false)){
|
||||
//already inserted
|
||||
return;
|
||||
}
|
||||
TilesRecord record = ctx
|
||||
.selectFrom(TILES)
|
||||
.where(TILES.X.eq(x))
|
||||
.and(TILES.Y.eq(y))
|
||||
.and(TILES.Z.eq(z))
|
||||
.fetchOne();
|
||||
|
||||
if (record == null) {
|
||||
//Insert
|
||||
record = ctx.newRecord(TILES);
|
||||
record.setX(x);
|
||||
record.setY(y);
|
||||
record.setZ(z);
|
||||
|
||||
record.insert();
|
||||
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
|
||||
long diff = System.currentTimeMillis() - now;
|
||||
if (diff > 1000){
|
||||
logger.warn("Insert of tile {}/{}/{} took {} ms", record.getX(), record.getY(), record.getZ(), diff);
|
||||
}
|
||||
}
|
||||
|
||||
//update
|
||||
record.setTile(data);
|
||||
record.setMtime(now);
|
||||
|
||||
record.store();
|
||||
|
||||
|
||||
long diff = System.currentTimeMillis() - now;
|
||||
if (diff > 1000){
|
||||
logger.warn("Insert of tile {}/{}/{} took {} ms", record.getX(), record.getY(), record.getZ(), diff);
|
||||
}
|
||||
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] get(int x, int y, int z) {
|
||||
|
||||
|
|
Loading…
Reference in New Issue