Database performance tweaks

Added the option to bulk load and cache entire world rows at
a time when using sqlite. Caching is implemented in the
database class this time around.
Enabled using --sqlite-cacheworldrow on the command line.

This is essentially re-introduces the database access strategy
that was used originally, but as an option.

Currently, one block is read at a time from the database.
The original behavior was to read and cache one entire world
row at a time, irrespective of whether or not only a (small)
subsection of the world was being mapped.
Under some circumstances, in particular if the entire world is
mapped or a relatively large percentage of it (blocks-wise), the
bulk loading and caching may outperform the one-by-one strategy,
even though it usually loads many more blocks than are actually
needed.

It seems that leveldb won't benefit from any kind of caching or
bulk loading of blocks, so none was implemented.

Leveldb does compile now. Still not tested on a database :-(

Improved the database statistics (printed with --verbose).
master
Rogier 2014-03-26 00:23:33 +01:00
parent 4ca967d40d
commit 10e6a746cd
9 changed files with 271 additions and 106 deletions

View File

@ -95,6 +95,11 @@ geometry:
forcegeometry:
Generate a map of the requested size, even if the world is smaller.
sqlite-cacheworldrow:
When using sqlite, read an entire world row at one, instead of reading
one block at a time.
This may improve performance when a large percentage of the world is mapped.
verbose:
report some useful/ interesting information:
- maximum coordinates of the world

View File

@ -107,6 +107,7 @@ TileGenerator::TileGenerator():
m_border(0),
m_backend("sqlite3"),
m_forceGeom(false),
m_sqliteCacheWorldRow(false),
m_image(0),
m_xMin(INT_MAX/16-1),
m_xMax(INT_MIN/16+1),
@ -142,6 +143,11 @@ void TileGenerator::setForceGeom(bool forceGeom)
m_forceGeom = forceGeom;
}
void TileGenerator::setSqliteCacheWorldRow(bool cacheWorldRow)
{
m_sqliteCacheWorldRow = cacheWorldRow;
}
void TileGenerator::setScaleColor(const std::string &scaleColor)
{
m_scaleColor = parseColor(scaleColor);
@ -319,8 +325,11 @@ void TileGenerator::parseColorsStream(std::istream &in)
void TileGenerator::openDb(const std::string &input)
{
if(m_backend == "sqlite3")
m_db = new DBSQLite3(input);
if(m_backend == "sqlite3") {
DBSQLite3 *db;
m_db = db = new DBSQLite3(input);
db->cacheWorldRow = m_sqliteCacheWorldRow;
}
#if USE_LEVELDB
else if(m_backend == "leveldb")
m_db = new DBLevelDB(input);
@ -469,28 +478,9 @@ void TileGenerator::createImage()
gdImageFilledRectangle(m_image, 0, 0, m_mapWidth + m_border - 1, m_mapHeight + m_border -1, rgb2int(m_bgColor.r, m_bgColor.g, m_bgColor.b));
}
std::map<int, TileGenerator::BlockList> TileGenerator::getBlocksOnZ(int zPos)
{
DBBlockList in = m_db->getBlocksOnZ(zPos);
std::map<int, BlockList> out;
for(DBBlockList::const_iterator it = in.begin(); it != in.end(); ++it) {
Block b = Block(decodeBlockPos(it->first), it->second);
if(out.find(b.first.x) == out.end()) {
BlockList bl;
out[b.first.x] = bl;
}
out[b.first.x].push_back(b);
}
return out;
}
TileGenerator::Block TileGenerator::getBlockOnPos(BlockPos pos)
{
int64_t iPos;
iPos = pos.x;
iPos += static_cast<int64_t>(pos.y) << 12;
iPos += static_cast<int64_t>(pos.z) << 24;
DBBlock in = m_db->getBlockOnPos(iPos);
DBBlock in = m_db->getBlockOnPos(pos.x, pos.y, pos.z);
Block out(pos,(const unsigned char *)"");
if (!in.second.empty()) {
@ -518,7 +508,6 @@ TileGenerator::Block TileGenerator::getBlockOnPos(BlockPos pos)
void TileGenerator::renderMap()
{
int blocks_selected = 0;
int blocks_rendered = 0;
BlockPos currentPos;
currentPos.x = INT_MIN;
@ -539,7 +528,6 @@ void TileGenerator::renderMap()
continue;
}
Block block = getBlockOnPos(pos);
blocks_selected++;
if (!block.second.empty()) {
const unsigned char *data = block.second.c_str();
size_t length = block.second.length();
@ -636,7 +624,12 @@ void TileGenerator::renderMap()
if(currentPos.z != INT_MIN && m_shading)
renderShading(currentPos.z);
if (verboseStatistics)
cout << "Statistics: Blocks selected: " << blocks_selected << "; blocks rendered: " << blocks_rendered << std::endl;
cout << "Statistics"
<< ": blocks read: " << m_db->getBlocksReadCount()
<< "; (" << m_db->getBlocksCachedCount() << " cached + "
<< m_db->getBlocksUnCachedCount() << " uncached)"
<< "; blocks rendered: " << blocks_rendered
<< std::endl;
}
inline void TileGenerator::renderMapBlock(const unsigned_string &mapBlock, const BlockPos &pos, int version)

View File

@ -98,6 +98,7 @@ public:
void setMinY(int y);
void setMaxY(int y);
void setForceGeom(bool forceGeom);
void setSqliteCacheWorldRow(bool cacheWorldRow);
void parseColorsFile(const std::string &fileName);
void setBackend(std::string backend);
void generate(const std::string &input, const std::string &output);
@ -111,7 +112,6 @@ private:
void renderMap();
std::list<int> getZValueList() const;
Block getBlockOnPos(BlockPos pos);
std::map<int, BlockList> getBlocksOnZ(int zPos);
void renderMapBlock(const unsigned_string &mapBlock, const BlockPos &pos, int version);
void renderShading(int zPos);
void renderScale();
@ -138,6 +138,7 @@ private:
int m_border;
std::string m_backend;
bool m_forceGeom;
bool m_sqliteCacheWorldRow;
DB *m_db;
gdImagePtr m_image;

View File

@ -15,7 +15,11 @@ inline std::string i64tos(int64_t i) {
return o.str();
}
DBLevelDB::DBLevelDB(const std::string &mapdir) {
DBLevelDB::DBLevelDB(const std::string &mapdir) :
m_blocksReadCount(0),
m_blocksCachedCount(0),
m_blocksUnCachedCount(0)
{
leveldb::Options options;
options.create_if_missing = false;
leveldb::Status status = leveldb::DB::Open(options, mapdir + "map.db", &m_db);
@ -27,6 +31,21 @@ DBLevelDB::~DBLevelDB() {
delete m_db;
}
int DBLevelDB::getBlocksReadCount(void)
{
return m_blocksReadCount;
}
int DBLevelDB::getBlocksCachedCount(void)
{
return m_blocksCachedCount;
}
int DBLevelDB::getBlocksUnCachedCount(void)
{
return m_blocksUnCachedCount;
}
std::vector<int64_t> DBLevelDB::getBlockPos() {
std::vector<int64_t> vec;
std::set<int64_t> s;
@ -40,37 +59,23 @@ std::vector<int64_t> DBLevelDB::getBlockPos() {
return vec;
}
DBBlockList DBLevelDB::getBlocksOnZ(int zPos)
{
DBBlockList blocks;
std::string datastr;
leveldb::Status status;
int64_t psMin;
int64_t psMax;
psMin = (zPos * 16777216l) - 0x800000;
psMax = (zPos * 16777216l) + 0x7fffff;
for(int64_t i = psMin; i <= psMax; i++) { // FIXME: This is still very very inefficent (even with m_bpcache)
if(m_bpcache.find(i) == m_bpcache.end())
continue;
status = m_db->Get(leveldb::ReadOptions(), i64tos(i), &datastr);
if(status.ok())
blocks.push_back( DBBlock( i, std::basic_string<unsigned char>( (const unsigned char*) datastr.c_str(), datastr.size() ) ) );
}
return blocks;
}
DBBlock DBLevelDB::getBlocksOnPos(int64_t iPos)
DBBlock DBLevelDB::getBlockOnPos(int x, int y, int z)
{
int64_t iPos;
DBBlock block(0,(const unsigned char *)"");
std::string datastr;
leveldb::Status status;
status = m_db->Get(leveldb::ReadOptions(), i64tos(Pos), &datastr);
if(status.ok())
iPos = static_cast<int64_t>(x);
iPos += static_cast<int64_t>(y) << 12;
iPos += static_cast<int64_t>(z) << 24;
status = m_db->Get(leveldb::ReadOptions(), i64tos(iPos), &datastr);
if(status.ok()) {
block = DBBlock( iPos, std::basic_string<unsigned char>( (const unsigned char*) datastr.c_str(), datastr.size() ) );
m_blocksReadCount++;
m_blocksUnCachedCount++;
}
return block;
}

View File

@ -8,11 +8,16 @@
class DBLevelDB : public DB {
public:
DBLevelDB(const std::string &mapdir);
virtual int getBlocksUnCachedCount(void);
virtual int getBlocksCachedCount(void);
virtual int getBlocksReadCount(void);
virtual std::vector<int64_t> getBlockPos();
virtual DBBlockList getBlocksOnZ(int zPos);
virtual DBBlock getBlockOnPos(int64_t iPos);
virtual DBBlock getBlockOnPos(int x, int y, int z);
~DBLevelDB();
private:
int m_blocksReadCount;
int m_blocksCachedCount;
int m_blocksUnCachedCount;
leveldb::DB *m_db;
std::set<int64_t> m_bpcache;
};

View File

@ -2,10 +2,15 @@
#include <stdexcept>
#include <unistd.h> // for usleep
DBSQLite3::DBSQLite3(const std::string &mapdir) :
m_getBlockPosStatement(NULL),
m_getBlocksOnZStatement(NULL),
m_getBlocksOnPosStatement(NULL)
cacheWorldRow(false),
m_blocksReadCount(0),
m_blocksCachedCount(0),
m_blocksUnCachedCount(0),
m_blockPosListStatement(NULL),
m_blocksOnZStatement(NULL),
m_blockOnPosStatement(NULL)
{
std::string db_name = mapdir + "map.sqlite";
@ -15,21 +20,36 @@ DBSQLite3::DBSQLite3(const std::string &mapdir) :
}
DBSQLite3::~DBSQLite3() {
if (m_getBlockPosStatement) sqlite3_finalize(m_getBlockPosStatement);
if (m_getBlocksOnZStatement) sqlite3_finalize(m_getBlocksOnZStatement);
if (m_getBlocksOnPosStatement) sqlite3_finalize(m_getBlocksOnPosStatement);
if (m_blockPosListStatement) sqlite3_finalize(m_blockPosListStatement);
if (m_blocksOnZStatement) sqlite3_finalize(m_blocksOnZStatement);
if (m_blockOnPosStatement) sqlite3_finalize(m_blockOnPosStatement);
sqlite3_close(m_db);
}
int DBSQLite3::getBlocksReadCount(void)
{
return m_blocksReadCount;
}
int DBSQLite3::getBlocksCachedCount(void)
{
return m_blocksCachedCount;
}
int DBSQLite3::getBlocksUnCachedCount(void)
{
return m_blocksUnCachedCount;
}
std::vector<int64_t> DBSQLite3::getBlockPos() {
std::vector<int64_t> vec;
std::string sql = "SELECT pos FROM blocks";
if (m_getBlockPosStatement || sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_getBlockPosStatement, 0) == SQLITE_OK) {
if (m_blockPosListStatement || sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_blockPosListStatement, 0) == SQLITE_OK) {
int result = 0;
while (true) {
result = sqlite3_step(m_getBlockPosStatement);
result = sqlite3_step(m_blockPosListStatement);
if(result == SQLITE_ROW) {
sqlite3_int64 blocknum = sqlite3_column_int64(m_getBlockPosStatement, 0);
sqlite3_int64 blocknum = sqlite3_column_int64(m_blockPosListStatement, 0);
vec.push_back(blocknum);
} else if (result == SQLITE_BUSY) // Wait some time and try again
usleep(10000);
@ -37,17 +57,58 @@ std::vector<int64_t> DBSQLite3::getBlockPos() {
break;
}
} else {
sqlite3_reset(m_blockPosListStatement);
throw std::runtime_error("Failed to get list of MapBlocks");
}
sqlite3_reset(m_blockPosListStatement);
return vec;
}
DBBlockList DBSQLite3::getBlocksOnZ(int zPos)
void DBSQLite3::prepareBlocksOnZStatement(void)
{
std::string sql = "SELECT pos, data FROM blocks WHERE (pos >= ? AND pos <= ?)";
if (!m_getBlocksOnZStatement && sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_getBlocksOnZStatement, 0) != SQLITE_OK) {
throw std::runtime_error("Failed to prepare statement");
//std::string sql = "SELECT pos, data FROM blocks WHERE (pos >= ? AND pos <= ?)";
std::string sql = "SELECT pos, data FROM blocks WHERE (pos BETWEEN ? AND ?)";
if (!m_blocksOnZStatement && sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_blocksOnZStatement, 0) != SQLITE_OK) {
throw std::runtime_error("Failed to prepare statement (blocksOnZStatement)");
}
}
void DBSQLite3::prepareBlockOnPosStatement(void)
{
std::string sql = "SELECT pos, data FROM blocks WHERE pos == ?";
if (!m_blockOnPosStatement && sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_blockOnPosStatement, 0) != SQLITE_OK) {
throw std::runtime_error("Failed to prepare SQL statement (blockOnPosStatement)");
}
}
// Apparently, this attempt at being smart, is actually quite inefficient for sqlite.
//
// For larger subsections of the map, it performs much worse than caching an entire
// world row (i.e. z coordinate). In cases where it *is* more efficient, no caching is
// much more efficient still.
//
// It seems that any computation on pos severely affects the performance (?)...
//
// For the moment, this function is not used.
void DBSQLite3::prepareBlocksYRangeStatement(void)
{
// This one seems to perform best:
std::string sql = "SELECT pos, data FROM blocks WHERE (pos BETWEEN ?1 AND ?2) AND (pos-?3)&4095 == 0 AND (pos-?3 BETWEEN ?4 AND ?5)";
// These perform worse:
//std::string sql = "SELECT pos, data FROM blocks WHERE (pos BETWEEN ?1 AND ?2) AND (pos-?3 BETWEEN ?4 AND ?5) AND (pos-?3)&4095 == 0";
//std::string sql = "SELECT pos, data FROM blocks WHERE (pos BETWEEN ?1 AND ?2) AND (pos-?3 BETWEEN ?4 AND ?5)";
//std::string sql = "SELECT pos, data FROM (select pos, data FROM blocks WHERE (pos BETWEEN ?1 AND ?2) ) WHERE (pos-?3 BETWEEN ?4 AND ?5) AND (pos-?3)&4095 == 0";
//std::string sql = "SELECT pos, data FROM (select pos, (pos-?3) AS pos3, data FROM blocks WHERE (pos BETWEEN ?1 AND ?2) ) WHERE (pos3 BETWEEN ?4 AND ?5) AND (pos3)&4095 == 0";
//std::string sql = "SELECT pos, data FROM (select pos, (pos-?3) AS pos3, data FROM blocks WHERE (pos BETWEEN ?1 AND ?2) ) WHERE (pos3 BETWEEN ?4 AND ?5)";
if (!m_blocksYRangeStatement && sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_blocksYRangeStatement, 0) != SQLITE_OK) {
throw std::runtime_error("Failed to prepare SQL statement (blocksYRangeStatement)");
}
}
void DBSQLite3::cacheBlocksOnZRaw(int zPos)
{
prepareBlocksOnZStatement();
DBBlockList blocks;
sqlite3_int64 psMin;
@ -55,47 +116,31 @@ DBBlockList DBSQLite3::getBlocksOnZ(int zPos)
psMin = (static_cast<sqlite3_int64>(zPos) * 16777216l) - 0x800000;
psMax = (static_cast<sqlite3_int64>(zPos) * 16777216l) + 0x7fffff;
sqlite3_bind_int64(m_getBlocksOnZStatement, 1, psMin);
sqlite3_bind_int64(m_getBlocksOnZStatement, 2, psMax);
int result = 0;
while (true) {
result = sqlite3_step(m_getBlocksOnZStatement);
if(result == SQLITE_ROW) {
sqlite3_int64 blocknum = sqlite3_column_int64(m_getBlocksOnZStatement, 0);
const unsigned char *data = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(m_getBlocksOnZStatement, 1));
int size = sqlite3_column_bytes(m_getBlocksOnZStatement, 1);
blocks.push_back(DBBlock(blocknum, std::basic_string<unsigned char>(data, size)));
} else if (result == SQLITE_BUSY) { // Wait some time and try again
usleep(10000);
} else {
break;
}
}
sqlite3_reset(m_getBlocksOnZStatement);
sqlite3_bind_int64(m_blocksOnZStatement, 1, psMin);
sqlite3_bind_int64(m_blocksOnZStatement, 2, psMax);
return blocks;
cacheBlocks(m_blocksOnZStatement);
}
DBBlock DBSQLite3::getBlockOnPos(int64_t iPos)
DBBlock DBSQLite3::getBlockOnPosRaw(sqlite3_int64 psPos)
{
std::string sql = "SELECT pos, data FROM blocks WHERE pos == ?";
if (!m_getBlocksOnPosStatement && sqlite3_prepare_v2(m_db, sql.c_str(), sql.length(), &m_getBlocksOnPosStatement, 0) != SQLITE_OK) {
throw std::runtime_error("Failed to prepare statement");
}
prepareBlockOnPosStatement();
DBBlock block(0,(const unsigned char *)"");
sqlite3_int64 psPos = static_cast<sqlite3_int64>(iPos);
sqlite3_bind_int64(m_getBlocksOnPosStatement, 1, psPos);
int result = 0;
sqlite3_bind_int64(m_blockOnPosStatement, 1, psPos);
while (true) {
result = sqlite3_step(m_getBlocksOnPosStatement);
result = sqlite3_step(m_blockOnPosStatement);
if(result == SQLITE_ROW) {
sqlite3_int64 blocknum = sqlite3_column_int64(m_getBlocksOnPosStatement, 0);
const unsigned char *data = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(m_getBlocksOnPosStatement, 1));
int size = sqlite3_column_bytes(m_getBlocksOnPosStatement, 1);
sqlite3_int64 blocknum = sqlite3_column_int64(m_blockOnPosStatement, 0);
const unsigned char *data = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(m_blockOnPosStatement, 1));
int size = sqlite3_column_bytes(m_blockOnPosStatement, 1);
block = DBBlock(blocknum, std::basic_string<unsigned char>(data, size));
m_blocksUnCachedCount++;
//std::cerr << "Read block " << blocknum << " from database" << std::endl;
break;
} else if (result == SQLITE_BUSY) { // Wait some time and try again
usleep(10000);
@ -103,8 +148,82 @@ DBBlock DBSQLite3::getBlockOnPos(int64_t iPos)
break;
}
}
sqlite3_reset(m_getBlocksOnPosStatement);
sqlite3_reset(m_blockOnPosStatement);
return block;
}
void DBSQLite3::cacheBlocksYRangeRaw(int x, int y, int z)
{
prepareBlocksYRangeStatement();
sqlite3_int64 psZPosFrom = (static_cast<sqlite3_int64>(z) << 24) - 0x800000;
sqlite3_int64 psZPosTo = (static_cast<sqlite3_int64>(z) << 24) + 0x7fffff;
sqlite3_int64 psPosZero = static_cast<sqlite3_int64>(x);
psPosZero += static_cast<sqlite3_int64>(z) << 24;
sqlite3_int64 psYPosFrom = 0;
sqlite3_int64 psYPosTo = static_cast<sqlite3_int64>(y) << 12;
sqlite3_bind_int64(m_blocksYRangeStatement, 1, psZPosFrom);
sqlite3_bind_int64(m_blocksYRangeStatement, 2, psZPosTo);
sqlite3_bind_int64(m_blocksYRangeStatement, 3, psPosZero);
sqlite3_bind_int64(m_blocksYRangeStatement, 4, psYPosFrom);
sqlite3_bind_int64(m_blocksYRangeStatement, 5, psYPosTo);
cacheBlocks(m_blocksYRangeStatement);
}
void DBSQLite3::cacheBlocks(sqlite3_stmt *SQLstatement)
{
int result = 0;
while (true) {
result = sqlite3_step(SQLstatement);
if(result == SQLITE_ROW) {
sqlite3_int64 blocknum = sqlite3_column_int64(SQLstatement, 0);
const unsigned char *data = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(SQLstatement, 1));
int size = sqlite3_column_bytes(SQLstatement, 1);
m_blockCache[blocknum] = DBBlock(blocknum, std::basic_string<unsigned char>(data, size));
m_blocksCachedCount++;
//std::cerr << "Cache block " << blocknum << " from database" << std::endl;
} else if (result == SQLITE_BUSY) { // Wait some time and try again
usleep(10000);
} else {
break;
}
}
sqlite3_reset(SQLstatement);
}
DBBlock DBSQLite3::getBlockOnPos(int x, int y, int z)
{
sqlite3_int64 psPos;
psPos = static_cast<sqlite3_int64>(x);
psPos += static_cast<sqlite3_int64>(y) << 12;
psPos += static_cast<sqlite3_int64>(z) << 24;
//std::cerr << "Block " << x << "," << y << "," << z << " -> " << psPos << std::endl;
m_blocksReadCount++;
BlockCache::const_iterator DBBlockSearch;
DBBlockSearch = m_blockCache.find(psPos);
if (DBBlockSearch == m_blockCache.end()) {
if (cacheWorldRow) {
m_blockCache.clear();
cacheBlocksOnZRaw(z);
DBBlockSearch = m_blockCache.find(psPos);
if (DBBlockSearch != m_blockCache.end()) {
return DBBlockSearch->second;
}
else {
return DBBlock(0, (const unsigned char *)"");
}
}
else {
return getBlockOnPosRaw(psPos);
}
}
else {
return DBBlockSearch->second;
}
}

