internal/egl: move app/internal/egl

We're about to move package app/headless to gpu/headless, and it needs
the egl package.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-03-05 15:52:27 +01:00
parent 86d17efc2c
commit 3af4e6accf
8 changed files with 5 additions and 5 deletions
+287
View File
@@ -0,0 +1,287 @@
// SPDX-License-Identifier: Unlicense OR MIT
// +build linux windows freebsd openbsd
package egl
import (
"errors"
"fmt"
"runtime"
"strings"
"gioui.org/gpu"
"gioui.org/internal/glimpl"
"gioui.org/internal/srgb"
)
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)
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
}
f, err := glimpl.NewFunctions(nil)
if err != nil {
return nil, err
}
c := &Context{
disp: eglDisp,
eglCtx: eglCtx,
c: f,
}
return c, 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.
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(nil)
if err != nil {
c.ReleaseCurrent()
return err
}
}
if c.refreshFBO {
c.refreshFBO = false
if err := c.srgbFBO.Refresh(c.width, c.height); err != nil {
c.ReleaseCurrent()
return err
}
}
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, 8)
}
// 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 {
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
}
+104
View File
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: Unlicense OR MIT
// +build linux freebsd openbsd
package egl
/*
#cgo linux,!android pkg-config: egl
#cgo freebsd openbsd android LDFLAGS: -lEGL
#cgo freebsd CFLAGS: -I/usr/local/include
#cgo freebsd LDFLAGS: -L/usr/local/lib
#cgo openbsd CFLAGS: -I/usr/X11R6/include
#cgo openbsd LDFLAGS: -L/usr/X11R6/lib
#cgo CFLAGS: -DEGL_NO_X11
#include <EGL/egl.h>
#include <EGL/eglext.h>
*/
import "C"
type (
_EGLint = C.EGLint
_EGLDisplay = C.EGLDisplay
_EGLConfig = C.EGLConfig
_EGLContext = C.EGLContext
_EGLSurface = C.EGLSurface
NativeDisplayType = C.EGLNativeDisplayType
NativeWindowType = C.EGLNativeWindowType
)
func loadEGL() error {
return nil
}
func eglChooseConfig(disp _EGLDisplay, attribs []_EGLint) (_EGLConfig, bool) {
var cfg C.EGLConfig
var ncfg C.EGLint
if C.eglChooseConfig(disp, &attribs[0], &cfg, 1, &ncfg) != C.EGL_TRUE {
return nilEGLConfig, false
}
return _EGLConfig(cfg), true
}
func eglCreateContext(disp _EGLDisplay, cfg _EGLConfig, shareCtx _EGLContext, attribs []_EGLint) _EGLContext {
ctx := C.eglCreateContext(disp, cfg, shareCtx, &attribs[0])
return _EGLContext(ctx)
}
func eglDestroySurface(disp _EGLDisplay, surf _EGLSurface) bool {
return C.eglDestroySurface(disp, surf) == C.EGL_TRUE
}
func eglDestroyContext(disp _EGLDisplay, ctx _EGLContext) bool {
return C.eglDestroyContext(disp, ctx) == C.EGL_TRUE
}
func eglGetConfigAttrib(disp _EGLDisplay, cfg _EGLConfig, attr _EGLint) (_EGLint, bool) {
var val _EGLint
ret := C.eglGetConfigAttrib(disp, cfg, attr, &val)
return val, ret == C.EGL_TRUE
}
func eglGetError() _EGLint {
return C.eglGetError()
}
func eglInitialize(disp _EGLDisplay) (_EGLint, _EGLint, bool) {
var maj, min _EGLint
ret := C.eglInitialize(disp, &maj, &min)
return maj, min, ret == C.EGL_TRUE
}
func eglMakeCurrent(disp _EGLDisplay, draw, read _EGLSurface, ctx _EGLContext) bool {
return C.eglMakeCurrent(disp, draw, read, ctx) == C.EGL_TRUE
}
func eglReleaseThread() bool {
return C.eglReleaseThread() == C.EGL_TRUE
}
func eglSwapBuffers(disp _EGLDisplay, surf _EGLSurface) bool {
return C.eglSwapBuffers(disp, surf) == C.EGL_TRUE
}
func eglSwapInterval(disp _EGLDisplay, interval _EGLint) bool {
return C.eglSwapInterval(disp, interval) == C.EGL_TRUE
}
func eglTerminate(disp _EGLDisplay) bool {
return C.eglTerminate(disp) == C.EGL_TRUE
}
func eglQueryString(disp _EGLDisplay, name _EGLint) string {
return C.GoString(C.eglQueryString(disp, name))
}
func eglGetDisplay(disp NativeDisplayType) _EGLDisplay {
return C.eglGetDisplay(disp)
}
func eglCreateWindowSurface(disp _EGLDisplay, conf _EGLConfig, win NativeWindowType, attribs []_EGLint) _EGLSurface {
eglSurf := C.eglCreateWindowSurface(disp, conf, win, &attribs[0])
return eglSurf
}
+164
View File
@@ -0,0 +1,164 @@
// SPDX-License-Identifier: Unlicense OR MIT
package egl
import (
"fmt"
"runtime"
"sync"
"unsafe"
syscall "golang.org/x/sys/windows"
"gioui.org/internal/glimpl"
gunsafe "gioui.org/internal/unsafe"
)
type (
_EGLint int32
_EGLDisplay uintptr
_EGLConfig uintptr
_EGLContext uintptr
_EGLSurface uintptr
NativeDisplayType uintptr
NativeWindowType uintptr
)
var (
libEGL = syscall.NewLazyDLL("libEGL.dll")
_eglChooseConfig = libEGL.NewProc("eglChooseConfig")
_eglCreateContext = libEGL.NewProc("eglCreateContext")
_eglCreateWindowSurface = libEGL.NewProc("eglCreateWindowSurface")
_eglDestroyContext = libEGL.NewProc("eglDestroyContext")
_eglDestroySurface = libEGL.NewProc("eglDestroySurface")
_eglGetConfigAttrib = libEGL.NewProc("eglGetConfigAttrib")
_eglGetDisplay = libEGL.NewProc("eglGetDisplay")
_eglGetError = libEGL.NewProc("eglGetError")
_eglInitialize = libEGL.NewProc("eglInitialize")
_eglMakeCurrent = libEGL.NewProc("eglMakeCurrent")
_eglReleaseThread = libEGL.NewProc("eglReleaseThread")
_eglSwapInterval = libEGL.NewProc("eglSwapInterval")
_eglSwapBuffers = libEGL.NewProc("eglSwapBuffers")
_eglTerminate = libEGL.NewProc("eglTerminate")
_eglQueryString = libEGL.NewProc("eglQueryString")
)
var loadOnce sync.Once
func loadEGL() error {
var err error
loadOnce.Do(func() {
err = loadDLLs()
})
return err
}
func loadDLLs() error {
if err := loadDLL(libEGL, "libEGL.dll"); err != nil {
return err
}
if err := loadDLL(glimpl.LibGLESv2, "libGLESv2.dll"); err != nil {
return err
}
// d3dcompiler_47.dll is needed internally for shader compilation to function.
return loadDLL(syscall.NewLazyDLL("d3dcompiler_47.dll"), "d3dcompiler_47.dll")
}
func loadDLL(dll *syscall.LazyDLL, name string) error {
err := dll.Load()
if err != nil {
return fmt.Errorf("egl: failed to load %s: %v", name, err)
}
return nil
}
func eglChooseConfig(disp _EGLDisplay, attribs []_EGLint) (_EGLConfig, bool) {
var cfg _EGLConfig
var ncfg _EGLint
a := &attribs[0]
r, _, _ := _eglChooseConfig.Call(uintptr(disp), uintptr(unsafe.Pointer(a)), uintptr(unsafe.Pointer(&cfg)), 1, uintptr(unsafe.Pointer(&ncfg)))
issue34474KeepAlive(a)
return cfg, r != 0
}
func eglCreateContext(disp _EGLDisplay, cfg _EGLConfig, shareCtx _EGLContext, attribs []_EGLint) _EGLContext {
a := &attribs[0]
c, _, _ := _eglCreateContext.Call(uintptr(disp), uintptr(cfg), uintptr(shareCtx), uintptr(unsafe.Pointer(a)))
issue34474KeepAlive(a)
return _EGLContext(c)
}
func eglCreateWindowSurface(disp _EGLDisplay, cfg _EGLConfig, win NativeWindowType, attribs []_EGLint) _EGLSurface {
a := &attribs[0]
s, _, _ := _eglCreateWindowSurface.Call(uintptr(disp), uintptr(cfg), uintptr(win), uintptr(unsafe.Pointer(a)))
issue34474KeepAlive(a)
return _EGLSurface(s)
}
func eglDestroySurface(disp _EGLDisplay, surf _EGLSurface) bool {
r, _, _ := _eglDestroySurface.Call(uintptr(disp), uintptr(surf))
return r != 0
}
func eglDestroyContext(disp _EGLDisplay, ctx _EGLContext) bool {
r, _, _ := _eglDestroyContext.Call(uintptr(disp), uintptr(ctx))
return r != 0
}
func eglGetConfigAttrib(disp _EGLDisplay, cfg _EGLConfig, attr _EGLint) (_EGLint, bool) {
var val uintptr
r, _, _ := _eglGetConfigAttrib.Call(uintptr(disp), uintptr(cfg), uintptr(attr), uintptr(unsafe.Pointer(&val)))
return _EGLint(val), r != 0
}
func eglGetDisplay(disp NativeDisplayType) _EGLDisplay {
d, _, _ := _eglGetDisplay.Call(uintptr(disp))
return _EGLDisplay(d)
}
func eglGetError() _EGLint {
e, _, _ := _eglGetError.Call()
return _EGLint(e)
}
func eglInitialize(disp _EGLDisplay) (_EGLint, _EGLint, bool) {
var maj, min uintptr
r, _, _ := _eglInitialize.Call(uintptr(disp), uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min)))
return _EGLint(maj), _EGLint(min), r != 0
}
func eglMakeCurrent(disp _EGLDisplay, draw, read _EGLSurface, ctx _EGLContext) bool {
r, _, _ := _eglMakeCurrent.Call(uintptr(disp), uintptr(draw), uintptr(read), uintptr(ctx))
return r != 0
}
func eglReleaseThread() bool {
r, _, _ := _eglReleaseThread.Call()
return r != 0
}
func eglSwapInterval(disp _EGLDisplay, interval _EGLint) bool {
r, _, _ := _eglSwapInterval.Call(uintptr(disp), uintptr(interval))
return r != 0
}
func eglSwapBuffers(disp _EGLDisplay, surf _EGLSurface) bool {
r, _, _ := _eglSwapBuffers.Call(uintptr(disp), uintptr(surf))
return r != 0
}
func eglTerminate(disp _EGLDisplay) bool {
r, _, _ := _eglTerminate.Call(uintptr(disp))
return r != 0
}
func eglQueryString(disp _EGLDisplay, name _EGLint) string {
r, _, _ := _eglQueryString.Call(uintptr(disp), uintptr(name))
return gunsafe.GoString(gunsafe.SliceOf(r))
}
// issue34474KeepAlive calls runtime.KeepAlive as a
// workaround for golang.org/issue/34474.
func issue34474KeepAlive(v interface{}) {
runtime.KeepAlive(v)
}