340 lines
8.0 KiB
C++
340 lines
8.0 KiB
C++
// 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 <Nazara/OpenGLRenderer/Wrapper/EGL/EGLContextBase.hpp>
|
|
#include <Nazara/Core/CallOnExit.hpp>
|
|
#include <Nazara/Core/Log.hpp>
|
|
#include <Nazara/Core/StringExt.hpp>
|
|
#include <Nazara/OpenGLRenderer/Wrapper/EGL/EGLLoader.hpp>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <Nazara/OpenGLRenderer/Debug.hpp>
|
|
|
|
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<EGLConfig, 0xFF> 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<Version, 3> 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<int, 2 * 2 + 1> 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<Version, 8> 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<int, 3 * 2 + 1> 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<Fallback::glClearDepthProc>(m_loader.LoadFunction("glClearDepth"));
|
|
if (!fallbacks.glClearDepth)
|
|
return false;
|
|
|
|
glClearDepthf = [](GLfloat depth)
|
|
{
|
|
const EGLContextBase* context = static_cast<const EGLContextBase*>(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;
|
|
}
|
|
}
|