mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
d52632b475
loadEGL used sync.Once incorrectly: the error returned by loadDLLs was assigned to a local variable inside loadEGL, so on the second call Do would not run and the function would return nil even though the DLLs were never loaded. This caused a nil pointer dereference when callers proceeded to use _eglGetDisplay and other uninitialized function pointers. Fix by replacing the sync.Once with sync.OnceValue, which correctly caches the return value of loadDLLs across all calls. Signed-off-by: Kevin Yuan <farproc@gmail.com>
188 lines
5.4 KiB
Go
188 lines
5.4 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
package egl
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
syscall "golang.org/x/sys/windows"
|
|
)
|
|
|
|
type (
|
|
_EGLint int32
|
|
_EGLDisplay uintptr
|
|
_EGLConfig uintptr
|
|
_EGLContext uintptr
|
|
_EGLSurface uintptr
|
|
NativeDisplayType uintptr
|
|
NativeWindowType uintptr
|
|
)
|
|
|
|
var (
|
|
libEGL = syscall.DLL{}
|
|
_eglChooseConfig *syscall.Proc
|
|
_eglCreateContext *syscall.Proc
|
|
_eglCreateWindowSurface *syscall.Proc
|
|
_eglDestroyContext *syscall.Proc
|
|
_eglDestroySurface *syscall.Proc
|
|
_eglGetConfigAttrib *syscall.Proc
|
|
_eglGetDisplay *syscall.Proc
|
|
_eglGetError *syscall.Proc
|
|
_eglInitialize *syscall.Proc
|
|
_eglMakeCurrent *syscall.Proc
|
|
_eglReleaseThread *syscall.Proc
|
|
_eglSwapInterval *syscall.Proc
|
|
_eglSwapBuffers *syscall.Proc
|
|
_eglTerminate *syscall.Proc
|
|
_eglQueryString *syscall.Proc
|
|
_eglWaitClient *syscall.Proc
|
|
)
|
|
|
|
var loadOnce = sync.OnceValue(loadDLLs)
|
|
|
|
func loadEGL() error {
|
|
return loadOnce()
|
|
}
|
|
|
|
func loadDLLs() error {
|
|
if err := loadDLL(&libEGL, "libEGL.dll"); err != nil {
|
|
return err
|
|
}
|
|
|
|
procs := map[string]**syscall.Proc{
|
|
"eglChooseConfig": &_eglChooseConfig,
|
|
"eglCreateContext": &_eglCreateContext,
|
|
"eglCreateWindowSurface": &_eglCreateWindowSurface,
|
|
"eglDestroyContext": &_eglDestroyContext,
|
|
"eglDestroySurface": &_eglDestroySurface,
|
|
"eglGetConfigAttrib": &_eglGetConfigAttrib,
|
|
"eglGetDisplay": &_eglGetDisplay,
|
|
"eglGetError": &_eglGetError,
|
|
"eglInitialize": &_eglInitialize,
|
|
"eglMakeCurrent": &_eglMakeCurrent,
|
|
"eglReleaseThread": &_eglReleaseThread,
|
|
"eglSwapInterval": &_eglSwapInterval,
|
|
"eglSwapBuffers": &_eglSwapBuffers,
|
|
"eglTerminate": &_eglTerminate,
|
|
"eglQueryString": &_eglQueryString,
|
|
"eglWaitClient": &_eglWaitClient,
|
|
}
|
|
for name, proc := range procs {
|
|
p, err := libEGL.FindProc(name)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to locate %s in %s: %w", name, libEGL.Name, err)
|
|
}
|
|
*proc = p
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func loadDLL(dll *syscall.DLL, name string) error {
|
|
handle, err := syscall.LoadLibraryEx(name, 0, syscall.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
|
|
if err != nil {
|
|
return fmt.Errorf("egl: failed to load %s: %v", name, err)
|
|
}
|
|
dll.Handle = handle
|
|
dll.Name = name
|
|
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 syscall.BytePtrToString((*byte)(unsafe.Pointer(r)))
|
|
}
|
|
|
|
func eglWaitClient() bool {
|
|
r, _, _ := _eglWaitClient.Call()
|
|
return r != 0
|
|
}
|
|
|
|
// issue34474KeepAlive calls runtime.KeepAlive as a
|
|
// workaround for golang.org/issue/34474.
|
|
func issue34474KeepAlive(v any) {
|
|
runtime.KeepAlive(v)
|
|
}
|