175 lines
4.2 KiB
Go
175 lines
4.2 KiB
Go
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
//go:build darwin || linux || openbsd
|
||
|
// +build darwin linux openbsd
|
||
|
|
||
|
package gl
|
||
|
|
||
|
/*
|
||
|
#cgo ios LDFLAGS: -framework OpenGLES
|
||
|
#cgo darwin,!ios LDFLAGS: -framework OpenGL
|
||
|
#cgo linux LDFLAGS: -lGLESv2
|
||
|
#cgo openbsd LDFLAGS: -L/usr/X11R6/lib/ -lGLESv2
|
||
|
|
||
|
#cgo android CFLAGS: -Dos_android
|
||
|
#cgo ios CFLAGS: -Dos_ios
|
||
|
#cgo darwin,!ios CFLAGS: -Dos_macos
|
||
|
#cgo darwin CFLAGS: -DGL_SILENCE_DEPRECATION
|
||
|
#cgo linux CFLAGS: -Dos_linux
|
||
|
#cgo openbsd CFLAGS: -Dos_openbsd
|
||
|
|
||
|
#cgo openbsd CFLAGS: -I/usr/X11R6/include/
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include "work.h"
|
||
|
|
||
|
uintptr_t process(struct fnargs* cargs, char* parg0, char* parg1, char* parg2, int count) {
|
||
|
uintptr_t ret;
|
||
|
|
||
|
ret = processFn(&cargs[0], parg0);
|
||
|
if (count > 1) {
|
||
|
ret = processFn(&cargs[1], parg1);
|
||
|
}
|
||
|
if (count > 2) {
|
||
|
ret = processFn(&cargs[2], parg2);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
*/
|
||
|
import "C"
|
||
|
|
||
|
import "unsafe"
|
||
|
|
||
|
const workbufLen = 3
|
||
|
|
||
|
type context struct {
|
||
|
cptr uintptr
|
||
|
debug int32
|
||
|
|
||
|
workAvailable chan struct{}
|
||
|
|
||
|
// work is a queue of calls to execute.
|
||
|
work chan call
|
||
|
|
||
|
// retvalue is sent a return value when blocking calls complete.
|
||
|
// It is safe to use a global unbuffered channel here as calls
|
||
|
// cannot currently be made concurrently.
|
||
|
//
|
||
|
// TODO: the comment above about concurrent calls isn't actually true: package
|
||
|
// app calls package gl, but it has to do so in a separate goroutine, which
|
||
|
// means that its gl calls (which may be blocking) can race with other gl calls
|
||
|
// in the main program. We should make it safe to issue blocking gl calls
|
||
|
// concurrently, or get the gl calls out of package app, or both.
|
||
|
retvalue chan C.uintptr_t
|
||
|
|
||
|
cargs [workbufLen]C.struct_fnargs
|
||
|
parg [workbufLen]*C.char
|
||
|
}
|
||
|
|
||
|
func (ctx *context) WorkAvailable() <-chan struct{} { return ctx.workAvailable }
|
||
|
|
||
|
type context3 struct {
|
||
|
*context
|
||
|
}
|
||
|
|
||
|
// NewContext creates a cgo OpenGL context.
|
||
|
//
|
||
|
// See the Worker interface for more details on how it is used.
|
||
|
func NewContext() (Context, Worker) {
|
||
|
glctx := &context{
|
||
|
workAvailable: make(chan struct{}, 1),
|
||
|
work: make(chan call, workbufLen),
|
||
|
retvalue: make(chan C.uintptr_t),
|
||
|
}
|
||
|
if C.GLES_VERSION == "GL_ES_2_0" {
|
||
|
return glctx, glctx
|
||
|
}
|
||
|
return context3{glctx}, glctx
|
||
|
}
|
||
|
|
||
|
// Version returns a GL ES version string, either "GL_ES_2_0" or "GL_ES_3_0".
|
||
|
// Future versions of the gl package may return "GL_ES_3_1".
|
||
|
func Version() string {
|
||
|
return C.GLES_VERSION
|
||
|
}
|
||
|
|
||
|
func (ctx *context) enqueue(c call) uintptr {
|
||
|
ctx.work <- c
|
||
|
|
||
|
select {
|
||
|
case ctx.workAvailable <- struct{}{}:
|
||
|
default:
|
||
|
}
|
||
|
|
||
|
if c.blocking {
|
||
|
return uintptr(<-ctx.retvalue)
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
func (ctx *context) DoWork() {
|
||
|
queue := make([]call, 0, workbufLen)
|
||
|
for {
|
||
|
// Wait until at least one piece of work is ready.
|
||
|
// Accumulate work until a piece is marked as blocking.
|
||
|
select {
|
||
|
case w := <-ctx.work:
|
||
|
queue = append(queue, w)
|
||
|
default:
|
||
|
return
|
||
|
}
|
||
|
blocking := queue[len(queue)-1].blocking
|
||
|
enqueue:
|
||
|
for len(queue) < cap(queue) && !blocking {
|
||
|
select {
|
||
|
case w := <-ctx.work:
|
||
|
queue = append(queue, w)
|
||
|
blocking = queue[len(queue)-1].blocking
|
||
|
default:
|
||
|
break enqueue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process the queued GL functions.
|
||
|
for i, q := range queue {
|
||
|
ctx.cargs[i] = *(*C.struct_fnargs)(unsafe.Pointer(&q.args))
|
||
|
ctx.parg[i] = (*C.char)(q.parg)
|
||
|
}
|
||
|
ret := C.process(&ctx.cargs[0], ctx.parg[0], ctx.parg[1], ctx.parg[2], C.int(len(queue)))
|
||
|
|
||
|
// Cleanup and signal.
|
||
|
queue = queue[:0]
|
||
|
if blocking {
|
||
|
ctx.retvalue <- ret
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func init() {
|
||
|
if unsafe.Sizeof(C.GLint(0)) != unsafe.Sizeof(int32(0)) {
|
||
|
panic("GLint is not an int32")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// cString creates C string off the Go heap.
|
||
|
// ret is a *char.
|
||
|
func (ctx *context) cString(str string) (uintptr, func()) {
|
||
|
ptr := unsafe.Pointer(C.CString(str))
|
||
|
return uintptr(ptr), func() { C.free(ptr) }
|
||
|
}
|
||
|
|
||
|
// cString creates a pointer to a C string off the Go heap.
|
||
|
// ret is a **char.
|
||
|
func (ctx *context) cStringPtr(str string) (uintptr, func()) {
|
||
|
s, free := ctx.cString(str)
|
||
|
ptr := C.malloc(C.size_t(unsafe.Sizeof((*int)(nil))))
|
||
|
*(*uintptr)(ptr) = s
|
||
|
return uintptr(ptr), func() {
|
||
|
free()
|
||
|
C.free(ptr)
|
||
|
}
|
||
|
}
|