// Copyright (C) 2021 Jérôme "Lynix" Leclercq (lynix680@gmail.com) // This file is part of the "Nazara Engine - OpenGL renderer" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include #include #include namespace Nz::GL { EGLContextBase::~EGLContextBase() { Destroy(); } bool EGLContextBase::Create(const ContextParams& params, const EGLContextBase* shareContext) { Destroy(); //< In case a previous display or surface hasn't been released m_params = params; if (!BindAPI()) return false; m_display = m_loader.GetDefaultDisplay(); std::size_t configCount; std::array configs; if (!ChooseConfig(configs.data(), configs.size(), &configCount)) return false; EGLint surfaceAttributes[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; std::size_t configIndex = 0; for (; configIndex < configCount; ++configIndex) { m_surface = m_loader.eglCreatePbufferSurface(m_display, configs[configIndex], surfaceAttributes); if (m_surface) break; } return CreateInternal(configs[configIndex], shareContext); } bool EGLContextBase::Create(const ContextParams& /*params*/, WindowHandle /*window*/, const EGLContextBase* /*shareContext*/) { NazaraError("Unexpected context creation call"); return false; } void EGLContextBase::Destroy() { if (m_handle != EGL_NO_CONTEXT) { assert(m_display != EGL_NO_DISPLAY); OnContextRelease(); NotifyContextDestruction(this); m_loader.eglDestroyContext(m_display, m_handle); m_handle = EGL_NO_CONTEXT; } if (m_surface != EGL_NO_SURFACE) { assert(m_display != EGL_NO_DISPLAY); m_loader.eglDestroySurface(m_display, m_surface); m_surface = EGL_NO_SURFACE; } if (m_display) { if (m_ownsDisplay) m_loader.eglTerminate(m_display); m_display = EGL_NO_DISPLAY; m_ownsDisplay = false; } } void EGLContextBase::EnableVerticalSync(bool enabled) { // TODO } void EGLContextBase::SwapBuffers() { m_loader.eglSwapBuffers(m_display, m_surface); } bool EGLContextBase::BindAPI() { if (m_params.type == ContextType::OpenGL_ES) { if (m_loader.eglBindAPI(EGL_OPENGL_ES_API) == EGL_TRUE) return true; if (m_loader.eglBindAPI(EGL_OPENGL_API) == EGL_TRUE) { m_params.type = ContextType::OpenGL; return true; } NazaraError("neither OpenGL nor OpenGL ES are supported"); return false; } else { if (m_loader.eglBindAPI(EGL_OPENGL_API) != EGL_TRUE) { NazaraError("OpenGL is not supported"); return false; } return true; } } bool EGLContextBase::ChooseConfig(EGLConfig* configs, std::size_t maxConfigCount, std::size_t* configCount) { EGLint configAttributes[] = { EGL_BUFFER_SIZE, EGLint(m_params.bitsPerPixel), EGL_DEPTH_SIZE, EGLint(m_params.depthBits), EGL_RENDERABLE_TYPE, (m_params.type == ContextType::OpenGL_ES) ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_BIT, EGL_SAMPLE_BUFFERS, EGLint((m_params.sampleCount > 1) ? 1 : 0), EGL_SAMPLES, EGLint((m_params.sampleCount > 1) ? m_params.sampleCount : 0), EGL_SURFACE_TYPE, EGL_PBUFFER_BIT | EGL_WINDOW_BIT, EGL_STENCIL_SIZE, EGLint(m_params.stencilBits), EGL_NONE }; EGLint numConfig = 0; if (m_loader.eglChooseConfig(m_display, configAttributes, configs, EGLint(maxConfigCount), &numConfig) != GL_TRUE) { NazaraError(std::string("failed to retrieve compatible EGL configurations: ") + EGLLoader::TranslateError(m_loader.eglGetError())); return false; } if (numConfig == 0) { NazaraError("no supported configuration matches required attributes"); return false; } *configCount = numConfig; return true; } bool EGLContextBase::CreateInternal(EGLConfig config, const EGLContextBase* shareContext) { struct Version { unsigned int major; unsigned int minor; }; if (m_params.type == ContextType::OpenGL_ES) { // Create OpenGL ES context std::array supportedGL_ESVersions = { { { 3, 2 }, { 3, 1 }, { 3, 0 } } }; for (const Version& version : supportedGL_ESVersions) { if (m_params.glMajorVersion != 0) { if (version.major > m_params.glMajorVersion) continue; if (m_params.glMinorVersion != 0 && version.minor > m_params.glMinorVersion) continue; } std::array attributes = { EGL_CONTEXT_MAJOR_VERSION, int(version.major), EGL_CONTEXT_MINOR_VERSION, int(version.minor), EGL_NONE }; m_handle = m_loader.eglCreateContext(m_display, config, (shareContext) ? shareContext->m_handle : nullptr, attributes.data()); if (m_handle) break; } } else { // Create OpenGL ES context std::array supportedGLVersions = { { { 4, 6 }, { 4, 5 }, { 4, 4 }, { 4, 3 }, { 4, 2 }, { 4, 1 }, { 4, 0 }, { 3, 3 } } }; for (const Version& version : supportedGLVersions) { if (m_params.glMajorVersion != 0) { if (version.major > m_params.glMajorVersion) continue; if (m_params.glMinorVersion != 0 && version.minor > m_params.glMinorVersion) continue; } std::array attributes = { EGL_CONTEXT_MAJOR_VERSION, int(version.major), EGL_CONTEXT_MINOR_VERSION, int(version.minor), EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_NONE }; m_handle = m_loader.eglCreateContext(m_display, config, (shareContext) ? shareContext->m_handle : nullptr, attributes.data()); if (m_handle) break; } } if (!m_handle) { NazaraError(std::string("failed to create EGL context: ") + EGLLoader::TranslateError(m_loader.eglGetError())); return false; } LoadEGLExt(); return true; } bool EGLContextBase::InitDisplay() { EGLint major, minor; if (m_loader.eglInitialize(m_display, &major, &minor) != EGL_TRUE) { NazaraError("failed to retrieve default EGL display"); return false; } m_ownsDisplay = true; const char* vendor = m_loader.eglQueryString(m_display, EGL_VENDOR); NazaraNotice("Initialized EGL " + std::to_string(major) + "." + std::to_string(minor) + " display (" + vendor + ")"); return true; } bool EGLContextBase::ImplementFallback(const std::string_view& function) { if (Context::ImplementFallback(function)) return true; if (m_params.type == ContextType::OpenGL_ES) return false; //< Implement fallback only for OpenGL (when emulating OpenGL ES) if (function == "glClearDepthf") { fallbacks.glClearDepth = reinterpret_cast(m_loader.LoadFunction("glClearDepth")); if (!fallbacks.glClearDepth) return false; glClearDepthf = [](GLfloat depth) { const EGLContextBase* context = static_cast(GetCurrentContext()); assert(context); context->fallbacks.glClearDepth(depth); }; } return true; } bool EGLContextBase::Activate() const { EGLBoolean succeeded = m_loader.eglMakeCurrent(m_display, m_surface, m_surface, m_handle); if (succeeded != EGL_TRUE) { NazaraError("failed to activate context"); return false; } return true; } void EGLContextBase::Desactivate() const { assert(GetCurrentContext() == this); EGLBoolean succeeded = m_loader.eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (succeeded != EGL_TRUE) NazaraError("failed to desactivate context"); } const Loader& EGLContextBase::GetLoader() { return m_loader; } bool EGLContextBase::LoadEGLExt() { if (!SetCurrentContext(this)) return false; const char* extensionString = m_loader.eglQueryString(m_display, EGL_EXTENSIONS); if (extensionString) { SplitString(extensionString, " ", [&](std::string_view extension) { m_supportedPlatformExtensions.emplace(extension); return true; }); } return true; } }