Selaa lähdekoodia

statistics util panel

leonsal 8 vuotta sitten
vanhempi
commit
42bb7b9c4e
2 muutettua tiedostoa jossa 112 lisäystä ja 60 poistoa
  1. 74 0
      util/stats/stats.go
  2. 38 60
      util/stats/table.go

+ 74 - 0
util/stats/stats.go

@@ -0,0 +1,74 @@
+package stats
+
+import (
+	"github.com/g3n/engine/gls"
+	"runtime"
+	"time"
+)
+
+// Stats contains several statistics usefull for performance evaluation
+type Stats struct {
+	Glstats    gls.Stats // GLS statistics structure
+	UnilocHits int       // Uniform location cache hits per frame
+	UnilocMiss int       // Uniform location cache misses per frame
+	Unisets    int       // Uniform sets per frame
+	Drawcalls  int       // Draw calls per frame
+	Cgocalls   int       // Cgo calls per frame
+	gs         *gls.GLS  // reference to gls state
+	glprev     gls.Stats // previous gls statistics
+	frames     int       // frame counter
+	cgocalls   int64     // previous number of cgo calls
+	last       time.Time // last update time
+}
+
+// NewStats creates and returns a pointer to a new statistics object
+func NewStats(gs *gls.GLS) *Stats {
+
+	s := new(Stats)
+	s.gs = gs
+	s.last = time.Now()
+	return s
+}
+
+// Update should be called in the render loop with the desired update interval.
+// Returns true when the interval has elapsed and the statistics has been updated.
+func (s *Stats) Update(d time.Duration) bool {
+
+	// If the specified time duration has not elapsed from previous update,
+	// nothing to do.
+	now := time.Now()
+	s.frames++
+	if s.last.Add(d).After(now) {
+		return false
+	}
+
+	// Update GLS statistics
+	s.gs.Stats(&s.Glstats)
+
+	// Calculates uniform location cache hits per frame
+	unilochits := s.Glstats.UnilocHits - s.glprev.UnilocHits
+	s.UnilocHits = int(float64(unilochits) / float64(s.frames))
+
+	// Calculates uniform location cache hits per frame
+	unilocmiss := s.Glstats.UnilocMiss - s.glprev.UnilocMiss
+	s.UnilocMiss = int(float64(unilocmiss) / float64(s.frames))
+
+	// Calculates uniforms sets per frame
+	unisets := s.Glstats.Unisets - s.glprev.Unisets
+	s.Unisets = int(float64(unisets) / float64(s.frames))
+
+	// Calculates draw calls per frame
+	drawcalls := s.Glstats.Drawcalls - s.glprev.Drawcalls
+	s.Drawcalls = int(float64(drawcalls) / float64(s.frames))
+
+	// Calculates number of cgo calls per frame
+	current := runtime.NumCgoCall()
+	cgocalls := current - s.cgocalls
+	s.Cgocalls = int(float64(cgocalls) / float64(s.frames))
+	s.cgocalls = current
+
+	s.glprev = s.Glstats
+	s.last = now
+	s.frames = 0
+	return true
+}

+ 38 - 60
util/stats/table.go

@@ -1,21 +1,16 @@
 package stats
 
 import (
-	"fmt"
-	"runtime"
-	"time"
-
 	"github.com/g3n/engine/gls"
 	"github.com/g3n/engine/gui"
+	"time"
 )
 
