go-opengl-pixel/pixelgl/thread.go

130 lines
3.0 KiB
Go

package pixelgl
import (
"errors"
"runtime"
"github.com/go-gl/gl/v2.1/gl"
)
// Due to the existance and usage of thread-local variables by OpenGL, it's recommended to
// execute all OpenGL calls from a single dedicated thread. This file defines functions to make
// it possible.
var (
callQueue = make(chan func())
//TODO: some OpenGL state variables will be here
)
func init() {
go func() {
runtime.LockOSThread()
for f := range callQueue {
getLastGLErr() // swallow unchecked errors
f()
}
}()
}
// Do executes a function inside a dedicated OpenGL thread.
// Do blocks until the function finishes.
//
// All OpenGL calls must be done in the dedicated thread.
func Do(f func()) {
done := make(chan bool)
callQueue <- func() {
f()
done <- true
}
<-done
}
// DoErr executes a function inside a dedicated OpenGL thread and returns an error to the called.
// DoErr blocks until the function finishes.
//
// All OpenGL calls must be done in the dedicated thread.
func DoErr(f func() error) error {
err := make(chan error)
callQueue <- func() {
err <- f()
}
return <-err
}
// DoVal executes a function inside a dedicated OpenGL thread and returns a value to the caller.
// DoVal blocks until the function finishes.
//
// All OpenGL calls must be done in the dedicated thread.
func DoVal(f func() interface{}) interface{} {
val := make(chan interface{})
callQueue <- func() {
val <- f()
}
return <-val
}
// DoGLErr is same as Do, but also return an error generated by OpenGL.
func DoGLErr(f func()) (gl error) {
glerr := make(chan error)
callQueue <- func() {
f()
glerr <- getLastGLErr()
}
return <-glerr
}
// DoErrGLErr is same as DoErr, but also returns an error generated by OpenGL.
func DoErrGLErr(f func() error) (_, gl error) {
err := make(chan error)
glerr := make(chan error)
callQueue <- func() {
err <- f()
glerr <- getLastGLErr()
}
return <-err, <-glerr
}
// DoValGLErr is same as DoVal, but also returns an error generated by OpenGL.
func DoValGLErr(f func() interface{}) (_ interface{}, gl error) {
val := make(chan interface{})
glerr := make(chan error)
callQueue <- func() {
val <- f()
glerr <- getLastGLErr()
}
return <-val, <-glerr
}
// getLastGLErr returns (and consumes) the last error generated by OpenGL.
// Don't use outside DoGLErr, DoErrGLErr and DoValGLErr.
func getLastGLErr() error {
err := uint32(gl.NO_ERROR)
for e := gl.GetError(); e != gl.NO_ERROR; e = gl.GetError() {
err = e
}
if err == gl.NO_ERROR {
return nil
}
switch err {
case gl.INVALID_ENUM:
return errors.New("invalid enum")
case gl.INVALID_VALUE:
return errors.New("invalid value")
case gl.INVALID_OPERATION:
return errors.New("invalid operation")
case gl.STACK_OVERFLOW:
return errors.New("stack overflow")
case gl.STACK_UNDERFLOW:
return errors.New("stack underflow")
case gl.OUT_OF_MEMORY:
return errors.New("out of memory")
case gl.INVALID_FRAMEBUFFER_OPERATION:
return errors.New("invalid framebuffer operation")
case gl.CONTEXT_LOST:
return errors.New("context lost")
default:
return errors.New("unknown error")
}
}