zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

work.go (4277B)


      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
     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 }