work.go 4.2 KB

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