Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

platforms/renderer-generic-egl: Support CPUAddressableDisplayProvider #3095

Merged
merged 11 commits into from
Oct 27, 2023
Merged
3 changes: 3 additions & 0 deletions include/platform/mir/graphics/egl_extensions.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ namespace mir
{
namespace graphics
{
auto has_egl_client_extension(char const* name) -> bool;
auto has_egl_extension(EGLDisplay dpy, char const* name) -> bool;
Saviq marked this conversation as resolved.
Show resolved Hide resolved

struct EGLExtensions
{
template<typename Ext>
Expand Down
20 changes: 20 additions & 0 deletions src/platform/graphics/egl_extensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,32 @@
*/

#include "mir/graphics/egl_extensions.h"
#include <EGL/egl.h>
#include <boost/throw_exception.hpp>
#include <stdexcept>
#include <cstring>

namespace mg=mir::graphics;

auto mg::has_egl_client_extension(char const* name) -> bool
{
return has_egl_extension(EGL_NO_DISPLAY, name);
}

auto mg::has_egl_extension(EGLDisplay dpy, char const* name) -> bool
{
auto extensions = eglQueryString(dpy, EGL_EXTENSIONS);
auto found_substring = strstr(extensions, name);
Saviq marked this conversation as resolved.
Show resolved Hide resolved
if (found_substring)
{
// Check that we haven't found a prefix of our extension name
auto end_of_match = found_substring + strlen(name);
// It's a match if it terminates with the end of the extension string, or with a space
return *end_of_match == '\0' || *end_of_match == ' ';
}
return false;
Saviq marked this conversation as resolved.
Show resolved Hide resolved
}

namespace
{

Expand Down
2 changes: 2 additions & 0 deletions src/platform/symbols.map
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ MIR_PLATFORM_2.16 {
mir::graphics::EventHandlerRegister::register_signal_handler*;
mir::graphics::EventHandlerRegister::unregister_fd_handler*;
mir::graphics::GammaCurves::GammaCurves*;
mir::graphics::has_egl_extension*;
mir::graphics::has_egl_client_extension*;
mir::graphics::LinearGammaLUTs::LinearGammaLUTs*;
mir::graphics::LinuxDmaBufUnstable::?LinuxDmaBufUnstable*;
mir::graphics::LinuxDmaBufUnstable::LinuxDmaBufUnstable*;
Expand Down
31 changes: 19 additions & 12 deletions src/platforms/renderer-generic-egl/buffer_allocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,20 @@ auto make_share_only_context(EGLDisplay dpy, std::optional<EGLContext> share_wit
};

EGLConfig cfg;
EGLint num_configs;

if (eglChooseConfig(dpy, config_attr, &cfg, 1, &num_configs) != EGL_TRUE || num_configs != 1)
if (!mg::has_egl_extension(dpy, "EGL_KHR_no_config_context"))
{
BOOST_THROW_EXCEPTION((mg::egl_error("Failed to find any matching EGL config")));
EGLint num_configs;

if (eglChooseConfig(dpy, config_attr, &cfg, 1, &num_configs) != EGL_TRUE || num_configs != 1)
{
BOOST_THROW_EXCEPTION((mg::egl_error("Failed to find any matching EGL config")));
}
}

else
{
cfg = EGL_NO_CONFIG_KHR;
}

auto ctx = eglCreateContext(dpy, cfg, share_with.value_or(EGL_NO_CONTEXT), context_attr);
if (ctx == EGL_NO_CONTEXT)
{
Expand All @@ -106,7 +113,7 @@ class SurfacelessEGLContext : public mir::renderer::gl::Context
ctx{make_share_only_context(dpy, {})}
{
}

SurfacelessEGLContext(EGLDisplay dpy, EGLContext share_with)
: dpy{dpy},
ctx{make_share_only_context(dpy, share_with)}
Expand All @@ -117,33 +124,33 @@ class SurfacelessEGLContext : public mir::renderer::gl::Context
{
eglDestroyContext(dpy, ctx);
}

void make_current() const override
{
if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE)
{
BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make context current"));
}
}

void release_current() const override
{
if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE)
{
BOOST_THROW_EXCEPTION(mg::egl_error("Failed to release current EGL context"));
}
}

auto make_share_context() const -> std::unique_ptr<Context> override
{
return std::unique_ptr<Context>{new SurfacelessEGLContext{dpy, ctx}};
}

explicit operator EGLContext() override
{
return ctx;
}
private:
private:
EGLDisplay const dpy;
EGLContext const ctx;
};
Expand Down Expand Up @@ -429,7 +436,7 @@ auto mge::GLRenderingProvider::surface_for_output(
return std::make_unique<EGLOutputSurface>(egl_display->alloc_framebuffer(config, ctx));
}
auto cpu_provider = framebuffer_provider->acquire_interface<CPUAddressableDisplayProvider>();

return std::make_unique<mgc::CPUCopyOutputSurface>(
dpy,
ctx,
Expand Down
43 changes: 39 additions & 4 deletions src/platforms/renderer-generic-egl/platform_symbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "mir/graphics/egl_extensions.h"
#define MIR_LOG_COMPONENT "gbm-kms"
#include "mir/log.h"

Expand Down Expand Up @@ -66,10 +67,44 @@ auto probe_rendering_platform(
maximum_suitability = mg::probe::hosted;
break;
}
/* TODO: We *can* drive a CPUAddressableDisplayProvider, too, but without
* an EGLDisplay from the GenericEGLDisplayProvider we'd need to check that
* the EGLDisplay we get from eglGetDisplay(EGL_DEFAULT_DISPLAY) is functional.
*/
if (display_provider->acquire_interface<mg::CPUAddressableDisplayProvider>())
{
// Check if the surfaceless platform is available
if (mg::has_egl_client_extension("EGL_EXT_platform_base") &&
mg::has_egl_client_extension("EGL_MESA_platform_surfaceless"))
{
// Check that we can actually initialise the EGL display
mg::EGLExtensions ext;
auto dpy=
ext.platform_base->eglGetPlatformDisplay(
EGL_PLATFORM_SURFACELESS_MESA,
EGL_DEFAULT_DISPLAY,
nullptr);
EGLint major, minor;
if (eglInitialize(dpy, &major, &minor) == EGL_TRUE)
{
if (std::make_pair(major, minor) >= std::make_pair(1, 4))
{
// OK, EGL will somehow provide us with a usable display
maximum_suitability = mg::probe::supported;
}
eglTerminate(dpy);
}
continue;
}
// Check that EGL_DEFAULT_DISPLAY is something we can use...
auto dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
EGLint major, minor;
if (eglInitialize(dpy, &major, &minor) == EGL_TRUE)
{
if (std::make_pair(major, minor) >= std::make_pair(1, 4))
{
// OK, EGL will somehow provide us with a usable display
maximum_suitability = mg::probe::supported;
}
eglTerminate(dpy);
}
}
}

std::vector<mg::SupportedDevice> supported_devices;
Expand Down
59 changes: 50 additions & 9 deletions src/platforms/renderer-generic-egl/rendering_platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,45 @@ namespace geom = mir::geometry;

namespace
{
auto egl_display_from_platforms(std::vector<std::shared_ptr<mg::DisplayInterfaceProvider>> const& displays) -> EGLDisplay
auto create_default_display() -> EGLDisplay
{
if (mg::has_egl_client_extension("EGL_EXT_platform_base")
&& mg::has_egl_client_extension("EGL_MESA_platform_surfaceless"))
{
// Explicitly create a Surfaceless display, when the extension is supported
mg::EGLExtensions ext;
return ext.platform_base->eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, nullptr);
}
// Otherwise, hope that the EGL implementation can pull a functional EGLDisplay out of its hat
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
}

auto egl_display_from_platforms(std::vector<std::shared_ptr<mg::DisplayInterfaceProvider>> const& displays) -> std::tuple<EGLDisplay, bool>
{
for (auto const& display : displays)
{
if (auto egl_provider = display->acquire_interface<mg::GenericEGLDisplayProvider>())
{
return egl_provider->get_egl_display();
return std::make_tuple(egl_provider->get_egl_display(), false);
}
}
// No Displays provide an EGL display
// We can still work, falling back to CPU-copy output, as long as we can get *any* EGL display
auto dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
auto dpy = create_default_display();
if (dpy == EGL_NO_DISPLAY)
{
BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to create any EGL display"}));
}
return dpy;
EGLint major, minor;
if (eglInitialize(dpy, &major, &minor) != EGL_TRUE)
{
BOOST_THROW_EXCEPTION(mg::egl_error("Failed to initialise EGL"));
}
if (std::make_pair(major, minor) < std::make_pair(1, 4))
{
BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to get EGL version >= 1.4"}));
}
return std::make_tuple(dpy, true);
}

auto make_share_only_context(EGLDisplay dpy, std::optional<EGLContext> share_context) -> EGLContext
Expand All @@ -69,11 +91,18 @@ auto make_share_only_context(EGLDisplay dpy, std::optional<EGLContext> share_con
};

EGLConfig cfg;
EGLint num_configs;
if (!mg::has_egl_extension(dpy, "EGL_KHR_no_config_context"))
{
EGLint num_configs;

if (eglChooseConfig(dpy, config_attr, &cfg, 1, &num_configs) != EGL_TRUE || num_configs != 1)
if (eglChooseConfig(dpy, config_attr, &cfg, 1, &num_configs) != EGL_TRUE || num_configs != 1)
{
BOOST_THROW_EXCEPTION((mg::egl_error("Failed to find any matching EGL config")));
}
}
else
{
BOOST_THROW_EXCEPTION((mg::egl_error("Failed to find any matching EGL config")));
cfg = EGL_NO_CONFIG_KHR;
}

auto ctx = eglCreateContext(dpy, cfg, share_context.value_or(EGL_NO_CONTEXT), context_attr);
Expand Down Expand Up @@ -168,7 +197,13 @@ auto maybe_make_dmabuf_provider(
}

mge::RenderingPlatform::RenderingPlatform(std::vector<std::shared_ptr<DisplayInterfaceProvider>> const& displays)
: dpy{egl_display_from_platforms(displays)},
: RenderingPlatform(egl_display_from_platforms(displays))
{
}

mge::RenderingPlatform::RenderingPlatform(std::tuple<EGLDisplay, bool> display)
: dpy{std::get<0>(display)},
owns_dpy{std::get<1>(display)},
ctx{std::make_unique<SurfacelessEGLContext>(dpy)},
dmabuf_provider{
maybe_make_dmabuf_provider(
Expand All @@ -178,7 +213,13 @@ mge::RenderingPlatform::RenderingPlatform(std::vector<std::shared_ptr<DisplayInt
{
}

mge::RenderingPlatform::~RenderingPlatform() = default;
mge::RenderingPlatform::~RenderingPlatform()
{
if (owns_dpy)
{
eglTerminate(dpy);
}
}

auto mge::RenderingPlatform::create_buffer_allocator(
mg::Display const& /*output*/) -> mir::UniqueModulePtr<mg::GraphicBufferAllocator>
Expand Down
3 changes: 3 additions & 0 deletions src/platforms/renderer-generic-egl/rendering_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ class RenderingPlatform : public graphics::RenderingPlatform
RenderingProvider::Tag const& type_tag) -> std::shared_ptr<RenderingProvider> override;

private:
explicit RenderingPlatform(std::tuple<EGLDisplay, bool> dpy);

EGLDisplay const dpy;
bool const owns_dpy;
std::unique_ptr<renderer::gl::Context> const ctx;
std::shared_ptr<DMABufEGLProvider> const dmabuf_provider;
};
Expand Down
Loading