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