| … | … | @@ -14,10 +14,20 @@ a reason the PLY writer is attractive — treat it as *information about those |
| 14 | 14 | projects*, not as a requirement on ours. We can depend on compiled packages if |
| 15 | 15 | there is ever a reason to. |
| 16 | 16 | |
| 17 | There is still a good reason to stay pure Python, and it is not the ToS: the | |
| 18 | add-on installs from a zip with no build step, on any machine, and every | |
| 19 | dependency we avoid is one that cannot break on someone's Blender. That is a | |
| 20 | preference we can trade away deliberately, rather than a rule. | |
| 17 | **And pure Python is not a goal.** Corrected 2026-08-18: if compiled code buys | |
| 18 | performance, take it. Zero-dependency install is a convenience, not a constraint, | |
| 19 | and it loses to speed. | |
| 20 | ||
| 21 | The first place that principle points is not native code, though. Measured on | |
| 22 | `ifs_splat.upload` at 1M splats: | |
| 23 | ||
| 24 | before 1952 ms of which corners.tolist() 739 ms + tris.tolist() 445 ms | |
| 25 | after 150 ms numpy passed straight to attr_fill / GPUIndexBuf | |
| 26 | ||
| 27 | 13x, by deleting two `.tolist()` calls that built four million Python tuples for | |
| 28 | an API that accepts arrays directly. Worth checking for that shape before | |
| 29 | reaching for a native extension — the Python overhead here was marshalling, not | |
| 30 | computation, and marshalling is usually removable without leaving Python. | |
| 21 | 31 | |
| 22 | 32 | --- |
| 23 | 33 |
| … | … | @@ -179,9 +179,13 @@ def upload(positions, colors, radius, width=SPLAT_WIDTH): |
| 179 | 179 | shader_format.attr_add(id="corner", comp_type='F32', len=2, fetch_mode='FLOAT') |
| 180 | 180 | shader_format.attr_add(id="splat_index", comp_type='I32', len=1, fetch_mode='INT') |
| 181 | 181 | vbo = gpu.types.GPUVertBuf(shader_format, len=count * 4) |
| 182 | vbo.attr_fill(id="corner", data=corners.tolist()) | |
| 183 | vbo.attr_fill(id="splat_index", data=indices.tolist()) | |
| 184 | ibo = gpu.types.GPUIndexBuf(type='TRIS', seq=tris.tolist()) | |
| 182 | # numpy STRAIGHT in, no .tolist(). Measured at 1M splats: the two list | |
| 183 | # conversions were 739 ms + 445 ms of a 1952 ms upload — 61% of the total, | |
| 184 | # spent building four million Python tuples that the API then unpacks again. | |
| 185 | # Both attr_fill and GPUIndexBuf accept arrays directly. | |
| 186 | vbo.attr_fill(id="corner", data=corners) | |
| 187 | vbo.attr_fill(id="splat_index", data=indices) | |
| 188 | ibo = gpu.types.GPUIndexBuf(type='TRIS', seq=tris) | |
| 185 | 189 | batch = gpu.types.GPUBatch(type='TRIS', buf=vbo, elem=ibo) |
| 186 | 190 | |
| 187 | 191 | return SplatCloud(batch, _to_texture(pos_rows, width), _to_texture(col_rows, width), |