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 }