This is a slightly redacted version of my proposal that has been shared with the Blender Foundation on SoC site. Will submit on 27th. Feedback welcome till then (:
Name
Ankit Meel
Contact:
Synopsis
Among the 3D formats available, some are simple in theory, yet effective for a lot of different use cases and supported by a multitude of software in the industry. The challenge they offer is the number of iterables. Stanford PLY, for example, quickly gets over a million vertices. STL being a lossy format, has to be stored with extra details, making it enormous. Importing such models faster and doing so in the memory limits is the aim of this project.
Benefits
It will cut the import time by several folds, thus improving user experience. Also, it enables the baseline models with 4 GB RAM (requirements page) to process huge models and not run out of memory. For Blender, it provides a basic structure to facilitate implementing other file formats in the future, instead of addons being written from scratch, in Python again.
Deliverables
-
Working importers and exporters for PLY & STL for ASCII and Binary formats
-
Providing a cross-platform way to assess the performance of both C++ and Python codes.
-
Also documenting the performance at various iterations in the logs on the wiki.
-
Since there is no change on the user interface side, no additional documentation is needed. However, sufficient external documentation and internal comments for the code is expected.
-
If possible and decided after discussion with the UI team, add a progress bar, or an entry in the logger in Blender.
-
Add OBJ support as much as possible using the new framework and port the last year’s branch.
Project Details
Please find examples of all the file types in the appendix.
I applied subdivision modifier with Catmull-Clark nine times (6+3) on the default cube, on factory settings, and exported it to PLY, STL-Binary and STL-ASCII. Here are the stats.
Format |
Size |
Time (Export / Import) (s) |
Peak memory (Export / Import) (GB) |
PLY ASCII |
524 MB |
111/ 146 |
5.09/ 4.31 |
STL ASCII |
553 MB |
27 / 59 |
.827/ 1.73 |
STL Binary |
157 MB |
14/ 31.5 |
.833/ 1.7 |
Also find some graphs here.
The biggest penalty in terms of time in the process are the loops, which get very time consuming when there are 6,291,456 vertices and 1,572,864 faces. I used py-spy for profiling.
-
return dict([(i.name, [i.load(format, stream) for j in range(i.count)]) for i in self.specs])
-
[x.load(format, stream) for x in self.properties]
-
for i, v in enumerate(ply_verts): # write vertices
-
for pf in ply_faces: # write faces
Following the precedent of multiple scientific libraries being written in C/C++ and using Cython to link the python wrapper, writing all the io operations in C++ is feasible. The blender/source/blender/io
already contains the Alembic, AVI, Collada, and USD files, so the newer ones will also be put there. Also, blender/source/blender/editors/io
will keep the operators’ linkage and handle the per-file-format preferences that are shown in the file browser.
I am reading the current approaches to iterate over mesh, textures, color, etc., in the files mentioned above. So I expect to keep things uniform and thus maintainable. The endian property in binary files would be handled similar to that in avi_endian.c. In week 7, during refactoring, the python addon is to be removed, keeping everything in one language and thus easing debugging, further improvements, etc.
Since Valgrind won’t work on 10.14, I’d be using Instruments.app. If necessary, high performance C++ profiler would be used for finer details.
Possible optimizations & plans:
-
Reading the file in chunks instead of all at once, using streams. Loop over all the lines only once.
-
Minimising copying of variables & using pointers to pass them around.
-
Using the knowledge about the format to read the data, instead of reading it in a different and later do conversions.
-
Minimising flush operations to the disk from the stream.
-
Separating lower level file reading operations in a separate layer for easy experimentation.
-
Using a minimal, bare bone data structure to store one vertex/ face/ any other property so it doesn’t add up to a much bigger number later.
Addressing memory mapping now, it isn’t a magic pill that improves performance in all cases. Many modern SSDs and networks provide read speed, which no longer is the bottleneck in the parsing. It has to be decided only after actual profiling, not directly applying memory map to the problem while making the bare minimum task that is to be done, complex. If the bottleneck turns out to be mesh processing, not the disk, I will look into distributing the file/ line reading process on multiple cores.
Project Schedule
The best time that I can work in is right now, which I am using to read the existing code of modifiers, iterations on mesh, modifiers, and the previous attempt. The college is closed and likely would remain so for at least 4-5 weeks. If it opens, it will overlap with the community bonding period, which I’ve already done (-: So that will not interfere with the rest of the timeline. The order of tasks, weekly is expected to remain as:
-
1-2-3 PLY I/E for both binary and ASCII (initially linking the files and setting up build might take time)
-
4-5 STL I/E for both binary and ASCII
-
6 Initial benchmarking and documentation for profiling
-
7 Major refactoring for separating low-level APIs from functional code & UI
-
8 Regression testing, documenting results for multiple files using both exporters for both implementations.
-
9-10 Optimisations, as discussed above and benchmarking, removing older python code using C++ based operators for wm events like in blender/source/blender/editors/io
-
11 Deciding and adding the progress bar. After discussing with the mentors, port as much I can from the fast IO 19 branch to add OBJ support.
-
12 Code Documentation, comments, review, merging, and buffer.
I will even further improve OBJ support after the GSoC is over. After which I intend to stay to help with bug triaging and fixing and learning new things in Blender.
Bio
I am Ankit Meel. I was introduced to C and C++ in the second semester, three years ago and have been using them since. Using Python, I’ve completed multiple assignments in machine learning, signal processing; I also completed a facial expression classification task on images, as a summer project. Other than that, I have done front end development, a server setup in NodeJS, socket programming using Python, and numerical analysis methods in Octave.
I’ve also gotten some exposure to Objective-C while doing a paper cut for alias redirection on macOS, D6679. In D6512, I made a partially working solution for icon theming. Been active for over eight months now, I’m also a member of the moderators group and coordinate with the bug triaging team and almost all other developers while triaging posts.
Interesting reads:
Appendix
ply
format ascii 1.0
comment Created by Blender 2.83 (sub 10) - www.blender.org, source file: ''
element vertex 4
property float x
property float y
property float z
property float nx
property float ny
property float nz
property float s
property float t
element face 1
property list uchar uint vertex_indices
end_header
-1.000000 -1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000
1.000000 -1.000000 0.000000 0.000000 0.000000 1.000000 1.000000 0.000000
1.000000 1.000000 0.000000 0.000000 0.000000 1.000000 1.000000 1.000000
-1.000000 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 1.000000
4 0 1 2 3
- STL ASCII : Plane (brackets added)
solid Exported from Blender-2.83 (sub 10)
facet normal 0.000000 0.000000 1.000000
outer loop
vertex -1.000000 -1.000000 0.000000 (a)
vertex 1.000000 -1.000000 0.000000 (b)
vertex 1.000000 1.000000 0.000000 (c)
endloop
endfacet
facet normal 0.000000 0.000000 1.000000
outer loop
vertex -1.000000 -1.000000 0.000000 (a)
vertex 1.000000 1.000000 0.000000 (b)
vertex -1.000000 1.000000 0.000000 (d)
endloop
endfacet
endsolid Exported from Blender-2.83 (sub 10)
4578 706f 7274 6564 2066 726f 6d20 426c
656e 6465 722d 322e 3833 2028 7375 6220
3130 2900 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
0200 0000 0000 0000 0000 0000 0000 803f
0000 80bf 0000 80bf 0000 0000 0000 803f
0000 80bf 0000 0000 0000 803f 0000 803f
0000 0000 0000 0000 0000 0000 0000 0000
803f 0000 80bf 0000 80bf 0000 0000 0000
803f 0000 803f 0000 0000 0000 80bf 0000
803f 0000 0000 0000