View File

@ -3,20 +3,48 @@
#include "db.h"
#include <sqlite3.h>
#if _cplusplus == 201103L
#include <unordered_map>
#else
#include <map>
#endif
#include <string>
#include <sstream>
class DBSQLite3 : public DB {
#if _cplusplus == 201103L
typedef std::unordered_map<uint64_t, DBBlock> BlockCache;
#else
typedef std::map<uint64_t, DBBlock> BlockCache;
#endif
public:
bool cacheWorldRow;
DBSQLite3(const std::string &mapdir);
virtual int getBlocksUnCachedCount(void);
virtual int getBlocksCachedCount(void);
virtual int getBlocksReadCount(void);
virtual std::vector<int64_t> getBlockPos();
virtual DBBlockList getBlocksOnZ(int zPos);
virtual DBBlock getBlockOnPos(int64_t iPos);
virtual DBBlock getBlockOnPos(int x, int y, int z);
~DBSQLite3();
private:
int m_blocksReadCount;
int m_blocksCachedCount;
int m_blocksUnCachedCount;
sqlite3 *m_db;
sqlite3_stmt *m_getBlockPosStatement;
sqlite3_stmt *m_getBlocksOnZStatement;
sqlite3_stmt *m_getBlocksOnPosStatement;
sqlite3_stmt *m_blockPosListStatement;
sqlite3_stmt *m_blocksOnZStatement;
sqlite3_stmt *m_blockOnPosStatement;
sqlite3_stmt *m_blocksYRangeStatement;
std::ostringstream m_getBlockSetStatementBlocks;
BlockCache m_blockCache;
void prepareBlocksOnZStatement(void);
void prepareBlockOnPosStatement(void);
void prepareBlocksYRangeStatement(void);
void cacheBlocksYRangeRaw(int x, int y, int z);
void cacheBlocksOnZRaw(int zPos);
DBBlock getBlockOnPosRaw(sqlite3_int64 psPos);
void cacheBlocks(sqlite3_stmt *SQLstatement);
};
#endif // _DB_SQLITE3_H

