Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dirty flag and integrate #1725

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion libopenage/gamestate/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Map::Map(const std::shared_ptr<GameState> &state,

auto sector = grid->get_sector(chunk_idx);
auto cost_field = sector->get_cost_field();
cost_field->set_cost(tile_idx, path_cost.second);
cost_field->set_cost(tile_idx, path_cost.second, time::TIME_ZERO);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions libopenage/pathfinding/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
add_sources(libopenage
cost_field.cpp
definitions.cpp
field_cache.cpp
flow_field.cpp
grid.cpp
integration_field.cpp
Expand Down
26 changes: 16 additions & 10 deletions libopenage/pathfinding/cost_field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ namespace openage::path {

CostField::CostField(size_t size) :
size{size},
cells(this->size * this->size, COST_MIN) {
cells(this->size * this->size, COST_MIN),
valid_until{time::TIME_MIN} {
log::log(DBG << "Created cost field with size " << this->size << "x" << this->size);
}

Expand All @@ -33,29 +34,34 @@ cost_t CostField::get_cost(size_t idx) const {
return this->cells.at(idx);
}

void CostField::set_cost(const coord::tile_delta &pos, cost_t cost) {
this->cells[pos.ne + pos.se * this->size] = cost;
void CostField::set_cost(const coord::tile_delta &pos, cost_t cost, const time::time_t &valid_until) {
this->set_cost(pos.ne + pos.se * this->size, cost, valid_until);
}

void CostField::set_cost(size_t x, size_t y, cost_t cost) {
this->cells[x + y * this->size] = cost;
}

void CostField::set_cost(size_t idx, cost_t cost) {
this->cells[idx] = cost;
void CostField::set_cost(size_t x, size_t y, cost_t cost, const time::time_t &valid_until) {
this->set_cost(x + y * this->size, cost, valid_until);
}

const std::vector<cost_t> &CostField::get_costs() const {
return this->cells;
}

void CostField::set_costs(std::vector<cost_t> &&cells) {
void CostField::set_costs(std::vector<cost_t> &&cells, const time::time_t &valid_until) {
ENSURE(cells.size() == this->cells.size(),
"cells vector has wrong size: " << cells.size()
<< "; expected: "
<< this->cells.size());

this->cells = std::move(cells);
this->valid_until = valid_until;
}

bool CostField::is_dirty(const time::time_t &time) {
return time >= this->valid_until;
}

void CostField::clean() {
this->valid_until = time::TIME_MAX;
}

} // namespace openage::path
34 changes: 30 additions & 4 deletions libopenage/pathfinding/cost_field.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <vector>

#include "pathfinding/types.h"
#include "time/time.h"


namespace openage {
Expand Down Expand Up @@ -64,25 +65,32 @@ class CostField {
*
* @param pos Coordinates of the cell (relative to field origin).
* @param cost Cost to set.
* @param changed Time at which the cost value is changed.
*/
void set_cost(const coord::tile_delta &pos, cost_t cost);
void set_cost(const coord::tile_delta &pos, cost_t cost, const time::time_t &valid_until);

/**
* Set the cost at a specified position.
*
* @param x X-coordinate of the cell.
* @param y Y-coordinate of the cell.
* @param cost Cost to set.
* @param changed Time at which the cost value is changed.
*/
void set_cost(size_t x, size_t y, cost_t cost);
void set_cost(size_t x, size_t y, cost_t cost, const time::time_t &valid_until);

/**
* Set the cost at a specified position.
*
* @param idx Index of the cell.
* @param cost Cost to set.
* @param changed Time at which the cost value is changed.
*/
void set_cost(size_t idx, cost_t cost);

inline void set_cost(size_t idx, cost_t cost, const time::time_t &until) {
cells[idx] = cost;
valid_until = until;
}

/**
* Get the cost field values.
Expand All @@ -95,15 +103,33 @@ class CostField {
* Set the cost field values.
*
* @param cells Cost field values.
* @param changed Time at which the cost value is changed.
*/
void set_costs(std::vector<cost_t> &&cells, const time::time_t &changed);

/**
* Check if the cost field is dirty at the specified time.
*
* @param time Cost field is dirty if the cost field is accessed after the time given in valid_until.
*/
void set_costs(std::vector<cost_t> &&cells);
bool is_dirty(const time::time_t &time);

/**
* Cleans the dirty flag by setting it to time_MAX.
*/
void clean();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should be called clear_dirty


private:
/**
* Side length of the field.
*/
size_t size;

/**
* Time the cost field was last changed.
*/
time::time_t valid_until;

/**
* Cost field values.
*/
Expand Down
20 changes: 11 additions & 9 deletions libopenage/pathfinding/demo/demo_0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,17 @@ void path_demo_0(const util::Path &path) {

// Cost field with some obstacles
auto cost_field = std::make_shared<CostField>(field_length);
cost_field->set_cost(coord::tile_delta{0, 0}, COST_IMPASSABLE);
cost_field->set_cost(coord::tile_delta{1, 0}, 254);
cost_field->set_cost(coord::tile_delta{4, 3}, 128);
cost_field->set_cost(coord::tile_delta{5, 3}, 128);
cost_field->set_cost(coord::tile_delta{6, 3}, 128);
cost_field->set_cost(coord::tile_delta{4, 4}, 128);
cost_field->set_cost(coord::tile_delta{5, 4}, 128);
cost_field->set_cost(coord::tile_delta{6, 4}, 128);
cost_field->set_cost(coord::tile_delta{1, 7}, COST_IMPASSABLE);

const time::time_t time = time::TIME_ZERO;
cost_field->set_cost(coord::tile_delta{0, 0}, COST_IMPASSABLE, time);
cost_field->set_cost(coord::tile_delta{1, 0}, 254, time);
cost_field->set_cost(coord::tile_delta{4, 3}, 128, time);
cost_field->set_cost(coord::tile_delta{5, 3}, 128, time);
cost_field->set_cost(coord::tile_delta{6, 3}, 128, time);
cost_field->set_cost(coord::tile_delta{4, 4}, 128, time);
cost_field->set_cost(coord::tile_delta{5, 4}, 128, time);
cost_field->set_cost(coord::tile_delta{6, 4}, 128, time);
cost_field->set_cost(coord::tile_delta{1, 7}, COST_IMPASSABLE, time);
log::log(INFO << "Created cost field");

// Create an integration field from the cost field
Expand Down
16 changes: 14 additions & 2 deletions libopenage/pathfinding/demo/demo_1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "pathfinding/portal.h"
#include "pathfinding/sector.h"
#include "util/timer.h"
#include "time/time_loop.h"
#include "time/clock.h"

#include "renderer/gui/integration/public/gui_application_with_logger.h"
#include "renderer/opengl/window.h"
Expand All @@ -25,13 +27,17 @@
namespace openage::path::tests {

void path_demo_1(const util::Path &path) {
auto time_loop = std::make_shared<time::TimeLoop>();
time_loop->run();
auto clock = time_loop->get_clock();
Comment on lines +30 to +32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you intending to use the time loop and clock for? The changes you made to the demo code do not make it very clear and I don't understand why we need a clock at all in this demo.

Also, the demo will deadlock in line 31 currently because time_loop->run() is an infinite loop that is supposed to run in its own thread.

auto grid = std::make_shared<Grid>(0, util::Vector2s{4, 3}, 10);

const time::time_t time = clock->get_time();
// Initialize the cost field for each sector.
for (auto &sector : grid->get_sectors()) {
auto cost_field = sector->get_cost_field();
std::vector<cost_t> sector_cost = sectors_cost.at(sector->get_id());
cost_field->set_costs(std::move(sector_cost));
cost_field->set_costs(std::move(sector_cost), time);
}

// Initialize portals between sectors.
Expand Down Expand Up @@ -87,16 +93,20 @@ void path_demo_1(const util::Path &path) {
coord::tile start{2, 26};
coord::tile target{36, 2};

const time::time_t request_time = clock->get_time();

PathRequest path_request{
grid->get_id(),
start,
target,
request_time
};

grid->init_portal_nodes();
timer.start();
Path path_result = pathfinder->get_path(path_request);
timer.stop();

log::log(INFO << "Pathfinding request at " << request_time);
log::log(INFO << "Pathfinding took " << timer.getval() / 1000 << " us");

// Create a renderer to display the grid and path
Expand Down Expand Up @@ -127,6 +137,7 @@ void path_demo_1(const util::Path &path) {
grid->get_id(),
start,
target,
clock->get_time()
};

timer.reset();
Expand All @@ -147,6 +158,7 @@ void path_demo_1(const util::Path &path) {
grid->get_id(),
start,
target,
clock->get_time()
};

timer.reset();
Expand Down
30 changes: 30 additions & 0 deletions libopenage/pathfinding/field_cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2024-2024 the openage authors. See copying.md for legal info.

#include "field_cache.h"

namespace openage::path {

void FieldCache::add(cache_key_t cache_key,
field_cache_t cache_entry) {
this->cache[cache_key] = cache_entry;
}

void FieldCache::evict(cache_key_t cache_key) {
this->cache.erase(cache_key);
}

bool FieldCache::is_cached(cache_key_t cache_key) {
return this->cache.contains(cache_key);
}

std::shared_ptr<IntegrationField> FieldCache::get_integration_field(cache_key_t cache_key) {
auto cached = this->cache.find(cache_key);
return cached->second.first;
}

std::shared_ptr<FlowField> FieldCache::get_flow_field(cache_key_t cache_key) {
auto cached = this->cache.find(cache_key);
return cached->second.second;
}

} // namespace openage::path
79 changes: 79 additions & 0 deletions libopenage/pathfinding/field_cache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2024-2024 the openage authors. See copying.md for legal info.

#pragma once

#include <memory>
#include <unordered_map>

#include "pathfinding/types.h"
#include "util/hash.h"

namespace openage {
namespace coord {
struct tile_delta;
} // namespace coord

namespace path {
class IntegrationField;
class FlowField;

/**
* Cache to store already calculated flow and integration fields for the pathfinding algorithm.
*/
class FieldCache {
public:
FieldCache() = default;
~FieldCache() = default;

/**
* Adds a new field cache entry to the cache with a given portal and sector cache key.
*/
void add(cache_key_t cache_key,
field_cache_t cache_entry);

/**
* Evicts a given field cache entry from the cache at the given cache key.
*/
void evict(cache_key_t cache_key);

/**
* Checks if there is a cached entry at a specific cache key.
*/
bool is_cached(cache_key_t cache_key);

/**
* Gets the integration field from a given cache entry.
*/
std::shared_ptr<IntegrationField> get_integration_field(cache_key_t cache_key);

/**
* Gets the flow field from a given cache entry.
*/
std::shared_ptr<FlowField> get_flow_field(cache_key_t cache_key);

private:
/**
* Hash function for the field cache.
*/
struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2> &pair) const {
return util::hash_combine(std::hash<T1>{}(pair.first), std::hash<T2>{}(pair.second));
}
};

/**
* Cache for already computed fields.
*
* Key is the portal ID and the sector ID from which the field was entered. Fields that are cached are
* cleared of dynamic flags, i.e. wavefront or LOS flags. These have to be recalculated
* when the field is reused.
*/
std::unordered_map<cache_key_t,
field_cache_t,
pair_hash>
cache;
};

} // namespace path
} // namespace openage
Loading