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