ludic.nav (17.10): the time paths take, counted in the shim
nav_us() gives the microseconds spent in nav_path across the loaded meshes. The shim times each path with the OS's monotonic clock (clock_gettime, or QueryPerformanceCounter from KERNEL32), so the DLL still needs no C++ runtime. Both libraries are rebuilt, and 13 tests pass on the Mac and the PC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
297578705e
commit
f65eda8b47
9 changed files with 58 additions and 9 deletions
|
|
@ -51,7 +51,7 @@ import "ludic.nav"
|
||||||
| `nav_next_corner(kind, filter, sx, sy, sz, ex, ey, ez) -> bool` | the next corner of that way, read back as the nearest point is (a walker's one question a frame) |
|
| `nav_next_corner(kind, filter, sx, sy, sz, ex, ey, ez) -> bool` | the next corner of that way, read back as the nearest point is (a walker's one question a frame) |
|
||||||
| `nav_straight(kind, filter, sx, sy, sz, ex, ez) -> bool` | does the straight line stay walkable |
|
| `nav_straight(kind, filter, sx, sy, sz, ex, ez) -> bool` | does the straight line stay walkable |
|
||||||
| `nav_random_near(kind, filter, x, y, z, r, seed) -> bool`, `nav_near_x/y/z()` | a reachable point about r away, from the caller's seed |
|
| `nav_random_near(kind, filter, x, y, z, r, seed) -> bool`, `nav_near_x/y/z()` | a reachable point about r away, from the caller's seed |
|
||||||
| `nav_asked()`, `nav_found()`, `nav_short()` | paths asked for, found and cut short since the meshes loaded (a game's measure of how often its walkers had a way) |
|
| `nav_asked()`, `nav_found()`, `nav_short()`, `nav_us()` | paths asked for, found and cut short since the meshes loaded, and the microseconds they took (timed in the shim by the OS clock: a game's measure of how often its walkers had a way, and what it cost) |
|
||||||
| `nav_area_cost(kind, filter, area, cost)` | how dear a kind of ground is to cross by that filter |
|
| `nav_area_cost(kind, filter, area, cost)` | how dear a kind of ground is to cross by that filter |
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
|
||||||
BIN
packages/ludic.nav/lib/macos-arm64/libludicnav.dylib
(Stored with Git LFS)
BIN
packages/ludic.nav/lib/macos-arm64/libludicnav.dylib
(Stored with Git LFS)
Binary file not shown.
BIN
packages/ludic.nav/lib/windows-x64/ludicnav.dll
(Stored with Git LFS)
BIN
packages/ludic.nav/lib/windows-x64/ludicnav.dll
(Stored with Git LFS)
Binary file not shown.
BIN
packages/ludic.nav/lib/windows-x64/ludicnav.lib
(Stored with Git LFS)
BIN
packages/ludic.nav/lib/windows-x64/ludicnav.lib
(Stored with Git LFS)
Binary file not shown.
|
|
@ -11,6 +11,7 @@ extern function nvc_nearest(h: pointer, x: float, y: float, z: float, out: point
|
||||||
extern function nvc_area_cost(h: pointer, f: int, area: int, cost: float) -> void = "nav_area_cost"
|
extern function nvc_area_cost(h: pointer, f: int, area: int, cost: float) -> void = "nav_area_cost"
|
||||||
extern function nvc_path(h: pointer, f: int, sx: float, sy: float, sz: float, ex: float, ey: float, ez: float, out: pointer, max: int) -> int = "nav_path"
|
extern function nvc_path(h: pointer, f: int, sx: float, sy: float, sz: float, ex: float, ey: float, ez: float, out: pointer, max: int) -> int = "nav_path"
|
||||||
extern function nvc_partial(h: pointer) -> int = "nav_partial"
|
extern function nvc_partial(h: pointer) -> int = "nav_partial"
|
||||||
|
extern function nvc_us(h: pointer) -> int = "nav_us"
|
||||||
extern function nvc_raycast(h: pointer, f: int, sx: float, sy: float, sz: float, ex: float, ez: float) -> int = "nav_raycast"
|
extern function nvc_raycast(h: pointer, f: int, sx: float, sy: float, sz: float, ex: float, ez: float) -> int = "nav_raycast"
|
||||||
extern function nvc_random_near(h: pointer, f: int, x: float, y: float, z: float, r: float, seed: int, out: pointer) -> int = "nav_random_near"
|
extern function nvc_random_near(h: pointer, f: int, x: float, y: float, z: float, r: float, seed: int, out: pointer) -> int = "nav_random_near"
|
||||||
extern function nvc_stage(size: int) -> pointer = "nav_stage"
|
extern function nvc_stage(size: int) -> pointer = "nav_stage"
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ NAV_SHIM void nav_area_cost(void *h, int f, int area, float cost) {
|
||||||
// the corners of a path from one point to another, at most max of them, into out: the count, or
|
// the corners of a path from one point to another, at most max of them, into out: the count, or
|
||||||
// -1 when either end is off the mesh. One that cannot reach the end stops as near it as the mesh
|
// -1 when either end is off the mesh. One that cannot reach the end stops as near it as the mesh
|
||||||
// allows, and nav_partial says so
|
// allows, and nav_partial says so
|
||||||
NAV_SHIM int nav_path(void *h, int f, float sx, float sy, float sz, float ex, float ey, float ez, float *out, int max) {
|
static int nav_path_in(Nav *n, int f, float sx, float sy, float sz, float ex, float ey, float ez, float *out, int max) {
|
||||||
Nav *n = static_cast<Nav *>(h);
|
|
||||||
float s[3] = {sx, sy, sz}, e[3] = {ex, ey, ez}, so[3], eo[3];
|
float s[3] = {sx, sy, sz}, e[3] = {ex, ey, ez}, so[3], eo[3];
|
||||||
dtPolyRef a = nav_poly_at(n, s, so), b = nav_poly_at(n, e, eo);
|
dtPolyRef a = nav_poly_at(n, s, so), b = nav_poly_at(n, e, eo);
|
||||||
n->partial = 0;
|
n->partial = 0;
|
||||||
|
|
@ -39,6 +38,15 @@ NAV_SHIM int nav_path(void *h, int f, float sx, float sy, float sz, float ex, fl
|
||||||
if (dtStatusFailed(n->query->findStraightPath(so, end, n->polys, np, out, nullptr, nullptr, &count, max, 0))) return -1;
|
if (dtStatusFailed(n->query->findStraightPath(so, end, n->polys, np, out, nullptr, nullptr, &count, max, 0))) return -1;
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
NAV_SHIM int nav_path(void *h, int f, float sx, float sy, float sz, float ex, float ey, float ez, float *out, int max) {
|
||||||
|
Nav *n = static_cast<Nav *>(h);
|
||||||
|
long long t0 = nav_now_ns();
|
||||||
|
int r = nav_path_in(n, f, sx, sy, sz, ex, ey, ez, out, max);
|
||||||
|
n->ns += nav_now_ns() - t0;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
// microseconds this mesh has spent finding paths
|
||||||
|
NAV_SHIM int nav_us(void *h) { return static_cast<int>(static_cast<Nav *>(h)->ns / 1000); }
|
||||||
NAV_SHIM int nav_partial(void *h) { return static_cast<Nav *>(h)->partial; }
|
NAV_SHIM int nav_partial(void *h) { return static_cast<Nav *>(h)->partial; }
|
||||||
|
|
||||||
// does the straight line from one point to another stay on the mesh? 1 yes, 0 no, -1 off it
|
// does the straight line from one point to another stay on the mesh? 1 yes, 0 no, -1 off it
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,27 @@
|
||||||
#define NAV_SHIM extern "C" __attribute__((visibility("default")))
|
#define NAV_SHIM extern "C" __attribute__((visibility("default")))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
extern "C" __declspec(dllimport) int __stdcall QueryPerformanceCounter(long long *);
|
||||||
|
extern "C" __declspec(dllimport) int __stdcall QueryPerformanceFrequency(long long *);
|
||||||
|
#else
|
||||||
|
#include <time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
// a monotonic clock in nanoseconds, from the OS alone (KERNEL32 on Windows: no C++ runtime to ship)
|
||||||
|
long long nav_now_ns() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
long long c = 0, f = 1;
|
||||||
|
QueryPerformanceCounter(&c);
|
||||||
|
QueryPerformanceFrequency(&f);
|
||||||
|
return (long long)((double)c * 1e9 / (double)f);
|
||||||
|
#else
|
||||||
|
struct timespec t;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||||
|
return (long long)t.tv_sec * 1000000000LL + t.tv_nsec;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
const int NAV_MAX_POLYS = 512; // polygons a path may cross; a longer one is cut there
|
const int NAV_MAX_POLYS = 512; // polygons a path may cross; a longer one is cut there
|
||||||
const int NAV_MAX_NODES = 4096; // the search's open list
|
const int NAV_MAX_NODES = 4096; // the search's open list
|
||||||
const int NAV_FILTERS = 8; // costs per kind of ground: one set per kind of walker's taste
|
const int NAV_FILTERS = 8; // costs per kind of ground: one set per kind of walker's taste
|
||||||
|
|
@ -30,6 +50,7 @@ struct Nav {
|
||||||
float ext[3] = {2.0f, 4.0f, 2.0f}; // how far to look for the mesh around a point
|
float ext[3] = {2.0f, 4.0f, 2.0f}; // how far to look for the mesh around a point
|
||||||
dtPolyRef polys[NAV_MAX_POLYS];
|
dtPolyRef polys[NAV_MAX_POLYS];
|
||||||
int partial = 0; // the last path stopped short of its end
|
int partial = 0; // the last path stopped short of its end
|
||||||
|
long long ns = 0; // time spent finding paths, for the measure (17.10)
|
||||||
};
|
};
|
||||||
|
|
||||||
// a navmesh with its query, from params (a set of tiles) or from one tile's data
|
// a navmesh with its query, from params (a set of tiles) or from one tile's data
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,15 @@ export function nav_path(nav_st: mut NavState, kind: int, filter: int, sx: float
|
||||||
export function nav_asked(nav_st: NavState) -> int { return nav_st.nv_asked }
|
export function nav_asked(nav_st: NavState) -> int { return nav_st.nv_asked }
|
||||||
export function nav_found(nav_st: NavState) -> int { return nav_st.nv_found }
|
export function nav_found(nav_st: NavState) -> int { return nav_st.nv_found }
|
||||||
export function nav_short(nav_st: NavState) -> int { return nav_st.nv_short }
|
export function nav_short(nav_st: NavState) -> int { return nav_st.nv_short }
|
||||||
|
# microseconds spent finding those paths, over every loaded mesh
|
||||||
|
export function nav_us(nav_st: NavState) -> int {
|
||||||
|
var us = 0
|
||||||
|
for k in 0 .. NAV_KINDS {
|
||||||
|
let h = nv_mesh(nav_st, k)
|
||||||
|
if h != null { us += nvc_us(h) }
|
||||||
|
}
|
||||||
|
return us
|
||||||
|
}
|
||||||
export function nav_partial(nav_st: NavState, kind: int) -> bool {
|
export function nav_partial(nav_st: NavState, kind: int) -> bool {
|
||||||
let h = nv_mesh(nav_st, kind)
|
let h = nv_mesh(nav_st, kind)
|
||||||
return h != null and nvc_partial(h) == 1
|
return h != null and nvc_partial(h) == 1
|
||||||
|
|
|
||||||
|
|
@ -124,4 +124,14 @@ program NavTest {
|
||||||
nav_reset(nav_st)
|
nav_reset(nav_st)
|
||||||
expect_eq(nav_polygons(nav_st, NAV_SMALL), 0)
|
expect_eq(nav_polygons(nav_st, NAV_SMALL), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "the time paths take is counted, and only while a mesh is loaded" (nav_st: mut NavState) {
|
||||||
|
expect_eq(nav_us(nav_st), 0)
|
||||||
|
expect(meadow(nav_st, NAV_PERSON, 0, true))
|
||||||
|
for i in 0 .. 2000 { nav_path(nav_st, NAV_PERSON, 0, 5.0, 0.0, 20.0, 35.0, 0.0, 20.0) }
|
||||||
|
expect(nav_us(nav_st) > 0)
|
||||||
|
expect(nav_us(nav_st) < 2000000)
|
||||||
|
nav_reset(nav_st)
|
||||||
|
expect_eq(nav_us(nav_st), 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue