work.go 4.2 KB

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