From cafda2cfae99b77e0ba8ab1c91dccb7695b3bc55 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Sun, 1 Jan 2017 22:12:12 +0100
Subject: [PATCH] backup before reinstall

---
 graphics.go  | 209 ++++++++++++++-------------------------------------
 interface.go |  21 ++++++
 picture.go   |  23 +++---
 window.go    | 150 +++++++++++++++++++++---------------
 4 files changed, 182 insertions(+), 221 deletions(-)
 create mode 100644 interface.go

diff --git a/graphics.go b/graphics.go
index 03e3893..132e470 100644
--- a/graphics.go
+++ b/graphics.go
@@ -9,72 +9,6 @@ import (
 	"github.com/pkg/errors"
 )
 
-// Drawer is anything that can be drawn. It's by no means a drawer inside your table.
-//
-// Drawer consists of a single methods: Draw. Draw methods takes any number of Transform
-// arguments. It applies these transforms in the reverse order and finally draws something
-// transformed by these transforms.
-//
-// Example:
-//
-//   // object is a drawer
-//   object.Draw(pixel.Position(pixel.V(100, 100).Rotate(math.Pi / 2)))
-//   camera := pixel.Camera(pixel.V(0, 0), pixel.V(500, 500), pixel.V(window.Size()))
-//   object.Draw(camera, pixel.Position(0).Scale(0.5))
-type Drawer interface {
-	Draw(t ...Transform)
-}
-
-// Group is used to effeciently handle a collection of objects with a common parent. Usually many
-// objects share a parent, using a group can significantly increase performance in these cases.
-//
-// To use a group, first, create a group and as it's parent use the common parent of the
-// collection of objects:
-//
-//   group := pixel.NewGroup(commonParent)
-//
-// Then, when creating the objects, use the group as their parent, instead of the original
-// common parent, but, don't forget to put everything into a With block, like this:
-//
-//   group.With(func() {
-//       object := newArbitratyObject(group, ...) // group is the parent of the object
-//   })
-//
-// When dealing with objects associated with a group, it's always necessary to wrap that into
-// a With block:
-//
-//   group.With(func() {
-//       for _, obj := range objectsWithCommonParent {
-//           // do something with obj
-//       }
-//   })
-//
-// That's all!
-type Group struct {
-	parent  pixelgl.Doer
-	context pixelgl.Context
-}
-
-// NewGroup creates a new group with the specified parent.
-func NewGroup(parent pixelgl.Doer) *Group {
-	return &Group{
-		parent: parent,
-	}
-}
-
-// With enables the parent of a group and executes sub.
-func (g *Group) With(sub func()) {
-	g.parent.Do(func(ctx pixelgl.Context) {
-		g.context = ctx
-		sub()
-	})
-}
-
-// Do just passes a cached context to sub.
-func (g *Group) Do(sub func(pixelgl.Context)) {
-	sub(g.context)
-}
-
 // Shape is a general drawable shape constructed from vertices.
 //
 // Vertices are specified in the vertex array of a shape. A shape can have a picture, a color
