Catalog: parallelize all-sky tile prefetch
This commit is contained in:
1 parent
d9f2c83b9f
commit
457c0728b4
10 files changed
+371
-55
No files matched your search
@@ -18,6 +18,7 @@ TEST_TARGET := build/test_geodesic
|
||||
FRAME_TEST_TARGET := build/test_frame
|
||||
SCHWARZSCHILD_TEST_TARGET := build/test_schwarzschild
|
||||
OBSERVER_TRACK_TEST_TARGET := build/test_observer_track
|
||||
CATALOG_PREFETCH_TEST_TARGET := build/test_catalog_prefetch
|
||||
|
||||
.PHONY: all clean run test minkowski schwarzschild
|
||||
|
||||
@@ -59,11 +60,15 @@ $(SCHWARZSCHILD_TEST_TARGET): tests/test_schwarzschild.c $(COMMON_SOURCES) src/s
|
||||
$(OBSERVER_TRACK_TEST_TARGET): tests/test_observer_track.c $(COMMON_SOURCES) | build
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(OPENMP_FLAGS) -Isrc $^ $(LDLIBS) -o $@
|
||||
|
||||
test: $(TEST_TARGET) $(FRAME_TEST_TARGET) $(SCHWARZSCHILD_TEST_TARGET) $(OBSERVER_TRACK_TEST_TARGET)
|
||||
$(CATALOG_PREFETCH_TEST_TARGET): tests/test_catalog_prefetch.c $(CORE_MINKOWSKI_SOURCES) | build
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(OPENMP_FLAGS) -Isrc $^ $(LDLIBS) -o $@
|
||||
|
||||
test: $(TEST_TARGET) $(FRAME_TEST_TARGET) $(SCHWARZSCHILD_TEST_TARGET) $(OBSERVER_TRACK_TEST_TARGET) $(CATALOG_PREFETCH_TEST_TARGET)
|
||||
./$(TEST_TARGET)
|
||||
./$(FRAME_TEST_TARGET)
|
||||
./$(SCHWARZSCHILD_TEST_TARGET)
|
||||
./$(OBSERVER_TRACK_TEST_TARGET)
|
||||
./$(CATALOG_PREFETCH_TEST_TARGET)
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
@@ -46,7 +46,44 @@ make ENABLE_PNG=1 SPACETIME=minkowski all
|
||||
```
|
||||
|
||||
The renderer lazily loads only tiles touched by source triangles in the frame.
|
||||
It prefetches them serially, so OpenMP splat workers read immutable tile data.
|
||||
It first collects and de-duplicates the frame's touched tile IDs on the main
|
||||
thread. Newly needed CSVs are then read by a bounded OpenMP loader group
|
||||
(`--catalog-load-workers N`, default `4`); each worker owns distinct tile
|
||||
files. The main thread commits their results to the cache only after that group
|
||||
finishes, so OpenMP splat workers always read immutable tile data and never
|
||||
race catalog I/O. A tile already loaded or previously found absent is not read
|
||||
again during the same renderer run. The cache deliberately remains live for
|
||||
the entire movie: final numerical-BBH renders commonly sweep across the full
|
||||
sky, so tile eviction would trade the normal case for repeated filesystem I/O.
|
||||
|
||||
Each frame reports the number of requested/newly loaded/unavailable tiles,
|
||||
newly loaded stars, and CSV-load wall time. The loader count is deliberately
|
||||
independent of the splat worker count: start at the default and benchmark on
|
||||
the target storage before raising it.
|
||||
|
||||
Warm-cache reference measurement, 2026-08-27, on the available partial
|
||||
all-sky directory (18 CSV tiles, 12,917 stars, 128x72, 2-degree field):
|
||||
|
||||
| Loader workers | CSV load time |
|
||||
| ---: | ---: |
|
||||
| 1 | 5 ms |
|
||||
| 2 | 3 ms |
|
||||
| 4 | 2 ms |
|
||||
| 8 | 3 ms |
|
||||
|
||||
This confirms the bounded-loader path and motivates the default of four; it is
|
||||
not a cold-cache throughput claim for the final full catalog. Reproduce it
|
||||
with the rotating observer fixture:
|
||||
|
||||
```sh
|
||||
./build/minkowski_sky --all-sky-catalog assets/2mass/processed/all_sky \
|
||||
--observer-track tests/data/minkowski_rotation_2s.csv \
|
||||
--frames-dir output/imgs/catalog_prefetch_bench/w4 --frames-prefix frame \
|
||||
--start-time 0 --duration 0 --fps 1 --width 128 --height 72 \
|
||||
--coarse-cell-pixels 8 --fov-deg 2 --exposure 1e13 \
|
||||
--catalog-load-workers 4
|
||||
```
|
||||
|
||||
Missing tiles are skipped until the downloader has produced them. Fully
|
||||
contained tiles skip per-star source-triangle containment; partial tiles retain
|
||||
that exact check. For 2MASS apparent-solid-angle amplitudes, start with
|
||||
|
||||
+173
-25
@@ -6,6 +6,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <omp.h>
|
||||
|
||||
#define PI 3.14159265358979323846
|
||||
|
||||
enum {
|
||||
@@ -108,26 +110,40 @@ static size_t tile_index(int ra_index, int dec_index)
|
||||
return (size_t)dec_index * CATALOG_ALL_SKY_RA_TILES + ra_index;
|
||||
}
|
||||
|
||||
static int load_tile_file(const StarCatalog *catalog, int ra_index,
|
||||
int dec_index, Star **stars, size_t *count)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int written;
|
||||
if (catalog == NULL || stars == NULL || count == NULL)
|
||||
return -1;
|
||||
*stars = NULL;
|
||||
*count = 0;
|
||||
written = snprintf(path, sizeof path, "%s/tile_ra%03d_dec%03d.csv",
|
||||
catalog->all_sky_root, ra_index, dec_index);
|
||||
if (written < 0 || (size_t)written >= sizeof path)
|
||||
return -1;
|
||||
StarCatalog temporary = {0};
|
||||
if (catalog_load_csv(&temporary, path))
|
||||
return -1;
|
||||
*stars = temporary.stars;
|
||||
*count = temporary.count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_tile(StarCatalog *catalog, int ra_index, int dec_index)
|
||||
{
|
||||
CatalogTile *tile = &catalog->tiles[tile_index(ra_index, dec_index)];
|
||||
char path[PATH_MAX];
|
||||
int written;
|
||||
Star *stars;
|
||||
size_t count;
|
||||
if (tile->state != 0)
|
||||
return tile->state == 1 ? 0 : -1;
|
||||
written = snprintf(path, sizeof path, "%s/tile_ra%03d_dec%03d.csv",
|
||||
catalog->all_sky_root, ra_index, dec_index);
|
||||
if (written < 0 || (size_t)written >= sizeof path) {
|
||||
if (load_tile_file(catalog, ra_index, dec_index, &stars, &count)) {
|
||||
tile->state = -1;
|
||||
return -1;
|
||||
}
|
||||
StarCatalog temporary = {0};
|
||||
if (catalog_load_csv(&temporary, path)) {
|
||||
tile->state = -1;
|
||||
return -1;
|
||||
}
|
||||
tile->stars = temporary.stars;
|
||||
tile->count = temporary.count;
|
||||
tile->stars = stars;
|
||||
tile->count = count;
|
||||
tile->state = 1;
|
||||
catalog->count += tile->count;
|
||||
return 0;
|
||||
@@ -255,15 +271,15 @@ static int tile_is_fully_contained(const double direction[3][3], int ra_index,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int catalog_visit_source_triangle(StarCatalog *catalog,
|
||||
const double direction[3][3],
|
||||
int load_missing, CatalogTileVisitor visitor,
|
||||
void *context)
|
||||
typedef int (*CatalogTileIndexVisitor)(int ra_index, int dec_index,
|
||||
void *context);
|
||||
|
||||
static int visit_source_triangle_tile_indices(
|
||||
const double direction[3][3], CatalogTileIndexVisitor visitor,
|
||||
void *context)
|
||||
{
|
||||
if (catalog == NULL || direction == NULL || visitor == NULL)
|
||||
if (direction == NULL || visitor == NULL)
|
||||
return -1;
|
||||
if (catalog->kind == STAR_CATALOG_MEMORY)
|
||||
return visitor(catalog->stars, catalog->count, 0, context);
|
||||
double longitude[3], latitude[3], unwrapped[3];
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
lon_lat_from_direction(direction[i], &longitude[i], &latitude[i]);
|
||||
@@ -311,17 +327,149 @@ int catalog_visit_source_triangle(StarCatalog *catalog,
|
||||
for (int raw_ra = all_ra ? 0 : ra_first;
|
||||
raw_ra <= (all_ra ? 359 : ra_last); ++raw_ra) {
|
||||
const int ra = (raw_ra % 360 + 360) % 360;
|
||||
CatalogTile *tile = &catalog->tiles[tile_index(ra, dec)];
|
||||
if (load_missing && load_tile(catalog, ra, dec))
|
||||
continue; /* Downloader has not finished this tile yet. */
|
||||
if (tile->state != 1 ||
|
||||
visitor(tile->stars, tile->count,
|
||||
tile_is_fully_contained(direction, ra, dec), context))
|
||||
if (visitor(ra, dec, context))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
StarCatalog *catalog;
|
||||
const double (*direction)[3];
|
||||
int load_missing;
|
||||
CatalogTileVisitor visitor;
|
||||
void *context;
|
||||
} CatalogVisitContext;
|
||||
|
||||
static int visit_catalog_tile(int ra_index, int dec_index, void *opaque)
|
||||
{
|
||||
CatalogVisitContext *context = opaque;
|
||||
CatalogTile *tile =
|
||||
&context->catalog->tiles[tile_index(ra_index, dec_index)];
|
||||
if (context->load_missing && load_tile(context->catalog, ra_index, dec_index))
|
||||
return 0; /* Downloader has not finished this tile yet. */
|
||||
if (tile->state != 1)
|
||||
return 0;
|
||||
return context->visitor(
|
||||
tile->stars, tile->count,
|
||||
tile_is_fully_contained(context->direction, ra_index, dec_index),
|
||||
context->context);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned char *requested;
|
||||
} CatalogMarkContext;
|
||||
|
||||
static int mark_catalog_tile(int ra_index, int dec_index, void *opaque)
|
||||
{
|
||||
CatalogMarkContext *context = opaque;
|
||||
context->requested[tile_index(ra_index, dec_index)] = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int catalog_mark_source_triangle_tiles(
|
||||
const double direction[3][3],
|
||||
unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT])
|
||||
{
|
||||
CatalogMarkContext context = {.requested = requested};
|
||||
if (requested == NULL)
|
||||
return -1;
|
||||
return visit_source_triangle_tile_indices(direction, mark_catalog_tile,
|
||||
&context);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
size_t tile_id;
|
||||
Star *stars;
|
||||
size_t count;
|
||||
int loaded;
|
||||
} CatalogPendingTile;
|
||||
|
||||
int catalog_prefetch_marked_tiles(
|
||||
StarCatalog *catalog,
|
||||
const unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT],
|
||||
int worker_count, CatalogPrefetchStats *stats)
|
||||
{
|
||||
CatalogPendingTile *pending = NULL;
|
||||
size_t pending_count = 0;
|
||||
double load_start;
|
||||
int result = -1;
|
||||
if (stats != NULL)
|
||||
*stats = (CatalogPrefetchStats){0};
|
||||
if (catalog == NULL || requested == NULL || worker_count <= 0)
|
||||
return -1;
|
||||
if (catalog->kind != STAR_CATALOG_ALL_SKY)
|
||||
return 0;
|
||||
for (size_t tile_id = 0; tile_id < CATALOG_ALL_SKY_TILE_COUNT; ++tile_id) {
|
||||
if (!requested[tile_id])
|
||||
continue;
|
||||
if (stats != NULL)
|
||||
++stats->requested_tiles;
|
||||
if (catalog->tiles[tile_id].state == 0)
|
||||
++pending_count;
|
||||
}
|
||||
if (pending_count == 0)
|
||||
return 0;
|
||||
pending = calloc(pending_count, sizeof *pending);
|
||||
if (pending == NULL)
|
||||
return -1;
|
||||
size_t pending_index = 0;
|
||||
for (size_t tile_id = 0; tile_id < CATALOG_ALL_SKY_TILE_COUNT; ++tile_id)
|
||||
if (requested[tile_id] && catalog->tiles[tile_id].state == 0)
|
||||
pending[pending_index++].tile_id = tile_id;
|
||||
if (worker_count > (int)pending_count)
|
||||
worker_count = (int)pending_count;
|
||||
load_start = omp_get_wtime();
|
||||
#pragma omp parallel for num_threads(worker_count) schedule(static)
|
||||
for (size_t i = 0; i < pending_count; ++i) {
|
||||
const int ra_index =
|
||||
(int)(pending[i].tile_id % CATALOG_ALL_SKY_RA_TILES);
|
||||
const int dec_index =
|
||||
(int)(pending[i].tile_id / CATALOG_ALL_SKY_RA_TILES);
|
||||
pending[i].loaded = !load_tile_file(
|
||||
catalog, ra_index, dec_index, &pending[i].stars, &pending[i].count);
|
||||
}
|
||||
if (stats != NULL)
|
||||
stats->load_seconds = omp_get_wtime() - load_start;
|
||||
/* Only this serial commit mutates the shared catalog cache. */
|
||||
for (size_t i = 0; i < pending_count; ++i) {
|
||||
CatalogTile *tile = &catalog->tiles[pending[i].tile_id];
|
||||
if (pending[i].loaded) {
|
||||
tile->stars = pending[i].stars;
|
||||
tile->count = pending[i].count;
|
||||
tile->state = 1;
|
||||
catalog->count += tile->count;
|
||||
if (stats != NULL) {
|
||||
++stats->newly_loaded_tiles;
|
||||
stats->newly_loaded_stars += tile->count;
|
||||
}
|
||||
} else {
|
||||
tile->state = -1;
|
||||
if (stats != NULL)
|
||||
++stats->unavailable_tiles;
|
||||
}
|
||||
}
|
||||
result = 0;
|
||||
free(pending);
|
||||
return result;
|
||||
}
|
||||
|
||||
int catalog_visit_source_triangle(StarCatalog *catalog,
|
||||
const double direction[3][3],
|
||||
int load_missing, CatalogTileVisitor visitor,
|
||||
void *context)
|
||||
{
|
||||
if (catalog == NULL || direction == NULL || visitor == NULL)
|
||||
return -1;
|
||||
if (catalog->kind == STAR_CATALOG_MEMORY)
|
||||
return visitor(catalog->stars, catalog->count, 0, context);
|
||||
CatalogVisitContext visit_context = {
|
||||
.catalog = catalog, .direction = direction, .load_missing = load_missing,
|
||||
.visitor = visitor, .context = context};
|
||||
return visit_source_triangle_tile_indices(direction, visit_catalog_tile,
|
||||
&visit_context);
|
||||
}
|
||||
|
||||
void catalog_destroy(StarCatalog *catalog)
|
||||
{
|
||||
if (catalog->tiles != NULL)
|
||||
|
||||
+26
-1
@@ -9,7 +9,12 @@ typedef struct {
|
||||
double amplitude;
|
||||
} Star;
|
||||
|
||||
enum { CATALOG_ALL_SKY_RA_TILES = 360, CATALOG_ALL_SKY_DEC_TILES = 180 };
|
||||
enum {
|
||||
CATALOG_ALL_SKY_RA_TILES = 360,
|
||||
CATALOG_ALL_SKY_DEC_TILES = 180,
|
||||
CATALOG_ALL_SKY_TILE_COUNT =
|
||||
CATALOG_ALL_SKY_RA_TILES * CATALOG_ALL_SKY_DEC_TILES
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Star *stars;
|
||||
@@ -33,6 +38,14 @@ typedef struct {
|
||||
typedef int (*CatalogTileVisitor)(const Star *stars, size_t count,
|
||||
int fully_contained, void *context);
|
||||
|
||||
typedef struct {
|
||||
size_t requested_tiles;
|
||||
size_t newly_loaded_tiles;
|
||||
size_t unavailable_tiles;
|
||||
size_t newly_loaded_stars;
|
||||
double load_seconds;
|
||||
} CatalogPrefetchStats;
|
||||
|
||||
/*
|
||||
* Synthetic lensing fixture: stars lie on the union of 10-degree longitude
|
||||
* and latitude lines, sampled every 2 degrees. The eight longitude/hemisphere
|
||||
@@ -44,6 +57,18 @@ int catalog_load_csv(StarCatalog *catalog, const char *path);
|
||||
/* The directory contains tile_raRRR_decDDD.csv plus optional .done markers.
|
||||
* Tiles are loaded only after a source triangle intersects them. */
|
||||
int catalog_load_all_sky(StarCatalog *catalog, const char *directory);
|
||||
/* Add every one-degree tile which can intersect a source triangle to the
|
||||
* caller-owned CATALOG_ALL_SKY_TILE_COUNT-byte request bitmap. */
|
||||
int catalog_mark_source_triangle_tiles(
|
||||
const double direction[3][3],
|
||||
unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT]);
|
||||
/* Load each requested, previously unseen tile once. File reads use at most
|
||||
* worker_count OpenMP workers, but cache mutation happens serially afterward,
|
||||
* leaving the cache immutable for later splatting. */
|
||||
int catalog_prefetch_marked_tiles(
|
||||
StarCatalog *catalog,
|
||||
const unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT],
|
||||
int worker_count, CatalogPrefetchStats *stats);
|
||||
/* Visit only 1-degree all-sky tiles that can intersect this source triangle.
|
||||
* Call with load_missing=1 before parallel rendering, then 0 inside workers. */
|
||||
int catalog_visit_source_triangle(StarCatalog *catalog,
|
||||
|
||||
+13
-17
@@ -259,19 +259,13 @@ static size_t splat_catalog_triangles(const FrameLensMesh *mesh,
|
||||
return images;
|
||||
}
|
||||
|
||||
static int prefetch_catalog_tile(const Star *stars, size_t count,
|
||||
int fully_contained, void *context) {
|
||||
(void)stars;
|
||||
(void)count;
|
||||
(void)fully_contained;
|
||||
(void)context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prefetch_catalog_for_mesh(const FrameLensMesh *mesh,
|
||||
StarCatalog *catalog) {
|
||||
StarCatalog *catalog,
|
||||
int worker_count,
|
||||
CatalogPrefetchStats *stats) {
|
||||
if (catalog->kind != STAR_CATALOG_ALL_SKY)
|
||||
return;
|
||||
unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT] = {0};
|
||||
for (size_t t = 0; t < mesh->triangle_count; ++t) {
|
||||
const LensVertex *vertex[3];
|
||||
if (!usable_triangle(mesh, &mesh->triangles[t], vertex))
|
||||
@@ -280,22 +274,24 @@ static void prefetch_catalog_for_mesh(const FrameLensMesh *mesh,
|
||||
{vertex[0]->n_infinity[0], vertex[0]->n_infinity[1], vertex[0]->n_infinity[2]},
|
||||
{vertex[1]->n_infinity[0], vertex[1]->n_infinity[1], vertex[1]->n_infinity[2]},
|
||||
{vertex[2]->n_infinity[0], vertex[2]->n_infinity[1], vertex[2]->n_infinity[2]}};
|
||||
(void)catalog_visit_source_triangle(catalog, direction, 1,
|
||||
prefetch_catalog_tile, NULL);
|
||||
(void)catalog_mark_source_triangle_tiles(direction, requested);
|
||||
}
|
||||
(void)catalog_prefetch_marked_tiles(catalog, requested, worker_count, stats);
|
||||
}
|
||||
|
||||
size_t frame_splat_catalog(const FrameLensMesh *mesh,
|
||||
StarCatalog *catalog, double *hdr, int width,
|
||||
int height, double exposure,
|
||||
const PointSpreadFunction *psf) {
|
||||
const PointSpreadFunction *psf,
|
||||
int catalog_load_workers,
|
||||
CatalogPrefetchStats *prefetch_stats) {
|
||||
if (mesh == NULL || catalog == NULL || hdr == NULL || exposure <= 0.0 ||
|
||||
psf == NULL || width <= 0 || height <= 0)
|
||||
psf == NULL || width <= 0 || height <= 0 || catalog_load_workers <= 0)
|
||||
return 0;
|
||||
|
||||
/* Tile I/O is deliberately serial and complete before OpenMP workers start.
|
||||
* The parallel splat pass then reads an immutable tile cache. */
|
||||
prefetch_catalog_for_mesh(mesh, catalog);
|
||||
/* A bounded parallel read phase completes before splatting. Its serial cache
|
||||
* commit leaves immutable tile data for the OpenMP splat workers. */
|
||||
prefetch_catalog_for_mesh(mesh, catalog, catalog_load_workers, prefetch_stats);
|
||||
|
||||
const size_t pixel_count = (size_t)width * height * 3;
|
||||
if (pixel_count > SIZE_MAX / sizeof(double) ||
|
||||
|
||||
+3
-1
@@ -38,7 +38,9 @@ int frame_lens_mesh_trace(FrameLensMesh *mesh, const SpacetimeSource *spacetime,
|
||||
size_t frame_splat_catalog(const FrameLensMesh *mesh,
|
||||
StarCatalog *catalog, double *hdr, int width,
|
||||
int height, double exposure,
|
||||
const PointSpreadFunction *psf);
|
||||
const PointSpreadFunction *psf,
|
||||
int catalog_load_workers,
|
||||
CatalogPrefetchStats *prefetch_stats);
|
||||
void frame_draw_mesh(const FrameLensMesh *mesh, double *hdr, int width,
|
||||
int height, double gray, double opacity);
|
||||
void frame_lens_mesh_destroy(FrameLensMesh *mesh);
|
||||
|
||||
+30
-5
@@ -31,6 +31,7 @@ typedef struct {
|
||||
double movie_start_time, movie_duration, movie_fps;
|
||||
double slab_duration;
|
||||
double minkowski_proper_acceleration;
|
||||
int catalog_load_workers;
|
||||
} Settings;
|
||||
|
||||
static int parse_int(const char *text, int *value) {
|
||||
@@ -108,7 +109,8 @@ static int parse_args(int argc, char **argv, Settings *s,
|
||||
.movie_duration = 2.0,
|
||||
.movie_fps = 30.0,
|
||||
.slab_duration = 64.0,
|
||||
.minkowski_proper_acceleration = 1.52};
|
||||
.minkowski_proper_acceleration = 1.52,
|
||||
.catalog_load_workers = 4};
|
||||
*write_path = NULL;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (!strcmp(argv[i], "--catalog") && i + 1 < argc)
|
||||
@@ -158,6 +160,8 @@ static int parse_args(int argc, char **argv, Settings *s,
|
||||
} else if (!strcmp(argv[i], "--proper-acceleration") && i + 1 < argc &&
|
||||
!parse_nonnegative(argv[++i],
|
||||
&s->minkowski_proper_acceleration)) {
|
||||
} else if (!strcmp(argv[i], "--catalog-load-workers") && i + 1 < argc &&
|
||||
!parse_int(argv[++i], &s->catalog_load_workers)) {
|
||||
} else if (!strcmp(argv[i], "--write-minkowski-accel-track") &&
|
||||
i + 1 < argc)
|
||||
s->write_minkowski_accel_track_path = argv[++i];
|
||||
@@ -204,14 +208,24 @@ static int render_observer_frame(const Settings *s, StarCatalog *catalog,
|
||||
free(hdr);
|
||||
return -1;
|
||||
}
|
||||
size_t images = frame_splat_catalog(&mesh, catalog, hdr, s->width, s->height,
|
||||
s->exposure, &s->psf);
|
||||
CatalogPrefetchStats prefetch = {0};
|
||||
size_t images = frame_splat_catalog(
|
||||
&mesh, catalog, hdr, s->width, s->height, s->exposure, &s->psf,
|
||||
s->catalog_load_workers, &prefetch);
|
||||
if (s->draw_mesh)
|
||||
frame_draw_mesh(&mesh, hdr, s->width, s->height, 0.5, 0.5);
|
||||
int result = write_tonemapped_image(output_path, hdr, s->width, s->height);
|
||||
fprintf(stderr, "Rendered %zu images from %zu catalog stars to %s (%s)\n",
|
||||
images, catalog->count, output_path,
|
||||
result == 0 ? "ok" : "write failed");
|
||||
if (catalog->kind == STAR_CATALOG_ALL_SKY)
|
||||
fprintf(stderr,
|
||||
"Catalog prefetch: %zu requested, %zu newly loaded (%zu stars), "
|
||||
"%zu unavailable in %.3f s; %d loader workers\n",
|
||||
prefetch.requested_tiles, prefetch.newly_loaded_tiles,
|
||||
prefetch.newly_loaded_stars, prefetch.unavailable_tiles,
|
||||
prefetch.load_seconds,
|
||||
s->catalog_load_workers);
|
||||
frame_lens_mesh_destroy(&mesh);
|
||||
free(hdr);
|
||||
return result;
|
||||
@@ -289,14 +303,24 @@ static int render_movie(const Settings *s, StarCatalog *catalog,
|
||||
free(hdr);
|
||||
goto done;
|
||||
}
|
||||
const size_t images = frame_splat_catalog(&movie.frames[i].mesh, catalog, hdr,
|
||||
s->width, s->height, s->exposure, &s->psf);
|
||||
CatalogPrefetchStats prefetch = {0};
|
||||
const size_t images = frame_splat_catalog(
|
||||
&movie.frames[i].mesh, catalog, hdr, s->width, s->height, s->exposure,
|
||||
&s->psf, s->catalog_load_workers, &prefetch);
|
||||
if (s->draw_mesh)
|
||||
frame_draw_mesh(&movie.frames[i].mesh, hdr, s->width, s->height, 0.5, 0.5);
|
||||
const int write_result = write_tonemapped_image(output_path, hdr, s->width, s->height);
|
||||
free(hdr);
|
||||
fprintf(stderr, "Rendered %zu images from %zu catalog stars to %s (%s)\n",
|
||||
images, catalog->count, output_path, write_result == 0 ? "ok" : "write failed");
|
||||
if (catalog->kind == STAR_CATALOG_ALL_SKY)
|
||||
fprintf(stderr,
|
||||
"Catalog prefetch: %zu requested, %zu newly loaded (%zu stars), "
|
||||
"%zu unavailable in %.3f s; %d loader workers\n",
|
||||
prefetch.requested_tiles, prefetch.newly_loaded_tiles,
|
||||
prefetch.newly_loaded_stars, prefetch.unavailable_tiles,
|
||||
prefetch.load_seconds,
|
||||
s->catalog_load_workers);
|
||||
if (write_result)
|
||||
goto done;
|
||||
}
|
||||
@@ -331,6 +355,7 @@ int main(int argc, char **argv) {
|
||||
"[--exposure E] [--observer-inward-speed V] "
|
||||
"[--psf-fwhm-pixels N] [--psf-moffat-beta N] "
|
||||
"[--coarse-cell-pixels N] [--draw-mesh] [--write-catalog PATH] "
|
||||
"[--catalog-load-workers N] "
|
||||
"[--observer-track PATH --frames-dir DIR --frames-prefix NAME "
|
||||
"--start-time T --duration T --fps N] "
|
||||
"[--proper-acceleration A --write-minkowski-accel-track PATH]\n",
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# An inertial observer at the origin whose spatial camera triad scans RA 0, 20,
|
||||
# and 40 degrees at Dec -75 degrees. Samples coincide with a 1 fps movie so
|
||||
# this fixture avoids interpolation-induced non-orthonormal intermediate triads.
|
||||
t,tau,x,y,z,e0t,e0x,e0y,e0z,e1t,e1x,e1y,e1z,e2t,e2x,e2y,e2z,e3t,e3x,e3y,e3z
|
||||
0,0,0,0,0,1,0,0,0,0,0.25881904510252074,-0.9659258262890683,0,0,0.9659258262890683,0.25881904510252074,0,0,0,0,1
|
||||
1,1,0,0,0,1,0,0,0,0,0.24321034680169396,-0.9659258262890683,0.08852132690137686,0,0.9076733711903687,0.25881904510252074,0.33036608954935215,0,-0.3420201433256687,0,0.9396926207859084
|
||||
2,2,0,0,0,1,0,0,0,0,0.19826689127414615,-0.9659258262890683,0.1663656753428019,0,0.739942111693848,0.25881904510252074,0.6208851530148457,0,-0.6427876096865394,0,0.766044443118978
|
||||
|
@@ -0,0 +1,71 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include "catalog.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int write_tile(const char *directory, int ra_index, int dec_index,
|
||||
double longitude) {
|
||||
char path[4096];
|
||||
const int written = snprintf(path, sizeof path, "%s/tile_ra%03d_dec%03d.csv",
|
||||
directory, ra_index, dec_index);
|
||||
if (written < 0 || written >= (int)sizeof path)
|
||||
return -1;
|
||||
FILE *file = fopen(path, "w");
|
||||
if (file == NULL)
|
||||
return -1;
|
||||
const int result =
|
||||
fputs("longitude_deg,latitude_deg,temperature_K,amplitude\n", file) == EOF ||
|
||||
fprintf(file, "%.1f,0,7000,1\n", longitude) < 0 ||
|
||||
fclose(file) != 0;
|
||||
return result ? -1 : 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
char directory[] = "/tmp/catalog_prefetch_XXXXXX";
|
||||
char path[4096];
|
||||
StarCatalog catalog = {0};
|
||||
unsigned char requested[CATALOG_ALL_SKY_TILE_COUNT] = {0};
|
||||
CatalogPrefetchStats first = {0}, second = {0};
|
||||
int result = 1;
|
||||
if (mkdtemp(directory) == NULL || write_tile(directory, 0, 90, 0.0) ||
|
||||
write_tile(directory, 1, 90, 1.0) ||
|
||||
catalog_load_all_sky(&catalog, directory))
|
||||
goto done;
|
||||
requested[90 * CATALOG_ALL_SKY_RA_TILES] = 1;
|
||||
requested[90 * CATALOG_ALL_SKY_RA_TILES + 1] = 1;
|
||||
requested[90 * CATALOG_ALL_SKY_RA_TILES + 2] = 1; /* Missing on purpose. */
|
||||
if (catalog_prefetch_marked_tiles(&catalog, requested, 4, &first) ||
|
||||
first.requested_tiles != 3 || first.newly_loaded_tiles != 2 ||
|
||||
first.unavailable_tiles != 1 || first.newly_loaded_stars != 2 ||
|
||||
catalog.count != 2 ||
|
||||
catalog.tiles[90 * CATALOG_ALL_SKY_RA_TILES].state != 1 ||
|
||||
catalog.tiles[90 * CATALOG_ALL_SKY_RA_TILES + 1].state != 1 ||
|
||||
catalog.tiles[90 * CATALOG_ALL_SKY_RA_TILES + 2].state != -1) {
|
||||
fputs("parallel catalog prefetch regression failed\n", stderr);
|
||||
goto done;
|
||||
}
|
||||
Star *first_tile = catalog.tiles[90 * CATALOG_ALL_SKY_RA_TILES].stars;
|
||||
if (catalog_prefetch_marked_tiles(&catalog, requested, 2, &second) ||
|
||||
second.requested_tiles != 3 || second.newly_loaded_tiles != 0 ||
|
||||
second.unavailable_tiles != 0 || second.newly_loaded_stars != 0 ||
|
||||
catalog.count != 2 ||
|
||||
catalog.tiles[90 * CATALOG_ALL_SKY_RA_TILES].stars != first_tile) {
|
||||
fputs("catalog prefetch cache reuse regression failed\n", stderr);
|
||||
goto done;
|
||||
}
|
||||
result = 0;
|
||||
done:
|
||||
catalog_destroy(&catalog);
|
||||
for (int ra = 0; ra < 3; ++ra) {
|
||||
const int written = snprintf(path, sizeof path, "%s/tile_ra%03d_dec090.csv",
|
||||
directory, ra);
|
||||
if (written > 0 && written < (int)sizeof path)
|
||||
unlink(path);
|
||||
}
|
||||
rmdir(directory);
|
||||
return result;
|
||||
}
|
||||
+4
-4
@@ -27,7 +27,7 @@ int main(void) {
|
||||
goto done;
|
||||
const size_t images =
|
||||
frame_splat_catalog(&mesh, &catalog, hdr, width, height, test_exposure,
|
||||
&psf);
|
||||
&psf, 1, NULL);
|
||||
if (images != 1 || hdr[3 * (50 * width + 50)] <= 0.0) {
|
||||
fputs("flat-space inverse lens-map regression failed\n", stderr);
|
||||
goto done;
|
||||
@@ -44,10 +44,10 @@ int main(void) {
|
||||
omp_set_dynamic(0);
|
||||
omp_set_num_threads(1);
|
||||
const size_t serial_images = frame_splat_catalog(
|
||||
&mesh, &catalog, serial_hdr, width, height, test_exposure, &psf);
|
||||
&mesh, &catalog, serial_hdr, width, height, test_exposure, &psf, 1, NULL);
|
||||
omp_set_num_threads(4);
|
||||
const size_t parallel_images = frame_splat_catalog(
|
||||
&mesh, &catalog, parallel_hdr, width, height, test_exposure, &psf);
|
||||
&mesh, &catalog, parallel_hdr, width, height, test_exposure, &psf, 1, NULL);
|
||||
omp_set_num_threads(original_threads);
|
||||
for (int value = 0; value < width * height * 3; ++value)
|
||||
if (fabs(serial_hdr[value] - parallel_hdr[value]) >
|
||||
@@ -102,7 +102,7 @@ int main(void) {
|
||||
if (frame_lens_mesh_build_coarse(&fine_mesh, width, height, 1, 0.1) ||
|
||||
frame_lens_mesh_trace(&fine_mesh, &spacetime, &observer, &trace) ||
|
||||
frame_splat_catalog(&fine_mesh, &fine_catalog, hdr, width, height,
|
||||
test_exposure, &psf) != 1) {
|
||||
test_exposure, &psf, 1, NULL) != 1) {
|
||||
fputs("fine source-triangle containment regression failed\n", stderr);
|
||||
frame_lens_mesh_destroy(&fine_mesh);
|
||||
goto done;
|
||||
|
||||
Reference in new issue
Block a user