Lightmap unwrap improvements - performance improvement #113720

Closed
Sebastian Witt wants to merge 2 commits from SebastianWitt/blender:Lightmap_performance into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
1 changed files with 44 additions and 15 deletions

View File

@ -296,28 +296,57 @@ def lightmap_uvpack(
tri_lengths = [trylens(f) for f in face_sel if f.loop_total == 3]
del trylens
def trilensdiff(t1, t2):
return (abs(t1[1][t1[2][0]] - t2[1][t2[2][0]]) +
abs(t1[1][t1[2][1]] - t2[1][t2[2][1]]) +
abs(t1[1][t1[2][2]] - t2[1][t2[2][2]]))
# To add triangles into the lightmap pack triangles are grouped in pairs to fill rectangular areas.
# In the following for each triangle we add the sorted triangle edge lengths (3d point) into a kd-Tree
# then iterate over all triangles and search for pairs of triangles by looking for the closest
# sorted triangle point.
# Additionally clusters of similar/equal triangles are parsed by searching for ranges in a second step
kd = mathutils.kdtree.KDTree(len(tri_lengths))
for i, (f, lens, o) in enumerate(tri_lengths):
vector = (lens[o[0]], lens[o[1]], lens[o[2]])
kd.insert(vector, i)
kd.balance()
while tri_lengths:
tri1 = tri_lengths.pop()
added_ids = [False] * len(tri_lengths)
wm = bpy.context.window_manager
num_tri_pairs = int(len(tri_lengths) / 2)
wm.progress_begin(0, num_tri_pairs) # log unwrap progress
pairs_added = 0
tri_equality_threshold = 0.00001 # add multiple pairs at once that are within this threshold
for i in range(len(tri_lengths)):
if added_ids[i]:
continue
tri1 = tri_lengths[i]
(f1, lens1, lo1) = tri1
if not tri_lengths:
sorted_l = (lens1[lo1[0]], lens1[lo1[1]], lens1[lo1[2]])
added_ids[i] = True
vec, nearest, dist = kd.find(sorted_l, filter=lambda idx: not added_ids[idx])
if not nearest or nearest < 0:
pretty_faces.append(prettyface((tri1, None)))
break
tri2 = tri_lengths[nearest]
pretty_faces.append(prettyface((tri1, tri2)))
pairs_added = pairs_added + 1
added_ids[nearest] = True
best_tri_index = -1
best_tri_diff = 100000000.0
# look in threshold proximity to add all similar/equal tris in one go.
# this code is not necessary but acts as a shortcut (~9% performance improvement)

It's not clear why the disabled code is useful to add. Either there should be a description of what the code does that's useful and why it's currently disabled, or remove it.

It's not clear why the disabled code is useful to add. Either there should be a description of what the code does that's useful and why it's currently disabled, or remove it.

Thanks @ideasman42 ! I forgot to reenable that branch after benchmarking. It is not necessary from a functionality perspective. It just skips parts of the triangle pair matching with an additional kd lookup to match all triangles that are the same (within a threshold).

From testing it seems to be ~9% speedup depending on how many similar triangles there are.
In practise this is 1.34s vs 1.46s - it doesn't really matter at that point and comes with the cost of code complexity.

I am happy to leave it in or remove it.
The new commit is with the if branch/shortcut enabled.

Thanks @ideasman42 ! I forgot to reenable that branch after benchmarking. It is not necessary from a functionality perspective. It just skips parts of the triangle pair matching with an additional kd lookup to match all triangles that are the same (within a threshold). From testing it seems to be ~9% speedup depending on how many similar triangles there are. In practise this is 1.34s vs 1.46s - it doesn't really matter at that point and comes with the cost of code complexity. I am happy to leave it in or remove it. The new commit is with the if branch/shortcut enabled.
if dist < tri_equality_threshold:
cluster_tri_ids = [idx for _, idx, _ in kd.find_range(sorted_l, tri_equality_threshold) if
not added_ids[idx]]
for i, tri2 in enumerate(tri_lengths):
diff = trilensdiff(tri1, tri2)
if diff < best_tri_diff:
best_tri_index = i
best_tri_diff = diff
if len(cluster_tri_ids) > 1:
for ci in range(0, len(cluster_tri_ids) - (len(cluster_tri_ids) % 2), 2):
pretty_faces.append(prettyface((tri_lengths[cluster_tri_ids[ci]],
tri_lengths[cluster_tri_ids[ci + 1]])))
added_ids[cluster_tri_ids[ci]] = added_ids[cluster_tri_ids[ci + 1]] = True
pairs_added = pairs_added + 1
pretty_faces.append(prettyface((tri1, tri_lengths.pop(best_tri_index))))
if i % 100 == 0:
wm.progress_update(i)
Review

I added some progress tracking. I am not too sure if this is desirable. It could be done without any wm.progress, with progress but no prints or with both.

I added some progress tracking. I am not too sure if this is desirable. It could be done without any wm.progress, with progress but no prints or with both.
print(f"Unwrap progress {int((100 * pairs_added / num_tri_pairs))}%")
wm.progress_end()
# Get the min, max and total areas
max_area = 0.0