Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 26 additions & 23 deletions src/isyntax/isyntax.c
Original file line number Diff line number Diff line change
Expand Up @@ -1977,7 +1977,6 @@ u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi
}

void isyntax_load_tile(isyntax_t* isyntax, isyntax_image_t* wsi, i32 scale, i32 tile_x, i32 tile_y,
block_allocator_t* ll_coeff_block_allocator,
u32* out_buffer_or_null, enum isyntax_pixel_format_t pixel_format) {
// printf("@@@ isyntax_load_tile scale=%d tile_x=%d tile_y=%d\n", scale, tile_x, tile_y);
isyntax_level_t* level = wsi->levels + scale;
Expand Down Expand Up @@ -2036,25 +2035,25 @@ void isyntax_load_tile(isyntax_t* isyntax, isyntax_image_t* wsi, i32 scale, i32

// TODO(avirodov): instead of releasing here, skip copy if still allocated.
if (child_top_left->color_channels[color].coeff_ll) {
block_free(ll_coeff_block_allocator, child_top_left->color_channels[color].coeff_ll);
block_free(isyntax->ll_coeff_block_allocator, child_top_left->color_channels[color].coeff_ll);
}
if (child_top_right->color_channels[color].coeff_ll) {
block_free(ll_coeff_block_allocator, child_top_right->color_channels[color].coeff_ll);
block_free(isyntax->ll_coeff_block_allocator, child_top_right->color_channels[color].coeff_ll);
}
if (child_bottom_left->color_channels[color].coeff_ll) {
block_free(ll_coeff_block_allocator, child_bottom_left->color_channels[color].coeff_ll);
block_free(isyntax->ll_coeff_block_allocator, child_bottom_left->color_channels[color].coeff_ll);
}
if (child_bottom_right->color_channels[color].coeff_ll) {
block_free(ll_coeff_block_allocator, child_bottom_right->color_channels[color].coeff_ll);
block_free(isyntax->ll_coeff_block_allocator, child_bottom_right->color_channels[color].coeff_ll);
}

// NOTE: malloc() and free() can become a bottleneck, they don't scale well especially across many threads.
// We use a custom block allocator to address this.
i64 start_malloc = get_clock();
child_top_left->color_channels[color].coeff_ll = (icoeff_t*)block_alloc(ll_coeff_block_allocator);
child_top_right->color_channels[color].coeff_ll = (icoeff_t*)block_alloc(ll_coeff_block_allocator);
child_bottom_left->color_channels[color].coeff_ll = (icoeff_t*)block_alloc(ll_coeff_block_allocator);
child_bottom_right->color_channels[color].coeff_ll = (icoeff_t*)block_alloc(ll_coeff_block_allocator);
child_top_left->color_channels[color].coeff_ll = (icoeff_t*)block_alloc(isyntax->ll_coeff_block_allocator);
child_top_right->color_channels[color].coeff_ll = (icoeff_t*)block_alloc(isyntax->ll_coeff_block_allocator);
child_bottom_left->color_channels[color].coeff_ll = (icoeff_t*)block_alloc(isyntax->ll_coeff_block_allocator);
child_bottom_right->color_channels[color].coeff_ll = (icoeff_t*)block_alloc(isyntax->ll_coeff_block_allocator);
elapsed_malloc += get_seconds_elapsed(start_malloc, get_clock());
i32 dest_stride = block_width;
// Blit top left child LL block
Expand Down Expand Up @@ -3348,6 +3347,22 @@ void isyntax_destroy(isyntax_t* isyntax) {
if (isyntax->h_coeff_block_allocator->is_valid) {
block_allocator_destroy(isyntax->h_coeff_block_allocator);
}
} else {
// Need to individually deallocate tiles from a shared allocator, especially if not tracked via a cache.
isyntax_image_t* wsi_image = isyntax->images + isyntax->wsi_image_index;
for (i32 i = 0; i < wsi_image->level_count; ++i) {
isyntax_level_t* level = wsi_image->levels + i;
if (level->tiles) {
for (i32 j = 0; j < level->tile_count; ++j) {
isyntax_tile_t* tile = level->tiles + j;
for (i32 color = 0; color < 3; ++color) {
isyntax_tile_channel_t* channel = tile->color_channels + color;
if (channel->coeff_ll) free(channel->coeff_ll);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this needs to be block_free...

if (channel->coeff_h) free(channel->coeff_h);
}
}
}
}
}
if (isyntax->black_dummy_coeff) {
free(isyntax->black_dummy_coeff);
Expand Down Expand Up @@ -3376,20 +3391,8 @@ void isyntax_destroy(isyntax_t* isyntax) {
}
for (i32 i = 0; i < image->level_count; ++i) {
isyntax_level_t* level = image->levels + i;
if (level->tiles) {
#if 0
for (i32 j = 0; j < level->tile_count; ++j) {
isyntax_tile_t* tile = level->tiles + j;
for (i32 color = 0; color < 3; ++color) {
isyntax_tile_channel_t* channel = tile->color_channels + color;
if (channel->coeff_ll) free(channel->coeff_ll);
if (channel->coeff_h) free(channel->coeff_h);
}
}
#endif
free(level->tiles);
level->tiles = NULL;
}
free(level->tiles);
level->tiles = NULL;
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/isyntax/isyntax.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,7 @@ void isyntax_set_work_queue(isyntax_t* isyntax, work_queue_t* work_queue);
bool isyntax_open(isyntax_t* isyntax, const char* filename, bool init_allocators);
void isyntax_destroy(isyntax_t* isyntax);
void isyntax_idwt(icoeff_t* idwt, i32 quadrant_width, i32 quadrant_height, bool output_steps_as_png, const char* png_name);
void isyntax_load_tile(isyntax_t* isyntax, isyntax_image_t* wsi, i32 scale, i32 tile_x, i32 tile_y, block_allocator_t* ll_coeff_block_allocator,
u32* out_buffer_or_null, enum isyntax_pixel_format_t pixel_format);
void isyntax_load_tile(isyntax_t* isyntax, isyntax_image_t* wsi, i32 scale, i32 tile_x, i32 tile_y, u32* out_buffer_or_null, enum isyntax_pixel_format_t pixel_format);
u32 isyntax_get_adjacent_tiles_mask(isyntax_level_t* level, i32 tile_x, i32 tile_y);
u32 isyntax_get_adjacent_tiles_mask_only_existing(isyntax_level_t* level, i32 tile_x, i32 tile_y);
u32 isyntax_idwt_tile_for_color_channel(isyntax_t* isyntax, isyntax_image_t* wsi, i32 scale, i32 tile_x, i32 tile_y, i32 color, icoeff_t* dest_buffer);
Expand Down
94 changes: 73 additions & 21 deletions src/isyntax/isyntax_reader.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,78 @@
#define LOG(msg, ...) console_print(msg, ##__VA_ARGS__)
#define LOG_VAR(fmt, var) console_print("%s: %s=" fmt "\n", __FUNCTION__, #var, var)

isyntax_cache_t* isyntax_cache_create(const char* debug_name_or_null, int32_t cache_size)
{
isyntax_cache_t* cache_ptr = malloc(sizeof(isyntax_cache_t));
memset(cache_ptr, 0, sizeof(*cache_ptr));
tile_list_init(&cache_ptr->cache_list, debug_name_or_null);
cache_ptr->target_cache_size = cache_size;
cache_ptr->mutex = benaphore_create();

// Note: rest of initialization is deferred to the first injection, as that is where we will know the block size.
return cache_ptr;
}

void isyntax_cache_inject(isyntax_cache_t* isyntax_cache, isyntax_t* isyntax) {
ASSERT(isyntax->ll_coeff_block_allocator == NULL);
ASSERT(isyntax->h_coeff_block_allocator == NULL);

if (!isyntax_cache->h_coeff_block_allocator.is_valid || !isyntax_cache->ll_coeff_block_allocator.is_valid) {
// Shouldn't ever partially initialize.
ASSERT(!isyntax_cache->h_coeff_block_allocator.is_valid);
ASSERT(!isyntax_cache->ll_coeff_block_allocator.is_valid);

isyntax_cache->allocator_block_width = isyntax->block_width;
isyntax_cache->allocator_block_height = isyntax->block_height;
size_t ll_coeff_block_size = isyntax->block_width * isyntax->block_height * sizeof(icoeff_t);
size_t block_allocator_maximum_capacity_in_blocks = GIGABYTES(32) / ll_coeff_block_size;
size_t ll_coeff_block_allocator_capacity_in_blocks = block_allocator_maximum_capacity_in_blocks / 4;
size_t h_coeff_block_size = ll_coeff_block_size * 3;
size_t h_coeff_block_allocator_capacity_in_blocks = ll_coeff_block_allocator_capacity_in_blocks * 3;
isyntax_cache->ll_coeff_block_allocator = block_allocator_create(ll_coeff_block_size, ll_coeff_block_allocator_capacity_in_blocks, MEGABYTES(256));
isyntax_cache->h_coeff_block_allocator = block_allocator_create(h_coeff_block_size, h_coeff_block_allocator_capacity_in_blocks, MEGABYTES(256));
}

// having multiple iSyntax with different block sizes opened with a shared cache is not supported.
ASSERT(isyntax_cache->allocator_block_width == isyntax->block_width);
ASSERT(isyntax_cache->allocator_block_height == isyntax->block_height);

isyntax->ll_coeff_block_allocator = &isyntax_cache->ll_coeff_block_allocator;
isyntax->h_coeff_block_allocator = &isyntax_cache->h_coeff_block_allocator;
isyntax->is_block_allocator_owned = false;
}

void isyntax_cache_destroy(isyntax_cache_t* isyntax_cache) {
if (isyntax_cache->ll_coeff_block_allocator.is_valid) {
block_allocator_destroy(&isyntax_cache->ll_coeff_block_allocator);
}
if (isyntax_cache->h_coeff_block_allocator.is_valid) {
block_allocator_destroy(&isyntax_cache->h_coeff_block_allocator);
}
benaphore_destroy(&isyntax_cache->mutex);
free(isyntax_cache);
}

void isyntax_cache_trim(isyntax_cache_t* isyntax_cache, i32 target_size) {
// TODO(avirodov): later will need to skip tiles that are reserved by other threads.
while (isyntax_cache->cache_list.count > target_size) {
isyntax_tile_t* tile = isyntax_cache->cache_list.tail;
tile_list_remove(&isyntax_cache->cache_list, tile);
for (int i = 0; i < 3; ++i) {
if (tile->has_ll) {
block_free(&isyntax_cache->ll_coeff_block_allocator, tile->color_channels[i].coeff_ll);
tile->color_channels[i].coeff_ll = NULL;
}
if (tile->has_h) {
block_free(&isyntax_cache->h_coeff_block_allocator, tile->color_channels[i].coeff_h);
tile->color_channels[i].coeff_h = NULL;
}
}
tile->has_ll = false;
tile->has_h = false;
}
}

void tile_list_init(isyntax_tile_list_t* list, const char* dbg_name) {
list->head = NULL;
list->tail = NULL;
Expand Down Expand Up @@ -206,7 +278,6 @@ static void isyntax_openslide_idwt(isyntax_cache_t* cache, isyntax_t* isyntax, i
ASSERT(pixels_buffer != NULL); // Shouldn't be asking for idwt at level 0 if we're not going to use the result for pixels.
isyntax_load_tile(isyntax, &isyntax->images[isyntax->wsi_image_index],
tile->tile_scale, tile->tile_x, tile->tile_y,
&cache->ll_coeff_block_allocator,
pixels_buffer, pixel_format);
return;
}
Expand All @@ -216,7 +287,6 @@ static void isyntax_openslide_idwt(isyntax_cache_t* cache, isyntax_t* isyntax, i
// the lls in the tile. Currently need to recompute idwt.
isyntax_load_tile(isyntax, &isyntax->images[isyntax->wsi_image_index],
tile->tile_scale, tile->tile_x, tile->tile_y,
&cache->ll_coeff_block_allocator,
pixels_buffer, pixel_format);
return;
}
Expand All @@ -231,7 +301,6 @@ static void isyntax_openslide_idwt(isyntax_cache_t* cache, isyntax_t* isyntax, i

isyntax_load_tile(isyntax, &isyntax->images[isyntax->wsi_image_index],
tile->tile_scale, tile->tile_x, tile->tile_y,
&cache->ll_coeff_block_allocator,
/*pixels_buffer=*/NULL, /*pixel_format=*/0);
}

Expand Down Expand Up @@ -396,23 +465,6 @@ void isyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* cache, int scale, in

// Cache trim. Since we have the result already, it is possible that tiles from this run will be trimmed here
// if cache is small or work happened on other threads.
// TODO(avirodov): later will need to skip tiles that are reserved by other threads.
while (cache->cache_list.count > cache->target_cache_size) {
isyntax_tile_t* tile = cache->cache_list.tail;
tile_list_remove(&cache->cache_list, tile);
for (int i = 0; i < 3; ++i) {
if (tile->has_ll) {
block_free(&cache->ll_coeff_block_allocator, tile->color_channels[i].coeff_ll);
tile->color_channels[i].coeff_ll = NULL;
}
if (tile->has_h) {
block_free(&cache->h_coeff_block_allocator, tile->color_channels[i].coeff_h);
tile->color_channels[i].coeff_h = NULL;
}
}
tile->has_ll = false;
tile->has_h = false;
}

isyntax_cache_trim(cache, cache->target_cache_size);
benaphore_unlock(&cache->mutex);
}
5 changes: 5 additions & 0 deletions src/isyntax/isyntax_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ void isyntax_tile_read(isyntax_t* isyntax, isyntax_cache_t* cache, int scale, in

void tile_list_init(isyntax_tile_list_t* list, const char* dbg_name);
void tile_list_remove(isyntax_tile_list_t* list, isyntax_tile_t* tile);

isyntax_cache_t* isyntax_cache_create(const char* debug_name_or_null, int32_t cache_size);
void isyntax_cache_inject(isyntax_cache_t* isyntax_cache, isyntax_t* isyntax);
void isyntax_cache_trim(isyntax_cache_t* isyntax_cache, i32 target_size);
void isyntax_cache_destroy(isyntax_cache_t* isyntax_cache);
9 changes: 2 additions & 7 deletions src/isyntax_example.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ int main(int argc, char** argv) {
libisyntax_init();

isyntax_t* isyntax;
if (libisyntax_open(filename, /*is_init_allocators=*/0, &isyntax) != LIBISYNTAX_OK) {
if (libisyntax_open(filename, &isyntax) != LIBISYNTAX_OK) {
printf("Failed to open %s\n", filename);
return -1;
}
Expand All @@ -66,21 +66,16 @@ int main(int argc, char** argv) {
LOG_VAR("%d", tile_width);
LOG_VAR("%d", tile_height);

isyntax_cache_t *isyntax_cache = NULL;
CHECK_LIBISYNTAX_OK(libisyntax_cache_create("example cache", 2000, &isyntax_cache));
CHECK_LIBISYNTAX_OK(libisyntax_cache_inject(isyntax_cache, isyntax));

// RGBA is what stbi expects.
uint32_t *pixels_rgba = malloc(tile_width * tile_height * 4);
CHECK_LIBISYNTAX_OK(libisyntax_tile_read(isyntax, isyntax_cache, level, tile_x, tile_y,
CHECK_LIBISYNTAX_OK(libisyntax_tile_read(isyntax, level, tile_x, tile_y,
pixels_rgba, LIBISYNTAX_PIXEL_FORMAT_RGBA));

printf("Writing %s...\n", output_png);
stbi_write_png(output_png, tile_width, tile_height, 4, pixels_rgba, tile_width * 4);
printf("Done writing %s.\n", output_png);

free(pixels_rgba);
libisyntax_cache_destroy(isyntax_cache);

} else if (argc >= 4) {

Expand Down
Loading