123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- // SPDX-License-Identifier: Unlicense OR MIT
- //go:build linux || windows || freebsd || openbsd
- // +build linux windows freebsd openbsd
- package egl
- import (
- "errors"
- "fmt"
- "runtime"
- "strings"
- "gioui.org/gpu"
- )
- type Context struct {
- disp _EGLDisplay
- eglCtx *eglContext
- eglSurf _EGLSurface
- }
- type eglContext struct {
- config _EGLConfig
- ctx _EGLContext
- visualID int
- srgb bool
- surfaceless bool
- }
- var (
- nilEGLDisplay _EGLDisplay
- nilEGLSurface _EGLSurface
- nilEGLContext _EGLContext
- nilEGLConfig _EGLConfig
- EGL_DEFAULT_DISPLAY NativeDisplayType
- )
- const (
- _EGL_ALPHA_SIZE = 0x3021
- _EGL_BLUE_SIZE = 0x3022
- _EGL_CONFIG_CAVEAT = 0x3027
- _EGL_CONTEXT_CLIENT_VERSION = 0x3098
- _EGL_DEPTH_SIZE = 0x3025
- _EGL_GL_COLORSPACE_KHR = 0x309d
- _EGL_GL_COLORSPACE_SRGB_KHR = 0x3089
- _EGL_GREEN_SIZE = 0x3023
- _EGL_EXTENSIONS = 0x3055
- _EGL_NATIVE_VISUAL_ID = 0x302e
- _EGL_NONE = 0x3038
- _EGL_OPENGL_ES2_BIT = 0x4
- _EGL_RED_SIZE = 0x3024
- _EGL_RENDERABLE_TYPE = 0x3040
- _EGL_SURFACE_TYPE = 0x3033
- _EGL_WINDOW_BIT = 0x4
- )
- func (c *Context) Release() {
- c.ReleaseSurface()
- if c.eglCtx != nil {
- eglDestroyContext(c.disp, c.eglCtx.ctx)
- c.eglCtx = nil
- }
- eglTerminate(c.disp)
- c.disp = nilEGLDisplay
- }
- func (c *Context) Present() error {
- if !eglSwapBuffers(c.disp, c.eglSurf) {
- return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError())
- }
- return nil
- }
- func NewContext(disp NativeDisplayType) (*Context, error) {
- if err := loadEGL(); err != nil {
- return nil, err
- }
- eglDisp := eglGetDisplay(disp)
- // eglGetDisplay can return EGL_NO_DISPLAY yet no error
- // (EGL_SUCCESS), in which case a default EGL display might be
- // available.
- if eglDisp == nilEGLDisplay {
- eglDisp = eglGetDisplay(EGL_DEFAULT_DISPLAY)
- }
- if eglDisp == nilEGLDisplay {
- return nil, fmt.Errorf("eglGetDisplay failed: 0x%x", eglGetError())
- }
- eglCtx, err := createContext(eglDisp)
- if err != nil {
- return nil, err
- }
- c := &Context{
- disp: eglDisp,
- eglCtx: eglCtx,
- }
- return c, nil
- }
- func (c *Context) RenderTarget() (gpu.RenderTarget, error) {
- return gpu.OpenGLRenderTarget{}, nil
- }
- func (c *Context) API() gpu.API {
- return gpu.OpenGL{}
- }
- func (c *Context) ReleaseSurface() {
- if c.eglSurf == nilEGLSurface {
- return
- }
- // Make sure any in-flight GL commands are complete.
- eglWaitClient()
- c.ReleaseCurrent()
- eglDestroySurface(c.disp, c.eglSurf)
- c.eglSurf = nilEGLSurface
- }
- func (c *Context) VisualID() int {
- return c.eglCtx.visualID
- }
- func (c *Context) CreateSurface(win NativeWindowType) error {
- eglSurf, err := createSurface(c.disp, c.eglCtx, win)
- c.eglSurf = eglSurf
- return err
- }
- func (c *Context) ReleaseCurrent() {
- if c.disp != nilEGLDisplay {
- eglMakeCurrent(c.disp, nilEGLSurface, nilEGLSurface, nilEGLContext)
- }
- }
- func (c *Context) MakeCurrent() error {
- // OpenGL contexts are implicit and thread-local. Lock the OS thread.
- runtime.LockOSThread()
- if c.eglSurf == nilEGLSurface && !c.eglCtx.surfaceless {
- return errors.New("no surface created yet EGL_KHR_surfaceless_context is not supported")
- }
- if !eglMakeCurrent(c.disp, c.eglSurf, c.eglSurf, c.eglCtx.ctx) {
- return fmt.Errorf("eglMakeCurrent error 0x%x", eglGetError())
- }
- return nil
- }
- func (c *Context) EnableVSync(enable bool) {
- if enable {
- eglSwapInterval(c.disp, 1)
- } else {
- eglSwapInterval(c.disp, 0)
- }
- }
- func hasExtension(exts []string, ext string) bool {
- for _, e := range exts {
- if ext == e {
- return true
- }
- }
- return false
- }
- func createContext(disp _EGLDisplay) (*eglContext, error) {
- major, minor, ret := eglInitialize(disp)
- if !ret {
- return nil, fmt.Errorf("eglInitialize failed: 0x%x", eglGetError())
- }
- // sRGB framebuffer support on EGL 1.5 or if EGL_KHR_gl_colorspace is supported.
- exts := strings.Split(eglQueryString(disp, _EGL_EXTENSIONS), " ")
- srgb := major > 1 || minor >= 5 || hasExtension(exts, "EGL_KHR_gl_colorspace")
- attribs := []_EGLint{
- _EGL_RENDERABLE_TYPE, _EGL_OPENGL_ES2_BIT,
- _EGL_SURFACE_TYPE, _EGL_WINDOW_BIT,
- _EGL_BLUE_SIZE, 8,
- _EGL_GREEN_SIZE, 8,
- _EGL_RED_SIZE, 8,
- _EGL_CONFIG_CAVEAT, _EGL_NONE,
- }
- if srgb {
- if runtime.GOOS == "linux" || runtime.GOOS == "android" {
- // Some Mesa drivers crash if an sRGB framebuffer is requested without alpha.
- // https://bugs.freedesktop.org/show_bug.cgi?id=107782.
- //
- // Also, some Android devices (Samsung S9) need alpha for sRGB to work.
- attribs = append(attribs, _EGL_ALPHA_SIZE, 8)
- }
- }
- attribs = append(attribs, _EGL_NONE)
- eglCfg, ret := eglChooseConfig(disp, attribs)
- if !ret {
- return nil, fmt.Errorf("eglChooseConfig failed: 0x%x", eglGetError())
- }
- if eglCfg == nilEGLConfig {
- supportsNoCfg := hasExtension(exts, "EGL_KHR_no_config_context")
- if !supportsNoCfg {
- return nil, errors.New("eglChooseConfig returned no configs")
- }
- }
- var visID _EGLint
- if eglCfg != nilEGLConfig {
- var ok bool
- visID, ok = eglGetConfigAttrib(disp, eglCfg, _EGL_NATIVE_VISUAL_ID)
- if !ok {
- return nil, errors.New("newContext: eglGetConfigAttrib for _EGL_NATIVE_VISUAL_ID failed")
- }
- }
- ctxAttribs := []_EGLint{
- _EGL_CONTEXT_CLIENT_VERSION, 3,
- _EGL_NONE,
- }
- eglCtx := eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs)
- if eglCtx == nilEGLContext {
- // Fall back to OpenGL ES 2 and rely on extensions.
- ctxAttribs := []_EGLint{
- _EGL_CONTEXT_CLIENT_VERSION, 2,
- _EGL_NONE,
- }
- eglCtx = eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs)
- if eglCtx == nilEGLContext {
- return nil, fmt.Errorf("eglCreateContext failed: 0x%x", eglGetError())
- }
- }
- return &eglContext{
- config: _EGLConfig(eglCfg),
- ctx: _EGLContext(eglCtx),
- visualID: int(visID),
- srgb: srgb,
- surfaceless: hasExtension(exts, "EGL_KHR_surfaceless_context"),
- }, nil
- }
- func createSurface(disp _EGLDisplay, eglCtx *eglContext, win NativeWindowType) (_EGLSurface, error) {
- var surfAttribs []_EGLint
- if eglCtx.srgb {
- surfAttribs = append(surfAttribs, _EGL_GL_COLORSPACE_KHR, _EGL_GL_COLORSPACE_SRGB_KHR)
- }
- surfAttribs = append(surfAttribs, _EGL_NONE)
- eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
- if eglSurf == nilEGLSurface && eglCtx.srgb {
- // Try again without sRGB.
- eglCtx.srgb = false
- surfAttribs = []_EGLint{_EGL_NONE}
- eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs)
- }
- if eglSurf == nilEGLSurface {
- return nilEGLSurface, fmt.Errorf("newContext: eglCreateWindowSurface failed 0x%x (sRGB=%v)", eglGetError(), eglCtx.srgb)
- }
- return eglSurf, nil
- }
|