From a300101d3f1a35b1f63def55815e4c15d145906d Mon Sep 17 00:00:00 2001 From: Ikalco <73481042+ikalco@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:33:07 -0600 Subject: [PATCH] consolidate EGL state and initialization --- include/aquamarine/renderer/EGLRenderer.hpp | 42 +- src/backend/drm/DRM.cpp | 4 +- src/renderer/EGLRenderer.cpp | 511 ++++++++++++++------ 3 files changed, 388 insertions(+), 169 deletions(-) diff --git a/include/aquamarine/renderer/EGLRenderer.hpp b/include/aquamarine/renderer/EGLRenderer.hpp index 8f20cf4..5b9a755 100644 --- a/include/aquamarine/renderer/EGLRenderer.hpp +++ b/include/aquamarine/renderer/EGLRenderer.hpp @@ -44,8 +44,10 @@ namespace Aquamarine { class CEGLRenderer { public: ~CEGLRenderer(); - static Hyprutils::Memory::CSharedPointer attempt(Hyprutils::Memory::CSharedPointer allocator_, - Hyprutils::Memory::CSharedPointer backend_); + + static Hyprutils::Memory::CSharedPointer attempt(Hyprutils::Memory::CSharedPointer backend_, int drmFD, bool GLES2 = true); + static Hyprutils::Memory::CSharedPointer attempt(Hyprutils::Memory::CSharedPointer backend_, + Hyprutils::Memory::CSharedPointer allocator_, bool GLES2 = true); int drmFD = -1; @@ -71,11 +73,6 @@ namespace Aquamarine { } gl; struct { - EGLDisplay display = nullptr; - EGLContext context = nullptr; - EGLSync lastBlitSync = nullptr; - int lastBlitSyncFD = -1; - PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = nullptr; PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr; @@ -87,6 +84,28 @@ namespace Aquamarine { PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR = nullptr; PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR = nullptr; PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = nullptr; + PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR = nullptr; + PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = nullptr; + PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = nullptr; + } proc; + + struct { + bool EXT_read_format_bgra = false; + bool EXT_texture_format_BGRA8888 = false; + bool EXT_platform_device = false; + bool KHR_platform_gbm = false; + bool EXT_image_dma_buf_import = false; + bool EXT_image_dma_buf_import_modifiers = false; + bool KHR_display_reference = false; + bool IMG_context_priority = false; + bool EXT_create_context_robustness = false; + } exts; + + struct { + EGLDisplay display = nullptr; + EGLContext context = nullptr; + EGLSync lastBlitSync = nullptr; + int lastBlitSyncFD = -1; } egl; struct { @@ -104,11 +123,16 @@ namespace Aquamarine { CEGLRenderer() = default; EGLImageKHR createEGLImage(const SDMABUFAttrs& attrs); - std::optional>> getModsForFormat(EGLint format); - bool initDRMFormats(); bool verifyDestinationDMABUF(const SDMABUFAttrs& attrs); void waitOnSync(int fd); int recreateBlitSync(); + + void loadEGLAPI(); + EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); + void initContext(bool GLES2); + void initResources(); + bool initDRMFormats(); + std::optional>> getModsForFormat(EGLint format); bool hasModifiers = false; Hyprutils::Memory::CWeakPointer backend; diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp index 7c0d2a8..190b002 100644 --- a/src/backend/drm/DRM.cpp +++ b/src/backend/drm/DRM.cpp @@ -562,7 +562,7 @@ bool Aquamarine::CDRMBackend::initMgpu() { return false; } - rendererState.renderer = CEGLRenderer::attempt(newAllocator, backend.lock()); + rendererState.renderer = CEGLRenderer::attempt(backend.lock(), newAllocator); if (!rendererState.renderer) { backend->log(AQ_LOG_ERROR, "drm: initMgpu: no renderer"); @@ -937,7 +937,7 @@ void Aquamarine::CDRMBackend::onReady() { if (!a) backend->log(AQ_LOG_ERROR, "drm: onReady: no renderer for gl formats"); else { - auto r = CEGLRenderer::attempt(a, backend.lock()); + auto r = CEGLRenderer::attempt(backend.lock(), a); if (!r) backend->log(AQ_LOG_ERROR, "drm: onReady: no renderer for gl formats"); else { diff --git a/src/renderer/EGLRenderer.cpp b/src/renderer/EGLRenderer.cpp index fb78ee9..d4976c5 100644 --- a/src/renderer/EGLRenderer.cpp +++ b/src/renderer/EGLRenderer.cpp @@ -17,11 +17,11 @@ using namespace Hyprutils::Math; #define WP CWeakPointer // static funcs -WP gBackend; +static WP gBackend; // ------------------- shader utils -GLuint compileShader(const GLuint& type, std::string src) { +static GLuint compileShader(const GLuint& type, std::string src) { auto shader = glCreateShader(type); auto shaderSource = src.c_str(); @@ -38,7 +38,7 @@ GLuint compileShader(const GLuint& type, std::string src) { return shader; } -GLuint createProgram(const std::string& vert, const std::string& frag) { +static GLuint createProgram(const std::string& vert, const std::string& frag) { auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert); if (vertCompiled == 0) return 0; @@ -97,22 +97,112 @@ void main() { // ------------------- egl stuff -inline void loadGLProc(void* pProc, const char* name) { +static inline void loadGLProc(void* pProc, const char* name) { void* proc = (void*)eglGetProcAddress(name); if (!proc) { - gBackend->log(AQ_LOG_ERROR, std::format("eglGetProcAddress({}) failed", name)); + gBackend->log(AQ_LOG_ERROR, std::format("eglGetProcAddress({}) failed, the display driver doesn't support it", name)); abort(); } *(void**)pProc = proc; } +static enum eBackendLogLevel eglLogToLevel(EGLint type) { + switch (type) { + case EGL_DEBUG_MSG_CRITICAL_KHR: return AQ_LOG_CRITICAL; + case EGL_DEBUG_MSG_ERROR_KHR: return AQ_LOG_ERROR; + case EGL_DEBUG_MSG_WARN_KHR: return AQ_LOG_WARNING; + case EGL_DEBUG_MSG_INFO_KHR: return AQ_LOG_DEBUG; + default: return AQ_LOG_DEBUG; + } +} + +static const char* eglErrorToString(EGLint error) { + switch (error) { + case EGL_SUCCESS: return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; + case EGL_BAD_DEVICE_EXT: return "EGL_BAD_DEVICE_EXT"; + case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; + default: return "Unknown"; + } +} + +static void eglLog(EGLenum error, const char* command, EGLint type, EGLLabelKHR thread, EGLLabelKHR obj, const char* msg) { + gBackend->log(eglLogToLevel(type), std::format("[EGL] Command {} errored out with {} (0x{}): {}", command, eglErrorToString(error), error, msg)); +} + +static bool drmDeviceHasName(const drmDevice* device, const std::string& name) { + for (size_t i = 0; i < DRM_NODE_MAX; i++) { + if (!(device->available_nodes & (1 << i))) + continue; + + if (device->nodes[i] == name) + return true; + } + return false; +} + // ------------------- +EGLDeviceEXT CEGLRenderer::eglDeviceFromDRMFD(int drmFD) { + EGLint nDevices = 0; + if (!proc.eglQueryDevicesEXT(0, nullptr, &nDevices)) { + backend->log(AQ_LOG_ERROR, "CEGLRenderer(drm): eglQueryDevicesEXT failed"); + return EGL_NO_DEVICE_EXT; + } + + if (nDevices <= 0) { + backend->log(AQ_LOG_ERROR, "CEGLRenderer(drm): no devices"); + return EGL_NO_DEVICE_EXT; + } + + std::vector devices; + devices.resize(nDevices); + + if (!proc.eglQueryDevicesEXT(nDevices, devices.data(), &nDevices)) { + backend->log(AQ_LOG_ERROR, "CEGLRenderer(drm): eglQueryDevicesEXT failed (2)"); + return EGL_NO_DEVICE_EXT; + } + + drmDevice* drmDev = nullptr; + if (int ret = drmGetDevice(drmFD, &drmDev); ret < 0) { + backend->log(AQ_LOG_ERROR, "CEGLRenderer(drm): drmGetDevice failed"); + drmFreeDevice(&drmDev); + return EGL_NO_DEVICE_EXT; + } + + for (auto const& d : devices) { + auto devName = proc.eglQueryDeviceStringEXT(d, EGL_DRM_DEVICE_FILE_EXT); + if (!devName) + continue; + + if (drmDeviceHasName(drmDev, devName)) { + backend->log(AQ_LOG_DEBUG, std::format("CEGLRenderer(drm): Using device {}", devName)); + drmFreeDevice(&drmDev); + return d; + } + } + + drmFreeDevice(&drmDev); + return EGL_NO_DEVICE_EXT; +} + std::optional>> CEGLRenderer::getModsForFormat(EGLint format) { // TODO: return std::expected when clang supports it EGLint len = 0; - if (!egl.eglQueryDmaBufModifiersEXT(egl.display, format, 0, nullptr, nullptr, &len)) { + if (!proc.eglQueryDmaBufModifiersEXT(egl.display, format, 0, nullptr, nullptr, &len)) { backend->log(AQ_LOG_ERROR, std::format("EGL: eglQueryDmaBufModifiersEXT failed for format {}", fourccToName(format))); return std::nullopt; } @@ -126,15 +216,20 @@ std::optional>> CEGLRenderer::getModsForFo mods.resize(len); external.resize(len); - egl.eglQueryDmaBufModifiersEXT(egl.display, format, len, mods.data(), external.data(), &len); + proc.eglQueryDmaBufModifiersEXT(egl.display, format, len, mods.data(), external.data(), &len); std::vector> result; result.reserve(mods.size()); + + bool linearIsExternal = false; for (size_t i = 0; i < mods.size(); ++i) { + if (external.at(i) && mods.at(i) == DRM_FORMAT_MOD_LINEAR) + linearIsExternal = true; result.emplace_back(mods.at(i), external.at(i)); } - if (std::ranges::find(mods, DRM_FORMAT_MOD_LINEAR) == mods.end() && mods.size() == 0) + // if the driver doesn't mark linear as external, add it. It's allowed unless the driver says otherwise. (e.g. nvidia) + if (!linearIsExternal && std::ranges::find(mods, DRM_FORMAT_MOD_LINEAR) == mods.end() && mods.size() == 0) result.emplace_back(DRM_FORMAT_MOD_LINEAR, true); return result; @@ -144,9 +239,9 @@ bool CEGLRenderer::initDRMFormats() { std::vector formats; EGLint len = 0; - egl.eglQueryDmaBufFormatsEXT(egl.display, 0, nullptr, &len); + proc.eglQueryDmaBufFormatsEXT(egl.display, 0, nullptr, &len); formats.resize(len); - egl.eglQueryDmaBufFormatsEXT(egl.display, len, formats.data(), &len); + proc.eglQueryDmaBufFormatsEXT(egl.display, len, formats.data(), &len); if (formats.size() == 0) { backend->log(AQ_LOG_ERROR, "EGL: Failed to get formats"); @@ -156,15 +251,18 @@ bool CEGLRenderer::initDRMFormats() { TRACE(backend->log(AQ_LOG_TRACE, "EGL: Supported formats:")); std::vector dmaFormats; + dmaFormats.reserve(formats.size()); for (auto const& fmt : formats) { std::vector> mods; - auto ret = getModsForFormat(fmt); - if (!ret.has_value()) - continue; + if (exts.EXT_image_dma_buf_import_modifiers) { + auto ret = getModsForFormat(fmt); + if (!ret.has_value()) + continue; - mods = *ret; + mods = *ret; + } hasModifiers = hasModifiers || mods.size() > 0; @@ -199,171 +297,268 @@ bool CEGLRenderer::initDRMFormats() { } Aquamarine::CEGLRenderer::~CEGLRenderer() { - eglMakeCurrent(egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroyContext(egl.display, egl.context); + if (egl.display) + eglMakeCurrent(egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglTerminate(egl.display); + if (egl.display && egl.context != EGL_NO_CONTEXT && egl.context != nullptr) + eglDestroyContext(egl.display, egl.context); + + if (egl.display) + eglTerminate(egl.display); eglReleaseThread(); } -SP CEGLRenderer::attempt(Hyprutils::Memory::CSharedPointer allocator_, SP backend_) { +void CEGLRenderer::loadEGLAPI() { + const std::string EGLEXTENSIONS = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + + backend->log(AQ_LOG_DEBUG, std::format("Supported EGL client extensions: ({}) {}", std::count(EGLEXTENSIONS.begin(), EGLEXTENSIONS.end(), ' '), EGLEXTENSIONS)); + + exts.KHR_display_reference = EGLEXTENSIONS.contains("KHR_display_reference"); + exts.EXT_platform_device = EGLEXTENSIONS.contains("EXT_platform_device"); + exts.KHR_platform_gbm = EGLEXTENSIONS.contains("KHR_platform_gbm"); + + loadGLProc(&proc.eglGetPlatformDisplayEXT, "eglGetPlatformDisplayEXT"); + loadGLProc(&proc.eglCreateImageKHR, "eglCreateImageKHR"); + loadGLProc(&proc.eglDestroyImageKHR, "eglDestroyImageKHR"); + loadGLProc(&proc.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT"); + loadGLProc(&proc.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT"); + loadGLProc(&proc.glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES"); + loadGLProc(&proc.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); + loadGLProc(&proc.eglDestroySyncKHR, "eglDestroySyncKHR"); + loadGLProc(&proc.eglWaitSyncKHR, "eglWaitSyncKHR"); + loadGLProc(&proc.eglCreateSyncKHR, "eglCreateSyncKHR"); + loadGLProc(&proc.eglDupNativeFenceFDANDROID, "eglDupNativeFenceFDANDROID"); + loadGLProc(&proc.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); + + if (EGLEXTENSIONS.contains("EGL_EXT_device_base") || EGLEXTENSIONS.contains("EGL_EXT_device_enumeration")) + loadGLProc(&proc.eglQueryDevicesEXT, "eglQueryDevicesEXT"); + + if (EGLEXTENSIONS.contains("EGL_EXT_device_base") || EGLEXTENSIONS.contains("EGL_EXT_device_query")) + loadGLProc(&proc.eglQueryDeviceStringEXT, "eglQueryDeviceStringEXT"); + + if (EGLEXTENSIONS.contains("EGL_KHR_debug")) { + loadGLProc(&proc.eglDebugMessageControlKHR, "eglDebugMessageControlKHR"); + static const EGLAttrib debugAttrs[] = { + EGL_DEBUG_MSG_CRITICAL_KHR, EGL_TRUE, EGL_DEBUG_MSG_ERROR_KHR, EGL_TRUE, EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE, EGL_DEBUG_MSG_INFO_KHR, EGL_TRUE, EGL_NONE, + }; + proc.eglDebugMessageControlKHR(::eglLog, debugAttrs); + } + + if (EGLEXTENSIONS.contains("EXT_platform_device")) { + loadGLProc(&proc.eglQueryDevicesEXT, "eglQueryDevicesEXT"); + loadGLProc(&proc.eglQueryDeviceStringEXT, "eglQueryDeviceStringEXT"); + } + + RASSERT(eglBindAPI(EGL_OPENGL_ES_API) != EGL_FALSE, "Couldn't bind to EGL's opengl ES API. This means your gpu driver f'd up. This is not a Hyprland or Aquamarine issue."); +} + +void CEGLRenderer::initContext(bool GLES2) { + RASSERT(egl.display != nullptr && egl.display != EGL_NO_DISPLAY, "CEGLRenderer: Can't create EGL context without display"); + + EGLint major, minor; + if (eglInitialize(egl.display, &major, &minor) == EGL_FALSE) { + backend->log(AQ_LOG_ERROR, "CEGLRenderer: fail, eglInitialize failed"); + return; + } + + std::string EGLEXTENSIONS = eglQueryString(egl.display, EGL_EXTENSIONS); + + exts.IMG_context_priority = EGLEXTENSIONS.contains("IMG_context_priority"); + exts.EXT_create_context_robustness = EGLEXTENSIONS.contains("EXT_create_context_robustness"); + exts.EXT_image_dma_buf_import = EGLEXTENSIONS.contains("EXT_image_dma_buf_import"); + exts.EXT_image_dma_buf_import_modifiers = EGLEXTENSIONS.contains("EXT_image_dma_buf_import_modifiers"); + + std::vector attrs; + + if (exts.IMG_context_priority) { + backend->log(AQ_LOG_DEBUG, "CEGLRenderer: IMG_context_priority supported, requesting high"); + attrs.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); + attrs.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + } + + if (exts.EXT_create_context_robustness) { + backend->log(AQ_LOG_DEBUG, "CEGLRenderer: EXT_create_context_robustness supported, requesting lose on reset"); + attrs.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT); + attrs.push_back(EGL_LOSE_CONTEXT_ON_RESET_EXT); + } + + attrs.push_back(EGL_CONTEXT_OPENGL_DEBUG); + attrs.push_back(Aquamarine::isTrace() ? EGL_TRUE : EGL_FALSE); + + auto attrsNoVer = attrs; + + if (GLES2) { + attrs.push_back(EGL_CONTEXT_MAJOR_VERSION); + attrs.push_back(2); + attrs.push_back(EGL_CONTEXT_MINOR_VERSION); + attrs.push_back(0); + } else { + attrs.push_back(EGL_CONTEXT_MAJOR_VERSION); + attrs.push_back(3); + attrs.push_back(EGL_CONTEXT_MINOR_VERSION); + attrs.push_back(2); + } + + attrs.push_back(EGL_NONE); + + egl.context = eglCreateContext(egl.display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data()); + if (egl.context == EGL_NO_CONTEXT) { + if (GLES2) { + backend->log(AQ_LOG_ERROR, "CEGLRenderer: Can't create renderer, eglCreateContext failed with GLES 2.0"); + return; + } + + backend->log(AQ_LOG_ERROR, "CEGLRenderer: eglCreateContext failed with GLES 3.2, retrying GLES 3.0"); + + attrs = attrsNoVer; + attrs.push_back(EGL_CONTEXT_MAJOR_VERSION); + attrs.push_back(3); + attrs.push_back(EGL_CONTEXT_MINOR_VERSION); + attrs.push_back(0); + + attrs.push_back(EGL_NONE); + + egl.context = eglCreateContext(egl.display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data()); + if (egl.context == EGL_NO_CONTEXT) { + backend->log(AQ_LOG_ERROR, "CEGLRenderer: Can't create renderer, eglCreateContext failed with both GLES 3.2 and GLES 3.0"); + return; + } + } + + if (exts.IMG_context_priority) { + EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; + eglQueryContext(egl.display, egl.context, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority); + if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG) + backend->log(AQ_LOG_DEBUG, "CEGLRenderer: Failed to get a high priority context"); + else + backend->log(AQ_LOG_DEBUG, "CEGLRenderer: Got a high priority context"); + } + + setEGL(); + + EGLEXTENSIONS = (const char*)glGetString(GL_EXTENSIONS); + + std::string gpuName = "unknown"; + char* drmName = drmGetDeviceNameFromFd2(drmFD); + if (drmName != nullptr) { + gpuName = std::string{drmName}; + free(drmName); + } + + backend->log(AQ_LOG_DEBUG, std::format("Creating {}CEGLRenderer on gpu {}", GLES2 ? "GLES2 " : "", gpuName)); + backend->log(AQ_LOG_DEBUG, std::format("Using: {}", (char*)glGetString(GL_VERSION))); + backend->log(AQ_LOG_DEBUG, std::format("Vendor: {}", (char*)glGetString(GL_VENDOR))); + backend->log(AQ_LOG_DEBUG, std::format("Renderer: {}", (char*)glGetString(GL_RENDERER))); + backend->log(AQ_LOG_DEBUG, std::format("Supported context extensions: ({}) {}", std::count(EGLEXTENSIONS.begin(), EGLEXTENSIONS.end(), ' '), EGLEXTENSIONS)); + + exts.EXT_read_format_bgra = EGLEXTENSIONS.contains("GL_EXT_read_format_bgra"); + exts.EXT_texture_format_BGRA8888 = EGLEXTENSIONS.contains("GL_EXT_texture_format_BGRA8888"); + + restoreEGL(); +} + +void CEGLRenderer::initResources() { + setEGL(); + + if (!exts.EXT_image_dma_buf_import || !initDRMFormats()) + backend->log(AQ_LOG_ERROR, "CEGLRenderer: initDRMFormats failed, dma-buf won't work"); + + gl.shader.program = createProgram(VERT_SRC, FRAG_SRC); + if (gl.shader.program == 0) + backend->log(AQ_LOG_ERROR, "CEGLRenderer: texture shader failed"); + + gl.shader.proj = glGetUniformLocation(gl.shader.program, "proj"); + gl.shader.posAttrib = glGetAttribLocation(gl.shader.program, "pos"); + gl.shader.texAttrib = glGetAttribLocation(gl.shader.program, "texcoord"); + gl.shader.tex = glGetUniformLocation(gl.shader.program, "tex"); + + gl.shaderExt.program = createProgram(VERT_SRC, FRAG_SRC_EXT); + if (gl.shaderExt.program == 0) + backend->log(AQ_LOG_ERROR, "CEGLRenderer: external texture shader failed"); + + gl.shaderExt.proj = glGetUniformLocation(gl.shaderExt.program, "proj"); + gl.shaderExt.posAttrib = glGetAttribLocation(gl.shaderExt.program, "pos"); + gl.shaderExt.texAttrib = glGetAttribLocation(gl.shaderExt.program, "texcoord"); + gl.shaderExt.tex = glGetUniformLocation(gl.shaderExt.program, "tex"); + + restoreEGL(); +} + +SP CEGLRenderer::attempt(SP backend_, int drmFD, bool GLES2) { SP renderer = SP(new CEGLRenderer()); - renderer->drmFD = allocator_->drmFD(); + renderer->drmFD = drmFD; renderer->backend = backend_; gBackend = backend_; - const std::string EGLEXTENSIONS = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + renderer->loadEGLAPI(); - if (!EGLEXTENSIONS.contains("KHR_platform_gbm")) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, no gbm support"); + if (!renderer->exts.EXT_platform_device) { + backend_->log(AQ_LOG_ERROR, "CEGLRenderer(drm): Can't create renderer, EGL doesn't support EXT_platform_device"); return nullptr; } - // init egl - - if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, eglBindAPI failed"); + EGLDeviceEXT device = renderer->eglDeviceFromDRMFD(drmFD); + if (device == EGL_NO_DEVICE_EXT) { + backend_->log(AQ_LOG_ERROR, "CEGLRenderer(drm): Can't create renderer, no matching devices found"); return nullptr; } - loadGLProc(&renderer->egl.eglGetPlatformDisplayEXT, "eglGetPlatformDisplayEXT"); - loadGLProc(&renderer->egl.eglCreateImageKHR, "eglCreateImageKHR"); - loadGLProc(&renderer->egl.eglDestroyImageKHR, "eglDestroyImageKHR"); - loadGLProc(&renderer->egl.glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES"); - loadGLProc(&renderer->egl.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); - loadGLProc(&renderer->egl.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT"); - loadGLProc(&renderer->egl.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT"); - loadGLProc(&renderer->egl.eglDestroySyncKHR, "eglDestroySyncKHR"); - loadGLProc(&renderer->egl.eglWaitSyncKHR, "eglWaitSyncKHR"); - loadGLProc(&renderer->egl.eglCreateSyncKHR, "eglCreateSyncKHR"); - loadGLProc(&renderer->egl.eglDupNativeFenceFDANDROID, "eglDupNativeFenceFDANDROID"); - - if (!renderer->egl.eglCreateSyncKHR) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, no eglCreateSyncKHR"); - return nullptr; + std::vector attrs; + if (renderer->exts.KHR_display_reference) { + attrs.push_back(EGL_TRACK_REFERENCES_KHR); + attrs.push_back(EGL_TRUE); } - if (!renderer->egl.eglDupNativeFenceFDANDROID) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, no eglDupNativeFenceFDANDROID"); - return nullptr; - } + attrs.push_back(EGL_NONE); - if (!renderer->egl.eglGetPlatformDisplayEXT) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, no eglGetPlatformDisplayEXT"); - return nullptr; - } - - if (!renderer->egl.eglCreateImageKHR) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, no eglCreateImageKHR"); - return nullptr; - } - - if (!renderer->egl.eglQueryDmaBufFormatsEXT) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, no eglQueryDmaBufFormatsEXT"); - return nullptr; - } - - if (!renderer->egl.eglQueryDmaBufModifiersEXT) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, no eglQueryDmaBufModifiersEXT"); - return nullptr; - } - - std::vector attrs = {EGL_NONE}; - renderer->egl.display = renderer->egl.eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, allocator_->gbmDevice, attrs.data()); + renderer->egl.display = renderer->proc.eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, attrs.data()); if (renderer->egl.display == EGL_NO_DISPLAY) { backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, eglGetPlatformDisplayEXT failed"); return nullptr; } - EGLint major, minor; - if (eglInitialize(renderer->egl.display, &major, &minor) == EGL_FALSE) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, eglInitialize failed"); + renderer->initContext(GLES2); + if (renderer->egl.context == nullptr || renderer->egl.context == EGL_NO_CONTEXT) + return nullptr; + + renderer->initResources(); + + return renderer; +} + +SP CEGLRenderer::attempt(SP backend_, Hyprutils::Memory::CSharedPointer allocator_, bool GLES2) { + SP renderer = SP(new CEGLRenderer()); + renderer->drmFD = allocator_->drmFD(); + renderer->backend = backend_; + gBackend = backend_; + + renderer->loadEGLAPI(); + + if (!renderer->exts.KHR_platform_gbm) { + backend_->log(AQ_LOG_ERROR, "CEGLRenderer(gbm): Can't create renderer, EGL doesn't support KHR_platform_gbm"); return nullptr; } - attrs.clear(); - - const std::string EGLEXTENSIONS2 = eglQueryString(renderer->egl.display, EGL_EXTENSIONS); - - if (EGLEXTENSIONS2.contains("IMG_context_priority")) { - attrs.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); - attrs.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + std::vector attrs; + if (renderer->exts.KHR_display_reference) { + attrs.push_back(EGL_TRACK_REFERENCES_KHR); + attrs.push_back(EGL_TRUE); } - if (EGLEXTENSIONS2.contains("EXT_create_context_robustness")) { - attrs.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT); - attrs.push_back(EGL_LOSE_CONTEXT_ON_RESET_EXT); - } - - if (!EGLEXTENSIONS2.contains("EXT_image_dma_buf_import_modifiers")) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, no EXT_image_dma_buf_import_modifiers ext"); - return nullptr; - } - - if (!EGLEXTENSIONS2.contains("EXT_image_dma_buf_import")) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, no EXT_image_dma_buf_import ext"); - return nullptr; - } - - attrs.push_back(EGL_CONTEXT_MAJOR_VERSION); - attrs.push_back(2); - attrs.push_back(EGL_CONTEXT_MINOR_VERSION); - attrs.push_back(0); - attrs.push_back(EGL_CONTEXT_OPENGL_DEBUG); - attrs.push_back(EGL_FALSE); - attrs.push_back(EGL_NONE); - renderer->egl.context = eglCreateContext(renderer->egl.display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data()); - if (renderer->egl.context == EGL_NO_CONTEXT) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, eglCreateContext failed"); + renderer->egl.display = renderer->proc.eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, allocator_->gbmDevice, attrs.data()); + if (renderer->egl.display == EGL_NO_DISPLAY) { + backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, eglGetPlatformDisplayEXT failed"); return nullptr; } - if (EGLEXTENSIONS2.contains("IMG_context_priority")) { - EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; - eglQueryContext(renderer->egl.display, renderer->egl.context, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority); - if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG) - backend_->log(AQ_LOG_DEBUG, "CEGLRenderer: didnt get a high priority context"); - else - backend_->log(AQ_LOG_DEBUG, "CEGLRenderer: got a high priority context"); - } - - // init shaders - - renderer->setEGL(); - - if (!renderer->initDRMFormats()) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, initDRMFormats failed"); + renderer->initContext(GLES2); + if (renderer->egl.context == nullptr || renderer->egl.context == EGL_NO_CONTEXT) return nullptr; - } - renderer->gl.shader.program = createProgram(VERT_SRC, FRAG_SRC); - if (renderer->gl.shader.program == 0) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, shader failed"); - return nullptr; - } - - renderer->gl.shader.proj = glGetUniformLocation(renderer->gl.shader.program, "proj"); - renderer->gl.shader.posAttrib = glGetAttribLocation(renderer->gl.shader.program, "pos"); - renderer->gl.shader.texAttrib = glGetAttribLocation(renderer->gl.shader.program, "texcoord"); - renderer->gl.shader.tex = glGetUniformLocation(renderer->gl.shader.program, "tex"); - - renderer->gl.shaderExt.program = createProgram(VERT_SRC, FRAG_SRC_EXT); - if (renderer->gl.shaderExt.program == 0) { - backend_->log(AQ_LOG_ERROR, "CEGLRenderer: fail, shaderExt failed"); - return nullptr; - } - - renderer->gl.shaderExt.proj = glGetUniformLocation(renderer->gl.shaderExt.program, "proj"); - renderer->gl.shaderExt.posAttrib = glGetAttribLocation(renderer->gl.shaderExt.program, "pos"); - renderer->gl.shaderExt.texAttrib = glGetAttribLocation(renderer->gl.shaderExt.program, "texcoord"); - renderer->gl.shaderExt.tex = glGetUniformLocation(renderer->gl.shaderExt.program, "tex"); - - renderer->restoreEGL(); - - backend_->log(AQ_LOG_DEBUG, "CEGLRenderer: success"); + renderer->initResources(); return renderer; } @@ -448,7 +643,7 @@ EGLImageKHR CEGLRenderer::createEGLImage(const SDMABUFAttrs& attrs) { attribs.push_back(EGL_NONE); - EGLImageKHR image = egl.eglCreateImageKHR(egl.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, (int*)attribs.data()); + EGLImageKHR image = proc.eglCreateImageKHR(egl.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, (int*)attribs.data()); if (image == EGL_NO_IMAGE_KHR) { backend->log(AQ_LOG_ERROR, std::format("EGL: EGLCreateImageKHR failed: {}", eglGetError())); return EGL_NO_IMAGE_KHR; @@ -496,7 +691,7 @@ SGLTex CEGLRenderer::glTex(Hyprutils::Memory::CSharedPointer buffa) { GLCALL(glBindTexture(tex.target, tex.texid)); GLCALL(glTexParameteri(tex.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GLCALL(glTexParameteri(tex.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - GLCALL(egl.glEGLImageTargetTexture2DOES(tex.target, tex.image)); + GLCALL(proc.glEGLImageTargetTexture2DOES(tex.target, tex.image)); GLCALL(glBindTexture(tex.target, 0)); return tex; @@ -523,7 +718,7 @@ void CEGLRenderer::waitOnSync(int fd) { attribs.push_back(dupFd); attribs.push_back(EGL_NONE); - EGLSyncKHR sync = egl.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs.data()); + EGLSyncKHR sync = proc.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs.data()); if (sync == EGL_NO_SYNC_KHR) { TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to create an egl sync for explicit")); if (dupFd >= 0) @@ -532,15 +727,15 @@ void CEGLRenderer::waitOnSync(int fd) { } // we got a sync, now we just tell egl to wait before sampling - if (egl.eglWaitSyncKHR(egl.display, sync, 0) != EGL_TRUE) { - if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE) + if (proc.eglWaitSyncKHR(egl.display, sync, 0) != EGL_TRUE) { + if (proc.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE) TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to destroy sync")); TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to wait on the sync object")); return; } - if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE) + if (proc.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE) TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to destroy sync")); } @@ -551,7 +746,7 @@ int CEGLRenderer::recreateBlitSync() { TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (recreateBlitSync): cleaning up old sync (fd {})", egl.lastBlitSyncFD))); // cleanup last sync - if (egl.eglDestroySyncKHR(egl.display, egl.lastBlitSync) != EGL_TRUE) + if (proc.eglDestroySyncKHR(egl.display, egl.lastBlitSync) != EGL_TRUE) TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to destroy old sync")); if (egl.lastBlitSyncFD >= 0) @@ -561,7 +756,7 @@ int CEGLRenderer::recreateBlitSync() { egl.lastBlitSync = nullptr; } - EGLSyncKHR sync = egl.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + EGLSyncKHR sync = proc.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); if (sync == EGL_NO_SYNC_KHR) { TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to create an egl sync for explicit")); return -1; @@ -570,10 +765,10 @@ int CEGLRenderer::recreateBlitSync() { // we need to flush otherwise we might not get a valid fd glFlush(); - int fd = egl.eglDupNativeFenceFDANDROID(egl.display, sync); + int fd = proc.eglDupNativeFenceFDANDROID(egl.display, sync); if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to dup egl fence fd")); - if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE) + if (proc.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE) TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to destroy new sync")); return -1; } @@ -605,7 +800,7 @@ void CEGLRenderer::clearBuffer(IBuffer* buf) { GLCALL(glGenRenderbuffers(1, &rboID)); GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID)); - GLCALL(egl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage)); + GLCALL(proc.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage)); GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); GLCALL(glGenFramebuffers(1, &fboID)); @@ -627,7 +822,7 @@ void CEGLRenderer::clearBuffer(IBuffer* buf) { glDeleteFramebuffers(1, &fboID); glDeleteRenderbuffers(1, &rboID); - egl.eglDestroyImageKHR(egl.display, rboImage); + proc.eglDestroyImageKHR(egl.display, rboImage); restoreEGL(); } @@ -706,7 +901,7 @@ CEGLRenderer::SBlitResult CEGLRenderer::blit(SP from, SP to, i GLCALL(glGenRenderbuffers(1, &rboID)); GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID)); - GLCALL(egl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage)); + GLCALL(proc.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage)); GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); GLCALL(glGenFramebuffers(1, &fboID)); @@ -819,9 +1014,9 @@ void CEGLRenderer::onBufferAttachmentDrop(CEGLRendererBufferAttachment* attachme if (attachment->fbo) GLCALL(glDeleteFramebuffers(1, &attachment->fbo)); if (attachment->eglImage) - egl.eglDestroyImageKHR(egl.display, attachment->eglImage); + proc.eglDestroyImageKHR(egl.display, attachment->eglImage); if (attachment->tex.image) - egl.eglDestroyImageKHR(egl.display, attachment->tex.image); + proc.eglDestroyImageKHR(egl.display, attachment->tex.image); restoreEGL(); }