diff --git a/src/platforms/common/server/CMakeLists.txt b/src/platforms/common/server/CMakeLists.txt
index 466ea7f6e03..3c1053f9070 100644
--- a/src/platforms/common/server/CMakeLists.txt
+++ b/src/platforms/common/server/CMakeLists.txt
@@ -10,6 +10,8 @@ add_library(server_platform_common STATIC
shm_buffer.cpp
one_shot_device_observer.h
one_shot_device_observer.cpp
+ cpu_copy_output_surface.cpp
+ cpu_copy_output_surface.h
)
target_include_directories(
diff --git a/src/platforms/common/server/cpu_copy_output_surface.cpp b/src/platforms/common/server/cpu_copy_output_surface.cpp
new file mode 100644
index 00000000000..9d126d66e23
--- /dev/null
+++ b/src/platforms/common/server/cpu_copy_output_surface.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright © Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 2 or 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * Authored by: Christopher James Halse Rogers
+ */
+
+#include
+#include
+#include
+
+#include "mir/graphics/egl_error.h"
+#include "mir/graphics/platform.h"
+
+#include "cpu_copy_output_surface.h"
+
+namespace mg = mir::graphics;
+namespace mgc = mg::common;
+namespace geom = mir::geometry;
+
+namespace
+{
+template
+class GLHandle
+{
+public:
+ GLHandle()
+ {
+ (*allocator)(1, &id);
+ }
+
+ ~GLHandle()
+ {
+ if (id)
+ (*deleter)(1, &id);
+ }
+
+ GLHandle(GLHandle const&) = delete;
+ GLHandle& operator=(GLHandle const&) = delete;
+
+ GLHandle(GLHandle&& from)
+ : id{from.id}
+ {
+ from.id = 0;
+ }
+
+ operator GLuint() const
+ {
+ return id;
+ }
+
+private:
+ GLuint id;
+};
+
+using RenderbufferHandle = GLHandle<&glGenRenderbuffers, &glDeleteRenderbuffers>;
+using FramebufferHandle = GLHandle<&glGenFramebuffers, &glDeleteFramebuffers>;
+
+auto ensure_context_current(EGLDisplay dpy, EGLContext ctx)
+ -> EGLContext
+{
+ if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE)
+ {
+ BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make context current"));
+ }
+ return ctx;
+}
+
+auto select_format_from(mg::CPUAddressableDisplayProvider const& provider) -> mg::DRMFormat
+{
+ std::optional best_format;
+ for (auto const format : provider.supported_formats())
+ {
+ switch(static_cast(format))
+ {
+ case DRM_FORMAT_ARGB8888:
+ case DRM_FORMAT_XRGB8888:
+ // ?RGB8888 is the easiest for us
+ return format;
+ case DRM_FORMAT_RGBA8888:
+ case DRM_FORMAT_RGBX8888:
+ // RGB?8888 requires an EGL extension, but is OK
+ best_format = format;
+ break;
+ }
+ }
+ if (best_format)
+ {
+ return *best_format;
+ }
+ BOOST_THROW_EXCEPTION((std::runtime_error{"Non-?RGB8888 formats not yet supported for display"}));
+}
+}
+
+class mgc::CPUCopyOutputSurface::Impl
+{
+public:
+ Impl(
+ EGLDisplay dpy,
+ EGLContext ctx,
+ std::shared_ptr allocator,
+ geom::Size size);
+
+ void bind();
+
+ void make_current();
+ void release_current();
+
+ auto commit() -> std::unique_ptr;
+
+ auto size() const -> geom::Size;
+ auto layout() const -> Layout;
+
+private:
+ std::shared_ptr const allocator;
+ EGLDisplay const dpy;
+ EGLContext const ctx;
+ geometry::Size const size_;
+ DRMFormat const format;
+ RenderbufferHandle const colour_buffer;
+ FramebufferHandle const fbo;
+};
+
+mgc::CPUCopyOutputSurface::CPUCopyOutputSurface(
+ EGLDisplay dpy,
+ EGLContext ctx,
+ std::shared_ptr allocator,
+ geom::Size size)
+ : impl{std::make_unique(dpy, ctx, std::move(allocator), size)}
+{
+}
+
+mgc::CPUCopyOutputSurface::~CPUCopyOutputSurface() = default;
+
+void mgc::CPUCopyOutputSurface::bind()
+{
+ impl->bind();
+}
+
+void mgc::CPUCopyOutputSurface::make_current()
+{
+ impl->make_current();
+}
+
+void mgc::CPUCopyOutputSurface::release_current()
+{
+ impl->make_current();
+}
+
+auto mgc::CPUCopyOutputSurface::commit() -> std::unique_ptr
+{
+ return impl->commit();
+}
+
+auto mgc::CPUCopyOutputSurface::size() const -> geom::Size
+{
+ return impl->size();
+}
+
+auto mgc::CPUCopyOutputSurface::layout() const -> Layout
+{
+ return impl->layout();
+}
+
+mgc::CPUCopyOutputSurface::Impl::Impl(
+ EGLDisplay dpy,
+ EGLContext ctx,
+ std::shared_ptr allocator,
+ geom::Size size)
+ : allocator{std::move(allocator)},
+ dpy{dpy},
+ ctx{ensure_context_current(dpy, ctx)},
+ size_{size},
+ format{select_format_from(*this->allocator)}
+{
+ glBindRenderbuffer(GL_RENDERBUFFER, colour_buffer);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, size_.width.as_int(), size_.height.as_int());
+
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colour_buffer);
+
+ auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ switch (status)
+ {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ BOOST_THROW_EXCEPTION((
+ std::runtime_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"}
+ ));
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+ // Somehow we've managed to attach buffers with mismatched sizes?
+ BOOST_THROW_EXCEPTION((
+ std::logic_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS"}
+ ));
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ BOOST_THROW_EXCEPTION((
+ std::logic_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"}
+ ));
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ // This is the only one that isn't necessarily a programming error
+ BOOST_THROW_EXCEPTION((
+ std::runtime_error{"FBO is incomplete: formats selected are not supported by this GL driver"}
+ ));
+ case 0:
+ BOOST_THROW_EXCEPTION((
+ mg::gl_error("Failed to verify GL Framebuffer completeness")));
+ }
+ BOOST_THROW_EXCEPTION((
+ std::runtime_error{
+ std::string{"Unknown GL framebuffer error code: "} + std::to_string(status)}));
+ }
+}
+
+void mgc::CPUCopyOutputSurface::Impl::bind()
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+}
+
+void mgc::CPUCopyOutputSurface::Impl::make_current()
+{
+ eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);
+}
+
+void mgc::CPUCopyOutputSurface::Impl::release_current()
+{
+ eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+}
+
+auto mgc::CPUCopyOutputSurface::Impl::commit() -> std::unique_ptr
+{
+ auto fb = allocator->alloc_fb(size_, format);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ {
+ /* TODO: We can usefully put this *into* DRMFormat */
+ GLenum pixel_layout = GL_INVALID_ENUM;
+ if (format == DRM_FORMAT_ARGB8888 || format == DRM_FORMAT_XRGB8888)
+ {
+ pixel_layout = GL_BGRA_EXT;
+ }
+ else if (format == DRM_FORMAT_RGBA8888 || format == DRM_FORMAT_RGBX8888)
+ {
+ pixel_layout = GL_RGBA;
+ }
+ auto mapping = fb->map_writeable();
+ /*
+ * TODO: This introduces a pipeline stall; GL must wait for all previous rendering commands
+ * to complete before glReadPixels returns. We could instead do something fancy with
+ * pixel buffer objects to defer this cost.
+ */
+ /*
+ * TODO: We are assuming that the framebuffer pixel format is RGBX
+ */
+ glReadPixels(
+ 0, 0,
+ size_.width.as_int(), size_.height.as_int(),
+ pixel_layout, GL_UNSIGNED_BYTE, mapping->data());
+ }
+ return fb;
+}
+
+auto mgc::CPUCopyOutputSurface::Impl::size() const -> geom::Size
+{
+ return size_;
+}
+
+auto mgc::CPUCopyOutputSurface::Impl::layout() const -> Layout
+{
+ return Layout::TopRowFirst;
+}
diff --git a/src/platforms/common/server/cpu_copy_output_surface.h b/src/platforms/common/server/cpu_copy_output_surface.h
new file mode 100644
index 00000000000..7a24c3369a7
--- /dev/null
+++ b/src/platforms/common/server/cpu_copy_output_surface.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright © Canonical Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License version 2 or 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ *
+ * Authored by: Christopher James Halse Rogers
+ */
+
+#include
+#include
+
+#include
+
+#include "mir/graphics/drm_formats.h"
+#include "mir/geometry/size.h"
+#include "mir/graphics/platform.h"
+#include "mir/renderer/gl/gl_surface.h"
+
+namespace mir::graphics
+{
+
+namespace common
+{
+class CPUCopyOutputSurface : public gl::OutputSurface
+{
+public:
+ CPUCopyOutputSurface(
+ EGLDisplay dpy,
+ EGLContext ctx,
+ std::shared_ptr allocator,
+ geometry::Size size);
+
+ ~CPUCopyOutputSurface() override;
+
+ void bind() override;
+
+ void make_current() override;
+
+ void release_current() override;
+
+ auto commit() -> std::unique_ptr override;
+
+ auto size() const -> geometry::Size override;
+
+ auto layout() const -> Layout override;
+
+private:
+ class Impl;
+ std::unique_ptr const impl;
+ std::shared_ptr const allocator;
+};
+}
+}
\ No newline at end of file
diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp
index c5bc5813845..7a803cb118e 100644
--- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp
+++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp
@@ -18,6 +18,7 @@
#include
#include "buffer_allocator.h"
+#include "cpu_copy_output_surface.h"
#include "mir/anonymous_shm_file.h"
#include "mir/graphics/display_buffer.h"
#include "mir/graphics/drm_formats.h"
@@ -583,152 +584,8 @@ class GLHandle
GLuint id;
};
-using RenderbufferHandle = GLHandle<&glGenRenderbuffers, &glDeleteRenderbuffers>;
-using FramebufferHandle = GLHandle<&glGenFramebuffers, &glDeleteFramebuffers>;
using TextureHandle = GLHandle<&glGenTextures, &glDeleteTextures>;
-auto select_format_from(mg::CPUAddressableDisplayProvider const& provider) -> mg::DRMFormat
-{
- std::optional best_format;
- for (auto const format : provider.supported_formats())
- {
- switch(static_cast(format))
- {
- case DRM_FORMAT_ARGB8888:
- case DRM_FORMAT_XRGB8888:
- // ?RGB8888 is the easiest for us
- return format;
- case DRM_FORMAT_RGBA8888:
- case DRM_FORMAT_RGBX8888:
- // RGB?8888 requires an EGL extension, but is OK
- best_format = format;
- break;
- }
- }
- if (best_format)
- {
- return *best_format;
- }
- BOOST_THROW_EXCEPTION((std::runtime_error{"Non-?RGB8888 formats not yet supported for display"}));
-}
-
-class CPUCopyOutputSurface : public mg::gl::OutputSurface
-{
-public:
- CPUCopyOutputSurface(
- std::unique_ptr ctx,
- std::shared_ptr allocator,
- geom::Size size)
- : allocator{std::move(allocator)},
- ctx{std::move(ctx)},
- size_{std::move(size)},
- format{select_format_from(*this->allocator)}
- {
- glBindRenderbuffer(GL_RENDERBUFFER, colour_buffer);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, size_.width.as_int(), size_.height.as_int());
-
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colour_buffer);
-
- auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE)
- {
- switch (status)
- {
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- BOOST_THROW_EXCEPTION((
- std::runtime_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"}
- ));
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
- // Somehow we've managed to attach buffers with mismatched sizes?
- BOOST_THROW_EXCEPTION((
- std::logic_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS"}
- ));
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- BOOST_THROW_EXCEPTION((
- std::logic_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"}
- ));
- case GL_FRAMEBUFFER_UNSUPPORTED:
- // This is the only one that isn't necessarily a programming error
- BOOST_THROW_EXCEPTION((
- std::runtime_error{"FBO is incomplete: formats selected are not supported by this GL driver"}
- ));
- case 0:
- BOOST_THROW_EXCEPTION((
- mg::gl_error("Failed to verify GL Framebuffer completeness")));
- }
- BOOST_THROW_EXCEPTION((
- std::runtime_error{
- std::string{"Unknown GL framebuffer error code: "} + std::to_string(status)}));
- }
- }
-
- void bind() override
- {
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- }
-
- void make_current() override
- {
- ctx->make_current();
- }
-
- void release_current() override
- {
- ctx->release_current();
- }
-
- auto commit() -> std::unique_ptr override
- {
- auto fb = allocator->alloc_fb(size_, format);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- {
- /* TODO: We can usefully put this *into* DRMFormat */
- GLenum pixel_layout = GL_INVALID_ENUM;
- if (format == DRM_FORMAT_ARGB8888 || format == DRM_FORMAT_XRGB8888)
- {
- pixel_layout = GL_RGBA;
- }
- else if (format == DRM_FORMAT_RGBA8888 || format == DRM_FORMAT_RGBX8888)
- {
- pixel_layout = GL_BGRA_EXT;
- }
- auto mapping = fb->map_writeable();
- /*
- * TODO: This introduces a pipeline stall; GL must wait for all previous rendering commands
- * to complete before glReadPixels returns. We could instead do something fancy with
- * pixel buffer objects to defer this cost.
- */
- /*
- * TODO: We are assuming that the framebuffer pixel format is RGBX
- */
- glReadPixels(
- 0, 0,
- size_.width.as_int(), size_.height.as_int(),
- pixel_layout, GL_UNSIGNED_BYTE, mapping->data());
- }
- return fb;
- }
-
- auto size() const -> geom::Size override
- {
- return size_;
- }
-
- auto layout() const -> Layout override
- {
- return Layout::TopRowFirst;
- }
-
-private:
- std::shared_ptr const allocator;
- std::unique_ptr const ctx;
- geom::Size const size_;
- mg::DRMFormat const format;
- RenderbufferHandle const colour_buffer;
- FramebufferHandle const fbo;
-};
-
auto make_stream_ctx(EGLDisplay dpy, EGLConfig cfg, EGLContext share_with) -> EGLContext
{
eglBindAPI(EGL_OPENGL_ES_API);
@@ -892,8 +749,9 @@ auto mge::GLRenderingProvider::surface_for_output(
auto fb_context = ctx->make_share_context();
fb_context->make_current();
- return std::make_unique(
- std::move(fb_context),
+ return std::make_unique(
+ dpy,
+ static_cast(*ctx),
cpu_provider,
size);
}
diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp
index 6aa595aeeb7..112c2f75980 100644
--- a/src/platforms/gbm-kms/server/buffer_allocator.cpp
+++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp
@@ -37,6 +37,7 @@
#include "mir/graphics/drm_formats.h"
#include "display_helpers.h"
#include "mir/graphics/egl_error.h"
+#include "cpu_copy_output_surface.h"
#include
#include
@@ -323,197 +324,6 @@ auto mgg::GLRenderingProvider::as_texture(std::shared_ptr buffer) -> std
namespace
{
-template
-class GLHandle
-{
-public:
- GLHandle()
- {
- (*allocator)(1, &id);
- }
-
- ~GLHandle()
- {
- if (id)
- (*deleter)(1, &id);
- }
-
- GLHandle(GLHandle const&) = delete;
- GLHandle& operator=(GLHandle const&) = delete;
-
- GLHandle(GLHandle&& from)
- : id{from.id}
- {
- from.id = 0;
- }
-
- operator GLuint() const
- {
- return id;
- }
-
-private:
- GLuint id;
-};
-
-using RenderbufferHandle = GLHandle<&glGenRenderbuffers, &glDeleteRenderbuffers>;
-using FramebufferHandle = GLHandle<&glGenFramebuffers, &glDeleteFramebuffers>;
-
-auto select_format_from(mg::CPUAddressableDisplayProvider const& provider) -> mg::DRMFormat
-{
- std::optional best_format;
- for (auto const format : provider.supported_formats())
- {
- switch(static_cast(format))
- {
- case DRM_FORMAT_ARGB8888:
- case DRM_FORMAT_XRGB8888:
- // ?RGB8888 is the easiest for us
- return format;
- case DRM_FORMAT_RGBA8888:
- case DRM_FORMAT_RGBX8888:
- // RGB?8888 requires an EGL extension, but is OK
- best_format = format;
- break;
- }
- }
- if (best_format)
- {
- return *best_format;
- }
- BOOST_THROW_EXCEPTION((std::runtime_error{"Non-?RGB8888 formats not yet supported for display"}));
-}
-
-auto ensure_context_current(EGLDisplay dpy, EGLContext ctx)
- -> EGLContext
-{
- if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE)
- {
- BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make share context current"));
- }
- return ctx;
-}
-
-class CPUCopyOutputSurface : public mg::gl::OutputSurface
-{
-public:
- CPUCopyOutputSurface(
- EGLDisplay dpy,
- EGLContext ctx,
- std::shared_ptr allocator,
- geom::Size size)
- : allocator{std::move(allocator)},
- dpy{dpy},
- ctx{ensure_context_current(dpy, ctx)},
- size_{std::move(size)},
- format{select_format_from(*this->allocator)}
- {
- glBindRenderbuffer(GL_RENDERBUFFER, colour_buffer);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, size_.width.as_int(), size_.height.as_int());
-
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colour_buffer);
-
- auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE)
- {
- switch (status)
- {
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- BOOST_THROW_EXCEPTION((
- std::runtime_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"}
- ));
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
- // Somehow we've managed to attach buffers with mismatched sizes?
- BOOST_THROW_EXCEPTION((
- std::logic_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS"}
- ));
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- BOOST_THROW_EXCEPTION((
- std::logic_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"}
- ));
- case GL_FRAMEBUFFER_UNSUPPORTED:
- // This is the only one that isn't necessarily a programming error
- BOOST_THROW_EXCEPTION((
- std::runtime_error{"FBO is incomplete: formats selected are not supported by this GL driver"}
- ));
- case 0:
- BOOST_THROW_EXCEPTION((
- mg::gl_error("Failed to verify GL Framebuffer completeness")));
- }
- BOOST_THROW_EXCEPTION((
- std::runtime_error{
- std::string{"Unknown GL framebuffer error code: "} + std::to_string(status)}));
- }
- }
-
- void bind() override
- {
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- }
-
- void make_current() override
- {
- eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);
- }
-
- void release_current() override
- {
- eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
-
- auto commit() -> std::unique_ptr override
- {
- auto fb = allocator->alloc_fb(size_, format);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- {
- /* TODO: We can usefully put this *into* DRMFormat */
- GLenum pixel_layout = GL_INVALID_ENUM;
- if (format == DRM_FORMAT_ARGB8888 || format == DRM_FORMAT_XRGB8888)
- {
- pixel_layout = GL_BGRA_EXT;
- }
- else if (format == DRM_FORMAT_RGBA8888 || format == DRM_FORMAT_RGBX8888)
- {
- pixel_layout = GL_RGBA;
- }
- auto mapping = fb->map_writeable();
- /*
- * TODO: This introduces a pipeline stall; GL must wait for all previous rendering commands
- * to complete before glReadPixels returns. We could instead do something fancy with
- * pixel buffer objects to defer this cost.
- */
- /*
- * TODO: We are assuming that the framebuffer pixel format is RGBX
- */
- glReadPixels(
- 0, 0,
- size_.width.as_int(), size_.height.as_int(),
- pixel_layout, GL_UNSIGNED_BYTE, mapping->data());
- }
- return fb;
- }
-
- auto size() const -> geom::Size override
- {
- return size_;
- }
-
- auto layout() const -> Layout override
- {
- return Layout::TopRowFirst;
- }
-
-private:
- std::shared_ptr const allocator;
- EGLDisplay const dpy;
- EGLContext const ctx;
- geom::Size const size_;
- mg::DRMFormat const format;
- RenderbufferHandle const colour_buffer;
- FramebufferHandle const fbo;
-};
-
class GBMOutputSurface : public mg::gl::OutputSurface
{
public:
@@ -778,7 +588,7 @@ auto mgg::GLRenderingProvider::surface_for_output(
}
auto cpu_provider = target->acquire_interface();
- return std::make_unique(
+ return std::make_unique(
dpy,
ctx,
std::move(cpu_provider),
diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp
index dfa84353f32..a6d4e78a90c 100644
--- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp
+++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp
@@ -35,6 +35,7 @@
#include "mir/graphics/display_buffer.h"
#include "mir/graphics/drm_formats.h"
#include "mir/graphics/egl_error.h"
+#include "cpu_copy_output_surface.h"
#include
#include
@@ -325,201 +326,6 @@ auto mge::GLRenderingProvider::as_texture(std::shared_ptr buffer) -> std
namespace
{
-template
-class GLHandle
-{
-public:
- GLHandle()
- {
- (*allocator)(1, &id);
- }
-
- ~GLHandle()
- {
- if (id)
- (*deleter)(1, &id);
- }
-
- GLHandle(GLHandle const&) = delete;
- GLHandle& operator=(GLHandle const&) = delete;
-
- GLHandle(GLHandle&& from)
- : id{from.id}
- {
- from.id = 0;
- }
-
- operator GLuint() const
- {
- return id;
- }
-
-private:
- GLuint id;
-};
-
-using RenderbufferHandle = GLHandle<&glGenRenderbuffers, &glDeleteRenderbuffers>;
-using FramebufferHandle = GLHandle<&glGenFramebuffers, &glDeleteFramebuffers>;
-
-auto select_format_from(mg::CPUAddressableDisplayProvider const& provider) -> mg::DRMFormat
-{
- std::optional best_format;
- for (auto const format : provider.supported_formats())
- {
- switch(static_cast(format))
- {
- case DRM_FORMAT_ARGB8888:
- case DRM_FORMAT_XRGB8888:
- // ?RGB8888 is the easiest for us
- return format;
- case DRM_FORMAT_RGBA8888:
- case DRM_FORMAT_RGBX8888:
- // RGB?8888 requires an EGL extension, but is OK
- best_format = format;
- break;
- }
- }
- if (best_format)
- {
- return *best_format;
- }
- BOOST_THROW_EXCEPTION((std::runtime_error{"Non-?RGB8888 formats not yet supported for display"}));
-}
-
-auto ensure_context_current(EGLDisplay dpy, EGLContext ctx)
- -> EGLContext
-{
- if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE)
- {
- BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make share context current"));
- }
- return ctx;
-}
-
-class CPUCopyOutputSurface : public mg::gl::OutputSurface
-{
-public:
- CPUCopyOutputSurface(
- EGLDisplay dpy,
- EGLContext ctx,
- std::shared_ptr allocator,
- geom::Size size)
- : allocator{std::move(allocator)},
- dpy{dpy},
- ctx{ensure_context_current(dpy, ctx)},
- size_{size},
- format{select_format_from(*this->allocator)}
- {
- if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE)
- {
- BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make share context current"));
- }
-
- glBindRenderbuffer(GL_RENDERBUFFER, colour_buffer);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, size_.width.as_int(), size_.height.as_int());
-
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colour_buffer);
-
- auto status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE)
- {
- switch (status)
- {
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
- BOOST_THROW_EXCEPTION((
- std::runtime_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"}
- ));
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
- // Somehow we've managed to attach buffers with mismatched sizes?
- BOOST_THROW_EXCEPTION((
- std::logic_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS"}
- ));
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
- BOOST_THROW_EXCEPTION((
- std::logic_error{"FBO is incomplete: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"}
- ));
- case GL_FRAMEBUFFER_UNSUPPORTED:
- // This is the only one that isn't necessarily a programming error
- BOOST_THROW_EXCEPTION((
- std::runtime_error{"FBO is incomplete: formats selected are not supported by this GL driver"}
- ));
- case 0:
- BOOST_THROW_EXCEPTION((
- mg::gl_error("Failed to verify GL Framebuffer completeness")));
- }
- BOOST_THROW_EXCEPTION((
- std::runtime_error{
- std::string{"Unknown GL framebuffer error code: "} + std::to_string(status)}));
- }
- }
-
- void bind() override
- {
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- }
-
- void make_current() override
- {
- eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);
- }
-
- void release_current() override
- {
- eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
-
- auto commit() -> std::unique_ptr override
- {
- auto fb = allocator->alloc_fb(size_, format);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- {
- /* TODO: We can usefully put this *into* DRMFormat */
- GLenum pixel_layout = GL_INVALID_ENUM;
- if (format == DRM_FORMAT_ARGB8888 || format == DRM_FORMAT_XRGB8888)
- {
- pixel_layout = GL_BGRA_EXT;
- }
- else if (format == DRM_FORMAT_RGBA8888 || format == DRM_FORMAT_RGBX8888)
- {
- pixel_layout = GL_RGBA;
- }
- auto mapping = fb->map_writeable();
- /*
- * TODO: This introduces a pipeline stall; GL must wait for all previous rendering commands
- * to complete before glReadPixels returns. We could instead do something fancy with
- * pixel buffer objects to defer this cost.
- */
- /*
- * TODO: We are assuming that the framebuffer pixel format is RGBX
- */
- glReadPixels(
- 0, 0,
- size_.width.as_int(), size_.height.as_int(),
- pixel_layout, GL_UNSIGNED_BYTE, mapping->data());
- }
- return fb;
- }
-
- auto size() const -> geom::Size override
- {
- return size_;
- }
-
- auto layout() const -> Layout override
- {
- return Layout::TopRowFirst;
- }
-
-private:
- std::shared_ptr const allocator;
- EGLDisplay const dpy;
- EGLContext const ctx;
- geom::Size const size_;
- mg::DRMFormat const format;
- RenderbufferHandle const colour_buffer;
- FramebufferHandle const fbo;
-};
class EGLOutputSurface : public mg::gl::OutputSurface
{
@@ -600,7 +406,7 @@ auto mge::GLRenderingProvider::surface_for_output(
}
auto cpu_provider = framebuffer_provider->acquire_interface();
- return std::make_unique(
+ return std::make_unique(
dpy,
ctx,
std::move(cpu_provider),