You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
NoiseTorch/vendor/gioui.org/app/internal/egl/egl.go

280 lines
7.1 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
// +build linux windows freebsd openbsd
package egl
import (
"errors"
"fmt"
"runtime"
"strings"
"gioui.org/app/internal/glimpl"
"gioui.org/app/internal/srgb"
"gioui.org/gpu/backend"
"gioui.org/gpu/gl"
)
type Context struct {
c *glimpl.Functions
disp _EGLDisplay
eglCtx *eglContext
eglSurf _EGLSurface
width, height int
refreshFBO bool
// For sRGB emulation.
srgbFBO *srgb.FBO
}
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() {
if c.srgbFBO != nil {
c.srgbFBO.Release()
c.srgbFBO = nil
}
c.ReleaseSurface()
if c.eglCtx != nil {
eglDestroyContext(c.disp, c.eglCtx.ctx)
eglTerminate(c.disp)
eglReleaseThread()
c.eglCtx = nil
}
c.disp = nilEGLDisplay
}
func (c *Context) Present() error {
if c.srgbFBO != nil {
c.srgbFBO.Blit()
}
if !eglSwapBuffers(c.disp, c.eglSurf) {
return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError())
}
if c.srgbFBO != nil {
c.srgbFBO.AfterPresent()
}
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,
c: new(glimpl.Functions),
}
return c, nil
}
func (c *Context) Functions() *glimpl.Functions {
return c.c
}
func (c *Context) Backend() (backend.Device, error) {
return gl.NewBackend(c.c)
}
func (c *Context) ReleaseSurface() {
if c.eglSurf == nilEGLSurface {
return
}
// Make sure any in-flight GL commands are complete.
c.c.Finish()
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, width, height int) error {
eglSurf, err := createSurface(c.disp, c.eglCtx, win)
c.eglSurf = eglSurf
c.width = width
c.height = height
c.refreshFBO = true
return err
}
func (c *Context) ReleaseCurrent() {
if c.disp != nilEGLDisplay {
eglMakeCurrent(c.disp, nilEGLSurface, nilEGLSurface, nilEGLContext)
}
}
func (c *Context) MakeCurrent() error {
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())
}
if c.eglCtx.srgb || c.eglSurf == nilEGLSurface {
return nil
}
if c.srgbFBO == nil {
var err error
c.srgbFBO, err = srgb.New(c.c)
if err != nil {
return err
}
}
if c.refreshFBO {
c.refreshFBO = false
return c.srgbFBO.Refresh(c.width, c.height)
}
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) needs alpha for sRGB to work.
attribs = append(attribs, _EGL_ALPHA_SIZE, 1)
}
// Only request a depth buffer if we're going to render directly to the framebuffer.
attribs = append(attribs, _EGL_DEPTH_SIZE, 16)
}
attribs = append(attribs, _EGL_NONE)
eglCfg, ret := eglChooseConfig(disp, attribs)
if !ret {
return nil, fmt.Errorf("eglChooseConfig failed: 0x%x", eglGetError())
}
if eglCfg == nilEGLConfig {
return nil, errors.New("eglChooseConfig returned 0 configs")
}
visID, ret := eglGetConfigAttrib(disp, eglCfg, _EGL_NATIVE_VISUAL_ID)
if !ret {
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
}