@@ -83,21 +17,21 @@ func (g *Group) Do(sub func(pixelgl.Context)) {
 // Usually you use this type only indirectly throught other specific shapes (sprites, polygons,
 // ...) embedding it.
 type Shape struct {
-	parent    pixelgl.Doer
 	picture   *Picture
 	color     color.Color
 	transform Transform
-	va        *pixelgl.VertexArray
+	vertices  []map[pixelgl.Attr]interface{}
+	vas       map[Target]*pixelgl.VertexArray
 }
 
 // NewShape creates a new shape with specified parent, picture, color, transform and vertex array.
-func NewShape(parent pixelgl.Doer, picture *Picture, c color.Color, transform Transform, va *pixelgl.VertexArray) *Shape {
+func NewShape(picture *Picture, c color.Color, transform Transform, vertices []map[pixelgl.Attr]interface{}) *Shape {
 	return &Shape{
-		parent:    parent,
 		picture:   picture,
 		color:     c,
 		transform: transform,
-		va:        va,
+		vertices:  vertices,
+		vas:       make(map[Target]*pixelgl.VertexArray),
 	}
 }
 
@@ -131,30 +65,45 @@ func (s *Shape) Transform() Transform {
 	return s.transform
 }
 
-// VertexArray changes the underlying vertex array of a shape.
-func (s *Shape) VertexArray() *pixelgl.VertexArray {
-	return s.va
+// Vertices returns the vertex attribute values of all vertices in a shape.
+//
+// Do not change!
+func (s *Shape) Vertices() []map[pixelgl.Attr]interface{} {
+	return s.vertices
 }
 
 // Draw draws a sprite transformed by the supplied transforms applied in the reverse order.
-func (s *Shape) Draw(t ...Transform) {
+func (s *Shape) Draw(target Target, t ...Transform) {
 	mat := mgl32.Ident3()
 	for i := range t {
 		mat = mat.Mul3(t[i].Mat())
 	}
 	mat = mat.Mul3(s.transform.Mat())
 
-	s.parent.Do(func(ctx pixelgl.Context) {
-		c := NRGBAModel.Convert(s.color).(NRGBA)
-		ctx.Shader().SetUniformAttr(maskColorVec4, mgl32.Vec4{float32(c.R), float32(c.G), float32(c.B), float32(c.A)})
-		ctx.Shader().SetUniformAttr(transformMat3, mat)
+	c := NRGBAModel.Convert(s.color).(NRGBA)
+
+	if s.vas[target] == nil {
+		s.vas[target] = target.MakeVertexArray(s.vertices)
+	}
+	va := s.vas[target]
+
+	pixelgl.Do(func() {
+		target.Begin()
+		defer target.End()
+
+		target.Shader().SetUniformAttr(maskColorVec4, mgl32.Vec4{float32(c.R), float32(c.G), float32(c.B), float32(c.A)})
+		target.Shader().SetUniformAttr(transformMat3, mat)
 
 		if s.picture != nil {
-			s.picture.Texture().Do(func(pixelgl.Context) {
-				s.va.Draw()
-			})
+			s.picture.Texture().Begin()
+			va.Begin()
+			va.Draw()
+			va.End()
+			s.picture.Texture().End()
 		} else {
-			s.va.Draw()
+			va.Begin()
+			va.Draw()
+			va.End()
 		}
 	})
 }
@@ -173,7 +122,7 @@ type MultiShape struct {
 // NewMultiShape creates a new multishape from several other shapes.
 //
 // If two of the supplied shapes have different pictures, this function panics.
-func NewMultiShape(parent pixelgl.Doer, shapes ...*Shape) *MultiShape {
+func NewMultiShape(shapes ...*Shape) *MultiShape {
 	var picture *Picture
 	for _, shape := range shapes {
 		if picture != nil && shape.Picture() != nil && shape.Picture().Texture().ID() != picture.Texture().ID() {
@@ -184,20 +133,9 @@ func NewMultiShape(parent pixelgl.Doer, shapes ...*Shape) *MultiShape {
 		}
 	}
 
-	var va *pixelgl.VertexArray
-
-	var indices []int
-	offset := 0
-	for _, shape := range shapes {
-		for _, i := range shape.va.Indices() {
-			indices = append(indices, offset+i)
-		}
-		offset += shape.VertexArray().NumVertices()
-	}
-
 	var vertices []map[pixelgl.Attr]interface{}
 	for _, shape := range shapes {
-		shapeVertices := shape.VertexArray().Vertices()
+		shapeVertices := shape.Vertices()
 
 		for vertex := range shapeVertices {
 			if pos, ok := shapeVertices[vertex][positionVec2]; ok {
@@ -221,22 +159,7 @@ func NewMultiShape(parent pixelgl.Doer, shapes ...*Shape) *MultiShape {
 		vertices = append(vertices, shapeVertices...)
 	}
 
-	parent.Do(func(ctx pixelgl.Context) {
-		var err error
-		va, err = pixelgl.NewVertexArray(
-			pixelgl.ContextHolder{Context: ctx},
-			ctx.Shader().VertexFormat(),
-			len(vertices),
-			indices,
-		)
-		if err != nil {
-			panic(errors.Wrap(err, "failed to create multishape"))
-		}
-	})
-
-	va.SetVertices(vertices)
-
-	return &MultiShape{NewShape(parent, picture, color.White, Position(0), va)}
+	return &MultiShape{NewShape(picture, color.White, Position(0), vertices)}
 }
 
 // Sprite is a picture that can be drawn on the screen. Optionally it can be color masked
@@ -252,25 +175,10 @@ type Sprite struct {
 
 // NewSprite creates a new sprite with the supplied picture. The sprite's size is the size of
 // the supplied picture.  If you want to change the sprite's size, change it's transform.
-func NewSprite(parent pixelgl.Doer, picture *Picture) *Sprite {
-	var va *pixelgl.VertexArray
-
-	parent.Do(func(ctx pixelgl.Context) {
-		var err error
-		va, err = pixelgl.NewVertexArray(
-			pixelgl.ContextHolder{Context: ctx},
-			ctx.Shader().VertexFormat(),
-			4,
-			[]int{0, 1, 2, 0, 2, 3},
-		)
-		if err != nil {
-			panic(errors.Wrap(err, "failed to create sprite"))
-		}
-	})
+func NewSprite(picture *Picture) *Sprite {
+	w, h := picture.Bounds().Size.XY()
 
 	vertices := make([]map[pixelgl.Attr]interface{}, 4)
-
-	w, h := picture.Bounds().Size.XY()
 	for i, p := range []Vec{V(0, 0), V(w, 0), V(w, h), V(0, h)} {
 		texCoord := V(
 			(picture.Bounds().X()+p.X())/float64(picture.Texture().Width()),
@@ -284,9 +192,16 @@ func NewSprite(parent pixelgl.Doer, picture *Picture) *Sprite {
 		}
 	}
 
-	va.SetVertices(vertices)
+	vertices = []map[pixelgl.Attr]interface{}{
+		vertices[0],
+		vertices[1],
+		vertices[2],
+		vertices[0],
+		vertices[2],
+		vertices[3],
+	}
 
-	return &Sprite{NewShape(parent, picture, color.White, Position(0), va)}
+	return &Sprite{NewShape(picture, color.White, Position(0), vertices)}
 }
 
 // LineColor a line shape (with sharp ends) filled with a single color.
@@ -296,26 +211,9 @@ type LineColor struct {
 	width float64
 }
 
-// NewLineColor creates a new line shape between points A and B filled with a single color. Parent
-// is an object that this shape belongs to, such as a window, or a graphics effect.
-func NewLineColor(parent pixelgl.Doer, c color.Color, a, b Vec, width float64) *LineColor {
-	var va *pixelgl.VertexArray
-
-	parent.Do(func(ctx pixelgl.Context) {
-		var err error
-		va, err = pixelgl.NewVertexArray(
-			pixelgl.ContextHolder{Context: ctx},
-			ctx.Shader().VertexFormat(),
-			4,
-			[]int{0, 1, 2, 1, 2, 3},
-		)
-		if err != nil {
-			panic(errors.Wrap(err, "failed to create line"))
-		}
-	})
-
+// NewLineColor creates a new line shape between points A and B filled with a single color.
+func NewLineColor(c color.Color, a, b Vec, width float64) *LineColor {
 	vertices := make([]map[pixelgl.Attr]interface{}, 4)
-
 	for i := 0; i < 4; i++ {
 		vertices[i] = map[pixelgl.Attr]interface{}{
 			colorVec4:    mgl32.Vec4{1, 1, 1, 1},
@@ -323,9 +221,16 @@ func NewLineColor(parent pixelgl.Doer, c color.Color, a, b Vec, width float64) *
 		}
 	}
 
-	va.SetVertices(vertices)
+	vertices = []map[pixelgl.Attr]interface{}{
+		vertices[0],
+		vertices[1],
+		vertices[2],
+		vertices[1],
+		vertices[2],
+		vertices[3],
+	}
 
-	lc := &LineColor{NewShape(parent, nil, c, Position(0), va), a, b, width}
+	lc := &LineColor{NewShape(nil, c, Position(0), vertices), a, b, width}
 	lc.setPoints()
 	return lc
 }
diff --git a/interface.go b/interface.go
new file mode 100644
index 0000000..6bfd693
--- /dev/null
+++ b/interface.go
@@ -0,0 +1,21 @@
+package pixel
+
+import "github.com/faiface/pixel/pixelgl"
+
+// Target is an OpenGL graphics destination such as a window, a canvas, and so on. Something that
+// you can draw on.
+type Target interface {
+	pixelgl.BeginEnder
+	Shader() *pixelgl.Shader
+
+	// MakeVertexArray returns a new vertex array drawable on the Target.
+	MakeVertexArray(vertices []map[pixelgl.Attr]interface{}) *pixelgl.VertexArray
+}
+
+// Drawer is anything that can be drawn. It's by no means a drawer inside your table.
+//
+// Drawer consists of a single methods: Draw. Draw takes a target and any number of transform
+// arguments. It's up to a Drawer to make sure that it draws correctly onto the provided target.
+type Drawer interface {
+	Draw(target Target, t ...Transform)
+}
diff --git a/picture.go b/picture.go
index 54f4e79..101551d 100644
--- a/picture.go
+++ b/picture.go
@@ -24,16 +24,19 @@ func NewPicture(img image.Image, smooth bool) *Picture {
 	rgba := image.NewRGBA(image.Rect(0, 0, img.Bounds().Dx(), img.Bounds().Dy()))
 	draw.Draw(rgba, rgba.Bounds(), img, img.Bounds().Min, draw.Src)
 
-	texture, err := pixelgl.NewTexture(
-		pixelgl.NoOpDoer,
-		img.Bounds().Dx(),
-		img.Bounds().Dy(),
-		smooth,
-		rgba.Pix,
-	)
-	if err != nil {
-		panic(errors.Wrap(err, "failed to create picture"))
-	}
+	var texture *pixelgl.Texture
+	pixelgl.Do(func() {
+		var err error
+		texture, err = pixelgl.NewTexture(
+			img.Bounds().Dx(),
+			img.Bounds().Dy(),
+			smooth,
+			rgba.Pix,
+		)
+		if err != nil {
+			panic(errors.Wrap(err, "failed to create picture"))
+		}
+	})
 
 	return &Picture{
 		texture: texture,
diff --git a/window.go b/window.go
index 4939d93..9879eec 100644
--- a/window.go
+++ b/window.go
@@ -2,7 +2,6 @@ package pixel
 
 import (
 	"image/color"
-	"sync"
 
 	"runtime"
 
@@ -58,10 +57,10 @@ type WindowConfig struct {
 
 // Window is a window handler. Use this type to manipulate a window (input, drawing, ...).
 type Window struct {
-	enabled       bool
-	window        *glfw.Window
-	config        WindowConfig
-	defaultShader *pixelgl.Shader
+	enabled bool
+	window  *glfw.Window
+	config  WindowConfig
+	shader  *pixelgl.Shader
 
 	// need to save these to correctly restore a fullscreen window
 	restore struct {
@@ -74,6 +73,8 @@ type Window struct {
 	}
 }
 
+var currentWindow *Window
+
 // NewWindow creates a new window with it's properties specified in the provided config.
 //
 // If window creation fails, an error is returned.
@@ -103,7 +104,11 @@ func NewWindow(config WindowConfig) (*Window, error) {
 		glfw.WindowHint(glfw.Maximized, bool2int[config.Maximized])
 		glfw.WindowHint(glfw.Samples, config.MSAASamples)
 
-		w.window, err = glfw.CreateWindow(int(config.Width), int(config.Height), config.Title, nil, nil)
+		var share *glfw.Window
+		if currentWindow != nil {
+			share = currentWindow.window
+		}
+		w.window, err = glfw.CreateWindow(int(config.Width), int(config.Height), config.Title, nil, share)
 		if err != nil {
 			return err
 		}
@@ -114,13 +119,11 @@ func NewWindow(config WindowConfig) (*Window, error) {
 		return nil, errors.Wrap(err, "creating window failed")
 	}
 
-	w.initInput()
-
-	w.SetFullscreen(config.Fullscreen)
+	pixelgl.Do(func() {
+		w.Begin()
+		defer w.End()
 
-	w.Do(func(pixelgl.Context) {
-		w.defaultShader, err = pixelgl.NewShader(
-			pixelgl.NoOpDoer,
+		w.shader, err = pixelgl.NewShader(
 			defaultVertexFormat,
 			defaultUniformFormat,
 			defaultVertexShader,
@@ -128,14 +131,21 @@ func NewWindow(config WindowConfig) (*Window, error) {
 		)
 
 		// default uniforms
-		w.defaultShader.SetUniformAttr(maskColorVec4, mgl32.Vec4{1, 1, 1, 1})
-		w.defaultShader.SetUniformAttr(transformMat3, mgl32.Ident3())
+		w.shader.Begin()
+		w.shader.SetUniformAttr(maskColorVec4, mgl32.Vec4{1, 1, 1, 1})
+		w.shader.SetUniformAttr(transformMat3, mgl32.Ident3())
+
+		// this is tricky, w.shader.End() is not needed here, because it will
+		// actually be ended by a deferred w.End() call
 	})
 	if err != nil {
 		w.Destroy()
 		return nil, errors.Wrap(err, "creating window failed")
 	}
 
+	w.initInput()
+	w.SetFullscreen(config.Fullscreen)
+
 	runtime.SetFinalizer(w, (*Window).Destroy)
 
 	return w, nil
@@ -150,37 +160,42 @@ func (w *Window) Destroy() {
 
 // Clear clears the window with a color.
 func (w *Window) Clear(c color.Color) {
-	w.Do(func(pixelgl.Context) {
-		pixelgl.DoNoBlock(func() {
-			c := NRGBAModel.Convert(c).(NRGBA)
-			gl.ClearColor(float32(c.R), float32(c.G), float32(c.B), float32(c.A))
-			gl.Clear(gl.COLOR_BUFFER_BIT)
-		})
+	pixelgl.DoNoBlock(func() {
+		w.Begin()
+		defer w.End()
+
+		c := NRGBAModel.Convert(c).(NRGBA)
+		gl.ClearColor(float32(c.R), float32(c.G), float32(c.B), float32(c.A))
+		gl.Clear(gl.COLOR_BUFFER_BIT)
 	})
 }
 
 // Update swaps buffers and polls events.
 func (w *Window) Update() {
-	w.Do(func(pixelgl.Context) {
-		pixelgl.Do(func() {
-			if w.config.VSync {
-				glfw.SwapInterval(1)
-			}
-			w.window.SwapBuffers()
-		})
-
-		w.updateInput()
-
-		pixelgl.Do(func() {
-			w, h := w.window.GetSize()
-			gl.Viewport(0, 0, int32(w), int32(h))
-		})
+	pixelgl.Do(func() {
+		w.Begin()
+		defer w.End()
+
+		if w.config.VSync {
+			glfw.SwapInterval(1)
+		}
+		w.window.SwapBuffers()
+	})
+
+	w.updateInput()
+
+	pixelgl.Do(func() {
+		w.Begin()
+		defer w.End()
+
+		w, h := w.window.GetSize()
+		gl.Viewport(0, 0, int32(w), int32(h))
 	})
 }
 
-// DefaultShader returns the default shader used by a window.
-func (w *Window) DefaultShader() *pixelgl.Shader {
-	return w.defaultShader
+// Shader returns the default shader used by a window.
+func (w *Window) Shader() *pixelgl.Shader {
+	return w.shader
 }
 
 // SetClosed sets the closed flag of a window.
@@ -327,33 +342,50 @@ func (w *Window) Restore() {
 	})
 }
 
-var currentWindow struct {
-	sync.Mutex
-	handler *Window
+// Begin makes the OpenGL context of a window current and binds it's shader.
+//
+// You usually do not need to use this method, however, you have to use it when you're
+// directly using this window's context (such as drawing on it using OpenGL).
+//
+// Note, that this method must be called inside the main OpenGL thread (pixelgl.Do/DoNoBlock/DoErr/DoVal).
+func (w *Window) Begin() {
+	if currentWindow != w {
+		w.window.MakeContextCurrent()
+		pixelgl.Init()
+		currentWindow = w
+	}
+	if w.shader != nil {
+		w.shader.Begin()
+	}
 }
 
-// Do makes the context of this window current, if it's not already, and executes sub.
-func (w *Window) Do(sub func(pixelgl.Context)) {
-	if !w.enabled {
-		currentWindow.Lock()
-		defer currentWindow.Unlock()
+// End unbinds the shader of a window.
+func (w *Window) End() {
+	if w.shader != nil {
+		w.shader.End()
+	}
+}
 
-		if currentWindow.handler != w {
-			pixelgl.Do(func() {
-				w.window.MakeContextCurrent()
-				pixelgl.Init()
-			})
-			currentWindow.handler = w
+// MakeVertexArray implements Target.
+func (w *Window) MakeVertexArray(vertices []map[pixelgl.Attr]interface{}) *pixelgl.VertexArray {
+	var va *pixelgl.VertexArray
+
+	pixelgl.Do(func() {
+		w.Begin()
+		defer w.End()
+
+		var err error
+		va, err = pixelgl.NewVertexArray(w.Shader(), len(vertices))
+		if err != nil {
+			panic(err)
 		}
-	}
 
-	w.enabled = true
-	if w.defaultShader != nil {
-		w.defaultShader.Do(sub)
-	} else {
-		sub(pixelgl.Context{})
-	}
-	w.enabled = false
+		va.Begin()
+		va.SetVertices(vertices)
+		va.End()
+	})
+
+	return va
 }
 
 var defaultVertexFormat = pixelgl.AttrFormat{
-- 
GitLab