replace reentrancy by enabled by more general binder

This commit is contained in:
faiface 2016-12-14 16:24:31 +01:00
parent cbfc770d4f
commit 1b01cba814
4 changed files with 161 additions and 279 deletions

View File

@ -16,9 +16,8 @@ type UniformFormat map[string]Attr
// Shader is an OpenGL shader program.
type Shader struct {
enabled bool
parent Doer
program uint32
program binder
vertexFormat VertexFormat
uniformFormat UniformFormat
uniforms map[Attr]int32
@ -30,6 +29,12 @@ type Shader struct {
func NewShader(parent Doer, vertexFormat VertexFormat, uniformFormat UniformFormat, vertexShader, fragmentShader string) (*Shader, error) {
shader := &Shader{
parent: parent,
program: binder{
restoreLoc: gl.CURRENT_PROGRAM,
bindFunc: func(obj uint32) {
gl.UseProgram(obj)
},
},
vertexFormat: vertexFormat,
uniformFormat: uniformFormat,
uniforms: make(map[Attr]int32),
@ -86,31 +91,31 @@ func NewShader(parent Doer, vertexFormat VertexFormat, uniformFormat UniformForm
// shader program
{
shader.program = gl.CreateProgram()
gl.AttachShader(shader.program, vshader)
gl.AttachShader(shader.program, fshader)
gl.LinkProgram(shader.program)
shader.program.obj = gl.CreateProgram()
gl.AttachShader(shader.program.obj, vshader)
gl.AttachShader(shader.program.obj, fshader)
gl.LinkProgram(shader.program.obj)
var (
success int32
infoLog = make([]byte, 512)
)
gl.GetProgramiv(shader.program, gl.LINK_STATUS, &success)
gl.GetProgramiv(shader.program.obj, gl.LINK_STATUS, &success)
if success == 0 {
gl.GetProgramInfoLog(shader.program, int32(len(infoLog)), nil, &infoLog[0])
gl.GetProgramInfoLog(shader.program.obj, int32(len(infoLog)), nil, &infoLog[0])
return fmt.Errorf("error linking shader program: %s", string(infoLog))
}
}
// uniforms
for uname, utype := range uniformFormat {
ulocation := gl.GetUniformLocation(shader.program, gl.Str(uname+"\x00"))
ulocation := gl.GetUniformLocation(shader.program.obj, gl.Str(uname+"\x00"))
if ulocation == -1 {
gl.DeleteProgram(shader.program)
gl.DeleteProgram(shader.program.obj)
return fmt.Errorf("shader does not contain uniform '%s'", uname)
}
if _, ok := shader.uniforms[utype]; ok {
gl.DeleteProgram(shader.program)
gl.DeleteProgram(shader.program.obj)
return fmt.Errorf("failed to create shader: invalid uniform format: duplicate uniform attribute")
}
shader.uniforms[utype] = ulocation
@ -130,14 +135,14 @@ func NewShader(parent Doer, vertexFormat VertexFormat, uniformFormat UniformForm
func (s *Shader) Delete() {
s.parent.Do(func(ctx Context) {
DoNoBlock(func() {
gl.DeleteProgram(s.program)
gl.DeleteProgram(s.program.obj)
})
})
}
// ID returns an OpenGL identifier of a shader program.
func (s *Shader) ID() uint32 {
return s.program
return s.program.obj
}
// VertexFormat returns the vertex attribute format of this shader. Do not change it.
@ -150,245 +155,94 @@ func (s *Shader) UniformFormat() UniformFormat {
return s.uniformFormat
}
// SetUniformInt sets the value of an uniform attribute Attr{Purpose: purpose, Type: Int}.
// SetUniformAttr sets the value of a uniform attribute of a shader.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformInt(purpose AttrPurpose, value int32) (ok bool) {
attr := Attr{Purpose: purpose, Type: Int}
// If the attribute does not exist, this method returns false.
//
// Supplied value must correspond to the type of the attribute. Correct types are these (right-hand is the type of the value):
// Attr{Type: Int}: int32
// Attr{Type: Float}: float32
// Attr{Type: Vec2}: mgl32.Vec2
// Attr{Type: Vec3}: mgl32.Vec3
// Attr{Type: Vec4}: mgl32.Vec4
// Attr{Type: Mat2}: mgl32.Mat2
// Attr{Type: Mat23}: mgl32.Mat2x3
// Attr{Type: Mat24}: mgl32.Mat2x4
// Attr{Type: Mat3}: mgl32.Mat3
// Attr{Type: Mat32}: mgl32.Mat3x2
// Attr{Type: Mat34}: mgl32.Mat3x4
// Attr{Type: Mat4}: mgl32.Mat4
// Attr{Type: Mat42}: mgl32.Mat4x2
// Attr{Type: Mat43}: mgl32.Mat4x3
// No other types are supported.
func (s *Shader) SetUniformAttr(attr Attr, value interface{}) (ok bool) {
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
gl.Uniform1i(s.uniforms[attr], value)
})
})
return true
}
// SetUniformFloat sets the value of an uniform attribute Attr{Purpose: purpose, Type: Float}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformFloat(purpose AttrPurpose, value float32) (ok bool) {
attr := Attr{Purpose: purpose, Type: Float}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
gl.Uniform1f(s.uniforms[attr], value)
})
})
return true
}
defer s.program.bind().restore()
// SetUniformVec2 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Vec2}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformVec2(purpose AttrPurpose, value mgl32.Vec2) (ok bool) {
attr := Attr{Purpose: purpose, Type: Vec2}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
gl.Uniform2f(s.uniforms[attr], value[0], value[1])
})
})
return true
}
// SetUniformVec3 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Vec3}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformVec3(purpose AttrPurpose, value mgl32.Vec3) (ok bool) {
attr := Attr{Purpose: purpose, Type: Vec3}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
gl.Uniform3f(s.uniforms[attr], value[0], value[1], value[2])
})
})
return true
}
// SetUniformVec4 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Vec4}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformVec4(purpose AttrPurpose, value mgl32.Vec4) (ok bool) {
attr := Attr{Purpose: purpose, Type: Vec4}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
gl.Uniform4f(s.uniforms[attr], value[0], value[1], value[2], value[3])
})
})
return true
}
// SetUniformMat2 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Mat2}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformMat2(purpose AttrPurpose, value mgl32.Mat2) (ok bool) {
attr := Attr{Purpose: purpose, Type: Mat2}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
switch attr.Type {
case Int:
value := value.(int32)
gl.Uniform1iv(s.uniforms[attr], 1, &value)
case Float:
value := value.(float32)
gl.Uniform1fv(s.uniforms[attr], 1, &value)
case Vec2:
value := value.(mgl32.Vec2)
gl.Uniform2fv(s.uniforms[attr], 1, &value[0])
case Vec3:
value := value.(mgl32.Vec3)
gl.Uniform3fv(s.uniforms[attr], 1, &value[0])
case Vec4:
value := value.(mgl32.Vec4)
gl.Uniform4fv(s.uniforms[attr], 1, &value[0])
case Mat2:
value := value.(mgl32.Mat2)
gl.UniformMatrix2fv(s.uniforms[attr], 1, false, &value[0])
})
})
return true
}
// SetUniformMat23 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Mat23}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformMat23(purpose AttrPurpose, value mgl32.Mat2x3) (ok bool) {
attr := Attr{Purpose: purpose, Type: Mat23}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
case Mat23:
value := value.(mgl32.Mat2x3)
gl.UniformMatrix2x3fv(s.uniforms[attr], 1, false, &value[0])
})
})
return true
}
// SetUniformMat24 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Mat24}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformMat24(purpose AttrPurpose, value mgl32.Mat2x4) (ok bool) {
attr := Attr{Purpose: purpose, Type: Mat24}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
case Mat24:
value := value.(mgl32.Mat2x4)
gl.UniformMatrix2x4fv(s.uniforms[attr], 1, false, &value[0])
})
})
return true
}
// SetUniformMat3 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Mat3}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformMat3(purpose AttrPurpose, value mgl32.Mat3) (ok bool) {
attr := Attr{Purpose: purpose, Type: Mat3}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
case Mat3:
value := value.(mgl32.Mat3)
gl.UniformMatrix3fv(s.uniforms[attr], 1, false, &value[0])
})
})
return true
}
// SetUniformMat32 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Mat32}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformMat32(purpose AttrPurpose, value mgl32.Mat3x2) (ok bool) {
attr := Attr{Purpose: purpose, Type: Mat32}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
case Mat32:
value := value.(mgl32.Mat3x2)
gl.UniformMatrix3x2fv(s.uniforms[attr], 1, false, &value[0])
})
})
return true
}
// SetUniformMat34 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Mat34}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformMat34(purpose AttrPurpose, value mgl32.Mat3x4) (ok bool) {
attr := Attr{Purpose: purpose, Type: Mat34}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
case Mat34:
value := value.(mgl32.Mat3x4)
gl.UniformMatrix3x4fv(s.uniforms[attr], 1, false, &value[0])
})
})
return true
}
// SetUniformMat4 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Mat4}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformMat4(purpose AttrPurpose, value mgl32.Mat4) (ok bool) {
attr := Attr{Purpose: purpose, Type: Mat4}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
case Mat4:
value := value.(mgl32.Mat4)
gl.UniformMatrix4fv(s.uniforms[attr], 1, false, &value[0])
})
})
return true
}
// SetUniformMat42 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Mat42}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformMat42(purpose AttrPurpose, value mgl32.Mat4x2) (ok bool) {
attr := Attr{Purpose: purpose, Type: Mat42}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
case Mat42:
value := value.(mgl32.Mat4x2)
gl.UniformMatrix4x2fv(s.uniforms[attr], 1, false, &value[0])
})
})
return true
}
// SetUniformMat43 sets the value of an uniform attribute Attr{Purpose: purpose, Type: Mat43}.
//
// Returns false if the attribute does not exist.
func (s *Shader) SetUniformMat43(purpose AttrPurpose, value mgl32.Mat4x3) (ok bool) {
attr := Attr{Purpose: purpose, Type: Mat43}
if _, ok := s.uniforms[attr]; !ok {
return false
}
s.Do(func(Context) {
DoNoBlock(func() {
case Mat43:
value := value.(mgl32.Mat4x3)
gl.UniformMatrix4x3fv(s.uniforms[attr], 1, false, &value[0])
default:
panic("set uniform attr: invalid attribute type")
}
})
})
return true
}
// 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
}
DoNoBlock(func() {
gl.UseProgram(s.program)
s.program.bind()
})
s.enabled = true
sub(ctx.WithShader(s))
s.enabled = false
DoNoBlock(func() {
gl.UseProgram(0)
s.program.restore()
})
})
}

