From e2b361a8cd2df931b63f00fa9223f5c1786db6f5 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Sun, 4 Dec 2016 20:28:50 +0100
Subject: [PATCH] make Do reentrant

---
 pixelgl/interface.go |  2 ++
 pixelgl/shader.go    |  7 +++++++
 pixelgl/texture.go   | 11 +++++++++--
 pixelgl/vertex.go    | 23 +++++++++++++++--------
 4 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/pixelgl/interface.go b/pixelgl/interface.go
index c079204..3dd71e6 100644
--- a/pixelgl/interface.go
+++ b/pixelgl/interface.go
@@ -27,6 +27,8 @@ package pixelgl
 // Also notice, that the functions are passing a Context around. This context contains the most important state variables.
 // Usually, you just pass it as you received it. If you want to pass a changed context to your child (e.g. your a shader),
 // use ctx.With* methods.
+//
+// If possible and makes sense, Do method should be reentrant.
 type Doer interface {
 	Do(sub func(Context))
 }
diff --git a/pixelgl/shader.go b/pixelgl/shader.go
index 2b24ebd..3023d8c 100644
--- a/pixelgl/shader.go
+++ b/pixelgl/shader.go
@@ -17,6 +17,7 @@ type UniformFormat map[string]Attr
 
 // Shader is an OpenGL shader program.
 type Shader struct {
+	enabled       bool
 	parent        Doer
 	program       uint32
 	vertexFormat  VertexFormat
@@ -383,6 +384,11 @@ func (s *Shader) SetUniformMat43(purpose AttrPurpose, value mgl32.Mat4x3) (ok bo
 // Do stars using a shader, executes sub, and stops using it.
 func (s *Shader) Do(sub func(Context)) {
 	s.parent.Do(func(ctx Context) {
+		if s.enabled {
+			sub(ctx.WithShader(s))
+			return
+		}
+		s.enabled = true
 		DoNoBlock(func() {
 			gl.UseProgram(s.program)
 		})
@@ -390,5 +396,6 @@ func (s *Shader) Do(sub func(Context)) {
 		DoNoBlock(func() {
 			gl.UseProgram(0)
 		})
+		s.enabled = false
 	})
 }
diff --git a/pixelgl/texture.go b/pixelgl/texture.go
index ae6d8a7..c79d8cf 100644
--- a/pixelgl/texture.go
+++ b/pixelgl/texture.go
@@ -7,8 +7,9 @@ import (
 
 // Texture is an OpenGL texture.
 type Texture struct {
-	parent Doer
-	tex    uint32
+	enabled bool
+	parent  Doer
+	tex     uint32
 }
 
 // NewTexture creates a new texture with the specified width and height.
@@ -63,6 +64,11 @@ func (t *Texture) ID() uint32 {
 // Do bind a texture, executes sub, and unbinds the texture.
 func (t *Texture) Do(sub func(Context)) {
 	t.parent.Do(func(ctx Context) {
+		if t.enabled {
+			sub(ctx)
+			return
+		}
+		t.enabled = true
 		DoNoBlock(func() {
 			gl.BindTexture(gl.TEXTURE_2D, t.tex)
 		})
@@ -70,5 +76,6 @@ func (t *Texture) Do(sub func(Context)) {
 		DoNoBlock(func() {
 			gl.BindTexture(gl.TEXTURE_2D, 0)
 		})
+		t.enabled = false
 	})
 }
diff --git a/pixelgl/vertex.go b/pixelgl/vertex.go
index f112dae..25012d4 100644
--- a/pixelgl/vertex.go
+++ b/pixelgl/vertex.go
@@ -69,14 +69,15 @@ const (
 // VertexArray is an OpenGL vertex array object that also holds it's own vertex buffer object.
 // From the user's points of view, VertexArray is an array of vertices that can be drawn.
 type VertexArray struct {
-	parent Doer
-	vao    uint32
-	vbo    uint32
-	format VertexFormat
-	stride int
-	count  int
-	attrs  map[Attr]int
-	mode   VertexDrawMode
+	enabled bool
+	parent  Doer
+	vao     uint32
+	vbo     uint32
+	format  VertexFormat
+	stride  int
+	count   int
+	attrs   map[Attr]int
+	mode    VertexDrawMode
 }
 
 // NewVertexArray creates a new empty vertex array and wraps another Doer around it.
@@ -341,6 +342,11 @@ func (va *VertexArray) SetVertexAttributeVec4(vertex int, purpose AttrPurpose, v
 // Do binds a vertex arrray and it's associated vertex buffer, executes sub, and unbinds the vertex array and it's vertex buffer.
 func (va *VertexArray) Do(sub func(Context)) {
 	va.parent.Do(func(ctx Context) {
+		if va.enabled {
+			sub(ctx)
+			return
+		}
+		va.enabled = true
 		DoNoBlock(func() {
 			gl.BindVertexArray(va.vao)
 			gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
@@ -351,5 +357,6 @@ func (va *VertexArray) Do(sub func(Context)) {
 			gl.BindBuffer(gl.ARRAY_BUFFER, 0)
 			gl.BindVertexArray(0)
 		})
+		va.enabled = false
 	})
 }
-- 
GitLab