2016-11-22 09:59:24 -06:00
|
|
|
package pixelgl
|
|
|
|
|
2016-11-23 11:50:49 -06:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"runtime"
|
|
|
|
|
2016-11-23 15:27:30 -06:00
|
|
|
"github.com/go-gl/gl/v3.3-core/gl"
|
2016-11-23 11:50:49 -06:00
|
|
|
)
|
2016-11-22 09:59:24 -06:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
2016-11-24 07:10:33 -06:00
|
|
|
var callQueue = make(chan func(), 32)
|
2016-11-22 09:59:24 -06:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
go func() {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
for f := range callQueue {
|
|
|
|
f()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2016-11-23 15:27:30 -06:00
|
|
|
// Init initializes OpenGL by loading the function pointers from the active OpenGL context.
|
2016-11-23 16:33:40 -06:00
|
|
|
// This function must be manually run inside the dedicated thread (Do, DoErr, DoVal, etc.).
|
2016-11-23 15:27:30 -06:00
|
|
|
//
|
|
|
|
// It must be called under the presence of an active OpenGL context, e.g., always after calling window.MakeContextCurrent().
|
|
|
|
// Also, always call this function when switching contexts.
|
|
|
|
func Init() {
|
2016-11-23 16:31:45 -06:00
|
|
|
err := gl.Init()
|
2016-11-23 15:27:30 -06:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-24 07:10:33 -06:00
|
|
|
// DoNoBlock executes a function inside a dedicated OpenGL thread.
|
|
|
|
// DoNoBlock does not wait until the function finishes.
|
|
|
|
func DoNoBlock(f func()) {
|
|
|
|
callQueue <- f
|
|
|
|
}
|
|
|
|
|
2016-11-22 09:59:24 -06:00
|
|
|
// 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
|
|
|
|
}
|
2016-11-23 11:50:49 -06:00
|
|
|
|
|
|
|
// 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() {
|
2016-11-23 16:12:23 -06:00
|
|
|
getLastGLErr() // swallow
|
2016-11-23 11:50:49 -06:00
|
|
|
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() {
|
2016-11-23 16:12:23 -06:00
|
|
|
getLastGLErr() // swallow
|
2016-11-23 11:50:49 -06:00
|
|
|
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() {
|
2016-11-23 16:12:23 -06:00
|
|
|
getLastGLErr() // swallow
|
2016-11-23 11:50:49 -06:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|