View File

@ -4,9 +4,8 @@ import "github.com/go-gl/gl/v3.3-core/gl"
// Texture is an OpenGL texture.
type Texture struct {
enabled bool
parent Doer
tex uint32
tex binder
width, height int
}
@ -15,14 +14,20 @@ type Texture struct {
func NewTexture(parent Doer, width, height int, pixels []uint8) (*Texture, error) {
texture := &Texture{
parent: parent,
tex: binder{
restoreLoc: gl.TEXTURE_BINDING_2D,
bindFunc: func(obj uint32) {
gl.BindTexture(gl.TEXTURE_2D, obj)
},
},
width: width,
height: height,
}
parent.Do(func(ctx Context) {
Do(func() {
gl.GenTextures(1, &texture.tex)
gl.BindTexture(gl.TEXTURE_2D, texture.tex)
gl.GenTextures(1, &texture.tex.obj)
defer texture.tex.bind().restore()
gl.TexImage2D(
gl.TEXTURE_2D,
@ -37,8 +42,6 @@ func NewTexture(parent Doer, width, height int, pixels []uint8) (*Texture, error
)
gl.GenerateMipmap(gl.TEXTURE_2D)
gl.BindTexture(gl.TEXTURE_2D, 0)
})
})
@ -49,14 +52,14 @@ func NewTexture(parent Doer, width, height int, pixels []uint8) (*Texture, error
func (t *Texture) Delete() {
t.parent.Do(func(ctx Context) {
DoNoBlock(func() {
gl.DeleteTextures(1, &t.tex)
gl.DeleteTextures(1, &t.tex.obj)
})
})
}
// ID returns an OpenGL identifier of a texture.
func (t *Texture) ID() uint32 {
return t.tex
return t.tex.obj
}
// Width returns the width of a texture in pixels.
@ -72,18 +75,12 @@ func (t *Texture) Height() int {
// 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
}
DoNoBlock(func() {
gl.BindTexture(gl.TEXTURE_2D, t.tex)
t.tex.bind()
})
t.enabled = true
sub(ctx)
t.enabled = false
DoNoBlock(func() {
gl.BindTexture(gl.TEXTURE_2D, 0)
t.tex.restore()
})
})
}

27
pixelgl/util.go Normal file
View File

@ -0,0 +1,27 @@
package pixelgl
import "github.com/go-gl/gl/v3.3-core/gl"
type binder struct {
restoreLoc uint32
bindFunc func(uint32)
obj uint32
prev []uint32
}
func (b *binder) bind() *binder {
var prev int32
gl.GetIntegerv(b.restoreLoc, &prev)
b.prev = append(b.prev, uint32(prev))
b.bindFunc(b.obj)
return b
}
func (b *binder) restore() *binder {
b.bindFunc(b.prev[len(b.prev)-1])
b.prev = b.prev[:len(b.prev)-1]
return b
}

View File

@ -43,9 +43,8 @@ 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 {
enabled bool
parent Doer
vao, vbo, ebo uint32
vao, vbo, ebo binder
vertexNum, indexNum int
format VertexFormat
usage VertexUsage
@ -60,9 +59,27 @@ type VertexArray struct {
func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexNum int, indices []int) (*VertexArray, error) {
va := &VertexArray{
parent: parent,
vao: binder{
restoreLoc: gl.VERTEX_ARRAY_BINDING,
bindFunc: func(obj uint32) {
gl.BindVertexArray(obj)
},
},
vbo: binder{
restoreLoc: gl.ARRAY_BUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindBuffer(gl.ARRAY_BUFFER, obj)
},
},
ebo: binder{
restoreLoc: gl.ELEMENT_ARRAY_BUFFER_BINDING,
bindFunc: func(obj uint32) {
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj)
},
},
vertexNum: vertexNum,
format: format,
usage: usage,
vertexNum: vertexNum,
stride: format.Size(),
attrs: make(map[Attr]int),
}
@ -83,16 +100,17 @@ func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexN
parent.Do(func(ctx Context) {
Do(func() {
gl.GenVertexArrays(1, &va.vao)
gl.BindVertexArray(va.vao)
gl.GenVertexArrays(1, &va.vao.obj)
va.vao.bind()
gl.GenBuffers(1, &va.vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
gl.GenBuffers(1, &va.vbo.obj)
defer va.vbo.bind().restore()
emptyData := make([]byte, vertexNum*va.stride)
gl.BufferData(gl.ARRAY_BUFFER, len(emptyData), gl.Ptr(emptyData), uint32(usage))
gl.GenBuffers(1, &va.ebo)
gl.GenBuffers(1, &va.ebo.obj)
defer va.ebo.bind().restore()
offset := 0
for i, attr := range format {
@ -120,12 +138,7 @@ func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexN
offset += attr.Type.Size()
}
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, va.ebo) // need to bind EBO, so that VAO registers it
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
gl.BindVertexArray(0)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
va.vao.restore()
})
})
@ -138,15 +151,15 @@ func NewVertexArray(parent Doer, format VertexFormat, usage VertexUsage, vertexN
func (va *VertexArray) Delete() {
va.parent.Do(func(ctx Context) {
DoNoBlock(func() {
gl.DeleteVertexArrays(1, &va.vao)
gl.DeleteBuffers(1, &va.vbo)
gl.DeleteVertexArrays(1, &va.vao.obj)
gl.DeleteBuffers(1, &va.vbo.obj)
})
})
}
// ID returns an OpenGL identifier of a vertex array.
func (va *VertexArray) ID() uint32 {
return va.vao
return va.vao.obj
}
// VertexNum returns the number of vertices in a vertex array.
@ -184,9 +197,8 @@ func (va *VertexArray) SetIndices(indices []int) {
}
va.indexNum = len(indices32)
DoNoBlock(func() {
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, va.ebo)
defer va.ebo.bind().restore()
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, 4*len(indices32), gl.Ptr(indices32), uint32(va.usage))
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
})
}
@ -195,7 +207,7 @@ func (va *VertexArray) SetIndices(indices []int) {
// If the vertex attribute does not exist, this method returns false. If the vertex is out of range,
// this method panics.
//
// Supplied value must correspond to the type of the attribute. Correct types are these (righ-hand is the type of value):
// Supplied value must correspond to the type of the attribute. Correct types are these (righ-hand is the type of the value):
// Attr{Type: Float}: float32
// Attr{Type: Vec2}: mgl32.Vec2
// Attr{Type: Vec3}: mgl32.Vec3
@ -211,7 +223,7 @@ func (va *VertexArray) SetVertexAttr(vertex int, attr Attr, value interface{}) (
}
DoNoBlock(func() {
gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
defer va.vbo.bind().restore()
offset := va.stride*vertex + va.attrs[attr]
@ -228,9 +240,9 @@ func (va *VertexArray) SetVertexAttr(vertex int, attr Attr, value interface{}) (
case Vec4:
value := value.(mgl32.Vec4)
gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
default:
panic("set vertex attr: invalid attribute type")
}
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
})
return true
@ -251,8 +263,8 @@ func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok
return nil, false
}
DoNoBlock(func() {
gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
Do(func() {
defer va.vbo.bind().restore()
offset := va.stride*vertex + va.attrs[attr]
@ -274,8 +286,6 @@ func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok
gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
value = data
}
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
})
return value, true
@ -284,19 +294,13 @@ func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok
// 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
}
DoNoBlock(func() {
gl.BindVertexArray(va.vao)
va.vao.bind()
})
va.enabled = true
sub(ctx)
va.enabled = false
DoNoBlock(func() {
gl.DrawElements(gl.TRIANGLES, int32(va.indexNum), gl.UNSIGNED_INT, gl.PtrOffset(0))
gl.BindVertexArray(0)
va.vao.restore()
})
})
}