Assignment 2 Lazy KD-Tree

Mattias Bergbom

Date submitted: 27 Apr 2006

Code emailed: 27 Apr 2006

Description of implementation approach and comments

I went about this assignment a bit too optimistically, trying to regard to every last bit of memory space and ending up with a real kludge that just wouldn't work. Once I realized I wasn't going anywhere with that one, I restarted and went with a much simpler approach instead.

I use the same type of array to store the nodes as in the original kdtree but with the addition that it can grow dynamically, and with that risking being reallocated. Thus, I only store byte offsets instead of raw pointers. Nodes are initialized 2-by-2, siblings always neighboring eachother to avoid the need of an extra child pointer in the node data struct. I also borrowed an extra bit from the flags union to store an 'isLazy' flag, and added a pointer to a 'PendingData' struct to the other union, all in all keeping the size of the nodes to 8 bytes.

By using the Intersect() and IntersectP() loops and the stack from the original kd-tree, and only adding a special case for when a node is lazy, I was hoping to keep the extra complexity down. Once a lazy node is approached, I compute the split plane and divide the primitives just as usual, and create two new lazy nodes which I initialize with all the data necessary to resume building the tree there at a later point. And this is probably the most obvious place needing improvement; with some work one could easily avoid initializing one of the children as lazy, and thus avoid the memory fragmentation etc. involved in allocating a new PendingData struct on the heap and also a new array of ints to store all its primitives.

Anyhow, once reaching a leaf, I check for any unintersectable primitives, refine those one step, and if the result is more than one primitive I stick it in a new lazy kd-tree and replace the original reference in the global primitives vector with the new tree's reference.

And that's about it as far as implementation goes. The result is somewhat disappointing but given the amount of time I wasted on the first approach I'm quite happy I ended up with any images at all. There's obviously lots of room for improvement; given that the poor times posted for the plants scene depend heavily on memory restrictions and thus excessive swapping, one should probably focus on the memory consumption. Except eliminating for the double-allocation I mentioned above one could also imagine e.g. using more global buffers, such as for the edge tables, and also recursively building a node down a certain couple of steps, just to avoid extra allocations as far as possible.

Update: The code that I submitted has a fix in it that limits the max depth of the tree to 32 as opposed to a previous, debug-purposed value. This gives a more consistent behaviour (~160% time) in the larger scenes, mainly due to less swapping.

Final Images Rendered with my implementation of lz-kdtree.cpp

killeroos-view1.pbrt (Killeroos visible)

KD Tree

Lazy KD Tree

Ratio

build time (secs)

17.1

0

0%

total time (secs)

32.2

24.9

77%

Num of nodes made

2.75M

1.19M

43%

Triangle ray intersections

673k

827k

123%

killeroos-view2.pbrt (Killeroos invisible)

KD Tree

Lazy KD Tree

Ratio

build time (secs)

17.4

0

0%

total time (secs)

29.9

13.2

44%

Num of nodes made

2.75M

47

0%

Triangle ray intersections

758k

776k

102%

killeroos-view3.pbrt (close-up)

KD Tree

Lazy KD Tree

Ratio

build time (secs)

22.1

0

0%

total time (secs)

39.9

20

50%

Num of nodes made

2.75M

410k

15%

Triangle ray intersections

644k

1.01M

157%

plants-view1.pbrt

KD Tree

Lazy KD Tree

Ratio

build time (secs)

~33

0

0%

total time (secs)

~613

988

161%

Num of nodes made

14.8M

5.04M

34%

Triangle ray intersections

20.3M

27.4M

135%

plants-view2.pbrt

KD Tree

Lazy KD Tree

Ratio

build time (secs)

~38

0

0%

total time (secs)

~981

1627

166%

Num of nodes made

14.8M

6.29M

42%

Triangle ray intersections

25.9M

31.1M

120%