Beispiel #1
0
func (p *Planetoid) Render(dp float64) {
	glh.With(glh.Matrix{gl.MODELVIEW}, func() {

		gl.Rotated(p.rising_node, 0, 1, 0)
		gl.Rotated(p.inclination, 0, 0, 1)

		gl.Rotated(p.phase0+p.phase, 0, 1, 0)
		p.phase += dp*10 + (1 - p.apogee)

		glh.With(glh.Matrix{gl.MODELVIEW}, func() {
			// TODO: Compute position correctly
			gl.Translated(p.apogee, 0, 0)
			Sphere(p.radius, 3)
		})

		// TODO: Avoid depth thrashing when trails overlap
		// Trail
		glh.With(glh.Matrix{gl.MODELVIEW}, func() {
			gl.Rotated(90, 1, 0, 0)
			gl.Scaled(p.apogee, p.apogee, 1)

			gl.LineWidth(2)
			glh.With(glh.Disable(gl.LIGHTING), func() {
				p.circle.Render(gl.LINE_STRIP)
			})
		})
	})
}
Beispiel #2
0
func (game *Game) Init(window *glfw.Window) {
	//Select the 'projection matrix'
	gl.MatrixMode(gl.PROJECTION)
	//Reset
	gl.LoadIdentity()
	//Scale everything down, to 1/10 scale
	gl.Scaled(0.1, 0.1, 0.1)
}
Beispiel #3
0
func PlaceObject(compiledPrim gl.List, scale [3]double, translation [3]double, rotation [4]double) {
	// objects are placed and transformed when necessary
	gl.PushMatrix()
	gl.Scaled(scale)
	gl.Translated(translation)
	gl.Rotated(rotation)
	gl.CallList(compiledPrim)
	gl.PopMatrix()
}
Beispiel #4
0
func DrawEntity(en Entity) {

	x := en.Xpos
	y := en.Ypos
	r := en.Rot
	s := en.Size
	col := en.Colour

	switch col {
	case "grey":
		gl.Color3d(0.5, 0.5, 0.5)
	case "red":
		gl.Color3d(1, 0, 0)
	case "green":
		gl.Color3d(0, 1, 0)
	case "blue":
		gl.Color3d(0, 0, 1)
	case "lblue":
		gl.Color3d(0.3, 0.3, 1)
	case "orange":
		gl.Color3d(1, 0.5, 0)
	case "yellow":
		gl.Color3d(1, 1, 0)
	case "purple":
		gl.Color3d(1, 0, 1)
	case "white":
		gl.Color3d(1, 1, 1)
	default:
		gl.Color3d(1, 1, 1)
	}

	gl.PushMatrix()

	gl.Translated(x, y, 0)
	gl.Scaled(s, s, s)
	//gl.Rotated(r*180/math.Pi, 0, 0, 1)
	gl.Rotated(r, 0, 0, 1)

	enx := [3]float64{-0.7, 0.7, 0}
	eny := [3]float64{1, 1, -1}

	//in OpenGL 3.2, the vertices below would be stored on the GPU
	//so all you would need to do is say DrawShape(A) or something

	gl.Begin(gl.TRIANGLES)
	gl.Vertex3d(enx[0], eny[0], 0)
	gl.Vertex3d(enx[1], eny[1], 0)
	gl.Vertex3d(enx[2], eny[2], 0)
	gl.End()

	gl.PopMatrix()

}
Beispiel #5
0
func (game *Game) Init(window *glfw.Window) {
	//Select the 'projection matrix'
	gl.MatrixMode(gl.PROJECTION)
	//Reset
	gl.LoadIdentity()
	//Scale everything down, to 1/10 scale
	gl.Scaled(0.1, 0.1, 0.1)
	a, b := ResolutionToGrid(winWidth, winHeight)
	wrtg := WidthResToGrid(winWidth)
	hrtg := HeightResToGrid(winHeight)

	fmt.Println("Some Arbitary tests")

	fmt.Println("res to grid", winWidth, "=", a, "*", winHeight, "=", b)
	fmt.Println("Wres to grid", wrtg)
	fmt.Println("Hres to grid", hrtg)
	fmt.Println("Width to grid (100)", WidthResToGrid(100))
	fmt.Println("Height to grid (100)", HeightResToGrid(100))
	a, b = ResolutionToGrid(100, 100)
	fmt.Println("Res to grid(100,100)", a, b)
	fmt.Println("")
	fmt.Println(">>>>>>> PRESS H FOR HELP <<<<<<<")
}
Beispiel #6
0
func main() {
	err := initGL()
	if err != nil {
		log.Printf("InitGL: %v", err)
		return
	}

	defer glfw.Terminate()

	mb := createBuffer()
	defer mb.Release()

	gl.Enable(gl.BLEND)
	//gl.BlendFunc(gl.SRC_ALPHA, gl.ONE)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

	f, err := os.Create("test.mpeg")
	if err != nil {
		log.Panicf("Unable to open output file: %q", err)
	}

	im := image.NewRGBA(image.Rect(0, 0, 512, 512))
	e, err := ffmpeg.NewEncoder(ffmpeg.CODEC_ID_H264, im, f)
	if err != nil {
		log.Panicf("Unable to start encoder: %q", err)
	}

	// Perform the rendering.
	var angle float64
	for glfw.WindowParam(glfw.Opened) > 0 {
		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
		gl.LoadIdentity()
		gl.Translatef(0, 0, -6)
		gl.Rotated(angle/10, 0, 1, 0)
		gl.Rotated(angle, 1, 1, 1)

		// Render a solid cube at half the scale.
		gl.Scalef(0.1, 0.1, 0.1)
		gl.Enable(gl.COLOR_MATERIAL)
		gl.Enable(gl.POLYGON_OFFSET_FILL)
		gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)
		mb.Render(gl.QUADS)

		// Render wireframe cubes, with incremental size.
		gl.Disable(gl.COLOR_MATERIAL)
		gl.Disable(gl.POLYGON_OFFSET_FILL)
		gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)

		for i := 0; i < 1000; i++ {
			ifl := float64(i)
			scale := 0.000005*ifl + 1.0 + 0.01*math.Sin(angle/10+ifl/10)
			gl.Scaled(scale, scale, scale)
			gl.Rotated((ifl+angle)/1000, 1, 1, 1)
			gl.Rotated((ifl+angle)/1100, -1, 1, 1)

			mb.Render(gl.QUADS)
		}

		glh.ClearAlpha(1)
		glh.CaptureRGBA(im)

		err := e.WriteFrame()
		if err != nil {
			panic(err)
		}

		angle += 0.5
		//glh.CaptureToPng("test.png")
		glfw.SwapBuffers()
	}
	e.Close()
}
Beispiel #7
0
func main_loop(data *ProgramData) {
	start := time.Now()
	frames := 0
	lastblocks := 0

	// Frame counter
	go func() {
		for {
			time.Sleep(time.Second)
			if *verbose {
				memstats := new(runtime.MemStats)
				runtime.ReadMemStats(memstats)
				fps := float64(frames) / time.Since(start).Seconds()
				blocks := len(data.blocks)
				bps := float64(blocks-lastblocks) / time.Since(start).Seconds()
				lastblocks = blocks
				log.Printf("fps = %5.2f; blocks = %4d; bps = %5.2f sparemem = %6d MB; alloc'd = %6.6f; (+footprint = %6.6f)",
					fps, len(data.blocks), bps, SpareRAM(), float64(memstats.Alloc)/1024/1024,
					float64(memstats.Sys-memstats.Alloc)/1024/1024)

				PrintTimers(frames)
			}
			start = time.Now()
			frames = 0
		}
	}()

	// Necessary at the moment to prevent eventual OOM
	go func() {
		for {
			time.Sleep(5 * time.Second)
			if *verbose {
				log.Print("GC()")
			}
			runtime.GC()
			if *verbose {
				GCStats()
			}
		}
	}()

	var i int64 = -int64(*nback)

	// TODO(pwaller): Make this work again
	// text := glh.MakeText(data.filename, 32)

	// Location of mouse in record space
	var rec, rec_actual int64 = 0, 0

	var stacktext, dwarftext []*glh.Text
	var recordtext *glh.Text = nil

	var mousex, mousey, mousedownx, mousedowny int
	var mousepx, mousepy float64
	var lbutton bool
	escape_hit := false

	glfw.SetMouseWheelCallback(func(pos int) {
		nback_prev := *nback
		if pos < 0 {
			*nback = 40 * 1024 << uint(-pos)
		} else {
			*nback = 40 * 1024 >> uint(pos)
		}
		//log.Print("Mousewheel position: ", pos, " nback: ", *nback)

		if rec == 0 {
			return
		}

		// mouse cursor is at screen "rec_actual", and it should be after
		// the transformation

		// We need to adjust `i` to keep this value constant:
		// rec_actual == i + int64(constpart * float64(*nback))
		//   where constpart <- (-const + 2.) / 4.
		// (that way, the mouse is still pointing at the same place after scaling)

		constpart := float64(rec_actual-i) / float64(nback_prev)

		rec_actual_after := i + int64(constpart*float64(*nback))
		delta := rec_actual_after - rec_actual
		i -= delta

		// Ensure the mouse cursor position doesn't change when zooming
		rec = rec_actual - i
	})

	update_text := func() {
		if DoneThisFrame(RenderText) {
			return
		}

		if recordtext != nil {
			recordtext.Destroy()
			recordtext = nil
		}
		//r := 0 //data.GetRecord(rec_actual)
		//if r != nil {
		//log.Print(data.records[rec_actual])
		//recordtext = MakeText(r.String(), 32)
		//}

		for j := range stacktext {
			stacktext[j].Destroy()
		}
		// TODO: Load records on demand
		if false {
			stack := data.GetStackNames(rec_actual)
			stacktext = make([]*glh.Text, len(stack))
			for j := range stack {
				stacktext[j] = glh.MakeText(stack[j], 32)
			}
		}
	}

	glfw.SetMouseButtonCallback(func(button, action int) {
		switch button {
		case glfw.Mouse1:
			switch action {
			case glfw.KeyPress:
				mousedownx, mousedowny = mousex, mousey
				lbutton = true

				r := data.GetRecord(rec_actual)
				if r != nil {
					if r.Type == MEMA_ACCESS {
						log.Print(r)
						ma := r.MemAccess()
						dwarf := data.GetDwarf(ma.Pc)
						log.Print("Can has dwarf? ", len(dwarf))
						for i := range dwarf {
							log.Print("  ", dwarf[i])
							//recordtext = MakeText(data.records[rec_actual].String(), 32)

						}
						log.Print("")

						for j := range dwarftext {
							dwarftext[j].Destroy()
						}
						dwarftext = make([]*glh.Text, len(dwarf))
						for j := range dwarf {
							dwarftext[j] = glh.MakeText(fmt.Sprintf("%q", dwarf[j]), 32)
						}
					}

					//recordtext = MakeText(data.records[rec_actual].String(), 32)
				}

			case glfw.KeyRelease:
				lbutton = false
			}
		}
	})

	glfw.SetMousePosCallback(func(x, y int) {

		px, py := glh.WindowToProj(x, y)
		// Record index
		rec = int64((py+2)*float64(*nback)/4. + 0.5)
		rec_actual = rec + i

		dpy := py - mousepy
		di := int64(-dpy * float64(*nback) / 4.)

		if lbutton {
			i += di
		}

		mousepx, mousepy = px, py
		mousex, mousey = x, y

		//log.Printf("Mouse motion: (%3d, %3d), (%f, %f), (%d, %d) dpy=%f di=%d",
		//x, y, px, py, rec, rec_actual, dpy, di)

		update_text()
	})

	glfw.SetKeyCallback(func(key, state int) {
		switch key {
		case glfw.KeyEsc:
			escape_hit = true
		}
	})

	draw_mousepoint := func() {

		// Draw the mouse point
		glh.With(glh.Matrix{gl.MODELVIEW}, func() {
			gl.Translated(0, -2, 0)
			gl.Scaled(1, 4/float64(*nback), 1)
			gl.Translated(0, float64(rec), 0)

			gl.PointSize(10)
			glh.With(glh.Primitive{gl.POINTS}, func() {
				gl.Color4f(1, 1, 1, 1)
				gl.Vertex3d(mousepx, 0, 0)
			})
		})
	}

	draw_text := func() {
		// Draw any text
		glh.With(glh.WindowCoords{}, func() {
			w, h := glh.GetViewportWHD()

			glh.With(glh.Attrib{gl.ENABLE_BIT}, func() {
				gl.Enable(gl.TEXTURE_2D)
				// text.Draw(0, 0)
				for text_idx := range stacktext {
					stacktext[text_idx].Draw(int(w*0.55), int(h)-35-text_idx*16)
				}
				for text_idx := range dwarftext {
					dwarftext[text_idx].Draw(int(w*0.55), 35+text_idx*16)
				}
				if recordtext != nil {
					recordtext.Draw(int(w*0.55), 35)
				}
			})
		})
	}

	Draw = func() {

		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

		// Draw the memory access/function data
		data.Draw(i, *nback)

		draw_mousepoint()
		draw_text()

		// Visible region quad
		// gl.Color4f(1, 1, 1, 0.25)
		// DrawQuadd(-2.1, -2.25, 4.4, 2.1 - -2.25)

		// StatsHUD()
	}

	interrupt := make(chan os.Signal)
	signal.Notify(interrupt, os.Interrupt)
	ctrlc_hit := false
	go func() {
		<-interrupt
		ctrlc_hit = true
	}()

	done := false
	for !done {
		done_this_frame = make(map[WorkType]bool)

		glh.With(&Timer{Name: "Draw"}, func() {
			Draw()
		})

		glfw.SwapBuffers()

		DoMainThreadWork()

		done = ctrlc_hit || escape_hit || glfw.WindowParam(glfw.Opened) == 0
		frames += 1
	}
}
Beispiel #8
0
func (block *Block) Draw(start, N int64, detailed bool) {
	if block.tex == nil {
		block.RequestTexture()
	}

	switch detailed {
	case true:
		block.detail_needed = true
		if block.vertex_data == nil {
			// Hey, we need vertices but don't have them! Let's fix that..
			block.RequestVertices()
		}
	default:
		block.detail_needed = false
	}

	width := uint64(len(block.display_active_pages)) * *PAGE_SIZE
	if width == 0 {
		width = 1
	}

	vc := glh.NewMeshBuffer(glh.RenderArrays,
		glh.NewPositionAttr(2, gl.FLOAT, gl.STATIC_DRAW),
		glh.NewPositionAttr(4, gl.UNSIGNED_INT, gl.STATIC_DRAW),
	)

	colors := make([]int32, 0)
	positions := make([]float32, 0)

	// var vc glh.ColorVertices

	if *pageboundaries {
		// boundary_color := color.RGBA{64, 64, 64, 255}

		// If we try and draw too many of these, X will hang
		if width / *PAGE_SIZE < 10000 {
			for p := uint64(0); p <= width; p += *PAGE_SIZE {
				x := float32(p) / float32(width)
				x = (x - 0.5) * 4

				colors = append(colors, 64, 64, 64, 255)
				positions = append(positions, x, float32(N))

				// vc.Add(glh.ColorVertex{boundary_color, glh.Vertex{x, 0}})
				// vc.Add(glh.ColorVertex{boundary_color, glh.Vertex{x, float32(N)}})
			}
		}
	}

	var border_color [4]float64

	gl.LineWidth(1)
	glh.With(&Timer{Name: "DrawPartial"}, func() {
		var x1, y1, x2, y2 float64
		glh.With(glh.Matrix{gl.MODELVIEW}, func() {
			// TODO: A little less co-ordinate insanity?
			gl.Translated(0, -2, 0)
			gl.Scaled(1, 4/float64(*nback), 1)
			gl.Translated(0, -float64(start), 0)

			x1, y1 = glh.ProjToWindow(-2, 0)
			x2, y2 = glh.ProjToWindow(-2+WIDTH, float64(N))

		})
		border_color = [4]float64{1, 1, 1, 1}

		glh.With(glh.Matrix{gl.MODELVIEW}, func() {
			gl.Translated(0, -2, 0)
			gl.Scaled(1, 4/float64(*nback), 1)
			gl.Translated(0, -float64(start), 0)

			// Page boundaries
			// TODO: Use different blending scheme on textured quads so that the
			//       lines show through
			glh.With(glh.Attrib{gl.ENABLE_BIT}, func() {
				gl.Disable(gl.LINE_SMOOTH)
				// vc.Draw(gl.LINES)
				vc.Render(gl.LINES)
			})
		})

		if block.tex != nil && (!detailed || block.vertex_data == nil) {
			border_color = [4]float64{0, 0, 1, 1}
			glh.With(glh.WindowCoords{Invert: true}, func() {
				gl.Color4f(1, 1, 1, 1)
				// Render textured block quad
				glh.With(block.tex, func() {
					glh.DrawQuadd(x1, y1, x2-x1, y2-y1)
				})
				glh.With(glh.Primitive{gl.LINES}, func() {
					glh.Squared(x1, y1, x2-x1, y2-y1)
				})
			})
			if block.vertex_data != nil && !block.detail_needed {
				// TODO: figure out when we can unload
				// Hey, we can unload you, because you are not needed
				block.vertex_data = nil
			}

		}
		if detailed && block.vertex_data != nil {
			glh.With(glh.Matrix{gl.MODELVIEW}, func() {
				// TODO: A little less co-ordinate insanity?
				gl.Translated(0, -2, 0)
				gl.Scaled(1, 4/float64(*nback), 1)
				gl.Translated(0, -float64(start), 0)

				gl.PointSize(2)
				block.vertex_data.Render(gl.POINTS)
			})
		}

		glh.With(glh.WindowCoords{Invert: true}, func() {
			// Block boundaries
			gl.Color4dv(&border_color)

			gl.LineWidth(1)
			glh.With(glh.Primitive{gl.LINE_LOOP}, func() {
				glh.Squared(x1, y1, x2-x1, y2-y1)
			})
		})
	})
}
Beispiel #9
0
func (block *Block) BuildTexture() {
	block.tex = glh.NewTexture(1024, 256)
	block.tex.Init()

	// TODO: use runtime.SetFinalizer() to clean up/delete the texture?
	glh.With(block.tex, func() {
		//gl.TexParameteri(gl.TEXTURE_2D, gl.GENERATE_MIPMAP, gl.TRUE)

		//gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)

		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST)
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
		// TODO: Only try and activate anisotropic filtering if it is available

		gl.TexParameterf(gl.TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, MaxAnisotropy)
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 1)
	})

	for i := 0; i < 2; i++ {
		glh.With(&glh.Framebuffer{Texture: block.tex, Level: i}, func() {
			glh.With(glh.Attrib{gl.COLOR_BUFFER_BIT}, func() {
				gl.ClearColor(1, 0, 0, 0)
				gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
			})
			viewport_proj := glh.Compound(glh.Attrib{gl.VIEWPORT_BIT},
				glh.Matrix{gl.PROJECTION})

			glh.With(viewport_proj, func() {
				gl.Viewport(0, 0, block.tex.W/(1<<uint(i)), block.tex.H/(1<<uint(i)))
				gl.LoadIdentity()
				//gl.Ortho(0, float64(tex.w), 0, float64(tex.h), -1, 1)
				gl.Ortho(-2, -2+WIDTH, 2, -2, -1, 1)

				glh.With(glh.Matrix{gl.MODELVIEW}, func() {
					gl.LoadIdentity()

					//gl.Hint(gl.LINES, gl.NICEST)
					//gl.LineWidth(4)
					/*
						glh.With(glh.Primitive{gl.LINES}, func() {
							gl.Color4f(1, 1, 1, 1)
							gl.Vertex2f(-2, 0)
							gl.Vertex2f(2, 0)
						})
					*/

					gl.PointSize(4)
					glh.With(glh.Matrix{gl.MODELVIEW}, func() {
						gl.Translated(0, -2, 0)
						gl.Scaled(1, 4/float64(block.nrecords), 1)

						block.vertex_data.Render(gl.POINTS)
					})

					/*
						gl.Color4f(0.5, 0.5, 1, 1)
						With(Primitive{gl.LINE_LOOP}, func() {
							b := 0.2
							Squared(-2.1+b, -2.1+b, 4.35-b*2, 4.2-b*2)
						})
					*/
				})
			})
		})
	}

	//block.img = block.tex.AsImage()
	if !block.detail_needed {
		block.vertex_data = nil
		runtime.GC()
	}

	blocks_rendered++
}
Beispiel #10
0
func InitStatsHUD() {
	plots := chart.ScatterChart{Title: "", Options: glchart.DarkStyle}
	start := time.Now()

	l := float64(start.UnixNano())
	r := float64(start.Add(2 * time.Second).UnixNano())

	plots.XRange.Fixed(l, r, 1e9)
	plots.YRange.Fixed(0.1, 100, 10)

	plots.XRange.TicSetting.Tics, plots.YRange.TicSetting.Tics = 1, 1
	plots.XRange.TicSetting.Mirror, plots.YRange.TicSetting.Mirror = 2, 2
	plots.XRange.TicSetting.Grid, plots.YRange.TicSetting.Grid = 2, 2

	plots.YRange.ShowZero = true

	//plots.XRange.Log = true
	//plots.YRange.Log = true

	plots.Key.Pos, plots.Key.Cols = "obc", 3

	plots.XRange.TicSetting.Format = func(f float64) string {
		t := time.Unix(int64(f)/1e9, int64(f)%1e9)
		return fmt.Sprintf("%.3v", time.Since(t))
	}

	memhelper.GetMaxRSS()

	var gpufree float64

	gpupoll := gpuinfo.PollGPUMemory()
	go func() {
		for gpustatus := range gpupoll {
			gpufree = float64(memhelper.ByteSize(gpustatus.Free()) * memhelper.MiB)
		}
	}()

	statistics := &Statistics{}
	statistics.Add(&plots, "GPU Free", "#FF9F00", func() float64 { return gpufree })
	statistics.Add(&plots, "SpareRAM()", "#ff0000", func() float64 { return float64(SpareRAM() * 1e6) })
	statistics.Add(&plots, "MaxRSS", "#FFE240", func() float64 { return float64(memhelper.GetMaxRSS()) })
	statistics.Add(&plots, "Heap Idle", "#33ff33", func() float64 { return float64(memstats.HeapIdle) })
	statistics.Add(&plots, "Alloc", "#FF6600", func() float64 { return float64(memstats.Alloc) })
	statistics.Add(&plots, "Heap Alloc", "#006699", func() float64 { return float64(memstats.HeapAlloc) })
	statistics.Add(&plots, "Sys", "#996699", func() float64 { return float64(memstats.Sys) })
	statistics.Add(&plots, "System Free", "#3333ff", func() float64 { return float64(SystemFree()) })
	statistics.Add(&plots, "nBlocks x 1e6", "#FFCC00", func() float64 { return float64(nblocks * 1e6) })
	statistics.Add(&plots, "nDrawn x 1e6", "#9C8AA5", func() float64 { return float64(blocks_rendered * 1e6) })

	go func() {
		top := 0.

		i := -1
		for {
			time.Sleep(250 * time.Millisecond)
			max := statistics.Update()
			if max > top {
				top = max
			}
			i++
			if i%4 != 0 {
				continue
			}

			segment := float64(1e9)
			if time.Since(start) > 10*time.Second {
				segment = 5e9
			}
			if time.Since(start) > 1*time.Minute {
				segment = 30e9
			}

			// Update axis limits
			nr := float64(time.Now().Add(2 * time.Second).UnixNano())
			plots.XRange.Fixed(l, nr, segment)
			plots.YRange.Fixed(-1e9, top*1.1, 500e6)
		}
	}()

	const pw, ph = 640, 480

	scalex, scaley := 0.4, 0.5

	chart_gfxcontext := glchart.New(pw, ph, "", 10, color.RGBA{})

	StatsHUD = func() {
		glh.With(glh.Matrix{gl.PROJECTION}, func() {
			gl.LoadIdentity()
			gl.Translated(1-scalex, scaley-1, 0)
			gl.Scaled(scalex, scaley, 1)
			gl.Ortho(0, pw, ph, 0, -1, 1)
			gl.Translated(0, -50, 0)

			glh.With(glh.Attrib{gl.ENABLE_BIT}, func() {
				gl.Disable(gl.DEPTH_TEST)
				glh.With(&Timer{Name: "Chart"}, func() {
					plots.Plot(chart_gfxcontext)
				})
			})
		})
	}

	// TODO: figure out why this is broken
	DumpStatsHUD = func() {
		s2f, _ := os.Create("statshud-dump.svg")
		mysvg := svg.New(s2f)
		mysvg.Start(1600, 800)
		mysvg.Rect(0, 0, 2000, 800, "fill: #ffffff")
		sgr := svgg.New(mysvg, 2000, 800, "Arial", 18,
			color.RGBA{0xff, 0xff, 0xff, 0xff})
		sgr.Begin()

		plots.Plot(sgr)

		sgr.End()
		mysvg.End()
		s2f.Close()
		log.Print("Saved statshud-dump.svg")
	}

	//log.Print("InitStatsHUD()")
}