view pkg/octree/builder.go @ 2477:9b1f0edf5fdc octree-diff

The concurrent octree build seems to be buggy. Use the old code instead till the problem is found.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 28 Feb 2019 12:03:02 +0100
parents 25e2578b76f3
children 930ca9c4e2a7
line wrap: on
line source

// This is Free Software under GNU Affero General Public License v >= 3.0
// without warranty, see README.md and license for details.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// License-Filename: LICENSES/AGPL-3.0.txt
//
// Copyright (C) 2018 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package octree

import (
	"bytes"
	"encoding/binary"
	"io"
	"log"
	"sync"
	"sync/atomic"

	"github.com/golang/snappy"
)

// Builder is used to turn a TIN into an Octree.
type Builder struct {
	t      *Tin
	nodes  int
	leaves int
	index  []int32

	mu sync.Mutex
}

type buildStep func(chan buildStep)

var cubes = [8][2]Vertex{
	makeCube(0),
	makeCube(1),
	makeCube(2),
	makeCube(3),
	makeCube(4),
	makeCube(5),
	makeCube(6),
	makeCube(7),
}

func makeCube(i int) [2]Vertex {
	var d Vertex
	if i&1 == 1 {
		d.X = 0.5
	}
	if i&2 == 2 {
		d.Y = 0.5
	}
	if i&4 == 4 {
		d.Z = 0.5
	}
	return [2]Vertex{
		Vertex{0.0, 0.0, 0.0}.Add(d),
		Vertex{0.5, 0.5, 0.5}.Add(d),
	}
}

// NewBuilder creates a new Builder for a TIN.
func NewBuilder(t *Tin) *Builder {
	return &Builder{t: t}
}

// Build builds the Octree.
func (tb *Builder) Build() {

	triangles := make([]int32, len(tb.t.Triangles))
	for i := range triangles {
		triangles[i] = int32(i)
	}

	/*
		n := runtime.NumCPU()

		steps := make(chan buildStep, n)

		var wg sync.WaitGroup
		for i := 0; i < n; i++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				for step := range steps {
					step(steps)
				}
			}()
		}

		tb.index = append(tb.index, 0)

		root := func(int32) {
			close(steps)
		}

		steps <- tb.buildConcurrent(
			triangles,
			tb.t.Min, tb.t.Max,
			0,
			root)

		wg.Wait()
	*/

	tb.buildRecursive(triangles, tb.t.Min, tb.t.Max, 0)
	tb.index[0] = int32(len(tb.index))
	log.Printf("info: num nodes: %d\n", tb.index[0])
	log.Printf("info: nodes: %d leaves: %d index %d\n",
		tb.nodes, tb.leaves, tb.index[0])
}

func (tb *Builder) buildConcurrent(
	triangles []int32,
	min, max Vertex,
	depth int,
	parent func(int32),
) buildStep {

	return func(steps chan buildStep) {

		// none concurrent for small parts.
		if len(triangles) < 1024 || depth > 8 {
			parent(tb.buildRecursive(triangles, min, max, depth))
			return
		}

		bbox := Interpolate(min, max)

		bboxes := make([][2]Vertex, len(cubes))

		for i := range cubes {
			bboxes[i] = [2]Vertex{
				bbox(cubes[i][0]),
				bbox(cubes[i][1]),
			}
		}

		var quandrants [8][]int32

		for _, tri := range triangles {
			triangle := tb.t.Triangles[tri]
			v0 := tb.t.Vertices[triangle[0]]
			v1 := tb.t.Vertices[triangle[1]]
			v2 := tb.t.Vertices[triangle[2]]

			l := v0
			l.Minimize(v1)
			l.Minimize(v2)

			h := v0
			h.Maximize(v1)
			h.Maximize(v2)

			for i := range bboxes {
				if !(h.Less(bboxes[i][0]) || bboxes[i][1].Less(l)) {
					quandrants[i] = append(quandrants[i], tri)
				}
			}
		}

		var used int32
		for i := range quandrants {
			if len(quandrants[i]) > 0 {
				used++
			}
		}

		pos := tb.allocNode()

		for i := range quandrants {
			if len(quandrants[i]) > 0 {
				j := i
				parent := func(v int32) {
					tb.index[j] = v
					if atomic.AddInt32(&used, -1) == 0 {
						parent(pos)
					}
				}
				step := tb.buildConcurrent(
					quandrants[j],
					bboxes[j][0], bboxes[j][1],
					depth+1,
					parent)
				select {
				case steps <- step:
				default:
					// all slots busy -> execute directly.
					step(steps)
				}
			}
		}
	}
}

