consolidate EGL state and initialization

This commit is contained in:
Ikalco 2025-01-23 22:33:07 -06:00
parent 70c8bdf371
commit a300101d3f
3 changed files with 388 additions and 169 deletions

View file

@ -44,8 +44,10 @@ namespace Aquamarine {
class CEGLRenderer {
public:
~CEGLRenderer();
static Hyprutils::Memory::CSharedPointer<CEGLRenderer> attempt(Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_,
Hyprutils::Memory::CSharedPointer<CBackend> backend_);
static Hyprutils::Memory::CSharedPointer<CEGLRenderer> attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend_, int drmFD, bool GLES2 = true);
static Hyprutils::Memory::CSharedPointer<CEGLRenderer> attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend_,
Hyprutils::Memory::CSharedPointer<CGBMAllocator> 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<std::vector<std::pair<uint64_t, bool>>> 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<std::vector<std::pair<uint64_t, bool>>> getModsForFormat(EGLint format);
bool hasModifiers = false;
Hyprutils::Memory::CWeakPointer<CBackend> backend;

View file

@ -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 {

View file

@ -17,11 +17,11 @@ using namespace Hyprutils::Math;
#define WP CWeakPointer
// static funcs
WP<CBackend> gBackend;
static WP<CBackend> 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<EGLDeviceEXT> 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<std::vector<std::pair<uint64_t, bool>>> 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<std::vector<std::pair<uint64_t, bool>>> 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<std::pair<uint64_t, bool>> 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<EGLint> 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<SGLFormat> dmaFormats;
dmaFormats.reserve(formats.size());
for (auto const& fmt : formats) {
std::vector<std::pair<uint64_t, bool>> 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> CEGLRenderer::attempt(Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_, SP<CBackend> 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<EGLint> 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> CEGLRenderer::attempt(SP<CBackend> backend_, int drmFD, bool GLES2) {
SP<CEGLRenderer> renderer = SP<CEGLRenderer>(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<EGLint> 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<EGLint> 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> CEGLRenderer::attempt(SP<CBackend> backend_, Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_, bool GLES2) {
SP<CEGLRenderer> renderer = SP<CEGLRenderer>(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<EGLint> 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<IBuffer> 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<IBuffer> from, SP<IBuffer> 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();
}