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