2
0
mirror of synced 2025-02-22 06:28:04 +00:00
mobile/gl/work.go
David Crawshaw 27e7ed5775 gl: check for concurrent use of a gl.Context
It is an error to use a gl.Context concurrently. (Each Context
contains a state machine, so there is no way this can work.)
Up until now only the GL driver could report such a misuse, and
it generally does a poor job of it. Our command buffer adds some
small oppertunity for the race detector to help, but it makes it
harder to debug other kinds of driver crashes, so it is worth
disabling in debug mode.

To make it easy, compiling with -tags gldebug now inserts an
explicit check that there are no other active GL calls outstanding.

Adding something like:

	go func() {
		for {
			glctx.GetInteger(gl.ALPHA_BITS)
		}
	}()

to x/mobile/example/basic now reliably crashes when compiled
with -tags gldebug, providing a stack trace that includes both
misbehaving goroutines.

Change-Id: I3d85d94220bca2a15eaf2742f13b44db1f3428bf
Reviewed-on: https://go-review.googlesource.com/15180
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2015-09-30 15:23:41 +00:00

145 lines
3.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.
// +build darwin linux cgo
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"
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 }
// 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),
}
return glctx, glctx
}
type call struct {
args C.struct_fnargs
parg unsafe.Pointer
blocking bool
}
func (ctx *context) enqueue(c call) C.uintptr_t {
ctx.work <- c
select {
case ctx.workAvailable <- struct{}{}:
default:
}
if c.blocking {
return <-ctx.retvalue
}
return 0
}
func (ctx *context) DoWork() {
queue := make([]call, 0, len(ctx.work)) // len(ctx.work) == 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] = 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 glBoolean(b bool) C.uintptr_t {
if b {
return TRUE
}
return FALSE
}