mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-17 00:34:01 -04:00
170 lines
4.0 KiB
Go
170 lines
4.0 KiB
Go
package gpu
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
|
|
"github.com/AvengeMedia/DankMaterialShell/core/internal/proto/linux_dmabuf"
|
|
wlclient "github.com/AvengeMedia/dankgo/wayland/client"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
var eglVendorDirs = []string{"/etc/glvnd/egl_vendor.d", "/usr/share/glvnd/egl_vendor.d"}
|
|
|
|
const (
|
|
dmabufFeedbackMinVersion = 4
|
|
// v6 stops sending main_device
|
|
dmabufMainDeviceMaxVersion = 5
|
|
compositorProbeTimeout = 2 * time.Second
|
|
sysDevCharDir = "/sys/dev/char"
|
|
)
|
|
|
|
func EGLVendorEnv() []string {
|
|
if os.Getenv("__EGL_VENDOR_LIBRARY_FILENAMES") != "" || os.Getenv("__EGL_VENDOR_LIBRARY_DIRS") != "" {
|
|
return nil
|
|
}
|
|
mesa, nvidia := findEGLVendorICDs(eglVendorDirs)
|
|
if mesa == "" || nvidia == "" {
|
|
return nil
|
|
}
|
|
driver, err := compositorRenderDriver()
|
|
if err != nil {
|
|
log.Debugf("egl: keeping glvnd vendor defaults: %v", err)
|
|
return nil
|
|
}
|
|
if driver == "nvidia" {
|
|
return nil
|
|
}
|
|
log.Infof("egl: compositor renders on %s, loading mesa vendor only", driver)
|
|
return []string{"__EGL_VENDOR_LIBRARY_FILENAMES=" + mesa}
|
|
}
|
|
|
|
type eglVendorICD struct {
|
|
ICD struct {
|
|
LibraryPath string `json:"library_path"`
|
|
} `json:"ICD"`
|
|
}
|
|
|
|
func findEGLVendorICDs(dirs []string) (mesa, nvidia string) {
|
|
for _, dir := range dirs {
|
|
entries, err := filepath.Glob(filepath.Join(dir, "*.json"))
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, path := range entries {
|
|
switch eglVendorLibrary(path) {
|
|
case "libEGL_mesa":
|
|
if mesa == "" {
|
|
mesa = path
|
|
}
|
|
case "libEGL_nvidia":
|
|
if nvidia == "" {
|
|
nvidia = path
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return mesa, nvidia
|
|
}
|
|
|
|
func eglVendorLibrary(path string) string {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
var icd eglVendorICD
|
|
if err := json.Unmarshal(data, &icd); err != nil {
|
|
return ""
|
|
}
|
|
base := filepath.Base(icd.ICD.LibraryPath)
|
|
name, _, _ := strings.Cut(base, ".so")
|
|
return name
|
|
}
|
|
|
|
func compositorRenderDriver() (string, error) {
|
|
dev, err := compositorMainDevice()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return drmDeviceDriver(sysDevCharDir, dev)
|
|
}
|
|
|
|
func drmDeviceDriver(charDir string, dev uint64) (string, error) {
|
|
link := filepath.Join(charDir, fmt.Sprintf("%d:%d", unix.Major(dev), unix.Minor(dev)), "device", "driver")
|
|
target, err := os.Readlink(link)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Base(target), nil
|
|
}
|
|
|
|
func compositorMainDevice() (uint64, error) {
|
|
display, err := wlclient.Connect("")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
ctx := display.Context()
|
|
defer ctx.Close()
|
|
if err := ctx.SetReadDeadline(time.Now().Add(compositorProbeTimeout)); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
registry, err := display.GetRegistry()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
var dmabuf *linux_dmabuf.ZwpLinuxDmabufV1
|
|
registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
|
|
if e.Interface != linux_dmabuf.ZwpLinuxDmabufV1InterfaceName || e.Version < dmabufFeedbackMinVersion {
|
|
return
|
|
}
|
|
candidate := linux_dmabuf.NewZwpLinuxDmabufV1(ctx)
|
|
if err := registry.Bind(e.Name, e.Interface, min(e.Version, dmabufMainDeviceMaxVersion), candidate); err != nil {
|
|
return
|
|
}
|
|
dmabuf = candidate
|
|
})
|
|
if err := display.Roundtrip(); err != nil {
|
|
return 0, err
|
|
}
|
|
if dmabuf == nil {
|
|
return 0, errors.New("compositor lacks linux-dmabuf feedback")
|
|
}
|
|
defer dmabuf.Destroy()
|
|
|
|
feedback, err := dmabuf.GetDefaultFeedback()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer feedback.Destroy()
|
|
|
|
var device []byte
|
|
done := false
|
|
feedback.SetMainDeviceHandler(func(e linux_dmabuf.ZwpLinuxDmabufFeedbackV1MainDeviceEvent) {
|
|
device = e.Device
|
|
})
|
|
feedback.SetDoneHandler(func(linux_dmabuf.ZwpLinuxDmabufFeedbackV1DoneEvent) {
|
|
done = true
|
|
})
|
|
for !done {
|
|
if err := ctx.Dispatch(); err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
return mainDeviceFromEvent(device)
|
|
}
|
|
|
|
func mainDeviceFromEvent(device []byte) (uint64, error) {
|
|
if len(device) != 8 {
|
|
return 0, fmt.Errorf("main_device has %d bytes, want 8", len(device))
|
|
}
|
|
return binary.NativeEndian.Uint64(device), nil
|
|
}
|