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