Skip to content

Commit

Permalink
Merge pull request #770 from zeux/simp-schedule
Browse files Browse the repository at this point in the history
simplify: Avoid early termination of passes for complex meshes
  • Loading branch information
zeux authored Sep 17, 2024
2 parents e50eac8 + 9dddd2f commit 610646d
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 12 deletions.
2 changes: 1 addition & 1 deletion demo/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,7 @@ static void simplifyAttr(bool skip_g)
{
vb[y * 3 + x][0] = float(x);
vb[y * 3 + x][1] = float(y);
vb[y * 3 + x][2] = 0.03f * x + 0.03f * (y % 2);
vb[y * 3 + x][2] = 0.03f * x + 0.03f * (y % 2) + (x == 2 && y == 7) * 0.03f;
vb[y * 3 + x][3] = r;
vb[y * 3 + x][4] = g;
vb[y * 3 + x][5] = b;
Expand Down
41 changes: 30 additions & 11 deletions src/simplifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1056,24 +1056,30 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const

static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapses, size_t collapse_count)
{
const int sort_bits = 11;
// we use counting sort to order collapses by error; since the exact sort order is not as critical,
// only top 12 bits of exponent+mantissa (8 bits of exponent and 4 bits of mantissa) are used.
// to avoid excessive stack usage, we clamp the exponent range as collapses with errors much higher than 1 are not useful.
const unsigned int sort_bits = 12;
const unsigned int sort_bins = 2048 + 512; // exponent range [-127, 32)

// fill histogram for counting sort
unsigned int histogram[1 << sort_bits];
unsigned int histogram[sort_bins];
memset(histogram, 0, sizeof(histogram));

for (size_t i = 0; i < collapse_count; ++i)
{
// skip sign bit since error is non-negative
unsigned int key = (collapses[i].errorui << 1) >> (32 - sort_bits);
unsigned int error = collapses[i].errorui;
unsigned int key = (error << 1) >> (32 - sort_bits);
key = key < sort_bins ? key : sort_bins - 1;

histogram[key]++;
}

// compute offsets based on histogram data
size_t histogram_sum = 0;

for (size_t i = 0; i < 1 << sort_bits; ++i)
for (size_t i = 0; i < sort_bins; ++i)
{
size_t count = histogram[i];
histogram[i] = unsigned(histogram_sum);
Expand All @@ -1086,7 +1092,9 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse
for (size_t i = 0; i < collapse_count; ++i)
{
// skip sign bit since error is non-negative
unsigned int key = (collapses[i].errorui << 1) >> (32 - sort_bits);
unsigned int error = collapses[i].errorui;
unsigned int key = (error << 1) >> (32 - sort_bits);
key = key < sort_bins ? key : sort_bins - 1;

sort_order[histogram[key]++] = unsigned(i);
}
Expand All @@ -1102,7 +1110,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
size_t edge_collapse_goal = triangle_collapse_goal / 2;

#if TRACE
size_t stats[4] = {};
size_t stats[7] = {};
#endif

for (size_t i = 0; i < collapse_count; ++i)
Expand All @@ -1112,19 +1120,28 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
TRACESTATS(0);

if (c.error > error_limit)
{
TRACESTATS(4);
break;
}

if (triangle_collapses >= triangle_collapse_goal)
{
TRACESTATS(5);
break;
}

// we limit the error in each pass based on the error of optimal last collapse; since many collapses will be locked
// as they will share vertices with other successfull collapses, we need to increase the acceptable error by some factor
float error_goal = edge_collapse_goal < collapse_count ? 1.5f * collapses[collapse_order[edge_collapse_goal]].error : FLT_MAX;

// on average, each collapse is expected to lock 6 other collapses; to avoid degenerate passes on meshes with odd
// topology, we only abort if we got over 1/6 collapses accordingly.
if (c.error > error_goal && triangle_collapses > triangle_collapse_goal / 6)
if (c.error > error_goal && c.error > result_error && triangle_collapses > triangle_collapse_goal / 6)
{
TRACESTATS(6);
break;
}

unsigned int i0 = c.v0;
unsigned int i1 = c.v1;
Expand Down Expand Up @@ -1216,11 +1233,13 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
}

#if TRACE
float error_goal_perfect = edge_collapse_goal < collapse_count ? collapses[collapse_order[edge_collapse_goal]].error : 0.f;
float error_goal_last = edge_collapse_goal < collapse_count ? 1.5f * collapses[collapse_order[edge_collapse_goal]].error : FLT_MAX;
float error_goal_limit = error_goal_last < error_limit ? error_goal_last : error_limit;

printf("removed %d triangles, error %e (goal %e); evaluated %d/%d collapses (done %d, skipped %d, invalid %d)\n",
int(triangle_collapses), sqrtf(result_error), sqrtf(error_goal_perfect),
int(stats[0]), int(collapse_count), int(edge_collapses), int(stats[1]), int(stats[2]));
printf("removed %d triangles, error %e (goal %e); evaluated %d/%d collapses (done %d, skipped %d, invalid %d); %s\n",
int(triangle_collapses), sqrtf(result_error), sqrtf(error_goal_limit),
int(stats[0]), int(collapse_count), int(edge_collapses), int(stats[1]), int(stats[2]),
stats[4] ? "error limit" : (stats[5] ? "count limit" : (stats[6] ? "error goal" : "out of collapses")));
#endif

return edge_collapses;
Expand Down

0 comments on commit 610646d

Please sign in to comment.