egl.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. //go:build linux || windows || freebsd || openbsd
  3. // +build linux windows freebsd openbsd
  4. package egl
  5. import (
  6. "errors"
  7. "fmt"
  8. "runtime"
  9. "strings"
  10. "gioui.org/gpu"
  11. )
  12. type Context struct {
  13. disp _EGLDisplay
  14. eglCtx *eglContext
  15. eglSurf _EGLSurface
  16. }
  17. type eglContext struct {
  18. config _EGLConfig
  19. ctx _EGLContext
  20. visualID int
  21. srgb bool
  22. surfaceless bool
  23. }
  24. var (
  25. nilEGLDisplay _EGLDisplay
  26. nilEGLSurface _EGLSurface
  27. nilEGLContext _EGLContext
  28. nilEGLConfig _EGLConfig
  29. EGL_DEFAULT_DISPLAY NativeDisplayType
  30. )
  31. const (
  32. _EGL_ALPHA_SIZE = 0x3021
  33. _EGL_BLUE_SIZE = 0x3022
  34. _EGL_CONFIG_CAVEAT = 0x3027
  35. _EGL_CONTEXT_CLIENT_VERSION = 0x3098
  36. _EGL_DEPTH_SIZE = 0x3025
  37. _EGL_GL_COLORSPACE_KHR = 0x309d
  38. _EGL_GL_COLORSPACE_SRGB_KHR = 0x3089
  39. _EGL_GREEN_SIZE = 0x3023
  40. _EGL_EXTENSIONS = 0x3055
  41. _EGL_NATIVE_VISUAL_ID = 0x302e
  42. _EGL_NONE = 0x3038
  43. _EGL_OPENGL_ES2_BIT = 0x4
  44. _EGL_RED_SIZE = 0x3024
  45. _EGL_RENDERABLE_TYPE = 0x3040
  46. _EGL_SURFACE_TYPE = 0x3033
  47. _EGL_WINDOW_BIT = 0x4
  48. )
  49. func (c *Context) Release() {
  50. c.ReleaseSurface()
  51. if c.eglCtx != nil {
  52. eglDestroyContext(c.disp, c.eglCtx.ctx)
  53. c.eglCtx = nil
  54. }
  55. eglTerminate(c.disp)
  56. c.disp = nilEGLDisplay
  57. }
  58. func (c *Context) Present() error {
  59. if !eglSwapBuffers(c.disp, c.eglSurf) {
  60. return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError())
  61. }
  62. return nil
  63. }
  64. func NewContext(disp NativeDisplayType) (*Context, error) {
  65. if err := loadEGL(); err != nil {
  66. return nil, err
  67. }
  68. eglDisp := eglGetDisplay(disp)
  69. // eglGetDisplay can return EGL_NO_DISPLAY yet no error
  70. // (EGL_SUCCESS), in which case a default EGL display might be
  71. // available.
  72. if eglDisp == nilEGLDisplay {
  73. eglDisp = eglGetDisplay(EGL_DEFAULT_DISPLAY)
  74. }
  75. if eglDisp == nilEGLDisplay {
  76. return nil, fmt.Errorf("eglGetDisplay failed: 0x%x", eglGetError())
  77. }
  78. eglCtx, err := createContext(eglDisp)
  79. if err != nil {
  80. return nil, err
  81. }
  82. c := &Context{
  83. disp: eglDisp,
  84. eglCtx: eglCtx,
  85. }
  86. return c, nil
  87. }
  88. func (c *Context) RenderTarget() (gpu.RenderTarget, error) {
  89. return gpu.OpenGLRenderTarget{}, nil
  90. }
  91. func (c *Context) API() gpu.API {
  92. return gpu.OpenGL{}
  93. }
  94. func (c *Context) ReleaseSurface() {
  95. if c.eglSurf == nilEGLSurface {
  96. return
  97. }
  98. // Make sure any in-flight GL commands are complete.
  99. eglWaitClient()
  100. c.ReleaseCurrent()
  101. eglDestroySurface(c.disp, c.eglSurf)
  102. c.eglSurf = nilEGLSurface
  103. }
  104. func (c *Context) VisualID() int {
  105. return c.eglCtx.visualID
  106. }
  107. func (c *Context) CreateSurface(win NativeWindowType) error {
  108. eglSurf, err := createSurface(c.disp, c.eglCtx, win)
  109. c.eglSurf = eglSurf
  110. return err
  111. }
  112. func (c *Context) ReleaseCurrent() {
  113. if c.disp != nilEGLDisplay {
  114. eglMakeCurrent(c.disp, nilEGLSurface, nilEGLSurface, nilEGLContext)
  115. }
  116. }
  117. func (c *Context) MakeCurrent() error {
  118. // OpenGL contexts are implicit and thread-local. Lock the OS thread.
  119. runtime.LockOSThread()
  120. if c.eglSurf == nilEGLSurface && !c.eglCtx.surfaceless {
  121. return errors.New("no surface created yet EGL_KHR_surfaceless_context is not supported")
  122. }
  123. if !eglMakeCurrent(c.disp, c.eglSurf, c.eglSurf, c.eglCtx.ctx) {
  124. return fmt.Errorf("eglMakeCurrent error 0x%x", eglGetError())
  125. }
  126. return nil
  127. }
  128. func (c *Context) EnableVSync(enable bool) {
  129. if enable {
  130. eglSwapInterval(c.disp, 1)
  131. } else {
  132. eglSwapInterval(c.disp, 0)
  133. }
  134. }
  135. func hasExtension(exts []string, ext string) bool {
  136. for _, e := range exts {
  137. if ext == e {
  138. return true
  139. }
  140. }
  141. return false
  142. }
  143. func createContext(disp _EGLDisplay) (*eglContext, error) {
  144. major, minor, ret := eglInitialize(disp)
  145. if !ret {
  146. return nil, fmt.Errorf("eglInitialize failed: 0x%x", eglGetError())
  147. }
  148. // sRGB framebuffer support on EGL 1.5 or if EGL_KHR_gl_colorspace is supported.
  149. exts := strings.Split(eglQueryString(disp, _EGL_EXTENSIONS), " ")
  150. srgb := major > 1 || minor >= 5 || hasExtension(exts, "EGL_KHR_gl_colorspace")
  151. attribs := []_EGLint{
  152. _EGL_RENDERABLE_TYPE, _EGL_OPENGL_ES2_BIT,
  153. _EGL_SURFACE_TYPE, _EGL_WINDOW_BIT,
  154. _EGL_BLUE_SIZE, 8,
  155. _EGL_GREEN_SIZE, 8,
  156. _EGL_RED_SIZE, 8,
  157. _EGL_CONFIG_CAVEAT, _EGL_NONE,
  158. }
  159. if srgb {
  160. if runtime.GOOS == "linux" || runtime.GOOS == "android" {
  161. // Some Mesa drivers crash if an sRGB framebuffer is requested without alpha.
  162. // https://bugs.freedesktop.org/show_bug.cgi?id=107782.
  163. //
  164. // Also, some Android devices (Samsung S9) need alpha for sRGB to work.
  165. attribs = append(attribs, _EGL_ALPHA_SIZE, 8)
  166. }
  167. }
  168. attribs = append(attribs, _EGL_NONE)
  169. eglCfg, ret := eglChooseConfig(disp, attribs)
  170. if !ret {
  171. return nil, fmt.Errorf("eglChooseConfig failed: 0x%x", eglGetError())
  172. }
  173. if eglCfg == nilEGLConfig {
  174. supportsNoCfg := hasExtension(exts, "EGL_KHR_no_config_context")
  175. if !supportsNoCfg {
  176. return nil, errors.New("eglChooseConfig returned no configs")
  177. }
  178. }
  179. var visID _EGLint
  180. if eglCfg != nilEGLConfig {
  181. var ok bool
  182. visID, ok = eglGetConfigAttrib(disp, eglCfg, _EGL_NATIVE_VISUAL_ID)
  183. if !ok {
  184. return nil, errors.New("newContext: eglGetConfigAttrib for _EGL_NATIVE_VISUAL_ID failed")
  185. }
  186. }
  187. ctxAttribs := []_EGLint{
  188. _EGL_CONTEXT_CLIENT_VERSION, 3,
  189. _EGL_NONE,
  190. }
  191. eglCtx := eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs)
  192. if eglCtx == nilEGLContext {
  193. // Fall back to OpenGL ES 2 and rely on extensions.
  194. ctxAttribs := []_EGLint{
  195. _EGL_CONTEXT_CLIENT_VERSION, 2,
  196. _EGL_NONE,
  197. }
  198. eglCtx = eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs)
  199. if eglCtx == nilEGLContext {
  200. return nil, fmt.Errorf("eglCreateContext failed: 0x%x", eglGetError())
  201. }
  202. }
  203. return &eglContext{
  204. config: _EGLConfig(eglCfg),
  205. ctx: _EGLContext(eglCtx),
  206. visualID: int(visID),
  207. srgb: srgb,
  208. surfaceless: hasExtension(exts, "EGL_KHR_surfaceless_context"),
  209. }, nil
  210. }
  211. func createSurface(disp _EGLDisplay, eglCtx *eglContext, win NativeWindowType) (_EGLSurface, error) {
  212. var surfAttribs []_EGLint
  213. if eglCtx.srgb {
  214. surfAttribs = append(surfAttribs, _EGL_GL_COLORSPACE_KHR, _EGL_GL_COLORSPACE_SRGB_KHR)
  215. }
  216. surfAttribs = append(surfAttribs, _EGL_NONE)
  217. eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
  218. if eglSurf == nilEGLSurface && eglCtx.srgb {
  219. // Try again without sRGB.
  220. eglCtx.srgb = false
  221. surfAttribs = []_EGLint{_EGL_NONE}
  222. eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
  223. }
  224. if eglSurf == nilEGLSurface {
  225. return nilEGLSurface, fmt.Errorf("newContext: eglCreateWindowSurface failed 0x%x (sRGB=%v)", eglGetError(), eglCtx.srgb)
  226. }
  227. return eglSurf, nil
  228. }