func (tb *Builder) allocNode() int32 {
	tb.mu.Lock()
	pos := int32(len(tb.index))
	tb.index = append(tb.index,
		0, 0, 0, 0,
		0, 0, 0, 0)
	tb.nodes++
	tb.mu.Unlock()
	return pos
}

func (tb *Builder) buildRecursive(
	triangles []int32,
	min, max Vertex,
	depth int,
) int32 {
	if len(triangles) <= 16 || depth > 8 {
		tb.mu.Lock()
		pos := len(tb.index)
		tb.index = append(tb.index, int32(len(triangles)))
		tb.index = append(tb.index, triangles...)
		//log.Printf("leaf entries: %d (%d)\n", len(triangles), depth)
		tb.leaves++
		tb.mu.Unlock()
		return int32(-(pos + 1))
	}

	bbox := Interpolate(min, max)

	bboxes := make([][2]Vertex, len(cubes))

	for i := range cubes {
		bboxes[i] = [2]Vertex{
			bbox(cubes[i][0]),
			bbox(cubes[i][1]),
		}
	}

	var quandrants [8][]int32

	for _, tri := range triangles {
		triangle := tb.t.Triangles[tri]
		v0 := tb.t.Vertices[triangle[0]]
		v1 := tb.t.Vertices[triangle[1]]
		v2 := tb.t.Vertices[triangle[2]]

		l := v0
		l.Minimize(v1)
		l.Minimize(v2)

		h := v0
		h.Maximize(v1)
		h.Maximize(v2)

		for i := range bboxes {
			if !(h.Less(bboxes[i][0]) || bboxes[i][1].Less(l)) {
				quandrants[i] = append(quandrants[i], tri)
			}
		}
	}

	pos := tb.allocNode()

	for i := range quandrants {
		if len(quandrants[i]) > 0 {
			child := tb.buildRecursive(
				quandrants[i],
				bboxes[i][0], bboxes[i][1],
				depth+1)
			tb.index[pos+int32(i)] = child
		}
	}

	return pos
}

func (tb *Builder) serialize(w io.Writer) error {
	var buf [binary.MaxVarintLen32]byte

	if err := binary.Write(w, binary.LittleEndian, tb.index[0]); err != nil {
		return err
	}

	var last int32
	var written int

	for _, x := range tb.index[1:] {
		delta := x - last
		n := binary.PutVarint(buf[:], int64(delta))
		for p := buf[:n]; len(p) > 0; p = p[n:] {
			var err error
			if n, err = w.Write(p); err != nil {
				return err
			}
			written += n
		}

		last = x
	}
	log.Printf("info: compressed octree index in bytes: %d (%d)\n",
		written, 4*len(tb.index))

	return nil
}

func (tb *Builder) writeTo(w io.Writer) error {
	out := snappy.NewBufferedWriter(w)
	if err := tb.t.serialize(out); err != nil {
		return err
	}
	if err := tb.serialize(out); err != nil {
		return err
	}
	return out.Flush()
}

// Bytes serializes an Octree into a byte slice.
func (tb *Builder) Bytes() ([]byte, error) {
	var buf bytes.Buffer
	if err := tb.writeTo(&buf); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// Tree returns an Octree from the Builder.
func (tb *Builder) Tree() *Tree {
	return &Tree{
		EPSG:      tb.t.EPSG,
		vertices:  tb.t.Vertices,
		triangles: tb.t.Triangles,
		index:     tb.index,
		Min:       tb.t.Min,
		Max:       tb.t.Max,
	}
}