[renderer] Use better heuristics for scrap allocation

More tuning is needed on the actual splits as it falls over when the
lower rect gets too low for the subrects being allocated. However, the
scrap allocator itself will prefer exact width/height fits with larger
cutoff over inexact cuts with smaller cutoff. Many thanks to tdb for the
suggestions.

Fixes the fps dropping from ~3700fps down to ~450fps (cumulative due to
loss of POT rounding and very poor splitting layout), with a bonus boost
to about 4900fps (all speeds at 800x450). The 2d sprites were mostly ok,
but the lightmaps forming a capital gamma shape in a 4k texture really
hurt. Now the lightmaps are a nice dense bar at the top of the texture,
and 2d sprites are pretty good (slight improvement coming next).
This commit is contained in:
Bill Currie 2022-09-20 19:32:49 +09:00
parent 1fdedbaf33
commit 058e919273
2 changed files with 44 additions and 9 deletions

View file

@ -268,10 +268,37 @@ VRect_SubRect (const vrect_t *rect, int width, int height)
if (height > rect->height) { if (height > rect->height) {
height = rect->height; height = rect->height;
} }
// First check for exact fits as no heuristics are necessary
if (width == rect->width) {
if (height == rect->height) {
// Exact fit, return a new rect of the same size
return VRect_New (rect->x, rect->y, rect->width, rect->height);
}
vrect_t *r = VRect_HSplit (rect, rect->y + height);
if (VRect_IsEmpty (r->next)) {
VRect_Delete (r->next);
r->next = 0;
}
return r;
} else if (height == rect->height) {
// Because rect's width ks known to be greater than the requested
// width, the split is guaranteed to produce two non-empty rectangles.
return VRect_VSplit (rect, rect->x + width);
}
int a = rect->height - height; int a = rect->height - height;
int b = rect->width - width; int b = rect->width - width;
vrect_t *r, *s; vrect_t *r, *s;
if (b * height > a * width) { if (b * height <= a * width || 16 * a > 15 * rect->height) {
// horizontal cuts produce better memory locality so favor them
r = VRect_HSplit (rect, rect->y + height);
if (VRect_IsEmpty (r->next)) {
VRect_Delete (r->next);
r->next = 0;
}
s = VRect_VSplit (r, r->x + width);
} else {
// a horizontal cut produces a larger off-cut rectangle than a vertical // a horizontal cut produces a larger off-cut rectangle than a vertical
// cut, so do a vertical cut first so the off-cut is as small as // cut, so do a vertical cut first so the off-cut is as small as
// possible // possible
@ -281,13 +308,6 @@ VRect_SubRect (const vrect_t *rect, int width, int height)
r->next = 0; r->next = 0;
} }
s = VRect_HSplit (r, r->y + height); s = VRect_HSplit (r, r->y + height);
} else {
r = VRect_HSplit (rect, rect->y + height);
if (VRect_IsEmpty (r->next)) {
VRect_Delete (r->next);
r->next = 0;
}
s = VRect_VSplit (r, r->x + width);
} }
if (VRect_IsEmpty (s->next)) { if (VRect_IsEmpty (s->next)) {

View file

@ -83,8 +83,23 @@ R_ScrapAlloc (rscrap_t *scrap, int width, int height)
best = t; best = t;
continue; continue;
} }
if ((*t)->width <= (*best)->width || (*t)->height <= (*best)->height) if ((*t)->width == width && (*t)->height == height) {
// exact fit
best = t; best = t;
break;
}
if ((*best)->height == height) {
if ((*t)->height == height && (*t)->width < (*best)->width) {
best = t;
}
} else if ((*best)->width == width) {
if ((*t)->width == width && (*t)->height < (*best)->height) {
best = t;
}
}
if ((*t)->width <= (*best)->width || (*t)->height <= (*best)->height) {
best = t;
}
} }
if (!best) if (!best)
return 0; // couldn't find a spot return 0; // couldn't find a spot