2
0
mirror of synced 2025-02-23 14:58:12 +00:00
mobile/gl/work.go
David Crawshaw 068a51c195 gl: batch calls onto a dedicated context thread
All GL function calls fill out a C.struct_fnargs and drop it on the
work queue. The Start function drains the work queue and hands
over a batch of calls to C.process which runs them. This allows
multiple GL calls to be executed in a single cgo call.

A GL call is marked as blocking if it returns a value, or if it
takes a Go pointer. In this case the call will not return until
C.process sends a value on the retvalue channel.

Change-Id: I4c76b2a8ad55f57b0c98d200d0fb708d4634e042
Reviewed-on: https://go-review.googlesource.com/10452
Reviewed-by: Nigel Tao <nigeltao@golang.org>
2015-06-01 15:35:03 +00:00

131 lines
2.6 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.
package gl
/*
#cgo darwin,amd64 LDFLAGS: -framework OpenGL
#cgo darwin,arm LDFLAGS: -framework OpenGLES
#cgo darwin,arm64 LDFLAGS: -framework OpenGLES
#cgo linux LDFLAGS: -lGLESv2
#cgo darwin,amd64 CFLAGS: -Dos_osx
#cgo darwin,arm CFLAGS: -Dos_ios
#cgo darwin,arm64 CFLAGS: -Dos_ios
#cgo linux CFLAGS: -Dos_linux
#include <stdint.h>
#include "work.h"
struct fnargs cargs[10];
uintptr_t ret;
void process(int count) {
int i;
for (i = 0; i < count; i++) {
processFn(&cargs[i]);
}
}
*/
import "C"
import "runtime"
// work is a queue of calls to execute.
var work = make(chan call, 10)
// 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.
var retvalue = make(chan C.uintptr_t)
type call struct {
blocking bool
fn func()
args C.struct_fnargs
}
// Do calls fn on the OS thread with the GL context.
func Do(fn func()) {
work <- call{
fn: fn,
blocking: true,
}
<-retvalue
}
// Stop stops the current GL processing.
func Stop() {
var call call
call.blocking = true
call.args.fn = C.glfnStop
work <- call
<-retvalue
}
// Start executes GL functions on a fixed OS thread, starting with initCtx.
// It blocks until Stop is called. Typical use:
//
// go gl.Start(func() {
// // establish a GL context, using for example, EGL.
// })
//
// // long running GL calls from any goroutine
//
// gl.Stop()
func Start(initCtx func()) {
runtime.LockOSThread()
initCtx()
queue := make([]call, 0, len(work))
for {
// Wait until at least one piece of work is ready.
// Accumulate work until a piece is marked as blocking.
select {
case w := <-work:
queue = append(queue, w)
}
blocking := queue[len(queue)-1].blocking
enqueue:
for len(queue) < cap(queue) && !blocking {
select {
case w := <-work:
queue = append(queue, w)
blocking = queue[len(queue)-1].blocking
default:
break enqueue
}
}
// Process the queued GL functions.
fn := queue[len(queue)-1].fn
stop := queue[len(queue)-1].args.fn == C.glfnStop
if fn != nil || stop {
queue = queue[:len(queue)-1]
}
for i := range queue {
C.cargs[i] = queue[i].args
}
C.process(C.int(len(queue)))
if fn != nil {
fn()
}
// Cleanup and signal.
queue = queue[:0]
if blocking {
retvalue <- C.ret
}
if stop {
return
}
}
}
func glBoolean(b bool) C.uintptr_t {
if b {
return TRUE
}
return FALSE
}