add_compile_options(-g -Og) | add_compile_options(-g -Og) | ||||
elseif(CMAKE_BUILD_TYPE STREQUAL DebugRelease) | elseif(CMAKE_BUILD_TYPE STREQUAL DebugRelease) | ||||
message(STATUS "Build mode: DebugRelease") | message(STATUS "Build mode: DebugRelease") | ||||
add_compile_options(-g -O3) | |||||
add_compile_options(-O3 -flto -DNDEBUG -g) | |||||
elseif(CMAKE_BUILD_TYPE STREQUAL Release) | elseif(CMAKE_BUILD_TYPE STREQUAL Release) | ||||
message(STATUS "Build mode: Release") | message(STATUS "Build mode: Release") | ||||
add_compile_options(-O3 -flto) | |||||
add_compile_options(-O3 -flto -DNDEBUG) | |||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto") | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -flto") | ||||
else() | else() | ||||
message(FATAL_ERROR "CMAKE_BUILD_TYPE must be Debug or Release.") | message(FATAL_ERROR "CMAKE_BUILD_TYPE must be Debug or Release.") |
data_.reset(new uint8_t[CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID)]); | data_.reset(new uint8_t[CHUNK_WIDTH * CHUNK_HEIGHT * sizeof(Tile::ID)]); | ||||
} | } | ||||
Tile::ID *getTileData(); | |||||
Tile::ID getTileID(RelPos pos); | |||||
void setTileID(RelPos pos, Tile::ID id, SDL_Texture *tex); | |||||
void setTileData(RelPos pos, Tile::ID id); | |||||
Tile::ID *getTileData() { | |||||
assert(isActive()); | |||||
return (Tile::ID *)data_.get(); | |||||
} | |||||
Tile::ID getTileID(RelPos pos) { | |||||
return getTileData()[pos.y * CHUNK_WIDTH + pos.x]; | |||||
} | |||||
void setTileID(RelPos pos, Tile::ID id, SDL_Texture *tex) { | |||||
getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id; | |||||
draw_list_.push_back({ pos, tex }); | |||||
} | |||||
void setTileData(RelPos pos, Tile::ID id) { | |||||
getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id; | |||||
need_render_ = true; | |||||
} | |||||
void render(const Context &ctx, SDL_Renderer *rnd); | void render(const Context &ctx, SDL_Renderer *rnd); | ||||
void compress(); | void compress(); |
WorldPlane &addPlane(const std::string &gen); | WorldPlane &addPlane(const std::string &gen); | ||||
WorldPlane &addPlane() { return addPlane(default_world_gen_); } | WorldPlane &addPlane() { return addPlane(default_world_gen_); } | ||||
Tile &getTileByID(Tile::ID id); | |||||
Tile &getTileByID(Tile::ID id) { return *tiles_[id]; } | |||||
Tile::ID getTileID(const std::string &name); | Tile::ID getTileID(const std::string &name); | ||||
Tile &getTile(const std::string &name); | Tile &getTile(const std::string &name); | ||||
Item &getItem(const std::string &name); | Item &getItem(const std::string &name); |
bool hasChunk(ChunkPos pos); | bool hasChunk(ChunkPos pos); | ||||
Chunk &getChunk(ChunkPos pos); | Chunk &getChunk(ChunkPos pos); | ||||
Chunk &slowGetChunk(ChunkPos pos); | |||||
void setTileID(TilePos pos, Tile::ID id); | void setTileID(TilePos pos, Tile::ID id); | ||||
void setTile(TilePos pos, const std::string &name); | void setTile(TilePos pos, const std::string &name); | ||||
Tile::ID getTileID(TilePos pos); | Tile::ID getTileID(TilePos pos); | ||||
Tile &getTile(TilePos pos); | Tile &getTile(TilePos pos); | ||||
private: | private: | ||||
std::map<std::pair<int, int>, Chunk> chunks_; | std::map<std::pair<int, int>, Chunk> chunks_; | ||||
std::vector<Chunk *> active_chunks_; | std::vector<Chunk *> active_chunks_; | ||||
std::vector<std::pair<ChunkPos, Chunk *>> tick_chunks_; | |||||
std::vector<std::unique_ptr<Entity>> entities_; | std::vector<std::unique_ptr<Entity>> entities_; | ||||
std::deque<Chunk *> chunk_init_list_; | std::deque<Chunk *> chunk_init_list_; |
uint8_t *Chunk::renderbuf = new uint8_t[CHUNK_WIDTH * TILE_SIZE * CHUNK_HEIGHT * TILE_SIZE * 4]; | uint8_t *Chunk::renderbuf = new uint8_t[CHUNK_WIDTH * TILE_SIZE * CHUNK_HEIGHT * TILE_SIZE * 4]; | ||||
Tile::ID *Chunk::getTileData() { | |||||
assert(isActive()); | |||||
return (Tile::ID *)data_.get(); | |||||
} | |||||
Tile::ID Chunk::getTileID(RelPos pos) { | |||||
return getTileData()[pos.y * CHUNK_WIDTH + pos.x]; | |||||
} | |||||
void Chunk::setTileID(RelPos pos, Tile::ID id, SDL_Texture *tex) { | |||||
getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id; | |||||
draw_list_.push_back({ pos, tex }); | |||||
} | |||||
void Chunk::setTileData(RelPos pos, Tile::ID id) { | |||||
getTileData()[pos.y * CHUNK_WIDTH + pos.x] = id; | |||||
need_render_ = true; | |||||
} | |||||
void Chunk::compress() { | void Chunk::compress() { | ||||
if (isCompressed()) | if (isCompressed()) | ||||
return; | return; |
static void chunkLine(int l, WorldPlane &plane, ChunkPos &abspos, const Vec2i &dir) { | static void chunkLine(int l, WorldPlane &plane, ChunkPos &abspos, const Vec2i &dir) { | ||||
for (int i = 0; i < l; ++i) { | for (int i = 0; i < l; ++i) { | ||||
plane.getChunk(abspos); | |||||
plane.slowGetChunk(abspos).keepActive(); | |||||
abspos += dir; | abspos += dir; | ||||
} | } | ||||
} | } | ||||
return iter->second; | return iter->second; | ||||
} | } | ||||
Tile &World::getTileByID(Tile::ID id) { | |||||
return *tiles_[id]; | |||||
} | |||||
Tile &World::getTile(const std::string &name) { | Tile &World::getTile(const std::string &name) { | ||||
Tile::ID id = getTileID(name); | Tile::ID id = getTileID(name); | ||||
return getTileByID(id); | return getTileByID(id); |
// This function will be a bit weird because it's a really fucking hot function. | // This function will be a bit weird because it's a really fucking hot function. | ||||
Chunk &WorldPlane::getChunk(ChunkPos pos) { | Chunk &WorldPlane::getChunk(ChunkPos pos) { | ||||
// This branch should be really predictable, there should basically never | |||||
// be no active chunks | |||||
if (active_chunks_.size() != 0) { | |||||
// The last chunk should be the most actively used chunk | |||||
Chunk *chunk = active_chunks_.back(); | |||||
if (chunk->pos_ == pos) { | |||||
chunk->keepActive(); | |||||
// First, look through all chunks which have been in use this tick | |||||
for (auto [chpos, chunk]: tick_chunks_) { | |||||
if (chpos == pos) | |||||
return *chunk; | return *chunk; | ||||
} | |||||
// Linear search through a small array is probably faster than tree lookup. | |||||
// Loop backwards because the hottest chunks should be at the end. | |||||
for (ssize_t i = active_chunks_.size() - 2; i >= 0; --i) { | |||||
chunk = active_chunks_[i]; | |||||
if (chunk->pos_ == pos) { | |||||
chunk->keepActive(); | |||||
// Ensure that the hot chunk is at the end by bubbling this one up | |||||
active_chunks_[i] = active_chunks_[i + 1]; | |||||
active_chunks_[i + 1] = chunk; | |||||
return *chunk; | |||||
} | |||||
} | |||||
} | } | ||||
// Slow path: Find a chunk in our global chunk map, | |||||
// creating a new one if necessary | |||||
Chunk &chunk = slowGetChunk(pos); | |||||
tick_chunks_.push_back({ pos, &chunk }); | |||||
return chunk; | |||||
} | |||||
Chunk &WorldPlane::slowGetChunk(ChunkPos pos) { | |||||
auto iter = chunks_.find(pos); | auto iter = chunks_.find(pos); | ||||
// Create chunk if that turns out to be necessary | // Create chunk if that turns out to be necessary | ||||
active_chunks_.push_back(&chunk); | active_chunks_.push_back(&chunk); | ||||
chunk_init_list_.push_back(&chunk); | chunk_init_list_.push_back(&chunk); | ||||
// Otherwise, tell it that it should keep itself alive a bit longer | |||||
} else { | |||||
// Otherwise, it might not be active, so let's activate it | |||||
} else if (!iter->second.isActive()) { | |||||
iter->second.keepActive(); | iter->second.keepActive(); | ||||
active_chunks_.push_back(&iter->second); | active_chunks_.push_back(&iter->second); | ||||
chunk_init_list_.push_back(&iter->second); | chunk_init_list_.push_back(&iter->second); | ||||
} | } | ||||
Tile::ID WorldPlane::getTileID(TilePos pos) { | Tile::ID WorldPlane::getTileID(TilePos pos) { | ||||
Chunk &chunk = getChunk(chunkPos(pos)); | |||||
return chunk.getTileID(relPos(pos)); | |||||
return getChunk(chunkPos(pos)).getTileID(relPos(pos)); | |||||
} | } | ||||
Tile &WorldPlane::getTile(TilePos pos) { | Tile &WorldPlane::getTile(TilePos pos) { | ||||
} | } | ||||
void WorldPlane::tick(float dt) { | void WorldPlane::tick(float dt) { | ||||
// Any chunk which has been in use since last tick should be kept alive | |||||
for (std::pair<ChunkPos, Chunk *> &ch: tick_chunks_) | |||||
ch.second->keepActive(); | |||||
tick_chunks_.clear(); | |||||
for (auto &ent: entities_) | for (auto &ent: entities_) | ||||
ent->tick(getContext(), dt); | ent->tick(getContext(), dt); | ||||