6
db.h
View File

@ -14,8 +14,10 @@ typedef std::list<DBBlock> DBBlockList;
class DB {
public:
virtual std::vector<int64_t> getBlockPos()=0;
virtual DBBlockList getBlocksOnZ(int zPos)=0;
virtual DBBlock getBlockOnPos(int64_t iPos)=0;
virtual int getBlocksUnCachedCount(void)=0;
virtual int getBlocksCachedCount(void)=0;
virtual int getBlocksReadCount(void)=0;
virtual DBBlock getBlockOnPos(int x, int y, int z)=0;
};
#endif // _DB_H

View File

@ -18,6 +18,8 @@
using namespace std;
#define OPT_SQLITE_CACHEWORLDROW 0x81
void usage()
{
const char *usage_text = "minetestmapper [options]\n"
@ -36,6 +38,7 @@ void usage()
" --backend <sqlite3/leveldb>\n"
" --geometry x:y+w+h\n"
" --forcegeometry\n"
" --sqlite-cacheworldrow\n"
" --verbose\n"
"Color format: '#000000'\n";
std::cout << usage_text;
@ -61,6 +64,7 @@ int main(int argc, char *argv[])
{"min-y", required_argument, 0, 'a'},
{"max-y", required_argument, 0, 'c'},
{"backend", required_argument, 0, 'd'},
{"sqlite-cacheworldrow", no_argument, 0, OPT_SQLITE_CACHEWORLDROW},
{"verbose", no_argument, 0, 'v'},
};
@ -119,6 +123,9 @@ int main(int argc, char *argv[])
case 'H':
generator.setShading(false);
break;
case OPT_SQLITE_CACHEWORLDROW:
generator.setSqliteCacheWorldRow(true);
break;
case 'a': {
istringstream iss;
iss.str(optarg);