util/rectpack: refactor to use standard mempools

This commit is contained in:
Andrei Alexeyev 2024-06-05 23:53:30 +02:00
parent f9c2748fae
commit a8322f5e1d
No known key found for this signature in database
GPG key ID: 72D26128040B9690
4 changed files with 87 additions and 99 deletions

View file

@ -122,9 +122,9 @@ static struct {
} fb;
struct {
MemArena arena;
Allocator alloc;
RectPack rectpack;
MemArena arena;
RectPackSectionPool rpspool;
} packer;
struct {
@ -257,18 +257,17 @@ void laserdraw_preload(ResourceGroup *rg) {
}
static void laserdraw_init_packer(void) {
rectpack_init(&ldraw.packer.rectpack, &ldraw.packer.alloc,
PACKING_SPACE_SIZE_W, PACKING_SPACE_SIZE_H);
rectpack_init(&ldraw.packer.rectpack, PACKING_SPACE_SIZE_W, PACKING_SPACE_SIZE_H);
}
static void laserdraw_reset_packer(void) {
marena_reset(&ldraw.packer.arena);
ldraw.packer.rpspool = (RectPackSectionPool) {};
laserdraw_init_packer();
}
void laserdraw_init(void) {
marena_init(&ldraw.packer.arena, 0);
allocator_init_from_arena(&ldraw.packer.alloc, &ldraw.packer.arena);
laserdraw_init_packer();
dynarray_ensure_capacity(&ldraw.queue, 64);
@ -300,7 +299,6 @@ void laserdraw_init(void) {
void laserdraw_shutdown(void) {
dynarray_free_data(&ldraw.queue);
allocator_deinit(&ldraw.packer.alloc);
marena_deinit(&ldraw.packer.arena);
fbmgr_group_destroy(ldraw.fb.group);
r_vertex_array_destroy(ldraw.pass1.va);
@ -333,7 +331,12 @@ static bool laserdraw_pack_laser(Laser *l, cmplxf *out_ofs, bool *rotated) {
FloatExtent bbox_size = { .as_cmplx = laser_packed_dimensions(l) };
RectPackSection *section = rectpack_add(
&ldraw.packer.rectpack, bbox_size.w, bbox_size.h, true);
&ldraw.packer.rectpack,
(RectPackSectionSource) {
.arena = &ldraw.packer.arena,
.pool = &ldraw.packer.rpspool,
},
bbox_size.w, bbox_size.h, true);
if(!section) {
return false;

View file

@ -11,6 +11,7 @@
#include "config.h"
#include "dynarray.h"
#include "events.h"
#include "memory/arena.h"
#include "renderer/api.h"
#include "util.h"
#include "util/glm.h"
@ -128,6 +129,8 @@ static struct {
Texture *render_tex;
Framebuffer *render_buf;
SpriteSheetAnchor spritesheets;
MemArena arena;
RectPackSectionPool rpspool;
struct {
SDL_mutex *new_face;
@ -191,6 +194,8 @@ static void *ft_realloc(FT_Memory mem, long cur_size, long new_size, void *block
static void init_fonts(void) {
FT_Error err;
marena_init(&globals.arena, sizeof(RectPackSection) * 128);
static typeof(*(FT_Memory)0) ft_mem = {
.alloc = ft_alloc,
.free = ft_free,
@ -252,6 +257,7 @@ static void shutdown_fonts(void) {
FT_Done_Library(globals.lib);
SDL_DestroyMutex(globals.mutex.new_face);
SDL_DestroyMutex(globals.mutex.done_face);
marena_deinit(&globals.arena);
}
static char *font_path(const char *name) {
@ -393,6 +399,13 @@ void font_set_kerning_enabled(Font *font, bool newval) {
#define SS_TEXTURE_TYPE TEX_TYPE_RGB_8
#define SS_TEXTURE_FLAGS 0
INLINE RectPackSectionSource rps_source(void) {
return (RectPackSectionSource) {
.arena = &globals.arena,
.pool = &globals.rpspool,
};
}
static SpriteSheet *add_spritesheet(SpriteSheetAnchor *spritesheets) {
auto ss = ALLOC(SpriteSheet, {
.tex = r_texture_create(&(TextureParams) {
@ -409,7 +422,7 @@ static SpriteSheet *add_spritesheet(SpriteSheetAnchor *spritesheets) {
})
});
rectpack_init(&ss->rectpack, &default_allocator, SS_WIDTH, SS_HEIGHT);
rectpack_init(&ss->rectpack, SS_WIDTH, SS_HEIGHT);
#ifdef DEBUG
char buf[128];
@ -429,7 +442,7 @@ static bool add_glyph_to_spritesheet(Glyph *glyph, Pixmap *pixmap, SpriteSheet *
uint padded_w = pixmap->width + 2 * GLYPH_SPRITE_PADDING;
uint padded_h = pixmap->height + 2 * GLYPH_SPRITE_PADDING;
glyph->spritesheet_section = rectpack_add(&ss->rectpack, padded_w, padded_h);
glyph->spritesheet_section = rectpack_add(&ss->rectpack, rps_source(), padded_w, padded_h, false);
if(glyph->spritesheet_section == NULL) {
return false;
@ -496,7 +509,7 @@ static const char *pixmode_name(FT_Pixel_Mode mode) {
static void delete_spritesheet(SpriteSheetAnchor *spritesheets, SpriteSheet *ss) {
r_texture_destroy(ss->tex);
rectpack_deinit(&ss->rectpack);
assert(rectpack_is_empty(&ss->rectpack));
alist_unlink(spritesheets, ss);
mem_free(ss);
}
@ -738,7 +751,7 @@ static void wipe_glyph_cache(Font *font) {
RectPackSection *section = g->spritesheet_section;
assume(section != NULL);
rectpack_reclaim(rp, section);
rectpack_reclaim(rp, rps_source(), section);
if(rectpack_is_empty(rp)) {
delete_spritesheet(&globals.spritesheets, ss);

View file

@ -35,28 +35,20 @@ static inline void section_make_used(RectPack *rp, RectPackSection *s) {
s->next = s->prev = s;
}
static RectPackSection *acquire_section(RectPack *rp) {
RectPackSection *s = list_pop(&rp->sections_freelist);
if(!s) {
s = ALLOC_VIA(rp->allocator, typeof(*s));
}
*s = (RectPackSection) { };
return s;
static RectPackSection *acquire_section(RectPackSectionSource secsrc) {
return mempool_acquire(secsrc.pool, secsrc.arena);
}
static void release_section(RectPack *rp, RectPackSection *s) {
list_push(&rp->sections_freelist, s);
static void release_section(RectPackSectionSource secsrc, RectPackSection *s) {
mempool_release(secsrc.pool, s);
}
void rectpack_init(RectPack *rp, Allocator *alloc, double width, double height) {
void rectpack_init(RectPack *rp, double width, double height) {
*rp = (RectPack) {
.root.rect = {
.top_left = CMPLX(0, 0),
.bottom_right = CMPLX(width, height),
},
.allocator = alloc,
};
list_push(&rp->unused_sections, &rp->root);
assert(rectpack_is_empty(rp));
@ -71,35 +63,6 @@ bool rectpack_is_empty(RectPack *rp) {
return false;
}
static void delete_subsections(RectPack *rp, RectPackSection *restrict s) {
if(s->children[0] != NULL) {
assume(s->children[1] != NULL);
assume(s->children[0]->parent == s);
assume(s->children[1]->parent == s);
delete_subsections(rp, s->children[0]);
release_section(rp, s->children[0]);
s->children[0] = NULL;
delete_subsections(rp, s->children[1]);
release_section(rp, s->children[1]);
s->children[1] = NULL;
}
}
void rectpack_reset(RectPack *rp) {
delete_subsections(rp, &rp->root);
}
void rectpack_deinit(RectPack *rp) {
delete_subsections(rp, &rp->root);
Allocator *alloc = rp->allocator;
for(RectPackSection *s; (s = list_pop(&rp->sections_freelist));) {
allocator_free(alloc, s);
}
}
static double section_fitness(RectPackSection *s, double w, double h) {
double sw = rect_width(s->rect);
double sh = rect_height(s->rect);
@ -111,7 +74,7 @@ static double section_fitness(RectPackSection *s, double w, double h) {
return sw * sh - w * h; // best area fit
}
void rectpack_reclaim(RectPack *rp, RectPackSection *s) {
void rectpack_reclaim(RectPack *rp, RectPackSectionSource secsrc, RectPackSection *s) {
assume(s->children[0] == NULL);
assume(s->children[1] == NULL);
@ -133,14 +96,14 @@ void rectpack_reclaim(RectPack *rp, RectPackSection *s) {
// NOTE: the following frees s->sibling and s, in unspecified order
release_section(rp, parent->children[0]);
release_section(secsrc, parent->children[0]);
parent->children[0] = NULL;
release_section(rp, parent->children[1]);
release_section(secsrc, parent->children[1]);
parent->children[1] = NULL;
if(parent != NULL) {
rectpack_reclaim(rp, parent);
rectpack_reclaim(rp, secsrc, parent);
}
RP_DEBUG("done reclaiming parent of %p", (void*)s);
@ -154,7 +117,7 @@ void rectpack_reclaim(RectPack *rp, RectPackSection *s) {
}
static RectPackSection *select_fittest_section(
RectPack *rp, double *width, double *height, bool allow_rotation
RectPack *rp, RectPackSectionSource secsrc, double *width, double *height, bool allow_rotation
) {
RectPackSection *best = NULL;
double fitness = DBL_MAX;
@ -201,10 +164,14 @@ static RectPackSection *select_fittest_section(
return best;
}
static RectPackSection *split_horizontal(RectPack *rp, RectPackSection *s, double width, double height);
static RectPackSection *split_vertical(RectPack *rp, RectPackSection *s, double width, double height);
static RectPackSection *split_horizontal(
RectPack *rp, RectPackSectionSource secsrc, RectPackSection *s, double width, double height);
static RectPackSection *split_vertical(
RectPack *rp, RectPackSectionSource secsrc, RectPackSection *s, double width, double height);
static RectPackSection *split_horizontal(RectPack *rp, RectPackSection *s, double width, double height) {
static RectPackSection *split_horizontal(
RectPack *rp, RectPackSectionSource secsrc, RectPackSection *s, double width, double height
) {
RP_DEBUG("spliting section %p of size %gx%g for rect %gx%g", (void*)s, rect_width(s->rect), rect_height(s->rect), width, height);
assert(rect_width(s->rect) >= width);
@ -213,10 +180,10 @@ static RectPackSection *split_horizontal(RectPack *rp, RectPackSection *s, doubl
if(rect_height(s->rect) == height) {
assert(rect_width(s->rect) > width);
RP_DEBUG("delegated to vertical split");
return split_vertical(rp, s, width, height);
return split_vertical(rp, secsrc, s, width, height);
}
auto sub = acquire_section(rp);
auto sub = acquire_section(secsrc);
rect_set_xywh(&sub->rect,
rect_x(s->rect),
rect_y(s->rect),
@ -228,7 +195,7 @@ static RectPackSection *split_horizontal(RectPack *rp, RectPackSection *s, doubl
sub->parent = s;
s->children[0] = sub;
s->children[1] = acquire_section(rp);
s->children[1] = acquire_section(secsrc);
rect_set_xywh(&s->children[1]->rect,
rect_x(s->rect),
rect_y(s->rect) + height,
@ -247,13 +214,15 @@ static RectPackSection *split_horizontal(RectPack *rp, RectPackSection *s, doubl
);
if(rect_width(sub->rect) != width) {
sub = split_vertical(rp, sub, width, height);
sub = split_vertical(rp, secsrc, sub, width, height);
}
return sub;
}
static RectPackSection *split_vertical(RectPack *rp, RectPackSection *s, double width, double height) {
static RectPackSection *split_vertical(
RectPack *rp, RectPackSectionSource secsrc, RectPackSection *s, double width, double height
) {
assert(rect_width(s->rect) >= width);
assert(rect_height(s->rect) >= height);
@ -262,10 +231,10 @@ static RectPackSection *split_vertical(RectPack *rp, RectPackSection *s, double
if(rect_width(s->rect) == width) {
assert(rect_height(s->rect) > height);
RP_DEBUG("delegated to horizontal split");
return split_horizontal(rp, s, width, height);
return split_horizontal(rp, secsrc, s, width, height);
}
auto sub = acquire_section(rp);
auto sub = acquire_section(secsrc);
rect_set_xywh(&sub->rect,
rect_x(s->rect),
rect_y(s->rect),
@ -277,7 +246,7 @@ static RectPackSection *split_vertical(RectPack *rp, RectPackSection *s, double
sub->parent = s;
s->children[0] = sub;
s->children[1] = acquire_section(rp);
s->children[1] = acquire_section(secsrc);
rect_set_xywh(&s->children[1]->rect,
rect_x(s->rect) + width,
rect_y(s->rect),
@ -296,26 +265,32 @@ static RectPackSection *split_vertical(RectPack *rp, RectPackSection *s, double
);
if(rect_height(sub->rect) != height) {
sub = split_horizontal(rp, sub, width, height);
sub = split_horizontal(rp, secsrc, sub, width, height);
}
return sub;
}
static RectPackSection *split(RectPack *rp, RectPackSection *s, double width, double height) {
static RectPackSection *split(
RectPack *rp, RectPackSectionSource secsrc, RectPackSection *s, double width, double height
) {
// short leftover axis split
if(rect_width(s->rect) - width < rect_height(s->rect) - height) {
return split_horizontal(rp, s, width, height);
return split_horizontal(rp, secsrc, s, width, height);
} else {
return split_vertical(rp, s, width, height);
return split_vertical(rp, secsrc, s, width, height);
}
}
RectPackSection *(rectpack_add)(
RectPack *rp, double width, double height, bool allow_rotation
RectPackSection *rectpack_add(
RectPack *rp,
RectPackSectionSource secsrc,
double width,
double height,
bool allow_rotation
) {
RectPackSection *s = select_fittest_section(rp, &width, &height, allow_rotation);
RectPackSection *s = select_fittest_section(rp, secsrc, &width, &height, allow_rotation);
if(s == NULL) {
return NULL;
@ -327,7 +302,7 @@ RectPackSection *(rectpack_add)(
return s;
}
return split(rp, s, width, height);
return split(rp, secsrc, s, width, height);
}
Rect rectpack_section_rect(RectPackSection *s) {

View file

@ -11,50 +11,47 @@
#include "geometry.h"
#include "list.h"
#include "memory/allocator.h"
#include "memory/mempool.h"
typedef struct RectPack RectPack;
typedef struct RectPackSection RectPackSection;
typedef MEMPOOL(RectPackSection) RectPackSectionPool;
typedef struct RectPackSectionSource RectPackSectionSource;
struct RectPackSection {
LIST_INTERFACE(RectPackSection);
Rect rect;
RectPackSection *parent;
RectPackSection *sibling;
RectPackSection *children[2];
Rect rect;
};
struct RectPack {
RectPackSection root;
RectPackSection *unused_sections;
RectPackSection *sections_freelist;
Allocator *allocator;
};
void rectpack_init(RectPack *rp, Allocator *alloc, double width, double height)
struct RectPackSectionSource {
RectPackSectionPool *pool;
MemArena *arena;
};
void rectpack_init(RectPack *rp, double width, double height)
attr_nonnull_all;
void rectpack_reset(RectPack *rp)
attr_nonnull(1);
void rectpack_deinit(RectPack *rp)
attr_nonnull(1);
RectPackSection *rectpack_add(RectPack *rp, double width, double height, bool allow_rotation)
attr_nonnull(1);
#define _rectpack_add_3(rp, width, height) \
rectpack_add(rp, width, height, false)
#define _rectpack_add_4(rp, width, height, allow_rotation) \
rectpack_add(rp, width, height, allow_rotation)
#define rectpack_add(...) \
MACROHAX_OVERLOAD_NARGS(_rectpack_add_, __VA_ARGS__)(__VA_ARGS__)
RectPackSection *rectpack_add(
RectPack *rp,
RectPackSectionSource secsrc,
double width,
double height,
bool allow_rotation
) attr_nonnull(1);
Rect rectpack_section_rect(RectPackSection *s)
attr_nonnull(1);
void rectpack_reclaim(RectPack *rp, RectPackSection *s)
attr_nonnull(1, 2);
void rectpack_reclaim(RectPack *rp, RectPackSectionSource secsrc, RectPackSection *s)
attr_nonnull(1, 3);
bool rectpack_is_empty(RectPack *rp)
attr_nonnull(1);