+// StatsTable is a gui.Table panel with statistics
 type StatsTable struct {
 	*gui.Table          // embedded table
 	fields     []*field // array of fields
-	prev       gls.Stats
-	frames     int
-	cgocalls   int64
-	last       time.Time
+	stats      *Stats   // statistics object
 }
 
 type field struct {
@@ -23,83 +18,66 @@ type field struct {
 	row int
 }
 
-func NewStatsTable(width, height float32) *StatsTable {
+// NewStatsTable creates and returns a pointer to a new statistics table panel
+func NewStatsTable(width, height float32, gs *gls.GLS) *StatsTable {
 
-	s := new(StatsTable)
+	st := new(StatsTable)
 	t, err := gui.NewTable(width, height, []gui.TableColumn{
-		{Id: "f", Header: "Stat", Width: 50, Minwidth: 32, Align: gui.AlignRight, Format: "%s", Resize: true, Expand: 1.5},
+		{Id: "f", Header: "Stat", Width: 50, Minwidth: 32, Align: gui.AlignRight, Format: "%s", Resize: true, Expand: 2},
 		{Id: "v", Header: "Value", Width: 50, Minwidth: 32, Align: gui.AlignRight, Format: "%d", Resize: false, Expand: 1},
 	})
 	if err != nil {
 		panic(err)
 	}
-	s.Table = t
-	s.ShowHeader(false)
-	s.addRow("shaders", "Shaders:")
-	s.addRow("vaos", "Vaos:")
-	s.addRow("vbos", "Vbos:")
-	s.addRow("textures", "Textures:")
-	s.addRow("unisets", "Uniforms/frame:")
-	s.addRow("cgocalls", "CGO calls/frame:")
-	s.last = time.Now()
-	s.cgocalls = runtime.NumCgoCall()
-	return s
+	st.Table = t
+	st.ShowHeader(false)
+	st.addRow("shaders", "Shaders:")
+	st.addRow("vaos", "Vaos:")
+	st.addRow("vbos", "Vbos:")
+	st.addRow("textures", "Textures:")
+	st.addRow("unisets", "Uniforms/frame:")
+	st.addRow("drawcalls", "Draw calls/frame:")
+	st.addRow("cgocalls", "CGO calls/frame:")
+	st.stats = NewStats(gs)
+	return st
 }
 
-func (s *StatsTable) Update(gs *gls.GLS, d time.Duration) {
+// Update should be called normally in the render loop with the desired update interval
+func (st *StatsTable) Update(d time.Duration) {
 
-	now := time.Now()
-	s.frames++
-	if s.last.Add(d).After(now) {
-		return
+	if st.stats.Update(d) {
+		st.update()
 	}
-	s.update(gs)
-	s.last = now
-	s.frames = 0
 }
 
-func (s *StatsTable) update(gs *gls.GLS) {
+func (st *StatsTable) update() {
 
-	var stats gls.Stats
-	gs.Stats(&stats)
-	for i := 0; i < len(s.fields); i++ {
-		f := s.fields[i]
+	for i := 0; i < len(st.fields); i++ {
+		f := st.fields[i]
 		switch f.id {
 		case "shaders":
-			if stats.Shaders != s.prev.Shaders {
-				s.Table.SetCell(f.row, "v", stats.Shaders)
-				fmt.Println("update")
-			}
+			st.Table.SetCell(f.row, "v", st.stats.Glstats.Shaders)
 		case "vaos":
-			if stats.Vaos != s.prev.Vaos {
-				s.Table.SetCell(f.row, "v", stats.Vaos)
-				fmt.Println("update")
-			}
+			st.Table.SetCell(f.row, "v", st.stats.Glstats.Vaos)
 		case "vbos":
-			if stats.Vbos != s.prev.Vbos {
-				s.Table.SetCell(f.row, "v", stats.Vbos)
-				fmt.Println("update")
-			}
+			st.Table.SetCell(f.row, "v", st.stats.Glstats.Vbos)
 		case "textures":
-			if stats.Textures != s.prev.Textures {
-				s.Table.SetCell(f.row, "v", stats.Textures)
-				fmt.Println("update")
-			}
+			st.Table.SetCell(f.row, "v", st.stats.Glstats.Textures)
+		case "unisets":
+			st.Table.SetCell(f.row, "v", st.stats.Unisets)
+		case "drawcalls":
+			st.Table.SetCell(f.row, "v", st.stats.Drawcalls)
 		case "cgocalls":
-			current := runtime.NumCgoCall()
-			calls := current - s.cgocalls
-			s.Table.SetCell(f.row, "v", int(float64(calls)/float64(s.frames)))
-			s.cgocalls = current
+			st.Table.SetCell(f.row, "v", st.stats.Cgocalls)
 		}
 	}
-	s.prev = stats
 }
 
-func (s *StatsTable) addRow(id, label string) {
+func (st *StatsTable) addRow(id, label string) {
 
 	f := new(field)
 	f.id = id
-	f.row = s.Table.RowCount()
-	s.Table.AddRow(map[string]interface{}{"f": label, "v": 0})
-	s.fields = append(s.fields, f)
+	f.row = st.Table.RowCount()
+	st.Table.AddRow(map[string]interface{}{"f": label, "v": 0})
+	st.fields = append(st.fields, f)
 }