github.com/as/shiny@v0.8.2/gl/work.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build darwin linux openbsd
     6  
     7  package gl
     8  
     9  /*
    10  #cgo ios                LDFLAGS: -framework OpenGLES
    11  #cgo darwin,amd64,!ios  LDFLAGS: -framework OpenGL
    12  #cgo darwin,arm         LDFLAGS: -framework OpenGLES
    13  #cgo darwin,arm64       LDFLAGS: -framework OpenGLES
    14  #cgo linux              LDFLAGS: -lGLESv2
    15  #cgo openbsd            LDFLAGS: -L/usr/X11R6/lib/ -lGLESv2
    16  
    17  #cgo android            CFLAGS: -Dos_android
    18  #cgo ios                CFLAGS: -Dos_ios
    19  #cgo darwin,amd64,!ios  CFLAGS: -Dos_osx
    20  #cgo darwin,arm         CFLAGS: -Dos_ios
    21  #cgo darwin,arm64       CFLAGS: -Dos_ios
    22  #cgo linux              CFLAGS: -Dos_linux
    23  #cgo openbsd            CFLAGS: -Dos_openbsd
    24  
    25  #cgo openbsd            CFLAGS: -I/usr/X11R6/include/
    26  
    27  #include <stdint.h>
    28  #include "work.h"
    29  
    30  uintptr_t process(struct fnargs* cargs, char* parg0, char* parg1, char* parg2, int count) {
    31  	uintptr_t ret;
    32  
    33  	ret = processFn(&cargs[0], parg0);
    34  	if (count > 1) {
    35  		ret = processFn(&cargs[1], parg1);
    36  	}
    37  	if (count > 2) {
    38  		ret = processFn(&cargs[2], parg2);
    39  	}
    40  
    41  	return ret;
    42  }
    43  */
    44  import "C"
    45  
    46  import "unsafe"
    47  
    48  const workbufLen = 3
    49  
    50  type context struct {
    51  	cptr  uintptr
    52  	debug int32
    53  
    54  	workAvailable chan struct{}
    55  
    56  	// work is a queue of calls to execute.
    57  	work chan call
    58  
    59  	// retvalue is sent a return value when blocking calls complete.
    60  	// It is safe to use a global unbuffered channel here as calls
    61  	// cannot currently be made concurrently.
    62  	//
    63  	// TODO: the comment above about concurrent calls isn't actually true: package
    64  	// app calls package gl, but it has to do so in a separate goroutine, which
    65  	// means that its gl calls (which may be blocking) can race with other gl calls
    66  	// in the main program. We should make it safe to issue blocking gl calls
    67  	// concurrently, or get the gl calls out of package app, or both.
    68  	retvalue chan C.uintptr_t
    69  
    70  	cargs [workbufLen]C.struct_fnargs
    71  	parg  [workbufLen]*C.char
    72  }
    73  
    74  func (ctx *context) WorkAvailable() <-chan struct{} { return ctx.workAvailable }
    75  
    76  type context3 struct {
    77  	*context
    78  }
    79  
    80  // NewContext creates a cgo OpenGL context.
    81  //
    82  // See the Worker interface for more details on how it is used.
    83  func NewContext() (Context, Worker) {
    84  	glctx := &context{
    85  		workAvailable: make(chan struct{}, 1),
    86  		work:          make(chan call, workbufLen),
    87  		retvalue:      make(chan C.uintptr_t),
    88  	}
    89  	if C.GLES_VERSION == "GL_ES_2_0" {
    90  		return glctx, glctx
    91  	}
    92  	return context3{glctx}, glctx
    93  }
    94  
    95  // Version returns a GL ES version string, either "GL_ES_2_0" or "GL_ES_3_0".
    96  // Future versions of the gl package may return "GL_ES_3_1".
    97  func Version() string {
    98  	return C.GLES_VERSION
    99  }
   100  
   101  func (ctx *context) enqueue(c call) uintptr {
   102  	ctx.work <- c
   103  
   104  	select {
   105  	case ctx.workAvailable <- struct{}{}:
   106  	default:
   107  	}
   108  
   109  	if c.blocking {
   110  		return uintptr(<-ctx.retvalue)
   111  	}
   112  	return 0
   113  }
   114  
   115  func (ctx *context) DoWork() {
   116  	queue := make([]call, 0, workbufLen)
   117  	for {
   118  		// Wait until at least one piece of work is ready.
   119  		// Accumulate work until a piece is marked as blocking.
   120  		select {
   121  		case w := <-ctx.work:
   122  			queue = append(queue, w)
   123  		default:
   124  			return
   125  		}
   126  		blocking := queue[len(queue)-1].blocking
   127  	enqueue:
   128  		for len(queue) < cap(queue) && !blocking {
   129  			select {
   130  			case w := <-ctx.work:
   131  				queue = append(queue, w)
   132  				blocking = queue[len(queue)-1].blocking
   133  			default:
   134  				break enqueue
   135  			}
   136  		}
   137  
   138  		// Process the queued GL functions.
   139  		for i, q := range queue {
   140  			ctx.cargs[i] = *(*C.struct_fnargs)(unsafe.Pointer(&q.args))
   141  			ctx.parg[i] = (*C.char)(q.parg)
   142  		}
   143  		ret := C.process(&ctx.cargs[0], ctx.parg[0], ctx.parg[1], ctx.parg[2], C.int(len(queue)))
   144  
   145  		// Cleanup and signal.
   146  		queue = queue[:0]
   147  		if blocking {
   148  			ctx.retvalue <- ret
   149  		}
   150  	}
   151  }
   152  
   153  func init() {
   154  	if unsafe.Sizeof(C.GLint(0)) != unsafe.Sizeof(int32(0)) {
   155  		panic("GLint is not an int32")
   156  	}
   157  }
   158  
   159  // cString creates C string off the Go heap.
   160  // ret is a *char.
   161  func (ctx *context) cString(str string) (uintptr, func()) {
   162  	ptr := unsafe.Pointer(C.CString(str))
   163  	return uintptr(ptr), func() { C.free(ptr) }
   164  }
   165  
   166  // cString creates a pointer to a C string off the Go heap.
   167  // ret is a **char.
   168  func (ctx *context) cStringPtr(str string) (uintptr, func()) {
   169  	s, free := ctx.cString(str)
   170  	ptr := C.malloc(C.size_t(unsafe.Sizeof((*int)(nil))))
   171  	*(*uintptr)(ptr) = s
   172  	return uintptr(ptr), func() {
   173  		free()
   174  		C.free(ptr)
   175  	}
   176  }