From f0d28daa71742a07923672604de8fb9b15f56571 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 3 Apr 2023 12:50:48 +1000 Subject: [PATCH 001/108] All the new-platform-API --- include/platform/mir/graphics/display.h | 2 +- .../platform/mir/graphics/display_buffer.h | 47 +- include/platform/mir/graphics/platform.h | 341 +++++++- include/platform/mir/renderer/gl/gl_surface.h | 60 ++ include/renderer/mir/renderer/renderer.h | 7 +- .../renderer/mir/renderer/renderer_factory.h | 10 +- .../renderer/gl/basic_buffer_render_target.h | 9 +- .../gl/mir/renderer/gl/buffer_render_target.h | 4 +- .../renderers/gl/mir/renderer/gl/context.h | 10 + .../test/mir/test/doubles/null_gl_context.h | 6 +- ...adless_display_buffer_compositor_factory.h | 18 +- src/platform/graphics/documentation.rst | 18 + .../eglstream-kms/server/CMakeLists.txt | 9 - .../eglstream-kms/server/buffer_allocator.cpp | 362 ++++++++- .../eglstream-kms/server/buffer_allocator.h | 18 +- .../eglstream-kms/server/display.cpp | 88 +-- src/platforms/eglstream-kms/server/display.h | 2 - .../eglstream-kms/server/platform.cpp | 191 +++-- src/platforms/eglstream-kms/server/platform.h | 24 +- .../eglstream-kms/server/platform_symbols.cpp | 336 +------- src/platforms/gbm-kms/server/CMakeLists.txt | 2 + .../gbm-kms/server/buffer_allocator.cpp | 726 ++++++++++++++++++ .../gbm-kms/server/buffer_allocator.h | 109 +++ .../gbm-kms/server/kms/CMakeLists.txt | 7 +- src/platforms/gbm-kms/server/kms/display.cpp | 609 +++++++++------ src/platforms/gbm-kms/server/kms/display.h | 54 +- .../gbm-kms/server/kms/display_buffer.cpp | 633 ++------------- .../gbm-kms/server/kms/display_buffer.h | 106 +-- src/platforms/gbm-kms/server/kms/dumb_fb.cpp | 336 ++++++++ src/platforms/gbm-kms/server/kms/dumb_fb.h | 58 ++ .../gbm-kms/server/kms/egl_helper.cpp | 8 +- src/platforms/gbm-kms/server/kms/egl_helper.h | 5 +- .../gbm-kms/server/kms/kms_framebuffer.cpp | 89 +++ .../gbm-kms/server/kms/kms_framebuffer.h | 43 ++ src/platforms/gbm-kms/server/kms/kms_output.h | 24 - src/platforms/gbm-kms/server/kms/platform.cpp | 255 +++++- src/platforms/gbm-kms/server/kms/platform.h | 56 +- .../gbm-kms/server/kms/platform_symbols.cpp | 18 +- .../gbm-kms/server/kms/real_kms_output.cpp | 282 +------ .../gbm-kms/server/kms/real_kms_output.h | 19 - .../server/kms/real_kms_output_container.cpp | 80 +- .../server/kms/real_kms_output_container.h | 9 +- .../renderer-generic-egl/buffer_allocator.cpp | 356 ++++++++- .../renderer-generic-egl/buffer_allocator.h | 30 +- .../renderer-generic-egl/platform_symbols.cpp | 4 +- .../rendering_platform.cpp | 76 +- .../renderer-generic-egl/rendering_platform.h | 14 +- src/platforms/wayland/display.cpp | 41 - src/platforms/wayland/display.h | 2 - src/platforms/wayland/displayclient.cpp | 124 ++- src/platforms/wayland/platform.cpp | 6 + src/platforms/wayland/platform.h | 3 + src/platforms/x11/graphics/display.cpp | 71 +- src/platforms/x11/graphics/display.h | 25 +- src/platforms/x11/graphics/display_buffer.cpp | 73 +- src/platforms/x11/graphics/display_buffer.h | 45 +- src/platforms/x11/graphics/egl_helper.cpp | 145 ++-- src/platforms/x11/graphics/egl_helper.h | 35 +- src/platforms/x11/graphics/platform.cpp | 44 +- src/platforms/x11/graphics/platform.h | 10 + src/renderers/gl/CMakeLists.txt | 2 +- .../gl/basic_buffer_render_target.cpp | 38 +- src/renderers/gl/renderer.cpp | 124 ++- src/renderers/gl/renderer.h | 27 +- src/renderers/gl/renderer_factory.cpp | 9 +- src/renderers/gl/renderer_factory.h | 8 +- src/server/compositor/CMakeLists.txt | 2 +- .../compositor/basic_screen_shooter.cpp | 7 +- src/server/compositor/basic_screen_shooter.h | 8 +- .../compositor/default_configuration.cpp | 30 +- .../default_display_buffer_compositor.cpp | 48 +- .../default_display_buffer_compositor.h | 12 +- ...ault_display_buffer_compositor_factory.cpp | 30 +- ...efault_display_buffer_compositor_factory.h | 18 +- src/server/graphics/default_configuration.cpp | 56 +- src/server/graphics/multiplexing_display.cpp | 6 - src/server/graphics/multiplexing_display.h | 2 - src/server/server.cpp | 1 - .../acceptance-tests/platforms/CMakeLists.txt | 12 + .../platforms/eglstream-kms_platform.cpp | 1 - .../platforms/test_rendering_platform.cpp | 68 +- .../platforms/test_rendering_platform.h | 8 + tests/include/mir/test/doubles/mock_display.h | 1 - .../mir/test/doubles/mock_display_buffer.h | 15 +- tests/include/mir/test/doubles/mock_drm.h | 28 +- .../test/doubles/mock_gl_rendering_provider.h | 36 + .../mock_output_surface.h} | 33 +- .../include/mir/test/doubles/mock_renderer.h | 3 +- tests/include/mir/test/doubles/null_display.h | 5 - .../mir/test/doubles/null_display_buffer.h | 7 +- .../test/doubles/null_display_sync_group.h | 6 +- .../include/mir/test/doubles/null_platform.h | 13 + .../test/doubles/stub_gl_rendering_provider.h | 77 ++ .../include/mir/test/doubles/stub_renderer.h | 5 +- .../test_surface_stack_with_compositor.cpp | 33 +- tests/mir_test_doubles/CMakeLists.txt | 9 + tests/mir_test_doubles/mock_drm.cpp | 50 ++ ...less_display_buffer_compositor_factory.cpp | 53 +- tests/mir_test_framework/headless_test.cpp | 14 +- tests/mir_test_framework/mmap_wrapper.cpp | 3 +- .../platform_graphics_throw.cpp | 15 + .../stubbed_graphics_platform.cpp | 19 + .../stubbed_graphics_platform.h | 9 + .../stubbed_server_configuration.cpp | 5 +- .../test_display_server.cpp | 20 +- .../graphics_platform_test_harness.cpp | 54 +- tests/unit-tests/compositor/CMakeLists.txt | 2 +- .../compositor/test_basic_screen_shooter.cpp | 4 +- ...test_default_display_buffer_compositor.cpp | 11 + .../graphics/test_multiplexing_display.cpp | 6 - tests/unit-tests/graphics/test_shm_buffer.cpp | 10 + .../platforms/eglstream-kms/CMakeLists.txt | 1 - .../eglstream-kms/server/CMakeLists.txt | 1 - .../platforms/gbm-kms/kms/CMakeLists.txt | 3 +- .../gbm-kms/kms/test_buffer_allocator.cpp | 137 ++++ .../platforms/gbm-kms/kms/test_display.cpp | 142 +++- .../gbm-kms/kms/test_display_buffer.cpp | 312 +------- .../kms/test_display_configuration.cpp | 15 +- .../gbm-kms/kms/test_display_generic.cpp | 17 +- .../kms/test_display_multi_monitor.cpp | 84 +- .../gbm-kms/kms/test_graphics_platform.cpp | 17 +- .../platforms/gbm-kms/kms/test_platform.cpp | 36 +- .../gbm-kms/kms/test_real_kms_output.cpp | 47 +- tests/unit-tests/platforms/test_display.h | 82 -- .../platforms/test_graphics_platform.h | 67 -- tests/unit-tests/platforms/x11/CMakeLists.txt | 4 +- .../unit-tests/platforms/x11/test_display.cpp | 28 +- .../platforms/x11/test_display_generic.cpp | 107 --- .../platforms/x11/test_graphics_platform.cpp | 76 -- tests/unit-tests/renderers/gl/CMakeLists.txt | 2 +- .../gl/test_basic_buffer_render_target.cpp | 12 +- .../renderers/gl/test_gl_renderer.cpp | 158 ++-- 132 files changed, 5247 insertions(+), 3358 deletions(-) create mode 100644 include/platform/mir/renderer/gl/gl_surface.h create mode 100644 src/platform/graphics/documentation.rst create mode 100644 src/platforms/gbm-kms/server/buffer_allocator.cpp create mode 100644 src/platforms/gbm-kms/server/buffer_allocator.h create mode 100644 src/platforms/gbm-kms/server/kms/dumb_fb.cpp create mode 100644 src/platforms/gbm-kms/server/kms/dumb_fb.h create mode 100644 src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp create mode 100644 src/platforms/gbm-kms/server/kms/kms_framebuffer.h create mode 100644 tests/include/mir/test/doubles/mock_gl_rendering_provider.h rename tests/include/mir/test/{as_render_target.h => doubles/mock_output_surface.h} (50%) create mode 100644 tests/include/mir/test/doubles/stub_gl_rendering_provider.h create mode 100644 tests/unit-tests/platforms/gbm-kms/kms/test_buffer_allocator.cpp delete mode 100644 tests/unit-tests/platforms/test_graphics_platform.h delete mode 100644 tests/unit-tests/platforms/x11/test_display_generic.cpp delete mode 100644 tests/unit-tests/platforms/x11/test_graphics_platform.cpp diff --git a/include/platform/mir/graphics/display.h b/include/platform/mir/graphics/display.h index 7131d4b3517..1d837cccde1 100644 --- a/include/platform/mir/graphics/display.h +++ b/include/platform/mir/graphics/display.h @@ -85,7 +85,7 @@ class DisplaySyncGroup /** * Interface to the display subsystem. */ -class Display : public renderer::gl::ContextSource +class Display { public: /** diff --git a/include/platform/mir/graphics/display_buffer.h b/include/platform/mir/graphics/display_buffer.h index 0af246568d6..793c01741a9 100644 --- a/include/platform/mir/graphics/display_buffer.h +++ b/include/platform/mir/graphics/display_buffer.h @@ -29,17 +29,24 @@ namespace mir namespace graphics { -class Buffer; +class Framebuffer; +class DisplayPlatform; -class NativeDisplayBuffer +struct DisplayElement { -protected: - NativeDisplayBuffer() = default; - virtual ~NativeDisplayBuffer() = default; - NativeDisplayBuffer(NativeDisplayBuffer const&) = delete; - NativeDisplayBuffer operator=(NativeDisplayBuffer const&) = delete; + /// Position and size of this element on-screen + geometry::Rectangle screen_positon; + /** Position and size of region of the buffer to sample from + * + * This will typically be (0,0) ->(buffer width, buffer height) + * to use the whole buffer (with screen_position.size = (buffer width, buffer height)). + * + * Scaling can be specified by having screen_position.size != source_position.size, + * clipping can be specified by having source_position.size != buffer.size + */ + geometry::RectangleF source_position; + std::shared_ptr buffer; }; - /** * Interface to an output framebuffer. */ @@ -64,7 +71,20 @@ class DisplayBuffer * caller should then render the list another way using a graphics * library such as OpenGL. **/ - virtual bool overlay(RenderableList const& renderlist) = 0; + virtual bool overlay(std::vector const& renderlist) = 0; + + /** + * Set the content for the next submission of this display + * + * This is basically a specialisation of overlay(), above, with extra constraints + * and guarantees. Namely: + * * The Framebuffer must be exactly view_area().size big, and + * * The DisplayPlatform guarantees that this call will succeed with a framebuffer + * allocated for this DisplayBuffer + * + * \param content + */ + virtual void set_next_image(std::unique_ptr content) = 0; /** * Returns a transformation that the renderer must apply to all rendering. @@ -75,14 +95,7 @@ class DisplayBuffer */ virtual glm::mat2 transformation() const = 0; - /** Returns a pointer to the native display buffer object backing this - * display buffer. - * - * The pointer to the native display buffer remains valid as long as the - * display buffer object is valid. - */ - virtual NativeDisplayBuffer* native_display_buffer() = 0; - + virtual auto owner() const -> std::shared_ptr = 0; protected: DisplayBuffer() = default; DisplayBuffer(DisplayBuffer const& c) = delete; diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index c976c80fdf0..b975d6b6d3b 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -19,10 +19,16 @@ #include #include +#include +#include +#include "mir/graphics/drm_formats.h" #include "mir/module_properties.h" #include "mir/module_deleter.h" #include "mir/udev/wrapper.h" +#include "mir/renderer/sw/pixel_source.h" + +#include namespace mir { @@ -38,18 +44,77 @@ namespace options class Option; class ProgramOption; } +namespace renderer::software +{ +class WriteMappableBuffer; +} /// Graphics subsystem. Mediates interaction between core system and /// the graphics environment. namespace graphics { class Buffer; +class Framebuffer; class Display; +class DisplayBuffer; class DisplayReport; class DisplayConfigurationPolicy; class GraphicBufferAllocator; class GLConfig; +class RendererInterfaceBase +{ +public: + class Tag + { + public: + Tag() = default; + virtual ~Tag() = default; + }; + + virtual ~RendererInterfaceBase() = default; + + class FramebufferProvider + { + public: + FramebufferProvider() = default; + virtual ~FramebufferProvider() = default; + + FramebufferProvider(FramebufferProvider const&) = delete; + auto operator=(FramebufferProvider const&) = delete; + + /** + * + * \note The returned Framebuffer may share ownership of the provided Buffer. + * It is not necessary for calling code to retain a reference to the Buffer. + * \param buffer + * \return + */ + virtual auto buffer_to_framebuffer(std::shared_ptr buffer) -> std::unique_ptr = 0; + }; + + virtual auto make_framebuffer_provider(DisplayBuffer const& target) + -> std::unique_ptr = 0; +}; + +namespace gl +{ +class Texture; +class OutputSurface; +} + +class GLRenderingProvider : public RendererInterfaceBase +{ +public: + class Tag : public RendererInterfaceBase::Tag + { + }; + + virtual auto as_texture(std::shared_ptr buffer) -> std::shared_ptr = 0; + + virtual auto surface_for_output(DisplayBuffer& db, GLConfig const& config) -> std::unique_ptr = 0; +}; + /** * \defgroup platform_enablement Mir platform enablement * @@ -71,11 +136,229 @@ class RenderingPlatform /** * Creates the buffer allocator subsystem. */ - virtual UniqueModulePtr create_buffer_allocator( - Display const& output) = 0; + virtual UniqueModulePtr create_buffer_allocator(Display const& output) = 0; + + /** + * Attempt to acquire a platform-specific interface from this RenderingPlatform + * + * Any given platform is not guaranteed to implement any specific interface, + * and the set of supported interfaces may depend on the runtime environment. + * + * Since this may result in a runtime probe the call may be costly, and the + * result should be saved rather than re-acquiring an interface each time. + * + * \tparam Interface + * \return On success: an occupied std::shared_ptr + * On failure: std::shared_ptr{nullptr} + */ + template + static auto acquire_interface(std::shared_ptr platform) -> std::shared_ptr + { + static_assert( + std::is_convertible_v, + "Can only acquire a Renderer interface; Interface must implement RendererInterfaceBase"); + + if (auto const base_interface = platform->maybe_create_interface(typename Interface::Tag{})) + { + if (auto const requested_interface = std::dynamic_pointer_cast(base_interface)) + { + return requested_interface; + } + BOOST_THROW_EXCEPTION(( + std::logic_error{ + "Implementation error! Platform returned object that does not support requested interface"})); + } + return nullptr; + } + +protected: + /** + * Acquire a specific hardware interface + * + * This should perform any runtime checks necessary to verify the requested interface is + * expected to work and return a pointer to an implementation of that interface. + * + * This function is guaranteed to be called with `this` managed by a `shared_ptr`; if + * the returned value needs to share ownership with `this`, calls to std::shared_from_this + * can be expected to work. + * + * \param type_tag [in] An instance of the Tag type for the requested interface. + * Implementations are expected to dynamic_cast<> this to + * discover the specific interface being requested. + * \return A pointer to an implementation of the RenderInterfaceBase-derived + * interface that corresponds to the most-derived type of tag_type. + */ + virtual auto maybe_create_interface( + RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr = 0; +}; + +class DisplayInterfaceBase +{ +public: + class Tag + { + public: + Tag() = default; + virtual ~Tag() = default; + }; + + virtual ~DisplayInterfaceBase() = default; +}; + + +class Framebuffer +{ +public: + Framebuffer() = default; + virtual ~Framebuffer() = default; +}; + + +class DumbDisplayProvider : public DisplayInterfaceBase +{ +public: + class Tag : public DisplayInterfaceBase::Tag + { + }; + + class MappableFB : + public Framebuffer, + public mir::renderer::software::WriteMappableBuffer + { + public: + MappableFB() = default; + virtual ~MappableFB() override = default; + }; + + class Allocator + { + public: + Allocator() = default; + virtual ~Allocator() = default; + + /** + * Acquire a buffer for rendering into + * + * This + * + * \return + */ + virtual auto acquire() -> std::unique_ptr = 0; + }; + + DumbDisplayProvider() = default; + + virtual auto allocator_for_db(DisplayBuffer const& db) + -> std::unique_ptr = 0; +}; + +class GBMDisplayProvider : public DisplayInterfaceBase +{ +public: + class Tag : public DisplayInterfaceBase::Tag + { + }; + + /** + * Check if the provided UDev device is the same hardware device as this display + * + * This can be either because they point to the same device node, or because + * the provided device is a Rendernode associated with the display hardware + */ + virtual auto is_same_device(mir::udev::Device const& render_device) const -> bool = 0; + + /** + * Check if the provided DisplayBuffer is driven by this device + */ + virtual auto is_same_device(DisplayBuffer const& db) const -> bool = 0; + + /** + * Get the GBM device for this display + */ + virtual auto gbm_device() const -> std::shared_ptr = 0; + + /** + * Formats supported for output + */ + virtual auto supported_formats() const -> std::vector = 0; + + /** + * Modifiers supported + */ + virtual auto modifiers_for_format(DRMFormat format) const -> std::vector = 0; + + class GBMSurface + { + public: + GBMSurface() = default; + virtual ~GBMSurface() = default; + + virtual operator gbm_surface*() const = 0; + + /** + * Commit the current EGL front buffer as a KMS-displayable Framebuffer + * + * Like the underlying gbm_sufrace_lock_front_buffer GBM API, it is a this + * must be called after at least one call to eglSwapBuffers, and at most + * once per eglSwapBuffers call. + * + * The Framebuffer should not be retained; a GBMSurface has a limited number + * of buffers available and attempting to claim a framebuffer when no buffers + * are free will result in an EBUSY std::system_error being raised. + */ + virtual auto claim_framebuffer() -> std::unique_ptr = 0; + }; + + virtual auto make_surface(geometry::Size size, DRMFormat format, std::span modifiers) -> std::unique_ptr = 0; +}; + +class DmaBufBuffer; + +class DmaBufDisplayProvider : public DisplayInterfaceBase +{ +public: + class Tag : public DisplayInterfaceBase::Tag + { + }; + + virtual auto framebuffer_for(std::shared_ptr buffer) + -> std::unique_ptr = 0; }; -class DisplayPlatform +#ifndef EGLStreamKHR +typedef void* EGLStreamKHR; +#endif + +class EGLStreamDisplayProvider : public DisplayInterfaceBase +{ +public: + class Tag : public DisplayInterfaceBase::Tag + { + }; + + virtual auto claim_stream_for_output(DisplayBuffer& db) -> EGLStreamKHR = 0; +}; + +class GenericEGLDisplayProvider : public DisplayInterfaceBase +{ +public: + class Tag : public DisplayInterfaceBase::Tag + { + }; + + virtual auto get_egl_display() -> EGLDisplay = 0; + + class EGLFramebuffer : public graphics::Framebuffer + { + public: + virtual void make_current() = 0; + virtual auto clone_handle() -> std::unique_ptr = 0; + }; + + virtual auto framebuffer_for_db(DisplayBuffer& db, GLConfig const& config, EGLContext share_context) -> std::unique_ptr = 0; +}; + +class DisplayPlatform : public std::enable_shared_from_this { public: DisplayPlatform() = default; @@ -90,6 +373,58 @@ class DisplayPlatform virtual UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, std::shared_ptr const& gl_config) = 0; + + /** + * Attempt to acquire a platform-specific interface from this DisplayPlatform + * + * Any given platform is not guaranteed to implement any specific interface, + * and the set of supported interfaces may depend on the runtime environment. + * + * Since this may result in a runtime probe the call may be costly, and the + * result should be saved rather than re-acquiring an interface each time. + * + * \tparam Interface + * \return On success: an occupied std::shared_ptr + * On failure: std::shared_ptr{nullptr} + */ + template + static auto acquire_interface(std::shared_ptr const& platform) -> std::shared_ptr + { + static_assert( + std::is_convertible_v, + "Can only acquire a Display interface; Interface must implement DisplayInterfaceBase"); + + if (auto const base_interface = platform->maybe_create_interface(typename Interface::Tag{})) + { + if (auto const requested_interface = std::dynamic_pointer_cast(base_interface)) + { + return requested_interface; + } + BOOST_THROW_EXCEPTION((std::logic_error{ + "Implementation error! Platform returned object that does not support requested interface"})); + } + return nullptr; + } + +protected: + /** + * Acquire a specific hardware interface + * + * This should perform any runtime checks necessary to verify the requested interface is + * expected to work and return a pointer to an implementation of that interface. + * + * This function is guaranteed to be called with `this` managed by a `shared_ptr`; if + * the returned value needs to share ownership with `this`, calls to std::shared_from_this + * can be expected to work. + * + * \param type_tag [in] An instance of the Tag type for the requested interface. + * Implementations are expected to dynamic_cast<> this to + * discover the specific interface being requested. + * \return A pointer to an implementation of the RenderInterfaceBase-derived + * interface that corresponds to the most-derived type of tag_type. + */ + virtual auto maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr = 0; }; /** diff --git a/include/platform/mir/renderer/gl/gl_surface.h b/include/platform/mir/renderer/gl/gl_surface.h new file mode 100644 index 00000000000..fa14c751449 --- /dev/null +++ b/include/platform/mir/renderer/gl/gl_surface.h @@ -0,0 +1,60 @@ +/* + * Copyright © 2021 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 + */ + +#ifndef MIR_RENDERER_GL_SURFACE_H_ +#define MIR_RENDERER_GL_SURFACE_H_ + +#include "mir/geometry/size.h" +#include + +namespace mir +{ +namespace graphics +{ +class Framebuffer; + +namespace gl +{ +class OutputSurface +{ +public: + virtual ~OutputSurface() = default; + + virtual void bind() = 0; + + virtual void make_current() = 0; + + // Naming: SwapBuffers? Commit? Claim current buffer? + virtual auto commit() -> std::unique_ptr = 0; + + /// Size, in pixels, of the underlying surface + virtual auto size() const -> mir::geometry::Size = 0; + + enum class Layout + { + TopRowFirst, //< First row has y-coördinate 0, y increases with each row. + BottomRowFirst, //< First row has y-coördinate $height, y decreases with each row + GL = BottomRowFirst //< GL texture layout is in decreasing-y order. + }; + virtual auto layout() const -> Layout = 0; +}; +} +} +} + +#endif //MIR_RENDERER_GL_SURFACE_H_ diff --git a/include/renderer/mir/renderer/renderer.h b/include/renderer/mir/renderer/renderer.h index 1c1ac7ef298..022995e5024 100644 --- a/include/renderer/mir/renderer/renderer.h +++ b/include/renderer/mir/renderer/renderer.h @@ -24,6 +24,11 @@ namespace mir { +namespace graphics +{ +class Framebuffer; +} + namespace renderer { @@ -34,7 +39,7 @@ class Renderer virtual void set_viewport(geometry::Rectangle const& rect) = 0; virtual void set_output_transform(glm::mat2 const&) = 0; - virtual void render(graphics::RenderableList const&) const = 0; + virtual auto render(graphics::RenderableList const&) const -> std::unique_ptr = 0; virtual void suspend() = 0; // called when render() is skipped protected: diff --git a/include/renderer/mir/renderer/renderer_factory.h b/include/renderer/mir/renderer/renderer_factory.h index df663f2ef43..bd0f30371ef 100644 --- a/include/renderer/mir/renderer/renderer_factory.h +++ b/include/renderer/mir/renderer/renderer_factory.h @@ -23,7 +23,11 @@ namespace mir { namespace graphics { -struct DisplayBuffer; +class GLRenderingProvider; +namespace gl +{ +class OutputSurface; +} } namespace renderer { @@ -39,7 +43,9 @@ class RendererFactory public: virtual ~RendererFactory() = default; - virtual std::unique_ptr create_renderer_for(gl::RenderTarget& render_target) = 0; + virtual auto create_renderer_for( + std::unique_ptr output_surface, + std::shared_ptr gl_provider) const -> std::unique_ptr = 0; protected: RendererFactory() = default; diff --git a/include/renderers/gl/mir/renderer/gl/basic_buffer_render_target.h b/include/renderers/gl/mir/renderer/gl/basic_buffer_render_target.h index ac351f68810..a3f36653252 100644 --- a/include/renderers/gl/mir/renderer/gl/basic_buffer_render_target.h +++ b/include/renderers/gl/mir/renderer/gl/basic_buffer_render_target.h @@ -31,19 +31,20 @@ namespace gl class Context; /// Not threadsafe, do not use concurrently -class BasicBufferRenderTarget: public BufferRenderTarget +class BasicBufferOutputSurface: public BufferOutputSurface { public: - BasicBufferRenderTarget(std::shared_ptr const& ctx); + BasicBufferOutputSurface(std::shared_ptr const& ctx); void set_buffer(std::shared_ptr const& buffer) override; auto size() const -> geometry::Size override; void make_current() override; - void release_current() override; - void swap_buffers() override; + auto commit() -> std::unique_ptr override; void bind() override; + auto layout() const -> Layout override; + private: class Framebuffer { diff --git a/include/renderers/gl/mir/renderer/gl/buffer_render_target.h b/include/renderers/gl/mir/renderer/gl/buffer_render_target.h index 3d71b013a66..eee19897ba9 100644 --- a/include/renderers/gl/mir/renderer/gl/buffer_render_target.h +++ b/include/renderers/gl/mir/renderer/gl/buffer_render_target.h @@ -17,8 +17,8 @@ #ifndef MIR_RENDERER_GL_BUFFER_RENDER_TARGET_H_ #define MIR_RENDERER_GL_BUFFER_RENDER_TARGET_H_ -#include "render_target.h" #include "mir/geometry/size.h" +#include "mir/renderer/gl/gl_surface.h" #include @@ -34,7 +34,7 @@ namespace gl { /// Not threadsafe, do not use concurrently -class BufferRenderTarget: public RenderTarget +class BufferOutputSurface: public graphics::gl::OutputSurface { public: virtual void set_buffer(std::shared_ptr const& buffer) = 0; diff --git a/include/renderers/gl/mir/renderer/gl/context.h b/include/renderers/gl/mir/renderer/gl/context.h index 7c4513c909f..a1fa970947c 100644 --- a/include/renderers/gl/mir/renderer/gl/context.h +++ b/include/renderers/gl/mir/renderer/gl/context.h @@ -17,6 +17,9 @@ #ifndef MIR_RENDERER_GL_CONTEXT_H_ #define MIR_RENDERER_GL_CONTEXT_H_ +#include +#include + namespace mir { namespace renderer @@ -32,6 +35,13 @@ class Context virtual void make_current() const = 0; virtual void release_current() const = 0; + /** + * Create an EGL context that shares this context's sharable-data + */ + virtual auto make_share_context() const -> std::unique_ptr = 0; + + explicit virtual operator EGLContext() = 0; + protected: Context() = default; Context(Context const&) = delete; diff --git a/include/test/mir/test/doubles/null_gl_context.h b/include/test/mir/test/doubles/null_gl_context.h index e602313cdc3..bfa4df2c24a 100644 --- a/include/test/mir/test/doubles/null_gl_context.h +++ b/include/test/mir/test/doubles/null_gl_context.h @@ -29,8 +29,10 @@ namespace doubles class NullGLContext : public renderer::gl::Context { public: - void make_current() const {} - void release_current() const {} + void make_current() const override {} + void release_current() const override {} + auto make_share_context() const -> std::unique_ptr override { return {}; } + explicit operator EGLContext() override { return EGL_NO_DISPLAY; } }; } diff --git a/include/test/mir_test_framework/headless_display_buffer_compositor_factory.h b/include/test/mir_test_framework/headless_display_buffer_compositor_factory.h index 654c89390f9..12b3276a206 100644 --- a/include/test/mir_test_framework/headless_display_buffer_compositor_factory.h +++ b/include/test/mir_test_framework/headless_display_buffer_compositor_factory.h @@ -18,16 +18,30 @@ #define MIR_TEST_FRAMEWORK_HEADLESS_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ #include "mir/compositor/display_buffer_compositor_factory.h" +namespace mir::graphics +{ +class GLRenderingProvider; +class GLConfig; +} + namespace mir_test_framework { class PassthroughTracker; struct HeadlessDisplayBufferCompositorFactory : mir::compositor::DisplayBufferCompositorFactory { - HeadlessDisplayBufferCompositorFactory(); - HeadlessDisplayBufferCompositorFactory(std::shared_ptr const& tracker); + HeadlessDisplayBufferCompositorFactory( + std::shared_ptr render_platform, + std::shared_ptr gl_config); + HeadlessDisplayBufferCompositorFactory( + std::shared_ptr render_platform, + std::shared_ptr gl_config, + std::shared_ptr const& tracker); + std::unique_ptr create_compositor_for( mir::graphics::DisplayBuffer& display_buffer) override; private: + std::shared_ptr const render_platform; + std::shared_ptr const gl_config; std::shared_ptr const tracker; }; } diff --git a/src/platform/graphics/documentation.rst b/src/platform/graphics/documentation.rst new file mode 100644 index 00000000000..ad7db6c49db --- /dev/null +++ b/src/platform/graphics/documentation.rst @@ -0,0 +1,18 @@ +Platform interfaces +=================== + +Top level design +---------------- + +Constraints +___________ + +* Platforms differ in their capabilities +* A single system may require multiple different platforms to support all the hardware + +Relevant objects: +----------------- + +`RenderingPlatform` + Provides graphics buffer allocation services, both to clients and internally to Mir. + In conjunction with the `DisplayPlatform` provides a diff --git a/src/platforms/eglstream-kms/server/CMakeLists.txt b/src/platforms/eglstream-kms/server/CMakeLists.txt index 3e5604e0cae..cb903d80c83 100644 --- a/src/platforms/eglstream-kms/server/CMakeLists.txt +++ b/src/platforms/eglstream-kms/server/CMakeLists.txt @@ -13,17 +13,8 @@ add_library(mirplatformgraphicseglstreamkmsobjects OBJECT platform.cpp buffer_allocator.cpp buffer_allocator.h - display.cpp - display.h - kms_display_configuration.h - kms_display_configuration.cpp - egl_output.h - egl_output.cpp utils.cpp utils.h - drm_event_handler.h - threaded_drm_event_handler.h - threaded_drm_event_handler.cpp ) target_link_libraries(mirplatformgraphicseglstreamkmsobjects diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index ff58a4ff8a5..8b175e725f5 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -15,9 +15,14 @@ */ #include +#include #include "buffer_allocator.h" #include "mir/anonymous_shm_file.h" +#include "mir/graphics/display_buffer.h" +#include "mir/graphics/egl_resources.h" +#include "mir/graphics/gl_config.h" +#include "mir/graphics/platform.h" #include "shm_buffer.h" #include "mir/graphics/buffer_properties.h" #include "mir/renderer/gl/context_source.h" @@ -33,6 +38,8 @@ #include "mir/renderer/gl/context.h" #include "mir/raii.h" #include "mir/wayland/protocol_error.h" +#include "mir/wayland/wayland_base.h" +#include "mir/renderer/gl/gl_surface.h" #define MIR_LOG_COMPONENT "platform-eglstream-kms" #include "mir/log.h" @@ -58,42 +65,10 @@ namespace geom = mir::geometry; #define EGL_WAYLAND_EGLSTREAM_WL 0x334B #endif /* EGL_WL_wayland_eglstream */ -namespace -{ -std::unique_ptr context_for_output(mg::Display const& output) -{ - try - { - auto& context_source = dynamic_cast(output); - - /* - * We care about no part of this context's config; we will do no rendering with it. - * All we care is that we can allocate texture IDs and bind a texture, which is - * config independent. - * - * That's not *entirely* true; we also need it to be on the same device as we want - * to do the rendering on, and that GL must support all the extensions we care about, - * but since we don't yet support heterogeneous hybrid and implementing that will require - * broader interface changes it's a safe enough requirement for now. - */ - return context_source.create_gl_context(); - } - catch (std::bad_cast const& err) - { - std::throw_with_nested( - boost::enable_error_info( - std::runtime_error{"Output platform cannot provide a GL context"}) - << boost::throw_function(__PRETTY_FUNCTION__) - << boost::throw_line(__LINE__) - << boost::throw_file(__FILE__)); - } -} -} - -mge::BufferAllocator::BufferAllocator(mg::Display const& output) - : wayland_ctx{context_for_output(output)}, +mge::BufferAllocator::BufferAllocator(std::unique_ptr ctx) + : wayland_ctx{ctx->make_share_context()}, egl_delegate{ - std::make_shared(context_for_output(output))} + std::make_shared(std::move(ctx))} { } @@ -569,3 +544,320 @@ auto mge::BufferAllocator::buffer_from_shm( std::move(on_consumed), std::move(on_release)); } + +namespace +{ +// libepoxy replaces the GL symbols with resolved-on-first-use function pointers +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>; +using TextureHandle = GLHandle<&glGenTextures, &glDeleteTextures>; + +class CPUCopyOutputSurface : public mg::gl::OutputSurface +{ +public: + CPUCopyOutputSurface( + std::unique_ptr ctx, + std::unique_ptr allocator, + geom::Size size) + : allocator{std::move(allocator)}, + ctx{std::move(ctx)}, + size_{std::move(size)} + { + 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(); + } + + auto commit() -> std::unique_ptr override + { + auto fb = allocator->acquire(); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + { + 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(), + GL_RGBA, 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::unique_ptr const allocator; + std::unique_ptr const ctx; + geom::Size const size_; + RenderbufferHandle const colour_buffer; + FramebufferHandle const fbo; +}; + +auto make_stream_ctx(EGLDisplay dpy, EGLConfig cfg, EGLContext share_with) -> EGLContext +{ + eglBindAPI(EGL_OPENGL_ES_API); + + EGLint const context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLContext context = eglCreateContext(dpy, cfg, share_with, context_attr); + if (context == EGL_NO_CONTEXT) + { + // One of the ways this will happen is if we try to create one of these + // on a different device to the display. + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); + } + + return context; +} + +auto make_output_surface(EGLDisplay dpy, EGLConfig cfg, EGLStreamKHR output_stream, geom::Size size) + -> EGLSurface +{ + EGLint const surface_attribs[] = { + EGL_WIDTH, size.width.as_int(), + EGL_HEIGHT, size.height.as_int(), + EGL_NONE, + }; + auto surface = eglCreateStreamProducerSurfaceKHR(dpy, cfg, output_stream, surface_attribs); + if (surface == EGL_NO_SURFACE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create StreamProducerSurface")); + } + return surface; +} + +class EGLStreamOutputSurface : public mg::gl::OutputSurface +{ +public: + EGLStreamOutputSurface( + EGLDisplay dpy, + EGLConfig config, + EGLContext display_share_context, + EGLStreamKHR output_stream, + geom::Size size) + : dpy{dpy}, + ctx{make_stream_ctx(dpy, config, display_share_context)}, + surface{make_output_surface(dpy, config, output_stream, size)}, + size_{std::move(size)} + { + } + + void bind() override + { + } + + void make_current() override + { + if (eglMakeCurrent(dpy, surface, surface, ctx) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make context current")); + } + } + + auto commit() -> std::unique_ptr override + { + if (eglSwapBuffers(dpy, surface) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("eglSwapBuffers failed")); + } + return {}; + } + + auto size() const -> geom::Size override + { + return size_; + } + + auto layout() const -> Layout override + { + return Layout::GL; + } + +private: + EGLDisplay const dpy; + EGLContext const ctx; + EGLSurface const surface; + geom::Size const size_; +}; + +auto pick_stream_surface_config(EGLDisplay dpy, mg::GLConfig const& gl_config) -> EGLConfig +{ + EGLint const config_attr[] = { + EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, gl_config.depth_buffer_bits(), + EGL_STENCIL_SIZE, gl_config.stencil_buffer_bits(), + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLConfig egl_config; + EGLint num_egl_configs{0}; + + if (eglChooseConfig(dpy, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE || + num_egl_configs != 1) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to choose ARGB EGL config")); + } + return egl_config; +} +} + +mge::GLRenderingProvider::GLRenderingProvider(EGLDisplay dpy, std::unique_ptr ctx) + : dpy{dpy}, + ctx{std::move(ctx)} +{ +} + +auto mir::graphics::eglstream::GLRenderingProvider::as_texture(std::shared_ptr buffer) + -> std::shared_ptr +{ + return std::dynamic_pointer_cast(std::move(buffer)); +} + +auto mge::GLRenderingProvider::surface_for_output( + mg::DisplayBuffer& db, + mg::GLConfig const& gl_config) -> std::unique_ptr +{ + if (auto stream_platform = DisplayPlatform::acquire_interface(db.owner())) + { + try + { + return std::make_unique( + dpy, + pick_stream_surface_config(dpy, gl_config), + static_cast(*ctx), + stream_platform->claim_stream_for_output(db), + db.view_area().size); + } + catch (std::exception const& err) + { + mir::log_info( + "Failed to create EGLStream-backed output surface: %s", + err.what()); + } + } + auto dumb_display = DisplayPlatform::acquire_interface(db.owner()); + + auto fb_context = ctx->make_share_context(); + fb_context->make_current(); + return std::make_unique( + std::move(fb_context), + dumb_display->allocator_for_db(db), + db.view_area().size); +} + +auto mge::GLRenderingProvider::make_framebuffer_provider(mir::graphics::DisplayBuffer const& /*target*/) + -> std::unique_ptr +{ + // TODO: *Can* we provide overlay support? + class NullFramebufferProvider : public FramebufferProvider + { + public: + auto buffer_to_framebuffer(std::shared_ptr) -> std::unique_ptr override + { + // It is safe to return nullptr; this will be treated as “this buffer cannot be used as + // a framebuffer”. + return {}; + } + }; + return std::make_unique(); +} diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.h b/src/platforms/eglstream-kms/server/buffer_allocator.h index ec4afe1b087..2a23441ee4f 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.h +++ b/src/platforms/eglstream-kms/server/buffer_allocator.h @@ -21,6 +21,7 @@ #include "mir/graphics/buffer_id.h" #include "mir/graphics/egl_extensions.h" #include "mir/graphics/egl_context_executor.h" +#include "mir/graphics/platform.h" #include "wayland-eglstream-controller.h" @@ -52,7 +53,7 @@ class BufferAllocator: public graphics::GraphicBufferAllocator { public: - BufferAllocator(graphics::Display const& output); + BufferAllocator(std::unique_ptr ctx); ~BufferAllocator(); std::shared_ptr alloc_software_buffer(geometry::Size size, MirPixelFormat format) override; @@ -91,6 +92,21 @@ class BufferAllocator: static struct wl_eglstream_controller_interface const impl; }; +class GLRenderingProvider : public graphics::GLRenderingProvider +{ +public: + GLRenderingProvider(EGLDisplay dpy, std::unique_ptr ctx); + + auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; + + auto make_framebuffer_provider(DisplayBuffer const& target) -> std::unique_ptr override; + + auto surface_for_output(DisplayBuffer& db, GLConfig const& gl_config) -> std::unique_ptr override; +private: + EGLDisplay dpy; + std::unique_ptr const ctx; +}; + } } } diff --git a/src/platforms/eglstream-kms/server/display.cpp b/src/platforms/eglstream-kms/server/display.cpp index ad2ff336498..5a85cc572fc 100644 --- a/src/platforms/eglstream-kms/server/display.cpp +++ b/src/platforms/eglstream-kms/server/display.cpp @@ -121,12 +121,11 @@ EGLContext create_context(EGLDisplay display, EGLConfig config, EGLContext share class DisplayBuffer : public mg::DisplaySyncGroup, - public mg::DisplayBuffer, - public mg::NativeDisplayBuffer, - public mir::renderer::gl::RenderTarget + public mg::DisplayBuffer { public: DisplayBuffer( + std::shared_ptr owner, mir::Fd drm_node, EGLDisplay dpy, EGLContext ctx, @@ -134,7 +133,8 @@ class DisplayBuffer std::shared_ptr event_handler, mge::kms::EGLOutput const& output, std::shared_ptr display_report) - : dpy{dpy}, + : owner{std::move(owner)}, + dpy{dpy}, ctx{create_context(dpy, config, ctx)}, layer{output.output_layer()}, crtc_id{output.crtc_id()}, @@ -228,7 +228,7 @@ class DisplayBuffer return view_area_; } - bool overlay(const mir::graphics::RenderableList& /*renderlist*/) override + bool overlay(std::vector const&) override { return false; } @@ -238,11 +238,6 @@ class DisplayBuffer return transform; } - mir::graphics::NativeDisplayBuffer* native_display_buffer() override - { - return this; - } - void for_each_display_buffer(const std::function& f) override { f(*this); @@ -286,6 +281,7 @@ class DisplayBuffer private: + std::shared_ptr const owner; EGLDisplay dpy; EGLContext ctx; EGLOutputLayerEXT layer; @@ -432,78 +428,6 @@ std::shared_ptr mge::Display::create_hardware_cursor() return nullptr; } -std::unique_ptr mge::Display::create_gl_context() const -{ - class GLContext : public renderer::gl::Context - { - public: - GLContext(EGLDisplay display, EGLContext shared_context) - : display{display}, - context{make_context(display, shared_context)} - { - } - - void make_current() const override - { - if (eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context) != EGL_TRUE) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make context current")); - } - } - - void release_current() const override - { - if (eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to release context")); - } - } - - private: - static EGLContext make_context(EGLDisplay dpy, EGLContext shared_context) - { - eglBindAPI(EGL_OPENGL_ES_API); - - static const EGLint context_attr[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - EGLint const config_attr[] = { - EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 0, - EGL_DEPTH_SIZE, 0, - EGL_STENCIL_SIZE, 0, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - EGLint num_egl_configs; - EGLConfig egl_config; - if (eglChooseConfig(dpy, config_attr, &egl_config, 1, &num_egl_configs) != EGL_TRUE) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to chose EGL config")); - } else if (num_egl_configs != 1) - { - BOOST_THROW_EXCEPTION(std::runtime_error{"Failed to find compatible EGL config"}); - } - - auto egl_context = eglCreateContext(dpy, egl_config, shared_context, context_attr); - if (egl_context == EGL_NO_CONTEXT) - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); - - return egl_context; - } - - EGLDisplay const display; - EGLContext const context; - }; - return std::make_unique(display, context); -} - bool mge::Display::apply_if_configuration_preserves_display_buffers( mg::DisplayConfiguration const& /*conf*/) { diff --git a/src/platforms/eglstream-kms/server/display.h b/src/platforms/eglstream-kms/server/display.h index 1b1e75c8a23..8604ef08dfb 100644 --- a/src/platforms/eglstream-kms/server/display.h +++ b/src/platforms/eglstream-kms/server/display.h @@ -63,8 +63,6 @@ class Display : public mir::graphics::Display std::shared_ptr create_hardware_cursor() override; - std::unique_ptr create_gl_context() const override; - private: mir::Fd const drm_node; EGLDisplay display; diff --git a/src/platforms/eglstream-kms/server/platform.cpp b/src/platforms/eglstream-kms/server/platform.cpp index da3febfd4d1..36b2879be1d 100644 --- a/src/platforms/eglstream-kms/server/platform.cpp +++ b/src/platforms/eglstream-kms/server/platform.cpp @@ -22,84 +22,153 @@ #include "utils.h" #include "mir/graphics/egl_error.h" +#include "mir/renderer/gl/context.h" #include #include -#include -#include namespace mg = mir::graphics; namespace mge = mir::graphics::eglstream; -namespace mgc = mir::graphics::common; - -mge::DisplayPlatform::DisplayPlatform( - ConsoleServices& console, - EGLDeviceEXT device, - std::shared_ptr display_report) - : display_report{std::move(display_report)}, - display{EGL_NO_DISPLAY} -{ - using namespace std::literals; - auto const devnum = devnum_for_device(device); - drm_device = console.acquire_device( - major(devnum), minor(devnum), - std::make_unique(drm_node)) - .get(); +namespace +{ +const auto mir_xwayland_option = "MIR_XWAYLAND_OPTION"; - if (drm_node == mir::Fd::invalid) +auto make_egl_context(EGLDisplay dpy) -> EGLContext +{ + EGLint const config_attr[] = { + EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, 0, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLConfig egl_config; + EGLint num_egl_configs{0}; + + if (eglChooseConfig(dpy, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE || + num_egl_configs != 1) { - BOOST_THROW_EXCEPTION(( - std::runtime_error{"Failed to acquire DRM device node for device"})); + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to choose ARGB EGL config")); } - int const drm_node_attrib[] = { - EGL_DRM_MASTER_FD_EXT, static_cast(drm_node), EGL_NONE + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE }; - display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, drm_node_attrib); + eglBindAPI(EGL_OPENGL_ES_API); - if (display == EGL_NO_DISPLAY) + EGLContext const ctx = eglCreateContext(dpy, egl_config, EGL_NO_CONTEXT, context_attr); + if (ctx == EGL_NO_CONTEXT) { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGLDisplay on the EGLDeviceEXT")); + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to create EGL context"))); } + return ctx; +} - EGLint major{1}; - EGLint minor{4}; - auto const required_egl_version_major = major; - auto const required_egl_version_minor = minor; - if (eglInitialize(display, &major, &minor) != EGL_TRUE) +class BasicEGLContext : public mir::renderer::gl::Context +{ +public: + BasicEGLContext(EGLDisplay dpy) + : dpy{dpy}, + ctx{make_egl_context(dpy)} { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to initialise EGL")); } - if ((major < required_egl_version_major) || - (major == required_egl_version_major && minor < required_egl_version_minor)) + + BasicEGLContext(EGLDisplay dpy, EGLContext copy_from) + : dpy{dpy}, + ctx{duplicate_context(dpy, copy_from)} { - BOOST_THROW_EXCEPTION((std::runtime_error{ - "Incompatible EGL version"s + - "Wanted 1.4, got " + std::to_string(major) + "." + std::to_string(minor)})); } -} -mir::UniqueModulePtr mge::DisplayPlatform::create_display( - std::shared_ptr const& configuration_policy, - std::shared_ptr const& gl_config) -{ - auto retval = - mir::make_module_ptr( - drm_node, - display, - configuration_policy, - *gl_config, - display_report); - return retval; -} + 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 EGL context current")); + } + } -namespace -{ -const auto mir_xwayland_option = "MIR_XWAYLAND_OPTION"; + 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 override + { + return std::make_unique(dpy, ctx); + } + + explicit operator EGLContext() override + { + return ctx; + } + +private: + static auto get_context_attrib(EGLDisplay dpy, EGLContext ctx, EGLenum attrib) -> EGLint + { + EGLint result; + if (eglQueryContext(dpy, ctx, attrib, &result) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query attribute of EGLContext")); + } + return result; + } + + static auto duplicate_context(EGLDisplay dpy, EGLContext copy_from) -> EGLContext + { + EGLint const api = get_context_attrib(dpy, copy_from, EGL_CONTEXT_CLIENT_TYPE); + std::optional client_version; + if (api == EGL_OPENGL_ES_API) + { + // Client version only exists for GLES contexts + client_version = get_context_attrib(dpy, copy_from, EGL_CONTEXT_CLIENT_VERSION); + } + + EGLint const config_id = get_context_attrib(dpy, copy_from, EGL_CONFIG_ID); + EGLConfig config; + int num_configs; + EGLint const attributes[] = { + EGL_CONFIG_ID, config_id, + EGL_NONE + }; + if (eglChooseConfig(dpy, attributes, &config, 1, &num_configs) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to find existing EGLContext's EGLConfig"))); + } + + std::vector ctx_attribs; + if (client_version) + { + ctx_attribs.push_back(EGL_CONTEXT_CLIENT_VERSION); + ctx_attribs.push_back(client_version.value()); + } + ctx_attribs.push_back(EGL_NONE); + eglBindAPI(api); + auto ctx = eglCreateContext(dpy, config, copy_from, ctx_attribs.data()); + if (ctx == EGL_NO_CONTEXT) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create duplicate context")); + } + return ctx; + } + + EGLDisplay const dpy; + EGLContext const ctx; +}; } -mge::RenderingPlatform::RenderingPlatform() +mge::RenderingPlatform::RenderingPlatform(EGLDisplay dpy) + : dpy{dpy}, + ctx{std::make_unique(dpy)} { setenv(mir_xwayland_option, "-eglstream", 1); } @@ -110,7 +179,17 @@ mge::RenderingPlatform::~RenderingPlatform() } mir::UniqueModulePtr mge::RenderingPlatform::create_buffer_allocator( - mg::Display const& output) + mg::Display const&) { - return mir::make_module_ptr(output); + return mir::make_module_ptr(ctx->make_share_context()); +} + +auto mge::RenderingPlatform::maybe_create_interface( + RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr +{ + if (dynamic_cast(&type_tag)) + { + return std::make_shared(dpy, ctx->make_share_context()); + } + return nullptr; } diff --git a/src/platforms/eglstream-kms/server/platform.h b/src/platforms/eglstream-kms/server/platform.h index dce1403c3a3..bece633b51d 100644 --- a/src/platforms/eglstream-kms/server/platform.h +++ b/src/platforms/eglstream-kms/server/platform.h @@ -43,30 +43,18 @@ namespace eglstream class RenderingPlatform : public graphics::RenderingPlatform { public: - RenderingPlatform(); + RenderingPlatform(EGLDisplay dpy); ~RenderingPlatform() override; UniqueModulePtr create_buffer_allocator(Display const& output) override; -}; - -class DisplayPlatform : public graphics::DisplayPlatform -{ -public: - DisplayPlatform( - ConsoleServices& console, - EGLDeviceEXT device, - std::shared_ptr display_report); - - UniqueModulePtr create_display( - std::shared_ptr const& /*initial_conf_policy*/, - std::shared_ptr const& /*gl_config*/) override; +protected: + auto maybe_create_interface( + RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr override; private: - std::shared_ptr const display_report; - EGLDisplay display; - mir::Fd drm_node; - std::unique_ptr drm_device; + EGLDisplay const dpy; + std::unique_ptr const ctx; }; } } diff --git a/src/platforms/eglstream-kms/server/platform_symbols.cpp b/src/platforms/eglstream-kms/server/platform_symbols.cpp index 0504b58959b..312c5fb9943 100644 --- a/src/platforms/eglstream-kms/server/platform_symbols.cpp +++ b/src/platforms/eglstream-kms/server/platform_symbols.cpp @@ -42,335 +42,41 @@ #include namespace mg = mir::graphics; -namespace mgc = mir::graphics::common; namespace mo = mir::options; namespace mge = mir::graphics::eglstream; -namespace -{ -EGLDeviceEXT find_device() -{ - int device_count{0}; - if (eglQueryDevicesEXT(0, nullptr, &device_count) != EGL_TRUE) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query device count with eglQueryDevicesEXT")); - } - - auto devices = std::make_unique(device_count); - if (eglQueryDevicesEXT(device_count, devices.get(), &device_count) != EGL_TRUE) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get device list with eglQueryDevicesEXT")); - } - - auto device = std::find_if(devices.get(), devices.get() + device_count, - [](EGLDeviceEXT device) - { - auto device_extensions = eglQueryDeviceStringEXT(device, EGL_EXTENSIONS); - if (device_extensions) - { - return strstr(device_extensions, "EGL_EXT_device_drm") != NULL; - } - return false; - }); - - if (device == (devices.get() + device_count)) - { - BOOST_THROW_EXCEPTION(std::runtime_error("Couldn't find EGLDeviceEXT supporting EGL_EXT_device_drm?")); - } - return *device; -} -} - -mir::UniqueModulePtr create_display_platform( - mg::SupportedDevice const&, - std::shared_ptr const& options, - std::shared_ptr const&, - std::shared_ptr const& console, - std::shared_ptr const& display_report) -{ - mir::assert_entry_point_signature(&create_display_platform); - - if (options->is_set(mo::debug_opt)) - { - mg::initialise_egl_logger(); - } - - return mir::make_module_ptr(*console, find_device(), display_report); -} - auto create_rendering_platform( - mg::SupportedDevice const& /*device*/, + mg::SupportedDevice const& device, std::vector> const& /*displays*/, mo::Option const&, mir::EmergencyCleanupRegistry&) -> mir::UniqueModulePtr { mir::assert_entry_point_signature(&create_rendering_platform); - return mir::make_module_ptr(); -} -void add_graphics_platform_options(boost::program_options::options_description& /*config*/) -{ - mir::assert_entry_point_signature(&add_graphics_platform_options); -} + EGLDisplay display{EGL_NO_DISPLAY}; + display = eglGetPlatformDisplayEXT( + EGL_PLATFORM_DEVICE_EXT, + std::any_cast(device.platform_data), + nullptr); -auto probe_display_platform( - std::shared_ptr const& console, - std::shared_ptr const& udev, - mo::ProgramOption const&) -> std::vector -{ - mir::assert_entry_point_signature(&probe_display_platform); - - std::vector missing_extensions; - for (char const* extension : { - "EGL_EXT_platform_base", - "EGL_EXT_platform_device", - "EGL_EXT_device_base",}) - { - if (!epoxy_has_egl_extension(EGL_NO_DISPLAY, extension)) - { - missing_extensions.push_back(extension); - } - } - - if (!missing_extensions.empty()) - { - std::stringstream message; - message << "Missing required extension" << (missing_extensions.size() > 1 ? "s:" : ":"); - for (auto missing_extension : missing_extensions) - { - message << " " << missing_extension; - } - - mir::log_debug("EGLStream platform is unsupported: %s", - message.str().c_str()); - return {}; - } - - int device_count{0}; - if (eglQueryDevicesEXT(0, nullptr, &device_count) != EGL_TRUE) + if (display == EGL_NO_DISPLAY) { - mir::log_info("Platform claims to support EGL_EXT_device_base, but " - "eglQueryDevicesEXT falied: %s", - mg::egl_category().message(eglGetError()).c_str()); - return {}; + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to create EGL Display"))); } - auto devices = std::make_unique(device_count); - if (eglQueryDevicesEXT(device_count, devices.get(), &device_count) != EGL_TRUE) + EGLint major_ver{1}, minor_ver{4}; + if (!eglInitialize(display, &major_ver, &minor_ver)) { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get device list with eglQueryDevicesEXT")); + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to initialise EGL"))); } - std::vector supported_devices; - for (auto i = 0; i != device_count; ++i) - { - auto const& device = devices[i]; - auto device_extensions = eglQueryDeviceStringEXT(device, EGL_EXTENSIONS); - if (device_extensions) - { - mir::log_debug("Found EGLDeviceEXT with device extensions: %s", - device_extensions); - // TODO: This test is not strictly correct (will incorrectly match - // EGL_EXT_device_drmish_but_not_drm) - if (strstr(device_extensions, "EGL_EXT_device_drm") != NULL) - { - // Check we can acquire the device... - mir::Fd drm_fd; - std::unique_ptr device_holder; - try - { - auto const devnum = mge::devnum_for_device(device); - - device_holder = console->acquire_device( - major(devnum), minor(devnum), - std::make_unique(drm_fd)).get(); - - supported_devices.emplace_back( - mg::SupportedDevice{ - udev->char_device_from_devnum(devnum), - mg::PlatformPriority::unsupported, - nullptr - }); - } - catch (std::exception const& e) - { - mir::log_info("Failed to query DRM node for EGLDevice: %s", e.what()); - continue; - } - - if (drm_fd == mir::Fd::invalid) - { - mir::log_debug( - "EGL_EXT_device_drm found, but can't acquire DRM node."); - continue; - } - - // Check that the drm device is usable by setting the interface version we use (1.4) - drmSetVersion sv; - sv.drm_di_major = 1; - sv.drm_di_minor = 4; - sv.drm_dd_major = -1; /* Don't care */ - sv.drm_dd_minor = -1; /* Don't care */ - - if (auto error = -drmSetInterfaceVersion(drm_fd, &sv)) - { - mir::log_warning( - "Failed to set DRM interface version on device: %i (%s)", - error, - strerror(error)); - continue; - } - - auto busid = std::unique_ptr{ - drmGetBusid(drm_fd), - &drmFreeBusid - }; - if (auto err = drmCheckModesettingSupported(busid.get())) - { - if (err == -ENOSYS) - { - mir::log_info("EGL_EXT_device_drm found, but no KMS support"); - mir::log_info("You may need to set the nvidia_drm.modeset kernel parameter"); - } - else - { - mir::log_warning( - "Failed to check DRM modesetting support for device %s: %s (%i)", - busid.get(), - strerror(-err), - -err); - } - continue; - } - - mg::kms::DRMModeResources kms_resources{drm_fd}; - if ( - kms_resources.num_connectors() == 0 || - kms_resources.num_encoders() == 0 || - kms_resources.num_crtcs() == 0) - { - mir::log_info("KMS support found, but device has no output hardware."); - mir::log_info("This is probably a render-only hybrid graphics device"); - continue; - } - - EGLDisplay display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, nullptr); - - if (display == EGL_NO_DISPLAY) - { - mir::log_debug("Failed to create EGLDisplay: %s", mg::egl_category().message(eglGetError()).c_str()); - continue; - } - - auto egl_init = mir::raii::paired_calls( - [&display]() - { - EGLint major_ver{1}, minor_ver{4}; - if (!eglInitialize(display, &major_ver, &minor_ver)) - { - mir::log_debug("Failed to initialise EGL: %s", mg::egl_category().message(eglGetError()).c_str()); - display = EGL_NO_DISPLAY; - } - }, - [&display]() - { - if (display != EGL_NO_DISPLAY) - { - eglTerminate(display); - } - }); - - if (display == EGL_NO_DISPLAY) - { - continue; - } - - eglBindAPI(EGL_OPENGL_ES_API); - EGLint const config_attribs[] = { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR, - EGL_NONE - }; - EGLConfig config; - EGLint num_configs; - if (eglChooseConfig(display, config_attribs, &config, 1, &num_configs) != EGL_TRUE) - { - mir::log_warning("Failed to create EGL context: no EGL_STREAM_BIT_KHR configs supported"); - continue; - } - EGLContext ctx{EGL_NO_CONTEXT}; - auto ctx_init = mir::raii::paired_calls( - [&ctx, display, config]() - { - EGLint const context_attr[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attr); - }, - [&ctx, display]() - { - if (ctx != EGL_NO_CONTEXT) - { - eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroyContext(display, ctx); - } - }); - - if (ctx == EGL_NO_CONTEXT) - { - mir::log_warning("Failed to create EGL context: %s", mg::egl_category().message(eglGetError()).c_str()); - continue; - } - - eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx); - - auto const gl_version = reinterpret_cast(glGetString(GL_VERSION)); - if (!gl_version) - { - mir::log_warning("glGetString(GL_VERSION) call failed. This probably indicates a problem with the GL drivers."); - } - else if (auto version = mge::parse_nvidia_version(gl_version)) - { - mir::log_debug("Detected NVIDIA driver version %i.%i", version->major, version->minor); - if (version->major < 396) - { - mir::log_warning( - "Detected NVIDIA driver version %i.%i is older than 396.xx", - version->major, - version->minor); - mir::log_warning( - "This driver is known to interact badly with Mir. See https://github.com/MirServer/mir/issues/650"); - mir::log_warning( - "Mir will not auto-load the eglstream-kms platform on this driver. To proceed anyway, manually specify the platform library."); - continue; - } - } - - if (epoxy_has_egl_extension(display, "EGL_EXT_output_base")) - { - supported_devices.back().support_level = mg::PlatformPriority::best; - } - else - { - mir::log_debug("EGL_EXT_device_drm found, but missing EGL_EXT_output_base extension."); - mir::log_debug("Available extensions are: %s", eglQueryString(display, EGL_EXTENSIONS)); - } - } - } - else - { - mir::log_debug("Found EGLDeviceEXT with no device extensions"); - } - } - if (supported_devices.empty()) - { - mir::log_debug( - "EGLDeviceEXTs found, but none are suitable for Mir"); - } + return mir::make_module_ptr(display); +} - return supported_devices; +void add_graphics_platform_options(boost::program_options::options_description& /*config*/) +{ + mir::assert_entry_point_signature(&add_graphics_platform_options); } auto probe_rendering_platform( @@ -430,7 +136,7 @@ auto probe_rendering_platform( supported_devices.emplace_back(mg::SupportedDevice{ udev->char_device_from_devnum(mge::devnum_for_device(device)), mg::PlatformPriority::unsupported, - nullptr + device }); } catch (std::exception const& e) @@ -490,7 +196,13 @@ auto probe_rendering_platform( } } } - if (supported_devices.empty()) + if (!std::any_of( + supported_devices.begin(), + supported_devices.end(), + [](auto const& device) + { + return device.support_level > mg::PlatformPriority::unsupported; + })) { mir::log_debug( "EGLDeviceEXTs found, but none are suitable for Mir"); diff --git a/src/platforms/gbm-kms/server/CMakeLists.txt b/src/platforms/gbm-kms/server/CMakeLists.txt index c97843c0180..fbef43749ed 100644 --- a/src/platforms/gbm-kms/server/CMakeLists.txt +++ b/src/platforms/gbm-kms/server/CMakeLists.txt @@ -4,6 +4,8 @@ add_library( mirsharedgbmservercommon-static STATIC display_helpers.cpp + buffer_allocator.cpp + buffer_allocator.h ) target_include_directories( diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp new file mode 100644 index 00000000000..7c5b5ddf538 --- /dev/null +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -0,0 +1,726 @@ +/* + * 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 . + */ + +#include "buffer_allocator.h" +#include "mir/graphics/gl_config.h" +#include "mir/graphics/linux_dmabuf.h" +#include "mir/anonymous_shm_file.h" +#include "mir/renderer/sw/pixel_source.h" +#include "mir/graphics/platform.h" +#include "shm_buffer.h" +#include "mir/graphics/egl_context_executor.h" +#include "mir/graphics/egl_extensions.h" +#include "mir/graphics/egl_error.h" +#include "mir/graphics/buffer_properties.h" +#include "mir/raii.h" +#include "mir/graphics/display.h" +#include "mir/renderer/gl/context.h" +#include "mir/renderer/gl/context_source.h" +#include "mir/graphics/egl_wayland_allocator.h" +#include "mir/executor.h" +#include "mir/renderer/gl/gl_surface.h" +#include "mir/graphics/display_buffer.h" +#include "kms/egl_helper.h" +#include "mir/graphics/drm_formats.h" +#include "display_helpers.h" +#include "mir/graphics/egl_error.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MIR_LOG_COMPONENT "gbm-kms-buffer-allocator" +#include +#include + +namespace mg = mir::graphics; +namespace mgg = mg::gbm; +namespace mgc = mg::common; +namespace geom = mir::geometry; + +namespace +{ +auto make_share_only_context(EGLDisplay dpy, std::optional share_with) -> EGLContext +{ + eglBindAPI(EGL_OPENGL_ES_API); + + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint const config_attr[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLConfig cfg; + 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"))); + } + + auto ctx = eglCreateContext(dpy, cfg, share_with.value_or(EGL_NO_CONTEXT), context_attr); + if (ctx == EGL_NO_CONTEXT) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); + } + return ctx; +} + +class SurfacelessEGLContext : public mir::renderer::gl::Context +{ +public: + SurfacelessEGLContext(EGLDisplay dpy) + : dpy{dpy}, + ctx{make_share_only_context(dpy, {})} + { + } + + SurfacelessEGLContext(EGLDisplay dpy, EGLContext share_with) + : dpy{dpy}, + ctx{make_share_only_context(dpy, share_with)} + { + } + + ~SurfacelessEGLContext() override + { + 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 override + { + return std::unique_ptr{new SurfacelessEGLContext{dpy, ctx}}; + } + + explicit operator EGLContext() override + { + return ctx; + } +private: + EGLDisplay const dpy; + EGLContext const ctx; +}; +} + +mgg::BufferAllocator::BufferAllocator(EGLDisplay dpy, EGLContext share_with) + : ctx{std::make_unique(dpy, share_with)}, + egl_delegate{ + std::make_shared(ctx->make_share_context())}, + egl_extensions(std::make_shared()) +{ +} + +std::shared_ptr mgg::BufferAllocator::alloc_software_buffer( + geom::Size size, MirPixelFormat format) +{ + if (!mgc::MemoryBackedShmBuffer::supports(format)) + { + BOOST_THROW_EXCEPTION( + std::runtime_error( + "Trying to create SHM buffer with unsupported pixel format")); + } + + return std::make_shared(size, format, egl_delegate); +} + +std::vector mgg::BufferAllocator::supported_pixel_formats() +{ + /* + * supported_pixel_formats() is kind of a kludge. The right answer depends + * on whether you're using hardware or software, and it depends on + * the usage type (e.g. scanout). In the future it's also expected to + * depend on the GPU model in use at runtime. + * To be precise, ShmBuffer now supports OpenGL compositing of all + * but one MirPixelFormat (bgr_888). But GBM only supports [AX]RGB. + * So since we don't yet have an adequate API in place to query what the + * intended usage will be, we need to be conservative and report the + * intersection of ShmBuffer and GBM's pixel format support. That is + * just these two. Be aware however you can create a software surface + * with almost any pixel format and it will also work... + * TODO: Convert this to a loop that just queries the intersection of + * gbm_device_is_format_supported and ShmBuffer::supports(), however not + * yet while the former is buggy. (FIXME: LP: #1473901) + */ + static std::vector const pixel_formats{ + mir_pixel_format_argb_8888, + mir_pixel_format_xrgb_8888 + }; + + return pixel_formats; +} + +void mgg::BufferAllocator::bind_display(wl_display* display, std::shared_ptr wayland_executor) +{ + auto context_guard = mir::raii::paired_calls( + [this]() { ctx->make_current(); }, + [this]() { ctx->release_current(); }); + auto dpy = eglGetCurrentDisplay(); + + try + { + mg::wayland::bind_display(dpy, display, *egl_extensions); + egl_display_bound = true; + } + catch (...) + { + log( + logging::Severity::warning, + MIR_LOG_COMPONENT, + std::current_exception(), + "Failed to bind EGL Display to Wayland display, falling back to software buffers"); + } + + try + { + mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; + dmabuf_extension = + std::unique_ptr>( + new LinuxDmaBufUnstable{ + display, + dpy, + egl_extensions, + modifier_ext, + }, + [wayland_executor](LinuxDmaBufUnstable* global) + { + // The global must be destroyed on the Wayland thread + wayland_executor->spawn( + [global]() + { + /* This is safe against double-frees, as the WaylandExecutor + * guarantees that work scheduled will only run while the Wayland + * event loop is running, and the main loop is stopped before + * wl_display_destroy() frees any globals + * + * This will, however, leak the global if the main loop is destroyed + * before the buffer allocator. Fixing that requires work in the + * wrapper generator. + */ + delete global; + }); + }); + mir::log_info("Enabled linux-dmabuf import support"); + } + catch (std::runtime_error const& error) + { + mir::log_info( + "Cannot enable linux-dmabuf import support: %s", error.what()); + mir::log( + mir::logging::Severity::debug, + MIR_LOG_COMPONENT, + std::current_exception(), + "Detailed error: "); + } + + this->wayland_executor = std::move(wayland_executor); +} + +void mgg::BufferAllocator::unbind_display(wl_display* display) +{ + if (egl_display_bound) + { + auto context_guard = mir::raii::paired_calls( + [this]() { ctx->make_current(); }, + [this]() { ctx->release_current(); }); + auto dpy = eglGetCurrentDisplay(); + + mg::wayland::unbind_display(dpy, display, *egl_extensions); + } +} + +std::shared_ptr mgg::BufferAllocator::buffer_from_resource( + wl_resource* buffer, + std::function&& on_consumed, + std::function&& on_release) +{ + auto context_guard = mir::raii::paired_calls( + [this]() { ctx->make_current(); }, + [this]() { ctx->release_current(); }); + + if (auto dmabuf = dmabuf_extension->buffer_from_resource( + buffer, + std::function{on_consumed}, + std::function{on_release}, + egl_delegate)) + { + return dmabuf; + } + return mg::wayland::buffer_from_resource( + buffer, + std::move(on_consumed), + std::move(on_release), + *egl_extensions, + egl_delegate); +} + +auto mgg::BufferAllocator::buffer_from_shm( + std::shared_ptr data, + std::function&& on_consumed, + std::function&& on_release) -> std::shared_ptr +{ + return std::make_shared( + std::move(data), + egl_delegate, + std::move(on_consumed), + std::move(on_release)); +} + +auto mgg::BufferAllocator::shared_egl_context() -> EGLContext +{ + return static_cast(*ctx); +} + +auto mgg::GLRenderingProvider::as_texture(std::shared_ptr buffer) -> std::shared_ptr +{ + return std::dynamic_pointer_cast(buffer); +} + +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>; + + +class CPUCopyOutputSurface : public mg::gl::OutputSurface +{ +public: + CPUCopyOutputSurface( + EGLDisplay dpy, + EGLContext ctx, + std::unique_ptr allocator, + geom::Size size) + : allocator{std::move(allocator)}, + dpy{dpy}, + ctx{ctx}, + size_{std::move(size)} + { + 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); + } + + auto commit() -> std::unique_ptr override + { + auto fb = allocator->acquire(); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + { + 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(), + GL_RGBA, 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::unique_ptr const allocator; + EGLDisplay const dpy; + EGLContext const ctx; + geom::Size const size_; + RenderbufferHandle const colour_buffer; + FramebufferHandle const fbo; +}; + +class GBMOutputSurface : public mg::gl::OutputSurface +{ +public: + GBMOutputSurface( + EGLDisplay dpy, + EGLContext share_context, + mg::GLConfig const& config, + mg::GBMDisplayProvider& display, + mg::DRMFormat format, + mir::geometry::Size size) + : GBMOutputSurface( + size, + dpy, + create_renderable(dpy, share_context, format, config, display, size)) + { + } + + void bind() override + { + if (!gbm_surface_has_free_buffers(*surface)) + { + BOOST_THROW_EXCEPTION((std::logic_error{"Attempt to render to GBM surface before releasing previous front buffer"})); + } + } + + void make_current() override + { + if (eglMakeCurrent(dpy, egl_surf, egl_surf, ctx) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make EGL context current")); + } + } + + auto commit() -> std::unique_ptr override + { + if (eglSwapBuffers(dpy, egl_surf) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("eglSwapBuffers failed")); + } + return surface->claim_framebuffer(); + } + + auto size() const -> geom::Size override + { + return size_; + } + + auto layout() const -> Layout override + { + return Layout::GL; + } + +private: + static auto get_matching_configs(EGLDisplay dpy, EGLint const attr[]) -> std::vector + { + EGLint num_egl_configs; + + // First query the number of matching configs… + if ((eglChooseConfig(dpy, attr, nullptr, 0, &num_egl_configs) == EGL_FALSE) || + (num_egl_configs == 0)) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to enumerate any matching EGL configs")); + } + + std::vector matching_configs(static_cast(num_egl_configs)); + if ((eglChooseConfig(dpy, attr, matching_configs.data(), static_cast(matching_configs.size()), &num_egl_configs) == EGL_FALSE) || + (num_egl_configs == 0)) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to acquire matching EGL configs")); + } + + matching_configs.resize(static_cast(num_egl_configs)); + return matching_configs; + } + + + static auto egl_config_for_format(EGLDisplay dpy, mg::GLConfig const& config, mg::DRMFormat format) -> std::optional + { + mg::DRMFormat::RGBComponentInfo const default_components = { 8, 8, 8, 0}; + + EGLint const config_attr[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, static_cast(format.components().value_or(default_components).red_bits), + EGL_GREEN_SIZE, static_cast(format.components().value_or(default_components).green_bits), + EGL_BLUE_SIZE, static_cast(format.components().value_or(default_components).blue_bits), + EGL_ALPHA_SIZE, static_cast(format.components().value_or(default_components).alpha_bits.value_or(0)), + EGL_DEPTH_SIZE, config.depth_buffer_bits(), + EGL_STENCIL_SIZE, config.stencil_buffer_bits(), + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + for (auto const& config : get_matching_configs(dpy, config_attr)) + { + EGLint id; + if (eglGetConfigAttrib(dpy, config, EGL_NATIVE_VISUAL_ID, &id) == EGL_FALSE) + { + mir::log_warning( + "Failed to query GBM format of EGLConfig: %s", + mg::egl_category().message(eglGetError()).c_str()); + continue; + } + + if (id == static_cast(format)) + { + // We've found our matching format, so we're done here. + return config; + } + } + return std::nullopt; + } + + static auto create_renderable( + EGLDisplay dpy, + EGLContext share_context, + mg::DRMFormat format, + mg::GLConfig const& config, + mg::GBMDisplayProvider& display, + mir::geometry::Size size) + -> std::tuple, EGLContext, EGLSurface> + { + mg::EGLExtensions::PlatformBaseEXT egl_ext; + + auto const [egl_cfg, resolved_format] = + [&]() -> std::pair + { + if (auto eglconfig = egl_config_for_format(dpy, config, format)) + { + return std::make_pair(eglconfig.value(), format); + } + + auto alternate_format = + [&]() -> std::optional + { + if (format.has_alpha()) + { + return format.opaque_equivalent(); + } + return format.alpha_equivalent(); + }(); + + if (alternate_format) + { + if (auto eglconfig = egl_config_for_format(dpy, config, *alternate_format)) + { + return std::make_pair(eglconfig.value(), *alternate_format); + } + } + + BOOST_THROW_EXCEPTION(( + std::runtime_error{ + std::string{"Failed to find EGL config matching DRM format: "} + + format.name()})); + }(); + + auto modifiers = display.modifiers_for_format(resolved_format); + + auto surf = display.make_surface(size, resolved_format, modifiers); + + auto egl_surf = egl_ext.eglCreatePlatformWindowSurface( + dpy, + egl_cfg, + *surf, + nullptr); + + if (egl_surf == EGL_NO_SURFACE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL window surface")); + } + + + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + auto egl_ctx = eglCreateContext(dpy, egl_cfg, share_context, context_attr); + if (egl_ctx == EGL_NO_CONTEXT) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); + } + + + return std::make_tuple(std::move(surf), egl_ctx, egl_surf); + } + + GBMOutputSurface( + geom::Size size, + EGLDisplay dpy, + std::tuple, EGLContext, EGLSurface> renderables) + : size_{size}, + surface{std::move(std::get<0>(renderables))}, + egl_surf{std::get<2>(renderables)}, + dpy{dpy}, + ctx{std::get<1>(renderables)} + { + } + + geom::Size const size_; + std::unique_ptr const surface; + EGLSurface const egl_surf; + EGLDisplay const dpy; + EGLContext const ctx; +}; +} + +auto mgg::GLRenderingProvider::surface_for_output(DisplayBuffer& db, GLConfig const& config) + -> std::unique_ptr +{ + if (bound_display && bound_display->is_same_device(db)) + { + return std::make_unique( + dpy, + ctx, + config, + *bound_display, + DRMFormat{DRM_FORMAT_XRGB8888}, + db.view_area().size); + } + auto dumb_display = DisplayPlatform::acquire_interface(db.owner()); + + return std::make_unique( + dpy, + ctx, + dumb_display->allocator_for_db(db), + db.view_area().size); +} + +auto mgg::GLRenderingProvider::make_framebuffer_provider(DisplayBuffer const& /*target*/) + -> std::unique_ptr +{ + // TODO: Make this not a null implementation, so bypass/overlays can work again + class NullFramebufferProvider : public FramebufferProvider + { + public: + auto buffer_to_framebuffer(std::shared_ptr) -> std::unique_ptr override + { + // It is safe to return nullptr; this will be treated as “this buffer cannot be used as + // a framebuffer”. + return {}; + } + }; + return std::make_unique(); +} + +mgg::GLRenderingProvider::GLRenderingProvider( + udev::Device const& device, + std::shared_ptr associated_display, + EGLDisplay dpy, + EGLContext ctx) + : device{device}, + bound_display{std::move(associated_display)}, + dpy{dpy}, + ctx{ctx} +{ +} diff --git a/src/platforms/gbm-kms/server/buffer_allocator.h b/src/platforms/gbm-kms/server/buffer_allocator.h new file mode 100644 index 00000000000..4b4aadb7e64 --- /dev/null +++ b/src/platforms/gbm-kms/server/buffer_allocator.h @@ -0,0 +1,109 @@ +/* + * 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 . + */ + +#ifndef MIR_GRAPHICS_EGL_GENERIC_BUFFER_ALLOCATOR_H_ +#define MIR_GRAPHICS_EGL_GENERIC_BUFFER_ALLOCATOR_H_ + +#include "mir/graphics/graphic_buffer_allocator.h" +#include "mir/graphics/linux_dmabuf.h" +#include "mir/graphics/platform.h" + +#include +#include + +#include + +namespace mir +{ +namespace renderer +{ +namespace gl +{ +class Context; +} +} +namespace graphics +{ +class Display; +struct EGLExtensions; + +namespace common +{ +class EGLContextExecutor; +} + +namespace gbm +{ + +class GLRenderingProvider; + +class BufferAllocator: + public graphics::GraphicBufferAllocator +{ +public: + BufferAllocator(EGLDisplay dpy, EGLContext share_with); + + std::shared_ptr alloc_software_buffer(geometry::Size size, MirPixelFormat) override; + std::vector supported_pixel_formats() override; + + void bind_display(wl_display* display, std::shared_ptr wayland_executor) override; + void unbind_display(wl_display* display) override; + auto buffer_from_resource( + wl_resource* buffer, + std::function&& on_consumed, + std::function&& on_release) -> std::shared_ptr override; + auto buffer_from_shm( + std::shared_ptr data, + std::function&& on_consumed, + std::function&& on_release) -> std::shared_ptr override; + + auto shared_egl_context() -> EGLContext; +private: + std::unique_ptr const ctx; + std::shared_ptr const egl_delegate; + std::shared_ptr wayland_executor; + std::unique_ptr> dmabuf_extension; + std::shared_ptr const egl_extensions; + bool egl_display_bound{false}; +}; + +class GLRenderingProvider : public graphics::GLRenderingProvider +{ +public: + GLRenderingProvider( + udev::Device const& device, + std::shared_ptr associated_display, + EGLDisplay dpy, + EGLContext ctx); + + auto make_framebuffer_provider(DisplayBuffer const& target) + -> std::unique_ptr override; + + auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; + + auto surface_for_output(DisplayBuffer& db, GLConfig const& config) -> std::unique_ptr override; + +private: + udev::Device const& device; + std::shared_ptr const bound_display; ///< Associated Display provider (if any - null is valid) + EGLDisplay const dpy; + EGLContext const ctx; +}; +} +} +} + +#endif // MIR_GRAPHICS_EGL_GENERIC_BUFFER_ALLOCATOR_H_ diff --git a/src/platforms/gbm-kms/server/kms/CMakeLists.txt b/src/platforms/gbm-kms/server/kms/CMakeLists.txt index a6670a5b29b..12822675cf1 100644 --- a/src/platforms/gbm-kms/server/kms/CMakeLists.txt +++ b/src/platforms/gbm-kms/server/kms/CMakeLists.txt @@ -37,12 +37,17 @@ add_library( egl_helper.cpp quirks.cpp quirks.h + kms_framebuffer.h + dumb_fb.cpp + dumb_fb.h ) target_link_libraries( mirplatformgraphicsgbmkmsobjects - mirsharedgbmservercommon-static + PRIVATE + mirsharedgbmservercommon-static + ${GBM_LDFLAGS} ${GBM_LIBRARIES} ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/symbols.map.in diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index cdba44dda5f..6627197af50 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -17,11 +17,14 @@ #include "display.h" #include "cursor.h" #include "kms/egl_helper.h" +#include "mir/graphics/platform.h" #include "platform.h" #include "display_buffer.h" #include "kms_display_configuration.h" #include "kms_output.h" #include "kms_page_flipper.h" +#include "kms_framebuffer.h" +#include "dumb_fb.h" #include "mir/console_services.h" #include "mir/graphics/overlapping_output_grouping.h" #include "mir/graphics/event_handler_register.h" @@ -32,16 +35,25 @@ #include "mir/geometry/rectangle.h" #include "mir/renderer/gl/context.h" #include "mir/graphics/drm_formats.h" +#include "mir/graphics/egl_error.h" #include #include -#include +#include +#include +#include +#include #define MIR_LOG_COMPONENT "gbm-kms" #include "mir/log.h" #include "kms-utils/drm_mode_resources.h" #include "kms-utils/kms_connector.h" +#include +#include +#include +#include + #include #include #include @@ -52,43 +64,6 @@ namespace geom = mir::geometry; namespace { - -class GBMGLContext : public mir::renderer::gl::Context -{ -public: - GBMGLContext(mgg::helpers::GBMHelper const& gbm, - mg::GLConfig const& gl_config, - EGLContext shared_context) - : egl{gl_config} - { - egl.setup(gbm, shared_context); - } - - void make_current() const override - { - egl.make_current(); - } - - void release_current() const override - { - egl.release_current(); - } - -private: - mgg::helpers::EGLHelper egl; -}; - -std::vector drm_fds_from_drm_helpers( - std::vector> const& helpers) -{ - std::vector fds; - for (auto const& helper: helpers) - { - fds.push_back(helper->fd); - } - return fds; -} - double calculate_vrefresh_hz(drmModeModeInfo const& mode) { if (mode.htotal == 0 || mode.vtotal == 0) @@ -117,97 +92,79 @@ char const* describe_connection_status(drmModeConnector const& connection) } } -void log_drm_details(std::vector> const& drm) +void log_drm_details(mir::Fd const& drm_fd) { mir::log_info("DRM device details:"); - for (auto const& device : drm) - { - auto version = std::unique_ptr{ - drmGetVersion(device->fd), - &drmFreeVersion}; - - auto device_name = std::unique_ptr{ - drmGetDeviceNameFromFd(device->fd), - &free - }; + auto version = std::unique_ptr{ + drmGetVersion(drm_fd), + &drmFreeVersion}; + + auto device_name = std::unique_ptr{ + drmGetDeviceNameFromFd(drm_fd), + &free + }; + + mir::log_info( + "%s: using driver %s [%s] (version: %i.%i.%i driver date: %s)", + device_name.get(), + version->name, + version->desc, + version->version_major, + version->version_minor, + version->version_patchlevel, + version->date); - mir::log_info( - "%s: using driver %s [%s] (version: %i.%i.%i driver date: %s)", - device_name.get(), - version->name, - version->desc, - version->version_major, - version->version_minor, - version->version_patchlevel, - version->date); - - try + try + { + mg::kms::DRMModeResources resources{drm_fd}; + for (auto const& connector : resources.connectors()) { - mg::kms::DRMModeResources resources{device->fd}; - for (auto const& connector : resources.connectors()) + mir::log_info( + "\tOutput: %s (%s)", + mg::kms::connector_name(connector).c_str(), + describe_connection_status(*connector)); + for (auto i = 0; i < connector->count_modes; ++i) { mir::log_info( - "\tOutput: %s (%s)", - mg::kms::connector_name(connector).c_str(), - describe_connection_status(*connector)); - for (auto i = 0; i < connector->count_modes; ++i) - { - mir::log_info( - "\t\tMode: %i×%i@%.2f", - connector->modes[i].hdisplay, - connector->modes[i].vdisplay, - calculate_vrefresh_hz(connector->modes[i])); - } + "\t\tMode: %i×%i@%.2f", + connector->modes[i].hdisplay, + connector->modes[i].vdisplay, + calculate_vrefresh_hz(connector->modes[i])); } } - catch (std::exception const& error) - { - mir::log_info( - "\tKMS not supported (%s)", - error.what()); - } + } + catch (std::exception const& error) + { + mir::log_info( + "\tKMS not supported (%s)", + error.what()); } } } -mgg::Display::Display(std::vector> const& drm, - std::shared_ptr const& gbm, - mgg::BypassOption bypass_option, - std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config, - std::shared_ptr const& listener) - : drm{drm}, - gbm(gbm), +mgg::Display::Display( + std::shared_ptr parent, + mir::Fd drm_fd, + mgg::BypassOption bypass_option, + std::shared_ptr const& initial_conf_policy, + std::shared_ptr const& listener) + : owner{std::move(parent)}, + drm_fd{std::move(drm_fd)}, listener(listener), monitor(mir::udev::Context()), - shared_egl{*gl_config}, output_container{ std::make_shared( - drm_fds_from_drm_helpers(drm), - [ - listener, - flippers = std::unordered_map>{} - ](int drm_fd) mutable - { - auto& flipper = flippers[drm_fd]; - if (!flipper) - { - flipper = std::make_shared(drm_fd, listener); - } - return flipper; - })}, + this->drm_fd, + std::make_shared(this->drm_fd, listener))}, current_display_configuration{output_container}, dirty_configuration{false}, - bypass_option(bypass_option), - gl_config{gl_config} + bypass_option(bypass_option) { - shared_egl.setup(*gbm); - monitor.filter_by_subsystem_and_type("drm", "drm_minor"); monitor.enable(); - log_drm_details(drm); + log_drm_details(this->drm_fd); initial_conf_policy->apply_to(current_display_configuration); @@ -368,11 +325,6 @@ void mgg::Display::clear_connected_unused_outputs() }); } -std::unique_ptr mgg::Display::create_gl_context() const -{ - return std::make_unique(*gbm, *gl_config, shared_egl.context()); -} - bool mgg::Display::apply_if_configuration_preserves_display_buffers( mg::DisplayConfiguration const& conf) { @@ -392,101 +344,6 @@ bool mgg::Display::apply_if_configuration_preserves_display_buffers( return result; } -namespace -{ -/* - * Add output to the grouping, maintaining the invariant that each vector of outputs - * is a single GPU memory domain. - */ -void add_to_drm_device_group( - std::vector>>& grouping, - std::shared_ptr&& output) -{ - for (auto &group : grouping) - { - /* - * We could be smarter about this, but being on the same DRM device is guaranteed - * to be in the same GPU memory domain :). - */ - if (group.front()->drm_fd() == output->drm_fd()) - { - group.push_back(std::move(output)); - break; - } - } - if (output) - { - grouping.push_back(std::vector>{std::move(output)}); - } -} - -auto get_equivalent_other_alphaness_format(mg::DRMFormat const format) -> std::optional -{ - if (format.has_alpha()) - { - return format.opaque_equivalent(); - } - else - { - return format.alpha_equivalent(); - } -} - -auto make_surface_with_egl_context( - geom::Size size, - mg::DRMFormat format, - mgg::helpers::GBMHelper const& gbm, - mg::GLConfig const& config, - EGLContext shared_context, - bool cross_gpu) - -> std::tuple -{ - auto surface = gbm.create_scanout_surface(size.width.as_uint32_t(), size.height.as_uint32_t(), format, cross_gpu); - auto raw_surface = surface.get(); - - try - { - return std::make_tuple( - std::move(surface), - mgg::helpers::EGLHelper{ - config, - gbm, - raw_surface, - format, - shared_context - }); - } - catch (mgg::helpers::EGLHelper::NoMatchingEGLConfig const&) - { - // If the format has an opaque/alpha equivalent, try that - auto equivalent_format = get_equivalent_other_alphaness_format(format); - if (!equivalent_format) - { - // No equivalent format to try, so bail - throw; - } - - surface = gbm.create_scanout_surface( - size.width.as_uint32_t(), - size.height.as_uint32_t(), - *equivalent_format, - cross_gpu); - raw_surface = surface.get(); - return std::make_tuple( - std::move(surface), - - mgg::helpers::EGLHelper{ - config, - gbm, - raw_surface, - *equivalent_format, - shared_context - }); - } -} - -} - void mgg::Display::configure_locked( mgg::RealKMSDisplayConfiguration const& kms_conf, std::lock_guard const&) @@ -529,8 +386,7 @@ void mgg::Display::configure_locked( [&](OverlappingOutputGroup const& group) { auto bounding_rect = group.bounding_rectangle(); - // Each vector is a single GPU memory domain - std::vector>> kms_output_groups; + std::vector> kms_outputs; glm::mat2 transformation; geom::Size current_mode_resolution; @@ -546,7 +402,7 @@ void mgg::Display::configure_locked( { kms_output->set_power_mode(conf_output.power_mode); kms_output->set_gamma(conf_output.gamma); - add_to_drm_device_group(kms_output_groups, std::move(kms_output)); + kms_outputs.push_back(std::move(kms_output)); } /* @@ -565,43 +421,16 @@ void mgg::Display::configure_locked( } else { - uint32_t const width = current_mode_resolution.width.as_uint32_t(); - uint32_t const height = current_mode_resolution.height.as_uint32_t(); - - for (auto const& group : kms_output_groups) - { - // TODO: Pull this out of the configuration - // TODO: Actually query available formats! - mg::DRMFormat format{GBM_FORMAT_XRGB8888}; - /* - * In a hybrid setup a scanout surface needs to be allocated differently if it - * needs to be able to be shared across GPUs. This likely reduces performance. - * - * As a first cut, assume every scanout buffer in a hybrid setup might need - * to be shared. - */ - auto [surface, egl] = make_surface_with_egl_context( - current_mode_resolution, - format, - *gbm, - *gl_config, - shared_egl.context(), - drm.size() != 1); - auto db = std::make_unique( - bypass_option, - listener, - group, - GBMOutputSurface{ - group.front()->drm_fd(), - std::move(surface), - width, height, - std::move(egl) - }, - bounding_rect, - transformation); - - display_buffers_new.push_back(std::move(db)); - } + auto db = std::make_unique( + owner, + drm_fd, + bypass_option, + listener, + kms_outputs, + bounding_rect, + transformation); + + display_buffers_new.push_back(std::move(db)); } }); @@ -615,3 +444,291 @@ void mgg::Display::configure_locked( /* Clear connected but unused outputs */ clear_connected_unused_outputs(); } + +namespace +{ +auto drm_get_cap_checked(mir::Fd const& drm_fd, uint64_t cap) -> uint64_t +{ + uint64_t value; + if (drmGetCap(drm_fd, cap, &value)) + { + + } + return value; +} + + +class DumbAllocator : public mg::DumbDisplayProvider::Allocator +{ +public: + DumbAllocator(mir::Fd drm_fd, mg::DisplayBuffer const& db) + : drm_fd(std::move(drm_fd)), + supports_modifiers{drm_get_cap_checked(this->drm_fd, DRM_CAP_ADDFB2_MODIFIERS) == 1}, + size{db.view_area().size} + { + } + + auto acquire() -> std::unique_ptr override + { + return std::make_unique(drm_fd, supports_modifiers, size); + } + +private: + mir::Fd const drm_fd; + bool const supports_modifiers; + mir::geometry::Size const size; +}; + +} + +mgg::DumbDisplayProvider::DumbDisplayProvider() +{ +} + +auto mgg::DumbDisplayProvider::allocator_for_db( + mg::DisplayBuffer const& db) -> std::unique_ptr +{ + auto const& kms_db = dynamic_cast(db); + return std::make_unique(kms_db.drm_fd(), db); +} + +namespace +{ +auto gbm_create_device_checked(mir::Fd fd) -> std::shared_ptr +{ + errno = 0; + auto device = gbm_create_device(fd); + if (!device) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + errno, + std::system_category(), + "Failed to create GBM device"})); + } + return { + device, + [fd](struct gbm_device* device) // Capture shared ownership of fd to keep gdm_device functional + { + if (device) + { + gbm_device_destroy(device); + } + } + }; +} +} + +mgg::GBMDisplayProvider::GBMDisplayProvider( + mir::Fd drm_fd, + mg::DisplayPlatform const* parent) + : fd{std::move(drm_fd)}, + gbm{gbm_create_device_checked(fd)}, + parent{parent} +{ +} + +auto mgg::GBMDisplayProvider::gbm_device() const -> std::shared_ptr +{ + return gbm; +} + + +auto mgg::GBMDisplayProvider::is_same_device(mir::udev::Device const& render_device) const -> bool +{ +#ifndef MIR_DRM_HAS_GET_DEVICE_FROM_DEVID + class CStrFree + { + public: + void operator()(char* str) + { + if (str) + { + free(str); + } + } + }; + + std::unique_ptr primary_node{drmGetPrimaryDeviceNameFromFd(fd)}; + std::unique_ptr render_node{drmGetRenderDeviceNameFromFd(fd)}; + + mir::log_debug("Checking whether %s is the same device as (%s, %s)...", render_device.devnode(), primary_node.get(), render_node.get()); + + if (primary_node) + { + if (strcmp(primary_node.get(), render_device.devnode()) == 0) + { + mir::log_debug("\t...yup."); + return true; + } + } + if (render_node) + { + if (strcmp(render_node.get(), render_device.devnode()) == 0) + { + mir::log_debug("\t...yup."); + return true; + } + } + + mir::log_debug("\t...nope."); + + return false; +#else + drmDevicePtr us{nullptr}, them{nullptr}; + + drmGetDeviceFromDevId(render_device.devno(), 0, &them); + drmGetDevice2(fd, 0, &us); + + bool result = drmDevicesEqual(us, them); + + drmDeviceFree(us); + drmDeviceFree(them); + + return result; +#endif +} + +auto mgg::GBMDisplayProvider::is_same_device(mg::DisplayBuffer const& db) const -> bool +{ + mir::log_debug("Is %p the same as %p?", db.owner().get(), parent); + return db.owner().get() == parent; +} + +auto mgg::GBMDisplayProvider::supported_formats() const -> std::vector +{ + // TODO: Pull out of KMS plane info + return { DRMFormat{DRM_FORMAT_XRGB8888}, DRMFormat{DRM_FORMAT_ARGB8888}}; +} + +auto mgg::GBMDisplayProvider::modifiers_for_format(DRMFormat /*format*/) const -> std::vector +{ + // TODO: Pull out off KMS plane info + return {}; +} + +namespace +{ +using LockedFrontBuffer = std::unique_ptr>; + +class GBMBoFramebuffer : public mgg::FBHandle +{ +public: + static auto framebuffer_for_frontbuffer(mir::Fd const& drm_fd, LockedFrontBuffer bo) -> std::unique_ptr + { + if (auto cached_fb = static_cast*>(gbm_bo_get_user_data(bo.get()))) + { + return std::unique_ptr{new GBMBoFramebuffer{std::move(bo), *cached_fb}}; + } + + auto fb_id = new std::shared_ptr{ + new uint32_t{0}, + [drm_fd](uint32_t* fb_id) + { + if (*fb_id) + { + drmModeRmFB(drm_fd, *fb_id); + } + delete fb_id; + }}; + uint32_t handles[4] = {gbm_bo_get_handle(bo.get()).u32, 0, 0, 0}; + uint32_t strides[4] = {gbm_bo_get_stride(bo.get()), 0, 0, 0}; + uint32_t offsets[4] = {0, 0, 0, 0}; + + auto format = gbm_bo_get_format(bo.get()); + + auto const width = gbm_bo_get_width(bo.get()); + auto const height = gbm_bo_get_height(bo.get()); + + /* Create a KMS FB object with the gbm_bo attached to it. */ + auto ret = drmModeAddFB2(drm_fd, width, height, format, + handles, strides, offsets, fb_id->get(), 0); + if (ret) + return nullptr; + + gbm_bo_set_user_data(bo.get(), fb_id, [](gbm_bo*, void* fb_ptr) { delete static_cast*>(fb_ptr); }); + + return std::unique_ptr{new GBMBoFramebuffer{std::move(bo), *fb_id}}; + } + + operator uint32_t() const override + { + return *fb_id; + } +private: + GBMBoFramebuffer(LockedFrontBuffer bo, std::shared_ptr fb) + : bo{std::move(bo)}, + fb_id{std::move(fb)} + { + } + + LockedFrontBuffer const bo; + std::shared_ptr const fb_id; +}; + +class GBMSurfaceImpl : public mgg::GBMDisplayProvider::GBMSurface +{ +public: + GBMSurfaceImpl(mir::Fd drm_fd, gbm_device* gbm, geom::Size size, mg::DRMFormat const format, std::span modifiers) + : drm_fd{std::move(drm_fd)}, + surface{ + gbm_surface_create_with_modifiers2( + gbm, + size.width.as_uint32_t(), size.height.as_uint32_t(), + format, + modifiers.empty() ? nullptr : modifiers.data(), + modifiers.size(), + GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT)} + { + if (!surface) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to create GBM surface"})); + } + } + + ~GBMSurfaceImpl() + { + gbm_surface_destroy(surface); + } + + GBMSurfaceImpl(GBMSurfaceImpl const&) = delete; + auto operator=(GBMSurfaceImpl const&) -> GBMSurfaceImpl const& = delete; + + operator gbm_surface*() const override + { + return surface; + } + + auto claim_framebuffer() -> std::unique_ptr override + { + if (!gbm_surface_has_free_buffers(surface)) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + EBUSY, + std::system_category(), + "Too many buffers consumed from GBM surface"})); + } + + LockedFrontBuffer bo{ + gbm_surface_lock_front_buffer(surface), + [this](gbm_bo* bo) { gbm_surface_release_buffer(surface, bo); }}; + + if (!bo) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to acquire GBM front buffer"})); + } + + return GBMBoFramebuffer::framebuffer_for_frontbuffer(drm_fd, std::move(bo)); + } +private: + mir::Fd const drm_fd; + gbm_surface* const surface; +}; +} + +auto mgg::GBMDisplayProvider::make_surface(geom::Size size, DRMFormat format, std::span modifiers) + -> std::unique_ptr +{ + return std::make_unique(fd, gbm.get(), std::move(size), format, modifiers); +} diff --git a/src/platforms/gbm-kms/server/kms/display.h b/src/platforms/gbm-kms/server/kms/display.h index 983b165d5be..6e8ae759f8d 100644 --- a/src/platforms/gbm-kms/server/kms/display.h +++ b/src/platforms/gbm-kms/server/kms/display.h @@ -24,6 +24,7 @@ #include "display_helpers.h" #include "egl_helper.h" #include "platform_common.h" +#include "mir/graphics/platform.h" #include #include @@ -34,6 +35,7 @@ namespace mir namespace graphics { +class DisplayPlatform; class DisplayReport; class DisplayBuffer; class DisplayConfigurationPolicy; @@ -56,12 +58,12 @@ class Cursor; class Display : public graphics::Display { public: - Display(std::vector> const& drm, - std::shared_ptr const& gbm, - BypassOption bypass_option, - std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config, - std::shared_ptr const& listener); + Display( + std::shared_ptr parent, + mir::Fd drm_fd, + BypassOption bypass_option, + std::shared_ptr const& initial_conf_policy, + std::shared_ptr const& listener); ~Display(); geometry::Rectangle view_area() const; @@ -81,19 +83,16 @@ class Display : public graphics::Display std::shared_ptr create_hardware_cursor() override; - std::unique_ptr create_gl_context() const override; - private: void clear_connected_unused_outputs(); + std::shared_ptr const owner; mutable std::mutex configuration_mutex; - std::vector> const drm; - std::shared_ptr const gbm; + mir::Fd const drm_fd; std::shared_ptr const listener; mir::udev::Monitor monitor; - helpers::EGLHelper shared_egl; - std::vector> display_buffers; std::shared_ptr const output_container; + std::vector> display_buffers; mutable RealKMSDisplayConfiguration current_display_configuration; mutable std::atomic dirty_configuration; @@ -103,9 +102,38 @@ class Display : public graphics::Display BypassOption bypass_option; std::weak_ptr cursor; - std::shared_ptr const gl_config; }; +class DumbDisplayProvider : public graphics::DumbDisplayProvider +{ +public: + DumbDisplayProvider(); + + auto allocator_for_db(graphics::DisplayBuffer const& db) + -> std::unique_ptr override; +}; + +class GBMDisplayProvider : public graphics::GBMDisplayProvider +{ +public: + GBMDisplayProvider(mir::Fd drm_fd, DisplayPlatform const* parent); + + auto is_same_device(mir::udev::Device const& render_device) const -> bool override; + + auto is_same_device(graphics::DisplayBuffer const& db) const -> bool override; + + auto gbm_device() const -> std::shared_ptr override; + + auto supported_formats() const -> std::vector override; + + auto modifiers_for_format(DRMFormat format) const -> std::vector override; + + auto make_surface(geometry::Size size, DRMFormat format, std::span modifier) -> std::unique_ptr override; +private: + mir::Fd const fd; + std::shared_ptr const gbm; + DisplayPlatform const* const parent; +}; } } } diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index 20d6e21a134..b8f90ae85dc 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -16,6 +16,8 @@ #include "display_buffer.h" #include "kms_output.h" +#include "dumb_fb.h" +#include "mir/fd.h" #include "mir/graphics/display_report.h" #include "mir/graphics/transformation.h" #include "bypass.h" @@ -40,399 +42,20 @@ #include #include -namespace mg = mir::graphics; namespace mgg = mir::graphics::gbm; namespace geom = mir::geometry; -namespace mgmh = mir::graphics::gbm::helpers; - -mgg::GBMOutputSurface::FrontBuffer::FrontBuffer() - : surf{nullptr}, - bo{nullptr} -{ -} - -mgg::GBMOutputSurface::FrontBuffer::FrontBuffer(gbm_surface* surface) - : surf{surface}, - bo{gbm_surface_lock_front_buffer(surface)} -{ - if (!bo) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to acquire front buffer of gbm_surface")); - } -} - -mgg::GBMOutputSurface::FrontBuffer::~FrontBuffer() -{ - if (surf) - { - gbm_surface_release_buffer(surf, bo); - } -} - -mgg::GBMOutputSurface::FrontBuffer::FrontBuffer(FrontBuffer&& from) - : surf{from.surf}, - bo{from.bo} -{ - const_cast(from.surf) = nullptr; - const_cast(from.bo) = nullptr; -} - -auto mgg::GBMOutputSurface::FrontBuffer::operator=(FrontBuffer&& from) -> FrontBuffer& -{ - if (surf) - { - gbm_surface_release_buffer(surf, bo); - } - - const_cast(surf) = from.surf; - const_cast(bo) = from.bo; - - const_cast(from.surf) = nullptr; - const_cast(from.bo) = nullptr; - - return *this; -} - -auto mgg::GBMOutputSurface::FrontBuffer::operator=(std::nullptr_t) -> FrontBuffer& -{ - return *this = FrontBuffer{}; -} - -mgg::GBMOutputSurface::FrontBuffer::operator gbm_bo*() -{ - return bo; -} - -mgg::GBMOutputSurface::FrontBuffer::operator bool() const -{ - return (surf != nullptr) && (bo != nullptr); -} - -namespace -{ -void require_extensions( - std::initializer_list extensions, - std::function const& extension_getter) -{ - std::stringstream missing_extensions; - - std::string const ext_string = extension_getter(); - - for (auto extension : extensions) - { - if (ext_string.find(extension) == std::string::npos) - { - missing_extensions << "Missing " << extension << std::endl; - } - } - - if (!missing_extensions.str().empty()) - { - BOOST_THROW_EXCEPTION(std::runtime_error( - std::string("Missing required extensions:\n") + missing_extensions.str())); - } -} - -void require_egl_extensions(EGLDisplay dpy, std::initializer_list extensions) -{ - require_extensions( - extensions, - [dpy]() -> std::string - { - char const* maybe_exts = eglQueryString(dpy, EGL_EXTENSIONS); - if (maybe_exts) - return maybe_exts; - return {}; - }); -} - -void require_gl_extensions(std::initializer_list extensions) -{ - require_extensions( - extensions, - []() -> std::string - { - char const *maybe_exts = - reinterpret_cast(glGetString(GL_EXTENSIONS)); - if (maybe_exts) - return maybe_exts; - return {}; - }); -} - -bool needs_bounce_buffer(mgg::KMSOutput const& destination, gbm_bo* source) -{ - return destination.buffer_requires_migration(source); -} - -const GLchar* const vshader = - { - "attribute vec4 position;\n" - "attribute vec2 texcoord;\n" - "varying vec2 v_texcoord;\n" - "void main() {\n" - " gl_Position = position;\n" - " v_texcoord = texcoord;\n" - "}\n" - }; - -const GLchar* const fshader = - { - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "uniform sampler2D tex;" - "varying vec2 v_texcoord;\n" - "void main() {\n" - " gl_FragColor = texture2D(tex, v_texcoord);\n" - "}\n" - }; - -class VBO -{ -public: - VBO(void const* data, size_t size) - { - glGenBuffers(1, &buf_id); - glBindBuffer(GL_ARRAY_BUFFER, buf_id); - glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - - ~VBO() - { - glDeleteBuffers(1, &buf_id); - } - - void bind() - { - glBindBuffer(GL_ARRAY_BUFFER, buf_id); - } - -private: - GLuint buf_id; -}; - -class NoAuxGlConfig : public mg::GLConfig -{ -public: - int depth_buffer_bits() const override - { - return 0; - } - int stencil_buffer_bits() const override - { - return 0; - } -}; - -class EGLBufferCopier -{ -public: - EGLBufferCopier( - mir::Fd const& drm_fd, - uint32_t width, - uint32_t height, - uint32_t format) - : eglCreateImageKHR{ - reinterpret_cast(eglGetProcAddress("eglCreateImageKHR"))}, - eglDestroyImageKHR{ - reinterpret_cast(eglGetProcAddress("eglDestroyImageKHR"))}, - glEGLImageTargetTexture2DOES{ - reinterpret_cast(eglGetProcAddress("glEGLImageTargetTexture2DOES"))}, - device{drm_fd}, - width{width}, - height{height}, - surface{device.create_scanout_surface(width, height, format, false)}, - egl{NoAuxGlConfig{}} - { - egl.setup(device, surface.get(), format, EGL_NO_CONTEXT, true); - - require_gl_extensions({ - "GL_OES_EGL_image" - }); - - require_egl_extensions( - eglGetCurrentDisplay(), - { - "EGL_KHR_image_base", - "EGL_EXT_image_dma_buf_import" - }); - - egl.make_current(); - - auto vertex = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertex, 1, &vshader, nullptr); - glCompileShader(vertex); - - int compiled; - glGetShaderiv (vertex, GL_COMPILE_STATUS, &compiled); - - if (!compiled) { - GLchar log[1024]; - - glGetShaderInfoLog (vertex, sizeof log - 1, NULL, log); - log[sizeof log - 1] = '\0'; - glDeleteShader (vertex); - - BOOST_THROW_EXCEPTION( - std::runtime_error(std::string{"Failed to compile vertex shader:\n"} + log)); - } - - - auto fragment = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragment, 1, &fshader, nullptr); - glCompileShader(fragment); - - glGetShaderiv (fragment, GL_COMPILE_STATUS, &compiled); - if (!compiled) { - GLchar log[1024]; - - glGetShaderInfoLog (fragment, sizeof log - 1, NULL, log); - log[sizeof log - 1] = '\0'; - glDeleteShader (fragment); - - BOOST_THROW_EXCEPTION( - std::runtime_error(std::string{"Failed to compile fragment shader:\n"} + log)); - } - - prog = glCreateProgram(); - glAttachShader(prog, vertex); - glAttachShader(prog, fragment); - glLinkProgram(prog); - glGetProgramiv (prog, GL_LINK_STATUS, &compiled); - if (!compiled) { - GLchar log[1024]; - - glGetProgramInfoLog (prog, sizeof log - 1, NULL, log); - log[sizeof log - 1] = '\0'; - - BOOST_THROW_EXCEPTION( - std::runtime_error(std::string{"Failed to link shader prog:\n"} + log)); - } - - glUseProgram(prog); - - attrpos = glGetAttribLocation(prog, "position"); - attrtex = glGetAttribLocation(prog, "texcoord"); - auto unitex = glGetUniformLocation(prog, "tex"); - - glGenTextures(1, &tex); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, tex); - - glUniform1i(unitex, 0); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - static GLfloat const dest_vert[4][2] = - { { -1.f, 1.f }, { 1.f, 1.f }, { 1.f, -1.f }, { -1.f, -1.f } }; - vert_data = std::make_unique(dest_vert, sizeof(dest_vert)); - - static GLfloat const tex_vert[4][2] = - { - { 0.f, 0.f }, { 1.f, 0.f }, { 1.f, 1.f }, { 0.f, 1.f }, - }; - tex_data = std::make_unique(tex_vert, sizeof(tex_vert)); - } - - EGLBufferCopier(EGLBufferCopier const&) = delete; - EGLBufferCopier& operator==(EGLBufferCopier const&) = delete; - - ~EGLBufferCopier() - { - egl.make_current(); - vert_data = nullptr; - tex_data = nullptr; - } - - mgg::GBMOutputSurface::FrontBuffer copy_front_buffer_from(mgg::GBMOutputSurface::FrontBuffer&& from) - { - egl.make_current(); - mir::Fd const dma_buf{gbm_bo_get_fd(from)}; - - glUseProgram(prog); - - EGLint const image_attrs[] = { - EGL_WIDTH, static_cast(width), - EGL_HEIGHT, static_cast(height), - EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_XRGB8888, - EGL_DMA_BUF_PLANE0_FD_EXT, static_cast(dma_buf), - EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, - EGL_DMA_BUF_PLANE0_PITCH_EXT, static_cast(gbm_bo_get_stride(from)), - EGL_NONE - }; - - auto image = eglCreateImageKHR( - eglGetCurrentDisplay(), - EGL_NO_CONTEXT, - EGL_LINUX_DMA_BUF_EXT, - nullptr, - image_attrs); - - if (image == EGL_NO_IMAGE_KHR) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGLImage from dma_buf")); - } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, tex); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); - - vert_data->bind(); - glVertexAttribPointer (attrpos, 2, GL_FLOAT, GL_FALSE, 0, 0); - - tex_data->bind(); - glVertexAttribPointer (attrtex, 2, GL_FLOAT, GL_FALSE, 0, 0); - - glEnableVertexAttribArray(attrpos); - glEnableVertexAttribArray(attrtex); - - GLubyte const idx[] = { 0, 1, 3, 2 }; - glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx); - - egl.swap_buffers(); - - eglDestroyImageKHR(eglGetCurrentDisplay(), image); - - egl.release_current(); - return mgg::GBMOutputSurface::FrontBuffer(surface.get()); - } - - private: - - PFNEGLCREATEIMAGEKHRPROC const eglCreateImageKHR; - PFNEGLDESTROYIMAGEKHRPROC const eglDestroyImageKHR; - PFNGLEGLIMAGETARGETTEXTURE2DOESPROC const glEGLImageTargetTexture2DOES; - - mgmh::GBMHelper const device; - uint32_t const width; - uint32_t const height; - mgg::GBMSurfaceUPtr const surface; - mgmh::EGLHelper egl; - GLuint prog; - GLuint tex; - GLint attrtex; - GLint attrpos; - std::unique_ptr vert_data; - std::unique_ptr tex_data; -}; -} mgg::DisplayBuffer::DisplayBuffer( - mgg::BypassOption option, + std::shared_ptr owner, + mir::Fd drm_fd, + mgg::BypassOption, std::shared_ptr const& listener, std::vector> const& outputs, - GBMOutputSurface&& surface_gbm, geom::Rectangle const& area, glm::mat2 const& transformation) - : listener(listener), - bypass_option(option), + : owner_{std::move(owner)}, + listener(listener), outputs(outputs), - surface{std::move(surface_gbm)}, area(area), transform{transformation}, needs_set_crtc{false}, @@ -440,64 +63,18 @@ mgg::DisplayBuffer::DisplayBuffer( { listener->report_successful_setup_of_native_resources(); - make_current(); - - listener->report_successful_egl_make_current_on_construction(); - - glClear(GL_COLOR_BUFFER_BIT); + auto initial_fb = std::make_shared(std::move(drm_fd), false, area.size); - surface.swap_buffers(); + auto mapping = initial_fb->map_writeable(); + ::memset(mapping->data(), 24, mapping->len()); - listener->report_successful_egl_buffer_swap_on_construction(); - - auto temporary_front = surface.lock_front(); - if (!temporary_front) - fatal_error("Failed to get frontbuffer"); - - if (needs_bounce_buffer(*outputs.front(), temporary_front)) - { - mir::log_info("Hybrid GPU setup detected; DisplayBuffer using EGL buffer copies for migration"); - get_front_buffer = std::bind( - std::mem_fn(&EGLBufferCopier::copy_front_buffer_from), - std::make_shared( - mir::Fd{mir::IntOwnedFd{outputs.front()->drm_fd()}}, - surface.size().width.as_int(), - surface.size().height.as_int(), - GBM_FORMAT_XRGB8888), - std::placeholders::_1); - } - else - { - mir::log_info("Detected single-GPU DisplayBuffer. Rendering will be sent directly to output"); - get_front_buffer = [](auto&& fb) { return std::move(fb); }; - } - - visible_composite_frame = get_front_buffer(std::move(temporary_front)); - - /* - * Check that our (possibly bounced) front buffer is usable on *all* the - * outputs we've been asked to output on. - */ - for (auto const& output : outputs) + visible_fb = std::move(initial_fb); + for (auto& output : outputs) { - if (output->buffer_requires_migration(visible_composite_frame)) - { - BOOST_THROW_EXCEPTION(std::invalid_argument( - "Attempted to create a DisplayBuffer spanning multiple GPU memory domains")); - } + output->set_crtc(*visible_fb); } - - set_crtc(*outputs.front()->fb_for(visible_composite_frame)); - - release_current(); - listener->report_successful_drm_mode_set_crtc_on_construction(); listener->report_successful_display_construction(); - surface.report_egl_configuration( - [&listener] (EGLDisplay disp, EGLConfig cfg) - { - listener->report_egl_configuration(disp, cfg); - }); } mgg::DisplayBuffer::~DisplayBuffer() @@ -520,33 +97,31 @@ void mgg::DisplayBuffer::set_transformation(glm::mat2 const& t, geometry::Rectan area = a; } -bool mgg::DisplayBuffer::overlay(RenderableList const& renderable_list) +bool mgg::DisplayBuffer::overlay(std::vector const& renderable_list) { - glm::mat2 static const no_transformation(1); - if (transform == no_transformation && - (bypass_option == mgg::BypassOption::allowed)) + // TODO: implement more than the most basic case. + if (renderable_list.size() != 1) { - mgg::BypassMatch bypass_match(area); - auto bypass_it = std::find_if(renderable_list.rbegin(), renderable_list.rend(), bypass_match); - if (bypass_it != renderable_list.rend()) - { - auto bypass_buffer = (*bypass_it)->buffer(); - auto dmabuf_image = dynamic_cast(bypass_buffer->native_buffer_base()); - if (dmabuf_image && - bypass_buffer->size() == surface.size()) - { - if (auto bufobj = outputs.front()->fb_for(*dmabuf_image)) - { - bypass_buf = bypass_buffer; - bypass_bufobj = bufobj; - return true; - } - } - } + return false; + } + + if (renderable_list[0].screen_positon != view_area()) + { + return false; + } + + if (renderable_list[0].source_position.top_left != geom::PointF {0,0} || + renderable_list[0].source_position.size.width.as_value() != view_area().size.width.as_int() || + renderable_list[0].source_position.size.height.as_value() != view_area().size.height.as_int()) + { + return false; } - bypass_buf = nullptr; - bypass_bufobj = nullptr; + if (auto fb = std::dynamic_pointer_cast(renderable_list[0].buffer)) + { + next_swap = std::move(fb); + return true; + } return false; } @@ -556,13 +131,6 @@ void mgg::DisplayBuffer::for_each_display_buffer( f(*this); } -void mgg::DisplayBuffer::swap_buffers() -{ - surface.swap_buffers(); - bypass_buf = nullptr; - bypass_bufobj = nullptr; -} - void mgg::DisplayBuffer::set_crtc(FBHandle const& forced_frame) { for (auto& output : outputs) @@ -591,20 +159,18 @@ void mgg::DisplayBuffer::post() */ wait_for_page_flip(); - std::shared_ptr bufobj; - if (bypass_buf) + if (!next_swap) { - bufobj = bypass_bufobj; - } - else - { - scheduled_composite_frame = get_front_buffer(surface.lock_front()); - bufobj = outputs.front()->fb_for(scheduled_composite_frame); - if (!bufobj) - fatal_error("Failed to get front buffer object"); + // Hey! No one has given us a next frame yet, so we don't have to change what's onscreen. + // Sweet! We can just bail. + return; } + /* + * Otherwise, pull the next frame into the pending slot + */ + scheduled_fb = std::move(next_swap); + next_swap = nullptr; - scheduled_fb = std::move(bufobj); /* * Try to schedule a page flip as first preference to avoid tearing. * [will complete in a background thread] @@ -631,7 +197,7 @@ void mgg::DisplayBuffer::post() // Predicted worst case render time for the next frame... auto predicted_render_time = 50ms; - if (bypass_buf) + if (holding_client_buffers) { /* * For composited frames we defer wait_for_page_flip till just before @@ -642,7 +208,6 @@ void mgg::DisplayBuffer::post() * Also, bypass does not need the deferred page flip because it has * no compositing/rendering step for which to save time for. */ - scheduled_bypass_frame = bypass_buf; wait_for_page_flip(); // It's very likely the next frame will be bypassed like this one so @@ -668,10 +233,6 @@ void mgg::DisplayBuffer::post() */ } - // Buffer lifetimes are managed exclusively by scheduled*/visible* now - bypass_buf = nullptr; - bypass_bufobj = nullptr; - recommend_sleep = 0ms; if (outputs.size() == 1) { @@ -715,38 +276,6 @@ void mgg::DisplayBuffer::wait_for_page_flip() page_flips_pending = false; } - - if (scheduled_bypass_frame || scheduled_composite_frame) - { - // Why are both of these grouped into a single statement? - // Because in either case both types of frame need releasing each time. - - visible_bypass_frame = scheduled_bypass_frame; - scheduled_bypass_frame = nullptr; - - visible_composite_frame = std::move(scheduled_composite_frame); - scheduled_composite_frame = nullptr; - } -} - -auto mgg::DisplayBuffer::size() const -> geometry::Size -{ - return surface.size(); -} - -void mgg::DisplayBuffer::make_current() -{ - surface.make_current(); -} - -void mgg::DisplayBuffer::bind() -{ - surface.bind(); -} - -void mgg::DisplayBuffer::release_current() -{ - surface.release_current(); } void mgg::DisplayBuffer::schedule_set_crtc() @@ -754,70 +283,30 @@ void mgg::DisplayBuffer::schedule_set_crtc() needs_set_crtc = true; } -mg::NativeDisplayBuffer* mgg::DisplayBuffer::native_display_buffer() -{ - return this; -} - -mgg::GBMOutputSurface::GBMOutputSurface( - int drm_fd, - GBMSurfaceUPtr&& surface, - uint32_t width, - uint32_t height, - helpers::EGLHelper&& egl) - : drm_fd{drm_fd}, - width{width}, - height{height}, - egl{std::move(egl)}, - surface{std::move(surface)} -{ -} - -mgg::GBMOutputSurface::GBMOutputSurface(GBMOutputSurface&& from) - : drm_fd{from.drm_fd}, - width{from.width}, - height{from.height}, - egl{std::move(from.egl)}, - surface{std::move(from.surface)} +auto mir::graphics::gbm::DisplayBuffer::owner() const -> std::shared_ptr { + return owner_; } -auto mgg::GBMOutputSurface::GBMOutputSurface::size() const -> geometry::Size +auto mgg::DisplayBuffer::drm_fd() const -> mir::Fd { - return {width, height}; + return mir::Fd{mir::IntOwnedFd{outputs.front()->drm_fd()}}; } -void mgg::GBMOutputSurface::make_current() +void mir::graphics::gbm::DisplayBuffer::set_next_image(std::unique_ptr content) { - if (!egl.make_current()) + std::vector const single_buffer = { + DisplayElement { + view_area(), + geom::RectangleF{ + {0, 0}, + {view_area().size.width.as_value(), view_area().size.height.as_value()}}, + std::move(content) + } + }; + if (!overlay(single_buffer)) { - fatal_error("Failed to make EGL surface current"); + // Oh, oh! We should be *guaranteed* to “overlay” a single Framebuffer; this is likely a programming error + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to post buffer to display"})); } } - -void mgg::GBMOutputSurface::release_current() -{ - egl.release_current(); -} - -void mgg::GBMOutputSurface::swap_buffers() -{ - if (!egl.swap_buffers()) - fatal_error("Failed to perform buffer swap"); -} - -void mgg::GBMOutputSurface::bind() -{ - -} - -auto mgg::GBMOutputSurface::lock_front() -> FrontBuffer -{ - return FrontBuffer{surface.get()}; -} - -void mgg::GBMOutputSurface::report_egl_configuration( - std::function const& to) -{ - egl.report_egl_configuration(to); -} diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.h b/src/platforms/gbm-kms/server/kms/display_buffer.h index 94634e3c823..a5fd6113620 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.h +++ b/src/platforms/gbm-kms/server/kms/display_buffer.h @@ -19,10 +19,10 @@ #include "mir/graphics/display_buffer.h" #include "mir/graphics/display.h" -#include "mir/renderer/gl/render_target.h" #include "display_helpers.h" #include "egl_helper.h" #include "platform_common.h" +#include "kms_framebuffer.h" #include #include @@ -44,74 +44,25 @@ class FBHandle; class KMSOutput; class NativeBuffer; -class GBMOutputSurface : public renderer::gl::RenderTarget -{ -public: - class FrontBuffer - { - public: - FrontBuffer(); - FrontBuffer(gbm_surface* surface); - FrontBuffer(FrontBuffer&& from); - - ~FrontBuffer(); - - FrontBuffer& operator=(FrontBuffer&& from); - FrontBuffer& operator=(std::nullptr_t); - - operator gbm_bo*(); - operator bool() const; - - private: - gbm_surface* const surf; - gbm_bo* const bo; - }; - - GBMOutputSurface( - int drm_fd, - GBMSurfaceUPtr&& surface, - uint32_t width, - uint32_t height, - helpers::EGLHelper&& egl); - GBMOutputSurface(GBMOutputSurface&& from); - - // gl::RenderTarget - auto size() const -> geometry::Size override; - void make_current() override; - void release_current() override; - void swap_buffers() override; - void bind() override; - - FrontBuffer lock_front(); - void report_egl_configuration(std::function const& to); -private: - int const drm_fd; - uint32_t width, height; - helpers::EGLHelper egl; - GBMSurfaceUPtr surface; -}; - class DisplayBuffer : public graphics::DisplayBuffer, - public graphics::DisplaySyncGroup, - public graphics::NativeDisplayBuffer, - public renderer::gl::RenderTarget + public graphics::DisplaySyncGroup { public: - DisplayBuffer(BypassOption bypass_options, - std::shared_ptr const& listener, - std::vector> const& outputs, - GBMOutputSurface&& surface_gbm, - geometry::Rectangle const& area, - glm::mat2 const& transformation); + DisplayBuffer( + std::shared_ptr owner, + mir::Fd drm_fd, + BypassOption bypass_options, + std::shared_ptr const& listener, + std::vector> const& outputs, + geometry::Rectangle const& area, + glm::mat2 const& transformation); ~DisplayBuffer(); geometry::Rectangle view_area() const override; - auto size() const -> geometry::Size override; - void make_current() override; - void release_current() override; - void swap_buffers() override; - bool overlay(RenderableList const& renderlist) override; - void bind() override; + + void set_next_image(std::unique_ptr content) override; + + bool overlay(std::vector const& renderlist) override; void for_each_display_buffer( std::function const& f) override; @@ -119,38 +70,31 @@ class DisplayBuffer : public graphics::DisplayBuffer, std::chrono::milliseconds recommended_sleep() const override; glm::mat2 transformation() const override; - NativeDisplayBuffer* native_display_buffer() override; void set_transformation(glm::mat2 const& t, geometry::Rectangle const& a); void schedule_set_crtc(); void wait_for_page_flip(); + auto owner() const -> std::shared_ptr override; + + auto drm_fd() const -> mir::Fd; private: bool schedule_page_flip(FBHandle const& bufobj); void set_crtc(FBHandle const&); - std::shared_ptr visible_bypass_frame, scheduled_bypass_frame; - std::shared_ptr bypass_buf{nullptr}; + std::shared_ptr const owner_; + bool holding_client_buffers{false}; std::shared_ptr bypass_bufobj{nullptr}; std::shared_ptr const listener; - BypassOption bypass_option; std::vector> outputs; - /* - * Destruction order is important here: - * - The GBMFrontBuffers depend on *either*: - * i) The GBMOutputSurface, or - * ii) The EGLBufferCopier hidden inside get_front_buffer - */ - std::function get_front_buffer; - GBMOutputSurface surface; - - GBMOutputSurface::FrontBuffer visible_composite_frame; - GBMOutputSurface::FrontBuffer scheduled_composite_frame; - - std::shared_ptr scheduled_fb{nullptr}; - std::shared_ptr visible_fb{nullptr}; + // Framebuffer handling + // KMS does not take a reference to submitted framebuffers; if you destroy a framebuffer while + // it's in use, KMS treat that as submitting a null framebuffer and turn off the display. + std::shared_ptr next_swap{nullptr}; //< Next frame to submit to the hardware + std::shared_ptr scheduled_fb{nullptr}; //< Frame currently submitted to the hardware, not yet on-screen + std::shared_ptr visible_fb{nullptr}; //< Frame currently onscreen geometry::Rectangle area; glm::mat2 transform; diff --git a/src/platforms/gbm-kms/server/kms/dumb_fb.cpp b/src/platforms/gbm-kms/server/kms/dumb_fb.cpp new file mode 100644 index 00000000000..6f9e2cdca97 --- /dev/null +++ b/src/platforms/gbm-kms/server/kms/dumb_fb.cpp @@ -0,0 +1,336 @@ +/* + * Copyright © 2021 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 "dumb_fb.h" + +#include "mir/geometry/forward.h" +#include "mir/log.h" +#include "mir_toolkit/common.h" + +#include +#include +#include +#include + +namespace mg = mir::graphics; +namespace mgg = mg::gbm; + +class mgg::DumbFB::DumbBuffer : public mir::renderer::software::RWMappableBuffer +{ + template + class Mapping : public mir::renderer::software::Mapping + { + public: + Mapping( + uint32_t width, uint32_t height, + uint32_t pitch, + T* data, + size_t len) + : size_{width, height}, + stride_{pitch}, + data_{data}, + len_{len} + { + } + + ~Mapping() + { + if (::munmap(const_cast::type *>(data_), len_) == -1) + { + // It's unclear how this could happen, but tell *someone* about it if it does! + mir::log_error("Failed to unmap CPU buffer: %s (%i)", strerror(errno), errno); + } + } + + [[nodiscard]] + auto format() const -> MirPixelFormat + { + return mir_pixel_format_xrgb_8888; + } + + [[nodiscard]] + auto stride() const -> mir::geometry::Stride + { + return stride_; + } + + [[nodiscard]] + auto size() const -> mir::geometry::Size + { + return size_; + } + + [[nodiscard]] + auto data() -> T* + { + return data_; + } + + [[nodiscard]] + auto len() const -> size_t + { + return len_; + } + + private: + mir::geometry::Size const size_; + mir::geometry::Stride const stride_; + T* const data_; + size_t const len_; + }; + +public: + ~DumbBuffer() + { + struct drm_mode_destroy_dumb params = { gem_handle }; + + if (auto const err = drmIoctl(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, ¶ms)) + { + mir::log_error("Failed destroy CPU-accessible buffer: %s (%i)", strerror(-err), -err); + } + } + + static auto create_dumb_buffer(mir::Fd drm_fd, mir::geometry::Size const& size) -> + std::unique_ptr + { + struct drm_mode_create_dumb params = {}; + + params.bpp = 32; + params.width = size.width.as_uint32_t(); + params.height = size.height.as_uint32_t(); + + if (auto const err = drmIoctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, ¶ms)) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + -err, + std::system_category(), + "Failed to allocate CPU-accessible buffer"})); + } + + return std::unique_ptr{ + new DumbBuffer{std::move(drm_fd), params}}; + } + + auto map_writeable() -> std::unique_ptr> override + { + auto const data = mmap_buffer(PROT_WRITE); + return std::make_unique>( + width(), height(), + pitch(), + static_cast(data), + size_); + } + auto map_readable() -> std::unique_ptr> override + { + auto const data = mmap_buffer(PROT_READ); + return std::make_unique>( + width(), height(),pitch(), + static_cast(data), + size_); + } + + auto map_rw() -> std::unique_ptr> override + { + auto const data = mmap_buffer(PROT_READ | PROT_WRITE); + return std::make_unique>( + width(), height(), + pitch(), + static_cast(data), + size_); + } + + [[nodiscard]] + auto handle() const -> uint32_t + { + return gem_handle; + } + [[nodiscard]] + auto pitch() const -> uint32_t + { + return pitch_; + } + [[nodiscard]] + auto width() const -> uint32_t + { + return width_; + } + [[nodiscard]] + auto height() const -> uint32_t + { + return height_; + } + + auto format() const -> MirPixelFormat override + { + return mir_pixel_format_xrgb_8888; + } + + auto stride() const -> geometry::Stride override + { + return geometry::Stride{pitch_}; + } + + auto size() const -> geometry::Size override + { + return geometry::Size{width_, height_}; + } +private: + DumbBuffer( + mir::Fd fd, + struct drm_mode_create_dumb const& params) + : drm_fd{std::move(fd)}, + width_{params.width}, + height_{params.height}, + pitch_{params.pitch}, + gem_handle{params.handle}, + size_{params.size} + { + } + + auto mmap_buffer(int access_mode) -> void* + { + struct drm_mode_map_dumb map_request = {}; + + map_request.handle = gem_handle; + + if (auto err = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_request)) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + -err, + std::system_category(), + "Failed to map buffer for CPU-access"})); + } + + auto map = mmap(0, size_, access_mode, MAP_SHARED, drm_fd, map_request.offset); + if (map == MAP_FAILED) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + errno, + std::system_category(), + "Failed to mmap() buffer"})); + } + + return map; + } + + mir::Fd const drm_fd; + uint32_t const width_; + uint32_t const height_; + uint32_t const pitch_; + uint32_t const gem_handle; + size_t const size_; +}; + +mgg::DumbFB::DumbFB(mir::Fd const& drm_fd, bool supports_modifiers, mir::geometry::Size const& size) + : DumbFB(drm_fd, supports_modifiers, DumbBuffer::create_dumb_buffer(drm_fd, size)) +{ +} + +mgg::DumbFB::~DumbFB() +{ + drmModeRmFB(drm_fd, fb_id); +} + +mgg::DumbFB::DumbFB( + mir::Fd drm_fd, + bool supports_modifiers, + std::unique_ptr buffer) + : drm_fd{std::move(drm_fd)}, + fb_id{fb_id_for_buffer(this->drm_fd, supports_modifiers, *buffer)}, + buffer{std::move(buffer)} +{ +} + +auto mgg::DumbFB::map_writeable() -> std::unique_ptr> +{ + return buffer->map_writeable(); +} + +auto mgg::DumbFB::format() const -> MirPixelFormat +{ + return buffer->format(); +} + +auto mgg::DumbFB::stride() const -> geometry::Stride +{ + return buffer->stride(); +} + +auto mgg::DumbFB::size() const -> geometry::Size +{ + return buffer->size(); +} + +mgg::DumbFB::operator uint32_t() const +{ + return fb_id; +} + +auto mgg::DumbFB::fb_id_for_buffer(mir::Fd const &drm_fd, bool supports_modifiers, DumbBuffer const& buf) -> uint32_t +{ + uint32_t fb_id; + uint32_t const pitches[4] = { buf.pitch(), 0, 0, 0 }; + uint32_t const handles[4] = { buf.handle(), 0, 0, 0 }; + uint32_t const offsets[4] = { 0, 0, 0, 0 }; + uint64_t const modifiers[4] = { DRM_FORMAT_MOD_LINEAR, 0, 0, 0 }; + if (supports_modifiers) + { + if (auto err = drmModeAddFB2WithModifiers( + drm_fd, + buf.width(), + buf.height(), + DRM_FORMAT_XRGB8888, + handles, + pitches, + offsets, + modifiers, + &fb_id, + DRM_MODE_FB_MODIFIERS)) + { + BOOST_THROW_EXCEPTION((std::system_error{ + -err, + std::system_category(), + "Failed to create DRM framebuffer from CPU-accessible buffer"})); + } + } + else + { + if (auto err = drmModeAddFB2( + drm_fd, + buf.width(), + buf.height(), + DRM_FORMAT_XRGB8888, + handles, + pitches, + offsets, + &fb_id, + 0)) + { + BOOST_THROW_EXCEPTION((std::system_error{ + -err, + std::system_category(), + "Failed to create DRM framebuffer from CPU-accessible buffer"})); + } + + + } + return fb_id; +} diff --git a/src/platforms/gbm-kms/server/kms/dumb_fb.h b/src/platforms/gbm-kms/server/kms/dumb_fb.h new file mode 100644 index 00000000000..28f9b1b6bd1 --- /dev/null +++ b/src/platforms/gbm-kms/server/kms/dumb_fb.h @@ -0,0 +1,58 @@ +/* + * Copyright © 2021 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 + */ + +#ifndef MIR_GRAPHICS_GBM_FB_H_ +#define MIR_GRAPHICS_GBM_FB_H_ + +#include "mir/fd.h" +#include "mir/graphics/platform.h" + +#include "kms_framebuffer.h" + +namespace mir::graphics::gbm +{ +class DumbFB : public FBHandle, public DumbDisplayProvider::MappableFB +{ +public: + DumbFB(mir::Fd const& drm_fd, bool supports_modifiers, mir::geometry::Size const& size); + ~DumbFB() override; + + auto map_writeable() -> std::unique_ptr> override; + + auto format() const -> MirPixelFormat override; + auto stride() const -> geometry::Stride override; + auto size() const -> geometry::Size override; + + operator uint32_t() const override; + + DumbFB(DumbFB const&) = delete; + DumbFB& operator=(DumbFB const&) = delete; +private: + class DumbBuffer; + + DumbFB(mir::Fd drm_fd, bool supports_modifiers, std::unique_ptr buffer); + static auto fb_id_for_buffer(mir::Fd const& drm_fd, bool supports_modifiers, DumbBuffer const& buf) -> uint32_t; + + mir::Fd const drm_fd; + uint32_t const fb_id; + std::unique_ptr const buffer; +}; + +} + +#endif //MIR_GRAPHICS_GBM_FB_H_ diff --git a/src/platforms/gbm-kms/server/kms/egl_helper.cpp b/src/platforms/gbm-kms/server/kms/egl_helper.cpp index 01dc4ef3a43..dba583827eb 100644 --- a/src/platforms/gbm-kms/server/kms/egl_helper.cpp +++ b/src/platforms/gbm-kms/server/kms/egl_helper.cpp @@ -44,7 +44,7 @@ mgmh::EGLHelper::EGLHelper( EGLContext shared_context) : EGLHelper(gl_config) { - setup(gbm, surface, gbm_format, shared_context, false); + setup(gbm.device, surface, gbm_format, shared_context, false); } mgmh::EGLHelper::EGLHelper(EGLHelper&& from) @@ -156,7 +156,7 @@ void mgmh::EGLHelper::setup(GBMHelper const& gbm, EGLContext shared_context) } void mgmh::EGLHelper::setup( - GBMHelper const& gbm, + gbm_device* const device, gbm_surface* surface_gbm, uint32_t gbm_format, EGLContext shared_context, @@ -169,14 +169,14 @@ void mgmh::EGLHelper::setup( EGL_NONE }; - egl_display = egl_display_for_gbm_device(gbm.device); + egl_display = egl_display_for_gbm_device(device); if (owns_egl) { initialise_egl(egl_display, 1, 4); should_terminate_egl = owns_egl; } - egl_config = egl_config_for_format(gbm_format); + egl_config = egl_config_for_format(static_cast(gbm_format)); egl_surface = platform_base.eglCreatePlatformWindowSurface( egl_display, diff --git a/src/platforms/gbm-kms/server/kms/egl_helper.h b/src/platforms/gbm-kms/server/kms/egl_helper.h index daa6c54b6b5..24f24bb3def 100644 --- a/src/platforms/gbm-kms/server/kms/egl_helper.h +++ b/src/platforms/gbm-kms/server/kms/egl_helper.h @@ -17,7 +17,7 @@ #ifndef MIR_GRAPHICS_GBM_EGL_HELPER_H_ #define MIR_GRAPHICS_GBM_EGL_HELPER_H_ -#include "display_helpers.h" +#include "../display_helpers.h" #include "mir/graphics/egl_extensions.h" #include #include @@ -49,13 +49,14 @@ class EGLHelper void setup(GBMHelper const& gbm); void setup(GBMHelper const& gbm, EGLContext shared_context); - void setup(GBMHelper const& gbm, gbm_surface* surface_gbm, uint32_t gbm_format, EGLContext shared_context, bool owns_egl); + void setup(gbm_device* const device, gbm_surface* surface_gbm, uint32_t gbm_format, EGLContext shared_context, bool owns_egl); bool swap_buffers(); bool make_current() const; bool release_current() const; EGLContext context() const { return egl_context; } + auto display() const -> EGLDisplay { return egl_display; } void report_egl_configuration(std::function); diff --git a/src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp b/src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp new file mode 100644 index 00000000000..0cd4e102be7 --- /dev/null +++ b/src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp @@ -0,0 +1,89 @@ +/* +<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp + * Copyright © Canonical Ltd. +|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp + * Copyright © 2017 Canonical Ltd. +======== + * Copyright © 2021 Canonical Ltd. +>>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp + * + * 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 . + */ + +<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp +#include "rendering_platform.h" +#include "buffer_allocator.h" +|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp +#include "gbm_platform.h" +#include "buffer_allocator.h" +======== +#include "kms_framebuffer.h" + +#include +>>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp + +<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp +namespace mg = mir::graphics; +namespace mge = mg::egl::generic; +|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp +namespace mg = mir::graphics; +namespace mgg = mir::graphics::gbm; +======== +namespace mgg = mir::graphics::gbm; +>>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp + +<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp +mge::RenderingPlatform::RenderingPlatform() +|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp +mgg::GBMPlatform::GBMPlatform( + std::shared_ptr const& udev, + std::shared_ptr const& drm) : + udev(udev), + drm(drm), + gbm{std::make_shared(drm->fd)} +======== +mgg::FBHandle::FBHandle(int drm_fd, uint32_t fb_id) + : drm_fd{drm_fd}, + fb_id{fb_id} +>>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp +{ +} + +<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp +auto mge::RenderingPlatform::create_buffer_allocator( + mg::Display const& output) -> mir::UniqueModulePtr +|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp +mir::UniqueModulePtr mgg::GBMPlatform::create_buffer_allocator( + Display const& output) +======== +mgg::FBHandle::~FBHandle() +>>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp +{ +<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp + return make_module_ptr(output); +|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp + return make_module_ptr(output); +======== + // TODO: Some sort of logging on failure? +>>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp +} + +auto mgg::FBHandle::get_drm_fb_id() const -> uint32_t +{ + return fb_id; +} + +mgg::FBHandle::operator uint32_t() const +{ + return fb_id; +} \ No newline at end of file diff --git a/src/platforms/gbm-kms/server/kms/kms_framebuffer.h b/src/platforms/gbm-kms/server/kms/kms_framebuffer.h new file mode 100644 index 00000000000..1e753d5326a --- /dev/null +++ b/src/platforms/gbm-kms/server/kms/kms_framebuffer.h @@ -0,0 +1,43 @@ +/* + * Copyright © 2021 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 + */ + +#ifndef MIR_GRAPHICS_GBM_KMS_FRAMEBUFFER_H_ +#define MIR_GRAPHICS_GBM_KMS_FRAMEBUFFER_H_ + +#include "mir/graphics/platform.h" + +namespace mir +{ +namespace graphics +{ +namespace gbm +{ +class FBHandle : public Framebuffer +{ +public: + virtual ~FBHandle() = default; + + virtual operator uint32_t() const = 0; +}; + +} +} +} + + +#endif //MIR_GRAPHICS_GBM_KMS_FRAMEBUFFER_H_ diff --git a/src/platforms/gbm-kms/server/kms/kms_output.h b/src/platforms/gbm-kms/server/kms/kms_output.h index 0404fc2e5ea..98a3713c559 100644 --- a/src/platforms/gbm-kms/server/kms/kms_output.h +++ b/src/platforms/gbm-kms/server/kms/kms_output.h @@ -91,30 +91,6 @@ class KMSOutput */ virtual void update_from_hardware_state(DisplayConfigurationOutput& to_update) const = 0; - // TODO: Move these to a DRM-device level object - /** - * Get a DRM FB backed by the buffer referred to by a gbm_bo. - * - * \param [in] bo The GBM bo containing the image - * \return An opaque handle to a DRM FB, usable in other KMSOutput calls. - * - * \note As suggested by the shared_ptr return value, returned FB handle may be - * a reference to an existing FB rather than a new import. - */ - virtual auto fb_for(gbm_bo* bo) const -> std::shared_ptr = 0; - virtual auto fb_for(DMABufBuffer const& buffer) const -> std::shared_ptr = 0; - - /** - * Check whether buffer need to be migrated to GPU-private memory for display. - * - * \param [in] bo GBM buffer to test - * \return True if buffer must be migrated to display-private memory in order to be displayed. - * If this method returns true the caller should probably copy it to a new buffer before - * calling fb_for(buffer), as acquiring a FBHandle to the buffer will likely make it - * unusable for rendering on the original GPU. - */ - virtual bool buffer_requires_migration(gbm_bo* bo) const = 0; - virtual int drm_fd() const = 0; protected: KMSOutput() = default; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 365ea56d36a..63a35a7a027 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -15,46 +15,251 @@ */ #include "platform.h" +#include "buffer_allocator.h" #include "display.h" #include "mir/console_services.h" #include "mir/emergency_cleanup_registry.h" +#include "mir/graphics/platform.h" +#include "mir/renderer/gl/context.h" #include "mir/udev/wrapper.h" +#include "kms_framebuffer.h" +#include "mir/graphics/egl_error.h" +#include "mir/graphics/egl_extensions.h" +#include "one_shot_device_observer.h" +#include #define MIR_LOG_COMPONENT "platform-graphics-gbm-kms" #include "mir/log.h" -#include +#include +#include namespace mg = mir::graphics; namespace mgg = mg::gbm; -namespace mgmh = mgg::helpers; -mgg::Platform::Platform(std::shared_ptr const& listener, - ConsoleServices& vt, - EmergencyCleanupRegistry&, - BypassOption bypass_option, - std::unique_ptr quirks) +namespace +{ +auto master_fd_for_device(mir::udev::Device const& device, mir::ConsoleServices& vt) -> std::tuple, mir::Fd> +{ + mir::Fd drm_fd; + auto device_handle = vt.acquire_device( + major(device.devnum()), minor(device.devnum()), + std::make_unique(drm_fd)) + .get(); + + if (drm_fd == mir::Fd::invalid) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to acquire DRM fd"})); + } + + return std::make_tuple(std::move(device_handle), std::move(drm_fd)); +} +} + +mgg::Platform::Platform( + udev::Device const& device, + std::shared_ptr const& listener, + ConsoleServices& vt, + EmergencyCleanupRegistry& registry, + BypassOption bypass_option) + : Platform(master_fd_for_device(device, vt), listener, registry, bypass_option) +{ +} + +mgg::Platform::Platform( + std::tuple, mir::Fd> drm, + std::shared_ptr const& listener, + EmergencyCleanupRegistry&, + BypassOption bypass_option) : udev{std::make_shared()}, - drm{helpers::DRMHelper::open_all_devices(udev, vt, *quirks)}, - // We assume the first DRM device is the boot GPU, and arbitrarily pick it as our - // shell renderer. - // - // TODO: expose multiple rendering GPUs to the shell. - gbm{std::make_shared(drm.front()->fd)}, listener{listener}, + device_handle{std::move(std::get<0>(drm))}, + drm_fd{std::move(std::get<1>(drm))}, bypass_option_{bypass_option} +{ + if (drm_fd == mir::Fd::invalid) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"WTF?"})); + } +} + +namespace +{ +auto gbm_device_for_udev_device( + mir::udev::Device const& device, + std::vector> const& displays) + -> std::variant, std::shared_ptr> +{ + /* First check to see whether our device exactly matches a display device. + * If so, we should use its GBM device + */ + for(auto const& display_device : displays) + { + if (auto gbm_display = mg::DisplayPlatform::acquire_interface(display_device)) + { + if (gbm_display->is_same_device(device)) + { + return gbm_display; + } + } + } + + // We don't match any display HW, create our own GBM device + if (auto node = device.devnode()) + { + auto fd = mir::Fd{open(node, O_RDWR | O_CLOEXEC)}; + if (fd < 0) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + errno, + std::system_category(), + "Failed to open DRM device"})); + } + std::shared_ptr gbm{ + gbm_create_device(fd), + [fd = std::move(fd)](gbm_device* device) + { + if (device) + { + gbm_device_destroy(device); + } + }}; + if (!gbm) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to create GBM device"})); + } + return gbm; + } + + BOOST_THROW_EXCEPTION(( + std::runtime_error{"Attempt to create GBM device from UDev device with no device node?!"})); +} + +/** + * Initialise an EGLDisplay and return the initialised display + */ +auto initialise_egl(EGLDisplay dpy, int minimum_major_version, int minimum_minor_version) -> EGLDisplay +{ + EGLint major, minor; + + if (eglInitialize(dpy, &major, &minor) == EGL_FALSE) + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to initialize EGL display")); + + if ((major < minimum_major_version) || + (major == minimum_major_version && minor < minimum_minor_version)) + { + std::stringstream msg; + msg << "Incompatible EGL version. Requested: " + << minimum_major_version << "." << minimum_minor_version + << " got: " << major << "." << minor; + BOOST_THROW_EXCEPTION((std::runtime_error{msg.str()})); + } + + return dpy; +} + +auto dpy_for_gbm_device(gbm_device* device) -> EGLDisplay +{ + mg::EGLExtensions::PlatformBaseEXT platform_ext; + + auto const egl_display = platform_ext.eglGetPlatformDisplay( + EGL_PLATFORM_GBM_KHR, // EGL_PLATFORM_GBM_MESA has the same value. + static_cast(device), + nullptr); + if (egl_display == EGL_NO_DISPLAY) + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get EGL display")); + + return egl_display; +} + +auto make_share_only_context(EGLDisplay dpy) -> EGLContext +{ + eglBindAPI(EGL_OPENGL_ES_API); + + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint const config_attr[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLConfig cfg; + 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"))); + } + + auto ctx = eglCreateContext(dpy, cfg, EGL_NO_CONTEXT, context_attr); + if (ctx == EGL_NO_CONTEXT) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); + } + return ctx; +} + +struct display_provider_or_nothing +{ + auto operator()(std::shared_ptr provider) { return provider; } + auto operator()(std::shared_ptr) { return std::shared_ptr{}; } +}; + +struct gbm_device_from_hw +{ + auto operator()(std::shared_ptr const& provider) { return provider->gbm_device(); } + auto operator()(std::shared_ptr device) { return device; } +}; +} + +mgg::RenderingPlatform::RenderingPlatform( + mir::udev::Device const& device, + std::vector> const& displays) + : RenderingPlatform(device.clone(), gbm_device_for_udev_device(device, displays)) { } +mgg::RenderingPlatform::RenderingPlatform( + std::unique_ptr udev_device, + std::variant, std::shared_ptr> hw) + : udev_device{std::move(udev_device)}, + device{std::visit(gbm_device_from_hw{}, hw)}, + bound_display{std::visit(display_provider_or_nothing{}, hw)}, + dpy{initialise_egl(dpy_for_gbm_device(device.get()), 1, 4)}, + share_ctx{make_share_only_context(dpy)} +{ +} + + +mir::UniqueModulePtr mgg::RenderingPlatform::create_buffer_allocator( + mg::Display const&) +{ + return make_module_ptr(dpy, share_ctx); +} + +auto mgg::RenderingPlatform::maybe_create_interface( + RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr +{ + if (dynamic_cast(&type_tag)) + { + return std::make_shared(*udev_device, bound_display, dpy, share_ctx); + } + return nullptr; +} + mir::UniqueModulePtr mgg::Platform::create_display( - std::shared_ptr const& initial_conf_policy, std::shared_ptr const& gl_config) + std::shared_ptr const& initial_conf_policy, std::shared_ptr const&) { return make_module_ptr( - drm, - gbm, + shared_from_this(), + drm_fd, bypass_option_, initial_conf_policy, - gl_config, listener); } @@ -62,3 +267,19 @@ mgg::BypassOption mgg::Platform::bypass_option() const { return bypass_option_; } + +auto mgg::Platform::maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr +{ + if (dynamic_cast(&type_tag)) + { + mir::log_debug("Using GBMDisplayProvider"); + return std::make_shared(drm_fd, this); + } + if (dynamic_cast(&type_tag)) + { + mir::log_debug("Using DumbDisplayProvider"); + return std::make_shared(); + } + return {}; +} diff --git a/src/platforms/gbm-kms/server/kms/platform.h b/src/platforms/gbm-kms/server/kms/platform.h index c667e458681..c36ff1484ae 100644 --- a/src/platforms/gbm-kms/server/kms/platform.h +++ b/src/platforms/gbm-kms/server/kms/platform.h @@ -21,6 +21,9 @@ #include "platform_common.h" #include "display_helpers.h" +#include +#include + namespace mir { class EmergencyCleanupRegistry; @@ -36,11 +39,12 @@ class Quirks; class Platform : public graphics::DisplayPlatform { public: - explicit Platform(std::shared_ptr const& reporter, - ConsoleServices& vt, - EmergencyCleanupRegistry& emergency_cleanup_registry, - BypassOption bypass_option, - std::unique_ptr quirks); + Platform( + udev::Device const& device, + std::shared_ptr const& reporter, + ConsoleServices& vt, + EmergencyCleanupRegistry& emergency_cleanup_registry, + BypassOption bypass_option); /* From Platform */ UniqueModulePtr create_display( @@ -48,15 +52,51 @@ class Platform : public graphics::DisplayPlatform std::shared_ptr const& gl_config) override; std::shared_ptr udev; - std::vector> const drm; - std::shared_ptr const gbm; - + std::shared_ptr const listener; +protected: + auto maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr override; + +public: BypassOption bypass_option() const; private: + Platform( + std::tuple, mir::Fd> drm, + std::shared_ptr const& reporter, + EmergencyCleanupRegistry& emergency_cleanup_registry, + BypassOption bypass_option); + + std::unique_ptr const device_handle; + mir::Fd const drm_fd; + BypassOption const bypass_option_; }; + +class RenderingPlatform : public graphics::RenderingPlatform +{ +public: + RenderingPlatform(udev::Device const& device, std::vector> const& displays); + + auto create_buffer_allocator( + graphics::Display const&) -> UniqueModulePtr override; + +protected: + auto maybe_create_interface( + RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr override; + +private: + RenderingPlatform( + std::unique_ptr udev_device, + std::variant, std::shared_ptr> hw); + + std::unique_ptr const udev_device; + std::shared_ptr const device; ///< gbm_device this platform is created on, always valid. + std::shared_ptr const bound_display; ///< Associated Display, if any (nullptr is valid) + EGLDisplay const dpy; + EGLContext const share_ctx; +}; } } } diff --git a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp index 4204007fce4..4b18efd85fd 100644 --- a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp +++ b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ +#include "mir/graphics/platform.h" #define MIR_LOG_COMPONENT "gbm-kms" #include "mir/log.h" @@ -52,7 +53,7 @@ char const* bypass_option_name{"bypass"}; } mir::UniqueModulePtr create_display_platform( - mg::SupportedDevice const&, + mg::SupportedDevice const& device, std::shared_ptr const& options, std::shared_ptr const& emergency_cleanup_registry, std::shared_ptr const& console, @@ -70,10 +71,19 @@ mir::UniqueModulePtr create_display_platform( if (!options->get(bypass_option_name)) bypass_option = mgg::BypassOption::prohibited; - auto quirks = std::make_unique(*options); - return mir::make_module_ptr( - report, *console, *emergency_cleanup_registry, bypass_option, std::move(quirks)); + *device.device, report, *console, *emergency_cleanup_registry, bypass_option); +} + +auto create_rendering_platform( + mg::SupportedDevice const& device, + std::vector> const& displays, + mo::Option const&, + mir::EmergencyCleanupRegistry&) -> mir::UniqueModulePtr +{ + mir::assert_entry_point_signature(&create_rendering_platform); + + return mir::make_module_ptr(*device.device, displays); } void add_graphics_platform_options(boost::program_options::options_description& config) diff --git a/src/platforms/gbm-kms/server/kms/real_kms_output.cpp b/src/platforms/gbm-kms/server/kms/real_kms_output.cpp index 8e67325df51..08311467829 100644 --- a/src/platforms/gbm-kms/server/kms/real_kms_output.cpp +++ b/src/platforms/gbm-kms/server/kms/real_kms_output.cpp @@ -15,6 +15,7 @@ */ #include "real_kms_output.h" +#include "kms_framebuffer.h" #include "mir/graphics/display_configuration.h" #include "page_flipper.h" #include "kms-utils/kms_connector.h" @@ -25,35 +26,13 @@ #include #include #include +#include namespace mg = mir::graphics; namespace mgg = mg::gbm; namespace mgk = mg::kms; namespace geom = mir::geometry; -class mgg::FBHandle -{ -public: - FBHandle(int drm_fd, uint32_t fb_id) - : drm_fd{drm_fd}, - fb_id{fb_id} - { - } - - ~FBHandle() - { - // TODO: Some sort of logging on failure? - drmModeRmFB(drm_fd, fb_id); - } - - auto get_drm_fb_id() const -> uint32_t - { - return fb_id; - } -private: - int const drm_fd; - uint32_t const fb_id; -}; mgg::RealKMSOutput::RealKMSOutput( @@ -162,11 +141,12 @@ bool mgg::RealKMSOutput::set_crtc(FBHandle const& fb) } auto ret = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id, - fb.get_drm_fb_id(), fb_offset.dx.as_int(), fb_offset.dy.as_int(), + fb, fb_offset.dx.as_int(), fb_offset.dy.as_int(), &connector->connector_id, 1, &connector->modes[mode_index]); if (ret) { + mir::log_error("Failed to set CRTC: %s (%i)", strerror(-ret), -ret); current_crtc = nullptr; return false; } @@ -233,7 +213,7 @@ bool mgg::RealKMSOutput::schedule_page_flip(FBHandle const& fb) } return page_flipper->schedule_flip( current_crtc->crtc_id, - fb.get_drm_fb_id(), + fb, connector->connector_id); } @@ -611,258 +591,6 @@ void mgg::RealKMSOutput::update_from_hardware_state( output.edid = edid; } -namespace -{ -void bo_user_data_destroy(gbm_bo* /*bo*/, void *data) -{ - auto bufobj = static_cast*>(data); - delete bufobj; -} -} - -auto mgg::RealKMSOutput::FBRegistry::lookup_or_create(int const drm_fd, gbm_bo* bo) - -> std::shared_ptr -{ - if (!bo) - return nullptr; - - /* - * Check if we have already set up this gbm_bo (the gbm-kms implementation is - * free to reuse gbm_bos). If so, return the associated FBHandle. - */ - auto bufobj = static_cast*>(gbm_bo_get_user_data(bo)); - if (bufobj) - { - return *bufobj; - } - - uint32_t fb_id{0}; - uint32_t handles[4] = {gbm_bo_get_handle(bo).u32, 0, 0, 0}; - uint32_t strides[4] = {gbm_bo_get_stride(bo), 0, 0, 0}; - uint32_t offsets[4] = {0, 0, 0, 0}; - - auto format = gbm_bo_get_format(bo); - /* - * Mir might use the old GBM_BO_ enum formats, but KMS and the rest of - * the world need fourcc formats, so convert... - */ - if (format == GBM_BO_FORMAT_XRGB8888) - format = GBM_FORMAT_XRGB8888; - else if (format == GBM_BO_FORMAT_ARGB8888) - format = GBM_FORMAT_ARGB8888; - - auto const width = gbm_bo_get_width(bo); - auto const height = gbm_bo_get_height(bo); - - /* Create a KMS FB object with the gbm_bo attached to it. */ - auto ret = drmModeAddFB2(drm_fd, width, height, format, - handles, strides, offsets, &fb_id, 0); - if (ret) - return nullptr; - - /* Create a FBHandle and associate it with the gbm_bo */ - - bufobj = new std::shared_ptr(new FBHandle{drm_fd, fb_id}); - gbm_bo_set_user_data(bo, bufobj, bo_user_data_destroy); - - return *bufobj; -} - -auto mgg::RealKMSOutput::fb_for(gbm_bo* bo) const -> std::shared_ptr -{ - return framebuffers.lookup_or_create(drm_fd(), bo); -} - -struct mgg::RealKMSOutput::FBRegistry::DMABufFB -{ - std::array const fds; - std::array const bo_handles; - std::weak_ptr handle; - - DMABufFB( - std::array fds, - std::array bo_handles, - std::weak_ptr handle) - : fds{std::move(fds)}, - bo_handles{bo_handles}, - handle{std::move(handle)} - { - } -}; - -auto mgg::RealKMSOutput::FBRegistry::lookup_or_create( - const int drm_fd, - const DMABufBuffer& image) -> std::shared_ptr -{ - // The DRM API expects a bunch of 4-element arrays, with unused elements set to 0 - std::array handles = {0, 0, 0, 0}; - std::array strides = {0, 0, 0, 0}; - std::array offsets = {0, 0, 0, 0}; - std::array modifiers = {0, 0, 0, 0}; - - std::array dma_bufs; - - auto const& planes = image.planes(); - for (auto i = 0u; i < planes.size() ; ++i) - { - strides[i] = planes[i].stride; - offsets[i] = planes[i].offset; - dma_bufs[i] = planes[i].dma_buf; - } - - // If we've got this FB already imported, we can just return that… - auto const existing_fb = std::find_if( - dmabuf_fbs.begin(), - dmabuf_fbs.end(), - [&dma_bufs](auto const& candidate) - { - for (auto i = 0u; i < dma_bufs.size(); ++i) - { - /* We can do a numerical compare here because the DMAbufFB structs keep a - * reference to their fds open, so any newly imported buffer will have - * a different integer fd handle; they can't accidentally alias. - * - * And the Wayland protocol only imports the FDs once, and then the client - * references the wl_buffer, so we can expect the FDs of a particular buffer - * to be numerically stable. - */ - if (static_cast(dma_bufs[i]) != static_cast(candidate->fds[i])) - { - return false; - } - } - return true; - }); - - uint32_t const* imported_handles; - if (existing_fb != dmabuf_fbs.end()) - { - if (auto const handle = (*existing_fb)->handle.lock()) - { - /* We have already imported the DMA-bufs *and* have a current FB for them. - * We can just return that. - */ - return handle; - } - /* We've imported the DMA-bufs, but have already deleted the FB associated with - * them. Grab the previously-imported handles, then re-create a FB. - */ - imported_handles = (*existing_fb)->bo_handles.data(); - } - else - { - // Otherwise, we need to import the DMA-bufs - for (auto i = 0u; i < planes.size(); ++i) - { - if (auto const error = drmPrimeFDToHandle(drm_fd, dma_bufs[i], &handles[i])) - { - BOOST_THROW_EXCEPTION(( - std::system_error{ - error, - std::system_category(), - "Failed to acquire GEM handle for DMA-BUF"})); - } - } - imported_handles = handles.data(); - } - - - // If there's a modifier set, propagate it to all components. - if (image.modifier().has_value()) - { - for (auto i = 0u; i < planes.size(); ++i) - { - modifiers[i] = image.modifier().value(); - } - } - - uint32_t drm_fb_id; - auto const ret = drmModeAddFB2WithModifiers( - drm_fd, - image.size().width.as_uint32_t(), - image.size().height.as_uint32_t(), - image.drm_fourcc(), - imported_handles, - strides.data(), - offsets.data(), - image.modifier().has_value() ? modifiers.data() : nullptr, - &drm_fb_id, - image.modifier().has_value() ? DRM_MODE_FB_MODIFIERS : 0); - - if (ret) - { - mir::log_debug( - "Failed to import dmabuf-based image as FB: %s", - std::system_category().message(ret).c_str()); - return nullptr; - } - - auto fb_handle = std::make_shared(drm_fd, drm_fb_id); - - if (existing_fb != dmabuf_fbs.end()) - { - // We already have the buffer data; we just need to re-create a handle - (*existing_fb)->handle = fb_handle; - } - else - { - dmabuf_fbs.push_back(std::make_shared(std::move(dma_bufs), handles, fb_handle)); - } - - return fb_handle; -} - -auto mgg::RealKMSOutput::fb_for(mg::DMABufBuffer const& image) const -> std::shared_ptr -{ - return framebuffers.lookup_or_create(drm_fd(), image); -} - -bool mgg::RealKMSOutput::buffer_requires_migration(gbm_bo* bo) const -{ - /* - * It's possible that some devices will not require migration - - * Intel GPUs can obviously scanout from main memory, as can USB outputs such as - * DisplayLink. - * - * For a first go, just say that *every* device scans out of GPU-private memory. - * - * To complicate matters, Mali's gbm-kms implementation does *not* return the same - * integer fd from gbm_device_get_fd() as the drm fd that the device was created - * from, and GBM may choose to internally open the DRM render node associated - * with the DRM node passed to gbm_create_device. - */ - - errno = 0; - auto const gbm_device_node = std::unique_ptr{ - drmGetPrimaryDeviceNameFromFd(gbm_device_get_fd(gbm_bo_get_device(bo))), - &free - }; - if (!gbm_device_node) - { - BOOST_THROW_EXCEPTION(( - std::system_error{ - errno, - std::system_category(), - "Failed to query DRM device backing GBM buffer"})); - } - - auto const drm_device_node = std::unique_ptr{ - drmGetPrimaryDeviceNameFromFd(drm_fd_), - &free - }; - if (!drm_device_node) - { - BOOST_THROW_EXCEPTION(( - std::system_error{ - errno, - std::system_category(), - "Failed to query DRM device node of display device"})); - } - - // These *should* match if we're on the same device - return strcmp(gbm_device_node.get(), drm_device_node.get()) != 0; -} - int mgg::RealKMSOutput::drm_fd() const { return drm_fd_; diff --git a/src/platforms/gbm-kms/server/kms/real_kms_output.h b/src/platforms/gbm-kms/server/kms/real_kms_output.h index fa6f25fc81e..a9ab4770ba3 100644 --- a/src/platforms/gbm-kms/server/kms/real_kms_output.h +++ b/src/platforms/gbm-kms/server/kms/real_kms_output.h @@ -64,10 +64,6 @@ class RealKMSOutput : public KMSOutput void refresh_hardware_state() override; void update_from_hardware_state(DisplayConfigurationOutput& output) const override; - auto fb_for(gbm_bo* bo) const -> std::shared_ptr override; - auto fb_for(DMABufBuffer const& image) const -> std::shared_ptr override; - - bool buffer_requires_migration(gbm_bo* bo) const override; int drm_fd() const override; private: @@ -77,21 +73,6 @@ class RealKMSOutput : public KMSOutput int const drm_fd_; std::shared_ptr const page_flipper; - /* TODO: This should really be owned by a DRM-device-level object, - * not per-output. We don't have one of those at the moment, so here'll do. - */ - class FBRegistry - { - public: - auto lookup_or_create(int const drm_fd, gbm_bo* bo) -> std::shared_ptr; - auto lookup_or_create(int const drm_fd, DMABufBuffer const& image) -> std::shared_ptr; - - struct DMABufFB; - private: - std::vector> dmabuf_fbs; - }; - FBRegistry mutable framebuffers; - kms::DRMModeConnectorUPtr connector; size_t mode_index; geometry::Displacement fb_offset; diff --git a/src/platforms/gbm-kms/server/kms/real_kms_output_container.cpp b/src/platforms/gbm-kms/server/kms/real_kms_output_container.cpp index dae9bf51c95..dfe9e5cce8b 100644 --- a/src/platforms/gbm-kms/server/kms/real_kms_output_container.cpp +++ b/src/platforms/gbm-kms/server/kms/real_kms_output_container.cpp @@ -22,10 +22,10 @@ namespace mgg = mir::graphics::gbm; mgg::RealKMSOutputContainer::RealKMSOutputContainer( - std::vector const& drm_fds, - std::function(int)> const& construct_page_flipper) - : drm_fds{drm_fds}, - construct_page_flipper{construct_page_flipper} + mir::Fd drm_fd, + std::shared_ptr page_flipper) + : drm_fd{std::move(drm_fd)}, + page_flipper{std::move(page_flipper)} { } @@ -39,58 +39,38 @@ void mgg::RealKMSOutputContainer::update_from_hardware_state() { decltype(outputs) new_outputs; - // TODO: Accumulate errors and present them all. - std::exception_ptr last_error; + auto const resources = std::make_unique(drm_fd); - for (auto drm_fd : drm_fds) + for (auto &&connector : resources->connectors()) { - std::unique_ptr resources; - try + // Caution: O(n²) here, but n is the number of outputs, so should + // conservatively be << 100. + auto existing_output = std::find_if( + outputs.begin(), + outputs.end(), + [&connector, this](auto const &candidate) + { + return + connector->connector_id == candidate->id() && + drm_fd == candidate->drm_fd(); + }); + + if (existing_output != outputs.end()) { - resources = std::make_unique(drm_fd); + // We could drop this down to O(n) by being smarter about moving out + // of the outputs vector. + // + // That's a bit of a faff, so just do the simple thing for now. + new_outputs.push_back(*existing_output); + new_outputs.back()->refresh_hardware_state(); } - catch (std::exception const&) + else { - last_error = std::current_exception(); - continue; + new_outputs.push_back(std::make_shared( + drm_fd, + std::move(connector), + page_flipper)); } - - for (auto &&connector : resources->connectors()) - { - // Caution: O(n²) here, but n is the number of outputs, so should - // conservatively be << 100. - auto existing_output = std::find_if( - outputs.begin(), - outputs.end(), - [&connector, drm_fd](auto const &candidate) - { - return - connector->connector_id == candidate->id() && - drm_fd == candidate->drm_fd(); - }); - - if (existing_output != outputs.end()) - { - // We could drop this down to O(n) by being smarter about moving out - // of the outputs vector. - // - // That's a bit of a faff, so just do the simple thing for now. - new_outputs.push_back(*existing_output); - new_outputs.back()->refresh_hardware_state(); - } - else - { - new_outputs.push_back(std::make_shared( - drm_fd, - std::move(connector), - construct_page_flipper(drm_fd))); - } - } - - } - if (new_outputs.empty() && last_error) - { - std::rethrow_exception(last_error); } outputs = new_outputs; diff --git a/src/platforms/gbm-kms/server/kms/real_kms_output_container.h b/src/platforms/gbm-kms/server/kms/real_kms_output_container.h index 661695ab4e4..942c4e4822a 100644 --- a/src/platforms/gbm-kms/server/kms/real_kms_output_container.h +++ b/src/platforms/gbm-kms/server/kms/real_kms_output_container.h @@ -18,6 +18,7 @@ #define MIR_GRAPHICS_GBM_REAL_KMS_OUTPUT_CONTAINER_H_ #include "kms_output_container.h" +#include "mir/fd.h" #include namespace mir @@ -33,16 +34,16 @@ class RealKMSOutputContainer : public KMSOutputContainer { public: RealKMSOutputContainer( - std::vector const& drm_fds, - std::function(int drm_fd)> const& construct_page_flipper); + mir::Fd drm_fd, + std::shared_ptr page_flipper); void for_each_output(std::function const&)> functor) const override; void update_from_hardware_state() override; private: - std::vector const drm_fds; + mir::Fd const drm_fd; std::vector> outputs; - std::function(int drm_fd)> const construct_page_flipper; + std::shared_ptr const page_flipper; }; } diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index d8de60fa0ef..6416d9ae27d 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -15,9 +15,11 @@ */ #include "buffer_allocator.h" +#include "mir/graphics/gl_config.h" #include "mir/graphics/linux_dmabuf.h" #include "mir/anonymous_shm_file.h" #include "mir/renderer/sw/pixel_source.h" +#include "mir/graphics/platform.h" #include "shm_buffer.h" #include "mir/graphics/egl_context_executor.h" #include "mir/graphics/egl_extensions.h" @@ -29,6 +31,10 @@ #include "mir/renderer/gl/context_source.h" #include "mir/graphics/egl_wayland_allocator.h" #include "mir/executor.h" +#include "mir/renderer/gl/gl_surface.h" +#include "mir/graphics/display_buffer.h" +#include "mir/graphics/drm_formats.h" +#include "mir/graphics/egl_error.h" #include #include @@ -39,6 +45,7 @@ #include #include +#include #include #include #include @@ -57,44 +64,98 @@ namespace geom = mir::geometry; namespace { -auto context_for_output(mg::Display const& output) -> std::unique_ptr +auto make_share_only_context(EGLDisplay dpy, std::optional share_with) -> EGLContext { - try + eglBindAPI(EGL_OPENGL_ES_API); + + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint const config_attr[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLConfig cfg; + EGLint num_configs; + + if (eglChooseConfig(dpy, config_attr, &cfg, 1, &num_configs) != EGL_TRUE || num_configs != 1) { - auto& context_source = dynamic_cast(output); - - /* - * We care about no part of this context's config; we will do no rendering with it. - * All we care is that we can allocate texture IDs and bind a texture, which is - * config independent. - * - * That's not *entirely* true; we also need it to be on the same device as we want - * to do the rendering on, and that GL must support all the extensions we care about, - * but since we don't yet support heterogeneous hybrid and implementing that will require - * broader interface changes it's a safe enough requirement for now. - */ - return context_source.create_gl_context(); - } - catch (std::bad_cast const& err) - { - std::throw_with_nested( - boost::enable_error_info( - std::runtime_error{"Output platform cannot provide a GL context"}) - << boost::throw_function(__PRETTY_FUNCTION__) - << boost::throw_line(__LINE__) - << boost::throw_file(__FILE__)); + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to find any matching EGL config"))); } + + auto ctx = eglCreateContext(dpy, cfg, share_with.value_or(EGL_NO_CONTEXT), context_attr); + if (ctx == EGL_NO_CONTEXT) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); + } + return ctx; } + +class SurfacelessEGLContext : public mir::renderer::gl::Context +{ +public: + explicit SurfacelessEGLContext(EGLDisplay dpy) + : dpy{dpy}, + ctx{make_share_only_context(dpy, {})} + { + } + + SurfacelessEGLContext(EGLDisplay dpy, EGLContext share_with) + : dpy{dpy}, + ctx{make_share_only_context(dpy, share_with)} + { + } + + ~SurfacelessEGLContext() override + { + 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 override + { + return std::unique_ptr{new SurfacelessEGLContext{dpy, ctx}}; + } + + explicit operator EGLContext() override + { + return ctx; + } +private: + EGLDisplay const dpy; + EGLContext const ctx; +}; } -mge::BufferAllocator::BufferAllocator(mg::Display const& output) - : ctx{context_for_output(output)}, +mge::BufferAllocator::BufferAllocator(EGLDisplay dpy, EGLContext share_with) + : ctx{std::make_unique(dpy, share_with)}, egl_delegate{ - std::make_shared(context_for_output(output))}, + std::make_shared(ctx->make_share_context())}, egl_extensions(std::make_shared()) { } +mge::BufferAllocator::~BufferAllocator() = default; + std::shared_ptr mge::BufferAllocator::alloc_software_buffer( geom::Size size, MirPixelFormat format) { @@ -249,3 +310,244 @@ auto mge::BufferAllocator::buffer_from_shm( std::move(on_consumed), std::move(on_release)); } + +auto mge::BufferAllocator::shared_egl_context() -> EGLContext +{ + return static_cast(*ctx); +} + +auto mge::GLRenderingProvider::as_texture(std::shared_ptr buffer) -> std::shared_ptr +{ + return std::dynamic_pointer_cast(buffer); +} + +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>; + + +class CPUCopyOutputSurface : public mg::gl::OutputSurface +{ +public: + CPUCopyOutputSurface( + EGLDisplay dpy, + EGLContext ctx, + std::unique_ptr allocator, + geom::Size size) + : allocator{std::move(allocator)}, + dpy{dpy}, + ctx{ctx}, + size_{std::move(size)} + { + 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); + } + + auto commit() -> std::unique_ptr override + { + auto fb = allocator->acquire(); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + { + 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(), + GL_RGBA, 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::unique_ptr const allocator; + EGLDisplay const dpy; + EGLContext const ctx; + geom::Size const size_; + RenderbufferHandle const colour_buffer; + FramebufferHandle const fbo; +}; + +class EGLOutputSurface : public mg::gl::OutputSurface +{ +public: + EGLOutputSurface( + std::unique_ptr fb, + geom::Size size) + : fb{std::move(fb)}, + size_{size} + { + } + + void bind() override + { + } + + void make_current() override + { + fb->make_current(); + } + + auto commit() -> std::unique_ptr override + { + return fb->clone_handle(); + } + + auto size() const -> geom::Size override + { + return size_; + } + + auto layout() const -> Layout override + { + return Layout::GL; + } + +private: + std::unique_ptr const fb; + geom::Size const size_; +}; +} + +auto mge::GLRenderingProvider::surface_for_output(DisplayBuffer& db, GLConfig const& config) + -> std::unique_ptr +{ + if (auto egl_display = DisplayPlatform::acquire_interface(db.owner())) + { + return std::make_unique( + egl_display->framebuffer_for_db(db, config, ctx), + db.view_area().size); + } + auto dumb_display = DisplayPlatform::acquire_interface(db.owner()); + + return std::make_unique( + dpy, + ctx, + dumb_display->allocator_for_db(db), + db.view_area().size); +} + +auto mge::GLRenderingProvider::make_framebuffer_provider(DisplayBuffer const& /*target*/) + -> std::unique_ptr +{ + // TODO: Make this not a null implementation, so bypass/overlays can work again + class NullFramebufferProvider : public FramebufferProvider + { + public: + auto buffer_to_framebuffer(std::shared_ptr) -> std::unique_ptr override + { + // It is safe to return nullptr; this will be treated as “this buffer cannot be used as + // a framebuffer”. + return {}; + } + }; + return std::make_unique(); +} + +mge::GLRenderingProvider::GLRenderingProvider( + EGLDisplay dpy, + EGLContext ctx) + : dpy{dpy}, + ctx{ctx} +{ +} diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.h b/src/platforms/renderer-generic-egl/buffer_allocator.h index 2c5b4b255e6..4533b6372c1 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.h +++ b/src/platforms/renderer-generic-egl/buffer_allocator.h @@ -19,6 +19,7 @@ #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/linux_dmabuf.h" +#include "mir/graphics/platform.h" #include #include @@ -47,12 +48,15 @@ class EGLContextExecutor; namespace egl::generic { +class GLRenderingProvider; + class BufferAllocator: public graphics::GraphicBufferAllocator { public: - explicit BufferAllocator(Display const& output); - + BufferAllocator(EGLDisplay dpy, EGLContext share_with); + ~BufferAllocator() override; + std::shared_ptr alloc_software_buffer(geometry::Size size, MirPixelFormat) override; std::vector supported_pixel_formats() override; @@ -66,8 +70,10 @@ class BufferAllocator: std::shared_ptr data, std::function&& on_consumed, std::function&& on_release) -> std::shared_ptr override; + + auto shared_egl_context() -> EGLContext; private: - std::shared_ptr const ctx; + std::unique_ptr const ctx; std::shared_ptr const egl_delegate; std::shared_ptr wayland_executor; std::unique_ptr> dmabuf_extension; @@ -75,6 +81,24 @@ class BufferAllocator: bool egl_display_bound{false}; }; +class GLRenderingProvider : public graphics::GLRenderingProvider +{ +public: + GLRenderingProvider( + EGLDisplay dpy, + EGLContext ctx); + + auto make_framebuffer_provider(DisplayBuffer const& target) + -> std::unique_ptr override; + + auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; + + auto surface_for_output(DisplayBuffer& db, GLConfig const& config) -> std::unique_ptr override; + +private: + EGLDisplay const dpy; + EGLContext const ctx; +}; } } } diff --git a/src/platforms/renderer-generic-egl/platform_symbols.cpp b/src/platforms/renderer-generic-egl/platform_symbols.cpp index 5e237589ab7..85746ade4e0 100644 --- a/src/platforms/renderer-generic-egl/platform_symbols.cpp +++ b/src/platforms/renderer-generic-egl/platform_symbols.cpp @@ -35,13 +35,13 @@ namespace mge = mg::egl::generic; auto create_rendering_platform( mg::SupportedDevice const&, - std::vector> const&, + std::vector> const& displays, mo::Option const&, mir::EmergencyCleanupRegistry&) -> mir::UniqueModulePtr { mir::assert_entry_point_signature(&create_rendering_platform); - return mir::make_module_ptr(); + return mir::make_module_ptr(displays); } void add_graphics_platform_options(boost::program_options::options_description&) diff --git a/src/platforms/renderer-generic-egl/rendering_platform.cpp b/src/platforms/renderer-generic-egl/rendering_platform.cpp index 827fb738619..65ea99a869f 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.cpp +++ b/src/platforms/renderer-generic-egl/rendering_platform.cpp @@ -16,16 +16,86 @@ #include "rendering_platform.h" #include "buffer_allocator.h" +#include "mir/graphics/platform.h" +#include "mir/graphics/egl_error.h" + +#include +#include namespace mg = mir::graphics; namespace mge = mg::egl::generic; -mge::RenderingPlatform::RenderingPlatform() +namespace +{ +auto egl_display_from_platforms(std::vector> const& displays) -> EGLDisplay +{ + for (auto const& display : displays) + { + if (auto egl_provider = mg::DisplayPlatform::acquire_interface(display)) + { + return egl_provider->get_egl_display(); + } + } + // 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); + if (dpy == EGL_NO_DISPLAY) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to create any EGL display"})); + } + return dpy; +} + +auto make_share_only_context(EGLDisplay dpy) -> EGLContext +{ + eglBindAPI(EGL_OPENGL_ES_API); + + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint const config_attr[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLConfig cfg; + 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"))); + } + + auto ctx = eglCreateContext(dpy, cfg, EGL_NO_CONTEXT, context_attr); + if (ctx == EGL_NO_CONTEXT) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); + } + return ctx; +} +} + +mge::RenderingPlatform::RenderingPlatform(std::vector> const& displays) + : dpy{egl_display_from_platforms(displays)}, + ctx{make_share_only_context(dpy)} { } auto mge::RenderingPlatform::create_buffer_allocator( - mg::Display const& output) -> mir::UniqueModulePtr + mg::Display const& /*output*/) -> mir::UniqueModulePtr +{ + return make_module_ptr(dpy, ctx); +} + +auto mge::RenderingPlatform::maybe_create_interface(RendererInterfaceBase::Tag const& tag) + -> std::shared_ptr { - return make_module_ptr(output); + if (dynamic_cast(&tag)) + { + return std::make_shared(dpy, ctx); + } + return nullptr; } diff --git a/src/platforms/renderer-generic-egl/rendering_platform.h b/src/platforms/renderer-generic-egl/rendering_platform.h index f58ab223639..af2a387961e 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.h +++ b/src/platforms/renderer-generic-egl/rendering_platform.h @@ -19,10 +19,10 @@ #include "mir/graphics/platform.h" +#include + namespace mir { -class EmergencyCleanupRegistry; -class ConsoleServices; namespace graphics::egl::generic { @@ -30,10 +30,18 @@ namespace graphics::egl::generic class RenderingPlatform : public graphics::RenderingPlatform { public: - explicit RenderingPlatform(); + explicit RenderingPlatform(std::vector> const& displays); auto create_buffer_allocator( graphics::Display const& output) -> UniqueModulePtr override; + +protected: + auto maybe_create_interface( + RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr override; + +private: + EGLDisplay const dpy; + EGLContext const ctx; }; } diff --git a/src/platforms/wayland/display.cpp b/src/platforms/wayland/display.cpp index 8d6c3d07395..67b0ea5fe8c 100644 --- a/src/platforms/wayland/display.cpp +++ b/src/platforms/wayland/display.cpp @@ -150,47 +150,6 @@ bool mgw::Display::apply_if_configuration_preserves_display_buffers(DisplayConfi return false; } -auto mgw::Display::create_gl_context() const -> std::unique_ptr -{ - EGLint const static context_attr[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - class GlContext : public mir::renderer::gl::Context - { - public: - GlContext(EGLDisplay egldisplay, EGLConfig eglconfig, EGLContext eglctx) : - egldisplay{egldisplay}, - eglctx{eglCreateContext(egldisplay, eglconfig, eglctx, context_attr)} {} - - ~GlContext() - { - eglDestroyContext(egldisplay, eglctx); - } - - void make_current() const override - { - if (eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, eglctx) != EGL_TRUE) - { - log_warning( - "%s FAILED: %s", - __PRETTY_FUNCTION__, - egl_category().message(eglGetError()).c_str()); - } - } - - void release_current() const override - { eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } - - private: - EGLDisplay const egldisplay; - EGLContext const eglctx; - }; - - return std::make_unique(egldisplay, eglconfig, eglctx); -} - void mgw::Display::for_each_display_sync_group(const std::function& f) { DisplayClient::for_each_display_sync_group(f); diff --git a/src/platforms/wayland/display.h b/src/platforms/wayland/display.h index fc57fa8ef8a..16c894db7a3 100644 --- a/src/platforms/wayland/display.h +++ b/src/platforms/wayland/display.h @@ -86,8 +86,6 @@ class Display : public mir::graphics::Display, auto create_hardware_cursor() -> std::shared_ptroverride; - auto create_gl_context() const -> std::unique_ptr override; - private: void keyboard_key(wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) override; diff --git a/src/platforms/wayland/displayclient.cpp b/src/platforms/wayland/displayclient.cpp index 27bdfe5e091..9f8b4d619fd 100644 --- a/src/platforms/wayland/displayclient.cpp +++ b/src/platforms/wayland/displayclient.cpp @@ -16,6 +16,7 @@ */ #include "displayclient.h" +#include "mir/graphics/platform.h" #include "mir/graphics/egl_error.h" #include @@ -39,8 +40,6 @@ namespace geom = mir::geometry; class mgw::DisplayClient::Output : public DisplaySyncGroup, - public renderer::gl::RenderTarget, - public NativeDisplayBuffer, public DisplayBuffer { public: @@ -60,7 +59,7 @@ class mgw::DisplayClient::Output : geom::Size output_size; wl_output* const output; - DisplayClient* const owner; + DisplayClient* const owner_; wl_surface* const surface; xdg_surface* shell_surface{nullptr}; @@ -99,16 +98,13 @@ class mgw::DisplayClient::Output : // DisplayBuffer implementation auto view_area() const -> geometry::Rectangle override; - bool overlay(RenderableList const& renderlist) override; + bool overlay(std::vector const& renderlist) override; auto transformation() const -> glm::mat2 override; - auto native_display_buffer() -> NativeDisplayBuffer* override; - - // RenderTarget implementation - auto size() const -> geom::Size override; - void make_current() override; - void release_current() override; - void swap_buffers() override; - void bind() override; + auto owner() const -> std::shared_ptr override; + void set_next_image(std::unique_ptr content) override; + +private: + std::unique_ptr next_frame; }; namespace @@ -125,7 +121,7 @@ mgw::DisplayClient::Output::Output( DisplayClient* owner, std::function on_change) : output{output}, - owner{owner}, + owner_{owner}, surface{wl_compositor_create_surface(owner->compositor)}, on_change{std::move(on_change)} { @@ -176,7 +172,7 @@ mgw::DisplayClient::Output::~Output() if (eglsurface != EGL_NO_SURFACE) { - eglDestroySurface(owner->egldisplay, eglsurface); + eglDestroySurface(owner_->egldisplay, eglsurface); } if (egl_window != nullptr) @@ -186,7 +182,7 @@ mgw::DisplayClient::Output::~Output() if (eglctx != EGL_NO_CONTEXT) { - eglDestroyContext(owner->egldisplay, eglctx); + eglDestroyContext(owner_->egldisplay, eglctx); } } @@ -290,7 +286,7 @@ void mgw::DisplayClient::Output::done() static xdg_surface_listener const shell_surface_listener{ [](void* self, auto, auto... args) { static_cast(self)->surface_configure(args...); }, }; - shell_surface = xdg_wm_base_get_xdg_surface(owner->shell, surface); + shell_surface = xdg_wm_base_get_xdg_surface(owner_->shell, surface); xdg_surface_add_listener(shell_surface, &shell_surface_listener, this); static xdg_toplevel_listener const shell_toplevel_listener{ @@ -331,11 +327,7 @@ void mgw::DisplayClient::Output::surface_configure(uint32_t serial) if (!has_initialized) { egl_window = wl_egl_window_create(surface, output_size.width.as_int(), output_size.height.as_int()); - eglsurface = eglCreateWindowSurface( - owner->egldisplay, - owner->eglconfig, - reinterpret_cast(egl_window), - nullptr); + eglsurface = eglCreatePlatformWindowSurface(owner_->egldisplay, owner_->eglconfig, egl_window, nullptr); has_initialized = true; } else if (size_is_changed) @@ -354,54 +346,6 @@ void mgw::DisplayClient::Output::for_each_display_buffer(std::function std::chrono::milliseconds -{ - return std::chrono::milliseconds{0}; -} - -auto mgw::DisplayClient::Output::view_area() const -> geometry::Rectangle -{ - return dcout.extents(); -} - -bool mgw::DisplayClient::Output::overlay(mir::graphics::RenderableList const&) -{ - return false; -} - -auto mgw::DisplayClient::Output::transformation() const -> glm::mat2 -{ - return glm::mat2{1}; -} - -auto mgw::DisplayClient::Output::native_display_buffer() -> NativeDisplayBuffer* -{ - return this; -} - -auto mgw::DisplayClient::Output::size() const -> geom::Size -{ - return output_size; -} - -void mgw::DisplayClient::Output::make_current() -{ - if (eglctx == EGL_NO_CONTEXT) - eglctx = eglCreateContext(owner->egldisplay, owner->eglconfig, owner->eglctx, ctxattribs); - - if (!eglMakeCurrent(owner->egldisplay, eglsurface, eglsurface, eglctx)) - BOOST_THROW_EXCEPTION(egl_error("Can't eglMakeCurrent")); -} - -void mgw::DisplayClient::Output::release_current() -{ - eglMakeCurrent(owner->egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); -} - -void mgw::DisplayClient::Output::swap_buffers() { struct FrameSync { @@ -416,7 +360,7 @@ void mgw::DisplayClient::Output::swap_buffers() static struct wl_callback_listener const frame_listener = { [](void* data, auto... args) - { static_cast(data)->frame_done(args...); }, + { static_cast(data)->frame_done(args...); }, }; wl_callback_add_listener(callback, &frame_listener, this); } @@ -450,23 +394,49 @@ void mgw::DisplayClient::Output::swap_buffers() }; auto const frame_sync = std::make_shared(surface); - owner->spawn([frame_sync]() - { - frame_sync->init(); - }); + owner_->spawn([frame_sync]() + { + frame_sync->init(); + }); // Avoid throttling compositing by blocking in eglSwapBuffers(). // Instead we use the frame "done" notification. - eglSwapInterval(owner->egldisplay, 0); + eglSwapInterval(owner_->egldisplay, 0); - if (eglSwapBuffers(owner->egldisplay, eglsurface) != EGL_TRUE) + if (eglSwapBuffers(owner_->egldisplay, eglsurface) != EGL_TRUE) BOOST_THROW_EXCEPTION(egl_error("Failed to perform buffer swap")); frame_sync->wait_for_done(); } -void mgw::DisplayClient::Output::bind() +auto mgw::DisplayClient::Output::recommended_sleep() const -> std::chrono::milliseconds +{ + return std::chrono::milliseconds{0}; +} + +auto mgw::DisplayClient::Output::view_area() const -> geometry::Rectangle +{ + return dcout.extents(); +} + +bool mgw::DisplayClient::Output::overlay(std::vector const&) +{ + return false; +} + +auto mgw::DisplayClient::Output::transformation() const -> glm::mat2 +{ + return glm::mat2{1}; +} + +auto mgw::DisplayClient::Output::owner() const -> std::shared_ptr +{ + return {}; +} + +void mgw::DisplayClient::Output::set_next_image(std::unique_ptr content) { + next_frame = std::move(content); } mgw::DisplayClient::DisplayClient( diff --git a/src/platforms/wayland/platform.cpp b/src/platforms/wayland/platform.cpp index c2a9f359971..35668efb594 100644 --- a/src/platforms/wayland/platform.cpp +++ b/src/platforms/wayland/platform.cpp @@ -33,3 +33,9 @@ mir::UniqueModulePtr mgw::Platform::create_display( { return mir::make_module_ptr(wl_display, gl_config, report); } + +auto mgw::Platform::maybe_create_interface( + const DisplayInterfaceBase::Tag& /*tag*/) -> std::shared_ptr +{ + return {}; +} diff --git a/src/platforms/wayland/platform.h b/src/platforms/wayland/platform.h index 79da7280d5a..6544c02ac7f 100644 --- a/src/platforms/wayland/platform.h +++ b/src/platforms/wayland/platform.h @@ -47,6 +47,9 @@ class Platform : public graphics::DisplayPlatform std::shared_ptr const& gl_config) override; private: + auto maybe_create_interface(DisplayInterfaceBase::Tag const& tag) + -> std::shared_ptr override; + struct wl_display* const wl_display; std::shared_ptr const report; }; diff --git a/src/platforms/x11/graphics/display.cpp b/src/platforms/x11/graphics/display.cpp index 5f738854bc1..8c7ca86d8c3 100644 --- a/src/platforms/x11/graphics/display.cpp +++ b/src/platforms/x11/graphics/display.cpp @@ -46,47 +46,15 @@ auto get_pixel_size_mm(mx::XCBConnection* conn) -> geom::SizeF float(screen->width_in_millimeters) / screen->width_in_pixels, float(screen->height_in_millimeters) / screen->height_in_pixels}; } - -class XGLContext : public mir::renderer::gl::Context -{ -public: - XGLContext(::Display* const x_dpy, - std::shared_ptr const& gl_config, - EGLContext const shared_ctx) - : egl{*gl_config, x_dpy, shared_ctx} - { - } - - ~XGLContext() = default; - - void make_current() const override - { - egl.make_current(); - } - - void release_current() const override - { - egl.release_current(); - } - -private: - mgx::helpers::EGLHelper const egl; -}; } mgx::X11Window::X11Window(mx::X11Resources* x11_resources, std::string title, - EGLDisplay egl_dpy, - geom::Size const size, - EGLConfig const egl_cfg) + geom::Size const size) : x11_resources{x11_resources} { auto const conn = x11_resources->conn.get(); - EGLint vid; - if (!eglGetConfigAttrib(egl_dpy, egl_cfg, EGL_NATIVE_VISUAL_ID, &vid)) - BOOST_THROW_EXCEPTION(mg::egl_error("Cannot get config attrib")); - uint32_t const value_mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK; uint32_t value_list[32]; value_list[0] = 0; // background_pixel @@ -134,15 +102,15 @@ mgx::X11Window::operator xcb_window_t() const return win; } -mgx::Display::Display(std::shared_ptr const& x11_resources, - std::string const title, - std::vector const& requested_sizes, - std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config, - std::shared_ptr const& report) - : shared_egl{*gl_config, x11_resources->xlib_dpy}, +mgx::Display::Display( + std::shared_ptr parent, + std::shared_ptr const& x11_resources, + std::string const title, + std::vector const& requested_sizes, + std::shared_ptr const& initial_conf_policy, + std::shared_ptr const& report) + : parent{std::move(parent)}, x11_resources{x11_resources}, - gl_config{gl_config}, pixel_size_mm{get_pixel_size_mm(x11_resources->conn.get())}, report{report} { @@ -154,9 +122,7 @@ mgx::Display::Display(std::shared_ptr const& x11_resources auto window = std::make_unique( x11_resources.get(), title, - shared_egl.display(), - actual_size, - shared_egl.config()); + actual_size); auto pf = x11_resources->conn->default_pixel_format(); auto configuration = DisplayConfiguration::build_output( pf, @@ -168,14 +134,9 @@ mgx::Display::Display(std::shared_ptr const& x11_resources requested_size.scale, mir_orientation_normal); auto display_buffer = std::make_unique( - x11_resources->xlib_dpy, - configuration->id, + this->parent, *window, - configuration->extents(), - actual_size, - shared_egl.context(), - report, - *gl_config); + configuration->extents()); top_left.x += as_delta(configuration->extents().size.width); outputs.push_back(std::make_unique( this, @@ -184,8 +145,6 @@ mgx::Display::Display(std::shared_ptr const& x11_resources std::move(configuration))); } - shared_egl.make_current(); - auto const display_config = configuration(); initial_conf_policy->apply_to(*display_config); configure(*display_config); @@ -283,11 +242,6 @@ auto mgx::Display::create_hardware_cursor() -> std::shared_ptr return nullptr; } -std::unique_ptr mgx::Display::create_gl_context() const -{ - return std::make_unique(x11_resources->xlib_dpy, gl_config, shared_egl.context()); -} - bool mgx::Display::apply_if_configuration_preserves_display_buffers( mg::DisplayConfiguration const& /*conf*/) { @@ -320,7 +274,6 @@ void mgx::Display::OutputInfo::set_size(geometry::Size const& size) return; } config->modes[0].size = size; - display_buffer->set_size(size); display_buffer->set_view_area(config->extents()); auto const handlers = owner->config_change_handlers; diff --git a/src/platforms/x11/graphics/display.h b/src/platforms/x11/graphics/display.h index 2f9e0abbf7f..2e270b0c43b 100644 --- a/src/platforms/x11/graphics/display.h +++ b/src/platforms/x11/graphics/display.h @@ -34,8 +34,6 @@ namespace mir namespace graphics { -class AtomicFrame; -class GLConfig; class DisplayReport; struct DisplayConfigurationOutput; class DisplayConfigurationPolicy; @@ -51,9 +49,7 @@ class X11Window public: X11Window(mir::X::X11Resources* x11_resources, std::string title, - EGLDisplay egl_dpy, - geometry::Size const size, - EGLConfig const egl_cfg); + geometry::Size const size); ~X11Window(); operator xcb_window_t() const; @@ -66,12 +62,13 @@ class X11Window class Display : public graphics::Display { public: - explicit Display(std::shared_ptr const& x11_resources, - std::string const title, - std::vector const& requested_size, - std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config, - std::shared_ptr const& report); + Display( + std::shared_ptr parent, + std::shared_ptr const& x11_resources, + std::string const title, + std::vector const& requested_size, + std::shared_ptr const& initial_conf_policy, + std::shared_ptr const& report); ~Display() noexcept; void for_each_display_sync_group(std::function const& f) override; @@ -91,8 +88,6 @@ class Display : public graphics::Display std::shared_ptr create_hardware_cursor() override; - std::unique_ptr create_gl_context() const override; - private: struct OutputInfo : ::mir::X::X11Resources::VirtualOutput { @@ -112,12 +107,10 @@ class Display : public graphics::Display std::shared_ptr const config; }; - helpers::EGLHelper const shared_egl; + std::shared_ptr const parent; std::shared_ptr const x11_resources; - std::shared_ptr const gl_config; geometry::SizeF pixel_size_mm; std::shared_ptr const report; - std::shared_ptr const last_frame; std::mutex mutable mutex; std::vector> outputs; diff --git a/src/platforms/x11/graphics/display_buffer.cpp b/src/platforms/x11/graphics/display_buffer.cpp index 07da8fa9368..a8d10823758 100644 --- a/src/platforms/x11/graphics/display_buffer.cpp +++ b/src/platforms/x11/graphics/display_buffer.cpp @@ -25,26 +25,14 @@ namespace mg=mir::graphics; namespace mgx=mg::X; namespace geom=mir::geometry; -mgx::DisplayBuffer::DisplayBuffer(::Display* const x_dpy, - DisplayConfigurationOutputId output_id, +mgx::DisplayBuffer::DisplayBuffer(std::shared_ptr parent, xcb_window_t win, - geometry::Rectangle const& view_area, - geometry::Size const& window_size, - EGLContext const shared_context, - std::shared_ptr const& r, - GLConfig const& gl_config) - : report{r}, + geometry::Rectangle const& view_area) + : parent{std::move(parent)}, area{view_area}, - window_size{window_size}, transform(1), - egl{gl_config, x_dpy, win, shared_context}, - output_id{output_id} + x_win{win} { - egl.report_egl_configuration( - [&r] (EGLDisplay disp, EGLConfig cfg) - { - r->report_egl_configuration(disp, cfg); - }); } geom::Rectangle mgx::DisplayBuffer::view_area() const @@ -52,35 +40,32 @@ geom::Rectangle mgx::DisplayBuffer::view_area() const return area; } -auto mgx::DisplayBuffer::size() const -> geom::Size +auto mgx::DisplayBuffer::overlay(std::vector const& /*renderlist*/) -> bool { - return window_size; + // We could, with a lot of effort, make something like overlay support work, but + // there's little point. + return false; } -void mgx::DisplayBuffer::make_current() +namespace { - if (!egl.make_current()) - fatal_error("Failed to make EGL surface current"); -} - -void mgx::DisplayBuffer::release_current() +template +auto unique_ptr_cast(std::unique_ptr ptr) -> std::unique_ptr { - egl.release_current(); -} - -bool mgx::DisplayBuffer::overlay(RenderableList const& /*renderlist*/) -{ - return false; + From* unowned_src = ptr.release(); + if (auto to_src = dynamic_cast(unowned_src)) + { + return std::unique_ptr{to_src}; + } + delete unowned_src; + BOOST_THROW_EXCEPTION(( + std::bad_cast())); } - -void mgx::DisplayBuffer::swap_buffers() -{ - if (!egl.swap_buffers()) - fatal_error("Failed to perform buffer swap"); } -void mgx::DisplayBuffer::bind() +void mgx::DisplayBuffer::set_next_image(std::unique_ptr content) { + next_frame = unique_ptr_cast(std::move(content)); } glm::mat2 mgx::DisplayBuffer::transformation() const @@ -93,9 +78,9 @@ void mgx::DisplayBuffer::set_view_area(geom::Rectangle const& a) area = a; } -void mgx::DisplayBuffer::set_size(geom::Size const& size) +auto mgx::DisplayBuffer::owner() const -> std::shared_ptr { - window_size = size; + return parent; } void mgx::DisplayBuffer::set_transformation(glm::mat2 const& t) @@ -103,11 +88,6 @@ void mgx::DisplayBuffer::set_transformation(glm::mat2 const& t) transform = t; } -mg::NativeDisplayBuffer* mgx::DisplayBuffer::native_display_buffer() -{ - return this; -} - void mgx::DisplayBuffer::for_each_display_buffer( std::function const& f) { @@ -116,9 +96,16 @@ void mgx::DisplayBuffer::for_each_display_buffer( void mgx::DisplayBuffer::post() { + next_frame->swap_buffers(); + next_frame.reset(); } std::chrono::milliseconds mgx::DisplayBuffer::recommended_sleep() const { return std::chrono::milliseconds::zero(); } + +auto mgx::DisplayBuffer::x11_window() const -> xcb_window_t +{ + return x_win; +} \ No newline at end of file diff --git a/src/platforms/x11/graphics/display_buffer.h b/src/platforms/x11/graphics/display_buffer.h index 6dcf9e11072..f9278faf635 100644 --- a/src/platforms/x11/graphics/display_buffer.h +++ b/src/platforms/x11/graphics/display_buffer.h @@ -20,6 +20,7 @@ #include "mir/graphics/display_buffer.h" #include "mir/graphics/display_configuration.h" #include "mir/graphics/display.h" +#include "mir/graphics/platform.h" #include "mir/renderer/gl/render_target.h" #include "egl_helper.h" @@ -31,7 +32,6 @@ namespace mir namespace graphics { -class AtomicFrame; class GLConfig; class DisplayReport; @@ -39,30 +39,24 @@ namespace X { class DisplayBuffer : public graphics::DisplayBuffer, - public graphics::DisplaySyncGroup, - public graphics::NativeDisplayBuffer, - public renderer::gl::RenderTarget + public graphics::DisplaySyncGroup { public: DisplayBuffer( - ::Display* const x_dpy, - DisplayConfigurationOutputId output_id, + std::shared_ptr parent, xcb_window_t win, - geometry::Rectangle const& view_area, - geometry::Size const& window_size, - EGLContext const shared_context, - std::shared_ptr const& r, - GLConfig const& gl_config); - - geometry::Rectangle view_area() const override; - auto size() const -> geometry::Size override; - void make_current() override; - void release_current() override; - void swap_buffers() override; - void bind() override; - bool overlay(RenderableList const& renderlist) override; + geometry::Rectangle const& view_area); + + auto view_area() const -> geometry::Rectangle override; + + auto overlay(std::vector const& renderlist) -> bool override; + void set_next_image(std::unique_ptr content) override; + + glm::mat2 transformation() const override; + + auto owner() const -> std::shared_ptr override; + void set_view_area(geometry::Rectangle const& a); - void set_size(geometry::Size const& size); void set_transformation(glm::mat2 const& t); void for_each_display_buffer( @@ -70,16 +64,13 @@ class DisplayBuffer : public graphics::DisplayBuffer, void post() override; std::chrono::milliseconds recommended_sleep() const override; - glm::mat2 transformation() const override; - NativeDisplayBuffer* native_display_buffer() override; - + auto x11_window() const -> xcb_window_t; private: - std::shared_ptr const report; + std::shared_ptr const parent; + std::shared_ptr next_frame; geometry::Rectangle area; - geometry::Size window_size; glm::mat2 transform; - helpers::EGLHelper const egl; - DisplayConfigurationOutputId const output_id; + xcb_window_t const x_win; }; } diff --git a/src/platforms/x11/graphics/egl_helper.cpp b/src/platforms/x11/graphics/egl_helper.cpp index 13e57d0a574..c44bbc9617a 100644 --- a/src/platforms/x11/graphics/egl_helper.cpp +++ b/src/platforms/x11/graphics/egl_helper.cpp @@ -19,31 +19,74 @@ #include "mir/graphics/gl_config.h" #include "mir/graphics/egl_error.h" +#include #include namespace mg = mir::graphics; namespace mgx = mg::X; namespace mgxh = mgx::helpers; -mgxh::EGLHelper::EGLHelper(GLConfig const& gl_config, ::Display* const x_dpy) - : EGLHelper{gl_config} +class mgxh::Framebuffer::EGLState { - eglBindAPI(EGL_OPENGL_ES_API); +public: + EGLState(EGLDisplay dpy, EGLContext ctx, EGLSurface surf) + : dpy{dpy}, + ctx{ctx}, + surf{surf} + { + } - static const EGLint context_attr[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; + ~EGLState() + { + eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(dpy, ctx); + eglDestroySurface(dpy, surf); + } + + EGLDisplay const dpy; + EGLContext const ctx; + EGLSurface const surf; +}; + +mgxh::Framebuffer::Framebuffer(EGLDisplay dpy, EGLContext ctx, EGLSurface surf) + : Framebuffer(std::make_shared(dpy, ctx, surf)) +{ +} - setup_internal(x_dpy, true); +mgxh::Framebuffer::Framebuffer(std::shared_ptr state) + : state{std::move(state)} +{ +} - egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attr); - if (egl_context == EGL_NO_CONTEXT) - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); +void mgxh::Framebuffer::make_current() +{ + if (eglMakeCurrent(state->dpy, state->surf, state->surf, state->ctx) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION((mg::egl_error("eglMakeCurrent failed"))); + } } -mgxh::EGLHelper::EGLHelper(GLConfig const& gl_config, ::Display* const x_dpy, EGLContext shared_context) - : EGLHelper{gl_config} +void mgxh::Framebuffer::swap_buffers() +{ + if (eglSwapBuffers(state->dpy, state->surf) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION((mg::egl_error("eglSwapBuffers failed"))); + } +} + +auto mgxh::Framebuffer::clone_handle() -> std::unique_ptr +{ + return std::unique_ptr{new Framebuffer(state)}; +} + +mgxh::EGLHelper::EGLHelper(::Display* const x_dpy) + : EGLHelper{0, 0} +{ + setup_internal(x_dpy, true); +} + +mgxh::EGLHelper::EGLHelper(GLConfig const& gl_config, ::Display* const x_dpy) + : EGLHelper{gl_config.stencil_buffer_bits(), gl_config.depth_buffer_bits()} { eglBindAPI(EGL_OPENGL_ES_API); @@ -52,9 +95,9 @@ mgxh::EGLHelper::EGLHelper(GLConfig const& gl_config, ::Display* const x_dpy, EG EGL_NONE }; - setup_internal(x_dpy, false); + setup_internal(x_dpy, true); - egl_context = eglCreateContext(egl_display, egl_config, shared_context, context_attr); + egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attr); if (egl_context == EGL_NO_CONTEXT) BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); } @@ -64,7 +107,7 @@ mgxh::EGLHelper::EGLHelper( ::Display* const x_dpy, xcb_window_t win, EGLContext shared_context) - : EGLHelper{gl_config} + : EGLHelper{gl_config.stencil_buffer_bits(), gl_config.depth_buffer_bits()} { eglBindAPI(EGL_OPENGL_ES_API); @@ -101,35 +144,19 @@ mgxh::EGLHelper::~EGLHelper() noexcept } } -bool mgxh::EGLHelper::swap_buffers() const -{ - auto ret = eglSwapBuffers(egl_display, egl_surface); - return (ret == EGL_TRUE); -} - -bool mgxh::EGLHelper::make_current() const -{ - auto ret = eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); - eglBindAPI(EGL_OPENGL_ES_API); - return (ret == EGL_TRUE); -} - -bool mgxh::EGLHelper::release_current() const -{ - auto ret = eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - return (ret == EGL_TRUE); -} - -mgxh::EGLHelper::EGLHelper(GLConfig const& gl_config) - : depth_buffer_bits{gl_config.depth_buffer_bits()}, - stencil_buffer_bits{gl_config.stencil_buffer_bits()}, +mgxh::EGLHelper::EGLHelper(int stencil_bits, int depth_bits) + : depth_buffer_bits{depth_bits}, + stencil_buffer_bits{stencil_bits}, egl_display{EGL_NO_DISPLAY}, egl_config{0}, egl_context{EGL_NO_CONTEXT}, egl_surface{EGL_NO_SURFACE}, should_terminate_egl{false} { } -void mgxh::EGLHelper::setup_internal(::Display* x_dpy, bool initialize) +auto mgxh::EGLHelper::framebuffer_for_window( + GLConfig const& conf, + xcb_window_t win, + EGLContext shared_context) -> std::unique_ptr { EGLint const config_attr[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, @@ -137,16 +164,42 @@ void mgxh::EGLHelper::setup_internal(::Display* x_dpy, bool initialize) EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, depth_buffer_bits, - EGL_STENCIL_SIZE, stencil_buffer_bits, + EGL_DEPTH_SIZE, conf.depth_buffer_bits(), + EGL_STENCIL_SIZE, conf.stencil_buffer_bits(), EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; - static const EGLint required_egl_version_major = 1; - static const EGLint required_egl_version_minor = 4; + eglBindAPI(EGL_OPENGL_ES_API); + + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; EGLint num_egl_configs; + if (eglChooseConfig(egl_display, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE || + num_egl_configs != 1) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to choose ARGB EGL config")); + } + + auto egl_surface = eglCreateWindowSurface(egl_display, egl_config, win, nullptr); + if(egl_surface == EGL_NO_SURFACE) + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL window surface")); + + auto egl_context = eglCreateContext(egl_display, egl_config, shared_context, context_attr); + if (egl_context == EGL_NO_CONTEXT) + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); + + return std::make_unique(egl_display, egl_context, egl_surface); +} + +void mgxh::EGLHelper::setup_internal(::Display* x_dpy, bool initialize) +{ + + static const EGLint required_egl_version_major = 1; + static const EGLint required_egl_version_minor = 4; egl_display = eglGetDisplay(static_cast(x_dpy)); if (egl_display == EGL_NO_DISPLAY) @@ -169,12 +222,6 @@ void mgxh::EGLHelper::setup_internal(::Display* x_dpy, bool initialize) should_terminate_egl = true; } - - if (eglChooseConfig(egl_display, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE || - num_egl_configs != 1) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to choose ARGB EGL config")); - } } void mgxh::EGLHelper::report_egl_configuration(std::function f) const diff --git a/src/platforms/x11/graphics/egl_helper.h b/src/platforms/x11/graphics/egl_helper.h index 7bc6e12eb62..a7d1dfb38a6 100644 --- a/src/platforms/x11/graphics/egl_helper.h +++ b/src/platforms/x11/graphics/egl_helper.h @@ -23,6 +23,8 @@ #include #include +#include "mir/graphics/platform.h" + typedef struct _XDisplay Display; namespace mir @@ -36,6 +38,28 @@ namespace X namespace helpers { +class Framebuffer : public GenericEGLDisplayProvider::EGLFramebuffer +{ +public: + /** + * Handle for an EGL output surface + * + * \note This takes ownership of \param ctx and \param surf; when the + * final handle generated from this Framebuffer is released, + * the EGL resources \param ctx and \param surff will be freed. + */ + Framebuffer(EGLDisplay dpy, EGLContext ctx, EGLSurface surf); + + void make_current() override; + auto clone_handle() -> std::unique_ptr override; + + void swap_buffers(); +private: + class EGLState; + Framebuffer(std::shared_ptr surf); + + std::shared_ptr const state; +}; class EGLHelper { @@ -43,24 +67,24 @@ class EGLHelper EGLHelper(const EGLHelper&) = delete; EGLHelper& operator=(const EGLHelper&) = delete; + explicit EGLHelper(::Display* const x_dpy); EGLHelper(GLConfig const& gl_config, ::Display* const x_dpy); EGLHelper(GLConfig const& gl_config, ::Display* const x_dpy, EGLContext shared_context); // Passing an XLib Display and an XCB window is, in fact, not a mistake EGLHelper(GLConfig const& gl_config, ::Display* const x_dpy, xcb_window_t win, EGLContext shared_context); ~EGLHelper() noexcept; - bool swap_buffers() const; - bool make_current() const; - bool release_current() const; + auto framebuffer_for_window(GLConfig const& conf, xcb_window_t win, EGLContext shared_context) + -> std::unique_ptr; - EGLContext context() const { return egl_context; } EGLDisplay display() const { return egl_display; } EGLConfig config() const { return egl_config; } EGLSurface surface() const { return egl_surface; } + EGLContext context() const { return egl_context; } void report_egl_configuration(std::function) const; private: - EGLHelper(GLConfig const& gl_config); + EGLHelper(int stencil_bits, int depth_bits); void setup_internal(::Display* const x_dpy, bool initialize); EGLint const depth_buffer_bits; @@ -71,7 +95,6 @@ class EGLHelper EGLSurface egl_surface; bool should_terminate_egl; }; - } } } diff --git a/src/platforms/x11/graphics/platform.cpp b/src/platforms/x11/graphics/platform.cpp index 18948dc2b75..bb29ee1dfb7 100644 --- a/src/platforms/x11/graphics/platform.cpp +++ b/src/platforms/x11/graphics/platform.cpp @@ -16,6 +16,10 @@ #include "platform.h" #include "display.h" +#include "display_buffer.h" +#include "egl_helper.h" +#include "mir/graphics/egl_error.h" +#include "mir/graphics/platform.h" #include "mir/options/option.h" @@ -119,7 +123,43 @@ mgx::Platform::Platform(std::shared_ptr const& x11_resourc mir::UniqueModulePtr mgx::Platform::create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config) + std::shared_ptr const& /*gl_config*/) { - return make_module_ptr(x11_resources, title, output_sizes, initial_conf_policy, gl_config, report); + return make_module_ptr(shared_from_this(), x11_resources, title, output_sizes, initial_conf_policy, report); +} + +class mgx::Platform::EGLDisplayProvider : public mg::GenericEGLDisplayProvider +{ +public: + EGLDisplayProvider(::Display* const x_dpy) + : egl_helper{x_dpy} + { + } + + auto get_egl_display() -> EGLDisplay + { + return egl_helper.display(); + } + + auto framebuffer_for_db(mg::DisplayBuffer& db, mg::GLConfig const& config, EGLContext share_context) + -> std::unique_ptr + { + auto& x11db = dynamic_cast(db); + return egl_helper.framebuffer_for_window(config, x11db.x11_window(), share_context); + } +private: + mgx::helpers::EGLHelper egl_helper; +}; + +auto mgx::Platform::maybe_create_interface(mir::graphics::DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr +{ + if (dynamic_cast(&type_tag)) + { + std::call_once( + provider_constructed, + [this]() { egl_provider = std::make_shared(x11_resources->xlib_dpy); }); + return egl_provider; + } + return {}; } diff --git a/src/platforms/x11/graphics/platform.h b/src/platforms/x11/graphics/platform.h index 6d26864f364..6ed98d26be5 100644 --- a/src/platforms/x11/graphics/platform.h +++ b/src/platforms/x11/graphics/platform.h @@ -21,6 +21,8 @@ #include "mir/graphics/platform.h" #include "mir/geometry/size.h" +#include + namespace mir { namespace X @@ -68,7 +70,15 @@ class Platform : public graphics::DisplayPlatform UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, std::shared_ptr const& gl_config) override; + +protected: + auto maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr override; + private: + class EGLDisplayProvider; + std::once_flag provider_constructed; + std::shared_ptr egl_provider; std::shared_ptr const x11_resources; std::string const title; std::shared_ptr const report; diff --git a/src/renderers/gl/CMakeLists.txt b/src/renderers/gl/CMakeLists.txt index f3acd38c141..25f829275db 100644 --- a/src/renderers/gl/CMakeLists.txt +++ b/src/renderers/gl/CMakeLists.txt @@ -19,7 +19,7 @@ ADD_LIBRARY( renderer.cpp renderer_factory.cpp - basic_buffer_render_target.cpp +# basic_buffer_render_target.cpp ) target_include_directories( diff --git a/src/renderers/gl/basic_buffer_render_target.cpp b/src/renderers/gl/basic_buffer_render_target.cpp index 1b58ec578df..2891474b72a 100644 --- a/src/renderers/gl/basic_buffer_render_target.cpp +++ b/src/renderers/gl/basic_buffer_render_target.cpp @@ -26,7 +26,7 @@ namespace mg = mir::graphics; namespace mrg = mir::renderer::gl; namespace mrs = mir::renderer::software; -mrg::BasicBufferRenderTarget::Framebuffer::Framebuffer(geometry::Size const& size) +mrg::BasicBufferOutputSurface::Framebuffer::Framebuffer(geometry::Size const& size) : size{size} { glGenRenderbuffers(1, &colour_buffer); @@ -75,13 +75,13 @@ mrg::BasicBufferRenderTarget::Framebuffer::Framebuffer(geometry::Size const& siz glViewport(0, 0, size.width.as_int(), size.height.as_int()); } -mrg::BasicBufferRenderTarget::Framebuffer::~Framebuffer() +mrg::BasicBufferOutputSurface::Framebuffer::~Framebuffer() { glDeleteFramebuffers(1, &fbo); glDeleteRenderbuffers(1, &colour_buffer); } -void mrg::BasicBufferRenderTarget::Framebuffer::copy_to(software::WriteMappableBuffer& buffer) +void mrg::BasicBufferOutputSurface::Framebuffer::copy_to(software::WriteMappableBuffer& buffer) { glBindFramebuffer(GL_FRAMEBUFFER, fbo); auto mapping = buffer.map_writeable(); @@ -103,17 +103,18 @@ void mrg::BasicBufferRenderTarget::Framebuffer::copy_to(software::WriteMappableB GL_BGRA_EXT, GL_UNSIGNED_BYTE, mapping->data()); } -void mrg::BasicBufferRenderTarget::Framebuffer::bind() +void mrg::BasicBufferOutputSurface::Framebuffer::bind() { glBindFramebuffer(GL_FRAMEBUFFER, fbo); } -mrg::BasicBufferRenderTarget::BasicBufferRenderTarget(std::shared_ptr const& ctx) +mrg::BasicBufferOutputSurface::BasicBufferOutputSurface(std::shared_ptr const& ctx) : ctx{ctx} { } -void mrg::BasicBufferRenderTarget::set_buffer(std::shared_ptr const& buffer) +void mrg::BasicBufferOutputSurface::set_buffer( + std::shared_ptr const& buffer) { this->buffer = buffer; if (framebuffer && framebuffer->size == buffer->size()) @@ -124,7 +125,7 @@ void mrg::BasicBufferRenderTarget::set_buffer(std::shared_ptrsize()); } -auto mrg::BasicBufferRenderTarget::size() const -> geometry::Size +auto mrg::BasicBufferOutputSurface::size() const -> geometry::Size { if (framebuffer) { @@ -136,26 +137,28 @@ auto mrg::BasicBufferRenderTarget::size() const -> geometry::Size } } -void mrg::BasicBufferRenderTarget::make_current() +void mrg::BasicBufferOutputSurface::make_current() { ctx->make_current(); } -void mrg::BasicBufferRenderTarget::release_current() -{ - ctx->release_current(); -} - -void mrg::BasicBufferRenderTarget::swap_buffers() +auto mrg::BasicBufferOutputSurface::commit() -> std::unique_ptr { if (!framebuffer || !buffer) { BOOST_THROW_EXCEPTION(std::logic_error("swap_buffers() called when buffer unset")); } framebuffer->copy_to(*buffer); + /* Because of the way the screencopy extension is set up we don't really need to return anything + * here. + * + * If the client provided us with a pool or queue of buffers to allocate from, or had asynchronous + * copying then there might be something worth returning here. + */ + return {}; } -void mrg::BasicBufferRenderTarget::bind() +void mrg::BasicBufferOutputSurface::bind() { if (!framebuffer) { @@ -163,3 +166,8 @@ void mrg::BasicBufferRenderTarget::bind() } framebuffer->bind(); } + +mir::graphics::gl::OutputSurface::Layout mir::renderer::gl::BasicBufferOutputSurface::layout() const +{ + return Layout::GL; +} diff --git a/src/renderers/gl/renderer.cpp b/src/renderers/gl/renderer.cpp index 533b4d97f44..a80dbf1ad87 100644 --- a/src/renderers/gl/renderer.cpp +++ b/src/renderers/gl/renderer.cpp @@ -24,9 +24,11 @@ #include "mir/log.h" #include "mir/report_exception.h" #include "mir/graphics/egl_error.h" +#include "mir/graphics/platform.h" #include "mir/graphics/texture.h" #include "mir/graphics/program_factory.h" #include "mir/graphics/program.h" +#include "mir/renderer/gl/gl_surface.h" #define GLM_FORCE_RADIANS #include @@ -44,38 +46,6 @@ namespace mgl = mir::gl; namespace mrg = mir::renderer::gl; namespace geom = mir::geometry; -mrg::CurrentRenderTarget::CurrentRenderTarget(RenderTarget& render_target) - : render_target{&render_target} -{ - ensure_current(); -} - -mrg::CurrentRenderTarget::~CurrentRenderTarget() -{ - render_target->release_current(); -} - -auto mrg::CurrentRenderTarget::size() const -> geom::Size -{ - return render_target->size(); -} - -void mrg::CurrentRenderTarget::ensure_current() -{ - render_target->make_current(); -} - -void mrg::CurrentRenderTarget::bind() -{ - ensure_current(); - render_target->bind(); -} - -void mrg::CurrentRenderTarget::swap_buffers() -{ - render_target->swap_buffers(); -} - namespace { template @@ -302,11 +272,23 @@ mrg::Renderer::Program::Program(GLuint program_id) alpha_uniform = glGetUniformLocation(id, "alpha"); } -mrg::Renderer::Renderer(RenderTarget& render_target) - : render_target(render_target), +namespace +{ +auto make_output_current(std::unique_ptr output) -> std::unique_ptr +{ + output->make_current(); + return output; +} +} + +mrg::Renderer::Renderer( + std::shared_ptr gl_interface, + std::unique_ptr output) + : output_surface{make_output_current(std::move(output))}, clear_color{0.0f, 0.0f, 0.0f, 1.0f}, program_factory{std::make_unique()}, - display_transform(1) + display_transform(1), + gl_interface{std::move(gl_interface)} { eglBindAPI(EGL_OPENGL_ES_API); EGLDisplay disp = eglGetCurrentDisplay(); @@ -360,7 +342,6 @@ mrg::Renderer::Renderer(RenderTarget& render_target) mrg::Renderer::~Renderer() { - render_target.ensure_current(); } void mrg::Renderer::tessellate(std::vector& primitives, @@ -370,9 +351,9 @@ void mrg::Renderer::tessellate(std::vector& primitives, primitives[0] = mgl::tessellate_renderable_into_rectangle(renderable, geom::Displacement{0,0}); } -void mrg::Renderer::render(mg::RenderableList const& renderables) const +auto mrg::Renderer::render(mg::RenderableList const& renderables) const -> std::unique_ptr { - render_target.bind(); + output_surface->bind(); glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); @@ -384,10 +365,13 @@ void mrg::Renderer::render(mg::RenderableList const& renderables) const draw(*r); } - render_target.swap_buffers(); + auto output = output_surface->commit(); + // Report any GL errors after commit, to catch any *during* commit while (auto const gl_error = glGetError()) mir::log_debug("GL error: %d", gl_error); + + return output; } void mrg::Renderer::draw(mg::Renderable const& renderable) const @@ -408,12 +392,7 @@ void mrg::Renderer::draw(mg::Renderable const& renderable) const ); } - auto const texture = std::dynamic_pointer_cast(renderable.buffer()); - if (!texture) - { - mir::log_error("Buffer does not support GL rendering!"); - return; - } + auto const texture = gl_interface->as_texture(renderable.buffer()); // All the programs are held by program_factory through its lifetime. Using pointers avoids // -Wdangling-reference. @@ -464,7 +443,7 @@ void mrg::Renderer::draw(mg::Renderable const& renderable) const 1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, - -1.0, 1.0, 0.0, 1.0 + 0.0, 0.0, 0.0, 1.0 }; } @@ -604,36 +583,53 @@ void mrg::Renderer::update_gl_viewport() * the logical viewport aspect ratio doesn't match the display aspect. * This keeps pixels square. Note "black"-bars are really glClearColor. */ - - auto const buf_size = render_target.size(); - GLint const buf_width = buf_size.width.as_value(), buf_height = buf_size.height.as_value(); - if (!buf_width || !buf_height) - { - return; - } - auto transformed_viewport = display_transform * glm::vec4(viewport.size.width.as_int(), viewport.size.height.as_int(), 0, 1); auto viewport_width = fabs(transformed_viewport[0]); auto viewport_height = fabs(transformed_viewport[1]); - GLint reduced_width = buf_width, reduced_height = buf_height; - // if viewport_aspect_ratio >= buf_aspect_ratio - if (viewport_width * buf_height >= buf_width * viewport_height) - reduced_height = buf_width * viewport_height / viewport_width; - else - reduced_width = buf_height * viewport_width / viewport_height; + auto const output_size = output_surface->size(); + auto const output_width = output_size.width.as_value(); + auto const output_height = output_size.height.as_value(); + + if (viewport_width > 0.0f && viewport_height > 0.0f && + output_width > 0 && output_height > 0) + { + GLint reduced_width = output_width, reduced_height = output_height; + // if viewport_aspect_ratio >= output_aspect_ratio + if (viewport_width * output_height >= output_width * viewport_height) + reduced_height = output_width * viewport_height / viewport_width; + else + reduced_width = output_height * viewport_width / viewport_height; - GLint offset_x = (buf_width - reduced_width) / 2; - GLint offset_y = (buf_height - reduced_height) / 2; + GLint offset_x = (output_width - reduced_width) / 2; + GLint offset_y = (output_height - reduced_height) / 2; - glViewport(offset_x, offset_y, reduced_width, reduced_height); + glViewport(offset_x, offset_y, reduced_width, reduced_height); + } } void mrg::Renderer::set_output_transform(glm::mat2 const& t) { - auto const new_display_transform = glm::mat4(t); + auto new_display_transform = glm::mat4(t); + + switch (output_surface->layout()) + { + case graphics::gl::OutputSurface::Layout::GL: + break; + case graphics::gl::OutputSurface::Layout::TopRowFirst: + // GL is going to render in its own coordinate system, but the OutputSurface + // wants the output to be the other way up. Get GL to render upside-down instead. + new_display_transform *= glm::mat4{ + 1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + }; + break; + } + if (new_display_transform != display_transform) { display_transform = new_display_transform; diff --git a/src/renderers/gl/renderer.h b/src/renderers/gl/renderer.h index 9d4b12455ca..0caf2c3dec6 100644 --- a/src/renderers/gl/renderer.h +++ b/src/renderers/gl/renderer.h @@ -22,7 +22,6 @@ #include #include #include -#include "mir/renderer/gl/render_target.h" #include #include @@ -31,38 +30,23 @@ namespace mir { -namespace graphics { class DisplayBuffer; } +namespace graphics { class DisplayBuffer; class GLRenderingProvider; } +namespace graphics::gl { class OutputSurface; } namespace renderer { namespace gl { -class CurrentRenderTarget -{ -public: - CurrentRenderTarget(RenderTarget& render_target); - ~CurrentRenderTarget(); - - auto size() const -> geometry::Size; - void ensure_current(); - void bind(); - void swap_buffers(); - -private: - renderer::gl::RenderTarget* const render_target; -}; - class Renderer : public renderer::Renderer { public: - /// render_target is owned externally, and must be kept alive as long as this object. - Renderer(RenderTarget& render_target); + Renderer(std::shared_ptr gl_interface, std::unique_ptr output); virtual ~Renderer(); // These are called with a valid GL context: void set_viewport(geometry::Rectangle const& rect) override; void set_output_transform(glm::mat2 const&) override; - void render(graphics::RenderableList const&) const override; + auto render(graphics::RenderableList const&) const -> std::unique_ptr override; // This is called _without_ a GL context: void suspend() override; @@ -87,7 +71,7 @@ class Renderer : public renderer::Renderer Program(GLuint program_id); }; private: - mutable CurrentRenderTarget render_target; + std::unique_ptr const output_surface; protected: /** @@ -124,6 +108,7 @@ class Renderer : public renderer::Renderer glm::mat4 screen_to_gl_coords; glm::mat4 display_transform; std::vector mutable primitives; + std::shared_ptr const gl_interface; }; } diff --git a/src/renderers/gl/renderer_factory.cpp b/src/renderers/gl/renderer_factory.cpp index f22fb7c7514..55003077cc6 100644 --- a/src/renderers/gl/renderer_factory.cpp +++ b/src/renderers/gl/renderer_factory.cpp @@ -16,11 +16,14 @@ #include "renderer_factory.h" #include "renderer.h" +#include "mir/graphics/platform.h" +#include "mir/renderer/gl/gl_surface.h" namespace mrg = mir::renderer::gl; -std::unique_ptr -mrg::RendererFactory::create_renderer_for(RenderTarget& render_target) +auto mrg::RendererFactory::create_renderer_for( + std::unique_ptr output_surface, + std::shared_ptr gl_provider) const -> std::unique_ptr { - return std::make_unique(render_target); + return std::make_unique(std::move(gl_provider), std::move(output_surface)); } diff --git a/src/renderers/gl/renderer_factory.h b/src/renderers/gl/renderer_factory.h index 82eed4f15b9..24f3def5204 100644 --- a/src/renderers/gl/renderer_factory.h +++ b/src/renderers/gl/renderer_factory.h @@ -21,6 +21,10 @@ namespace mir { +namespace graphics +{ +class GLRenderingProvider; +} namespace renderer { namespace gl @@ -29,7 +33,9 @@ namespace gl class RendererFactory : public renderer::RendererFactory { public: - std::unique_ptr create_renderer_for(RenderTarget& render_target) override; + auto create_renderer_for( + std::unique_ptr output_surface, + std::shared_ptr gl_provider) const -> std::unique_ptr override; }; } diff --git a/src/server/compositor/CMakeLists.txt b/src/server/compositor/CMakeLists.txt index 092b29f21c3..bdbadb66b3f 100644 --- a/src/server/compositor/CMakeLists.txt +++ b/src/server/compositor/CMakeLists.txt @@ -17,7 +17,7 @@ set( multi_monitor_arbiter.cpp dropping_schedule.cpp queueing_schedule.cpp - basic_screen_shooter.cpp +# basic_screen_shooter.cpp null_screen_shooter.cpp ) diff --git a/src/server/compositor/basic_screen_shooter.cpp b/src/server/compositor/basic_screen_shooter.cpp index c9e113c99bb..9910c4aee93 100644 --- a/src/server/compositor/basic_screen_shooter.cpp +++ b/src/server/compositor/basic_screen_shooter.cpp @@ -33,7 +33,7 @@ namespace geom = mir::geometry; mc::BasicScreenShooter::Self::Self( std::shared_ptr const& scene, std::shared_ptr const& clock, - std::unique_ptr&& render_target, + std::unique_ptr&& render_target, std::unique_ptr&& renderer) : scene{scene}, render_target{std::move(render_target)}, @@ -63,9 +63,8 @@ auto mc::BasicScreenShooter::Self::render( render_target->bind(); renderer->set_viewport(area); - renderer->render(renderable_list); +// renderer->render(renderable_list); - render_target->release_current(); renderable_list.clear(); return captured_time; @@ -75,7 +74,7 @@ mc::BasicScreenShooter::BasicScreenShooter( std::shared_ptr const& scene, std::shared_ptr const& clock, Executor& executor, - std::unique_ptr&& render_target, + std::unique_ptr&& render_target, std::unique_ptr&& renderer) : self{std::make_shared(scene, clock, std::move(render_target), std::move(renderer))}, executor{executor} diff --git a/src/server/compositor/basic_screen_shooter.h b/src/server/compositor/basic_screen_shooter.h index 7763f6c9d9c..5212ebd4915 100644 --- a/src/server/compositor/basic_screen_shooter.h +++ b/src/server/compositor/basic_screen_shooter.h @@ -30,7 +30,7 @@ namespace renderer class Renderer; namespace gl { -class BufferRenderTarget; +class BufferOutputSurface; } } namespace compositor @@ -44,7 +44,7 @@ class BasicScreenShooter: public ScreenShooter std::shared_ptr const& scene, std::shared_ptr const& clock, Executor& executor, - std::unique_ptr&& render_target, + std::unique_ptr&& render_target, std::unique_ptr&& renderer); void capture( @@ -58,7 +58,7 @@ class BasicScreenShooter: public ScreenShooter Self( std::shared_ptr const& scene, std::shared_ptr const& clock, - std::unique_ptr&& render_target, + std::unique_ptr&& render_target, std::unique_ptr&& renderer); auto render( @@ -67,7 +67,7 @@ class BasicScreenShooter: public ScreenShooter std::mutex mutex; std::shared_ptr const scene; - std::unique_ptr const render_target; + std::unique_ptr const render_target; std::unique_ptr const renderer; std::shared_ptr const clock; }; diff --git a/src/server/compositor/default_configuration.cpp b/src/server/compositor/default_configuration.cpp index 85e5cd7614f..6c9e80e0aa5 100644 --- a/src/server/compositor/default_configuration.cpp +++ b/src/server/compositor/default_configuration.cpp @@ -19,24 +19,18 @@ #include "mir/shell/shell.h" #include "buffer_stream_factory.h" #include "default_display_buffer_compositor_factory.h" +#include "mir/executor.h" #include "multi_threaded_compositor.h" #include "gl/renderer_factory.h" #include "basic_screen_shooter.h" #include "null_screen_shooter.h" #include "mir/main_loop.h" -#include "mir/graphics/display.h" -#include "mir/executor.h" -#include "mir/renderer/gl/basic_buffer_render_target.h" -#include "mir/renderer/gl/context.h" -#include "mir/renderer/renderer.h" -#include "mir/log.h" - +#include "mir/graphics/platform.h" #include "mir/options/configuration.h" namespace mc = mir::compositor; namespace ms = mir::scene; -namespace mf = mir::frontend; -namespace mrg = mir::renderer::gl; +namespace mg = mir::graphics; std::shared_ptr mir::DefaultServerConfiguration::the_buffer_stream_factory() @@ -54,8 +48,14 @@ mir::DefaultServerConfiguration::the_display_buffer_compositor_factory() return display_buffer_compositor_factory( [this]() { - return wrap_display_buffer_compositor_factory(std::make_shared( - the_renderer_factory(), the_compositor_report())); + auto rendering_platform = the_rendering_platforms().back(); + if (auto gl_provider = + mg::RenderingPlatform::acquire_interface(std::move(rendering_platform))) + { + return wrap_display_buffer_compositor_factory(std::make_shared( + std::move(gl_provider), the_gl_config(), the_renderer_factory(), the_compositor_report())); + } + BOOST_THROW_EXCEPTION((std::runtime_error{"Selected rendering platform does not support GL"})); }); } @@ -98,9 +98,9 @@ std::shared_ptr mir::DefaultServerConfiguration: auto mir::DefaultServerConfiguration::the_screen_shooter() -> std::shared_ptr { return screen_shooter( - [this]() -> std::shared_ptr + []() -> std::shared_ptr { - try +/* try { auto render_target = std::make_unique(the_display()->create_gl_context()); auto renderer = the_renderer_factory()->create_renderer_for(*render_target); @@ -109,7 +109,7 @@ auto mir::DefaultServerConfiguration::the_screen_shooter() -> std::shared_ptr std::shared_ptr(thread_pool_executor); } +*/ + return std::make_shared(thread_pool_executor); }); } diff --git a/src/server/compositor/default_display_buffer_compositor.cpp b/src/server/compositor/default_display_buffer_compositor.cpp index aa356d8ccc5..585c59aa0ea 100644 --- a/src/server/compositor/default_display_buffer_compositor.cpp +++ b/src/server/compositor/default_display_buffer_compositor.cpp @@ -16,11 +16,14 @@ #include "default_display_buffer_compositor.h" +#include "mir/compositor/compositor_report.h" #include "mir/compositor/scene.h" #include "mir/compositor/scene_element.h" #include "mir/graphics/renderable.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/buffer.h" +#include "mir/graphics/platform.h" +#include "mir/compositor/buffer_stream.h" #include "mir/renderer/renderer.h" #include "occlusion.h" @@ -29,10 +32,12 @@ namespace mg = mir::graphics; mc::DefaultDisplayBufferCompositor::DefaultDisplayBufferCompositor( mg::DisplayBuffer& display_buffer, + graphics::GLRenderingProvider& gl_provider, std::shared_ptr const& renderer, - std::shared_ptr const& report) : + std::shared_ptr const& report) : display_buffer(display_buffer), renderer(renderer), + fb_adaptor{gl_provider.make_framebuffer_provider(display_buffer)}, report(report) { } @@ -58,14 +63,48 @@ void mc::DefaultDisplayBufferCompositor::composite(mc::SceneElementSequence&& sc /* * Note: Buffer lifetimes are ensured by the two objects holding * references to them; scene_elements and renderable_list. - * So no buffer is going to be released back to the client till + * So no buffer is going to be releaswayland_executored back to the client till * both of those containers get destroyed (end of the function). * Actually, there's a third reference held by the texture cache * in GLRenderer, but that gets released earlier in render(). */ scene_elements.clear(); // Those in use are still in renderable_list - if (display_buffer.overlay(renderable_list)) + std::vector framebuffers; + framebuffers.reserve(renderable_list.size()); + + for (auto const& renderable : renderable_list) + { + auto fb = fb_adaptor->buffer_to_framebuffer(renderable->buffer()); + if (!fb) + { + break; + } + geometry::Rectangle clipped_dest; + if (renderable->clip_area()) + { + clipped_dest = intersection_of(renderable->screen_position(), *renderable->clip_area()); + } + else + { + clipped_dest = renderable->screen_position(); + } + geometry::SizeF const source_size{ + clipped_dest.size.width.as_value(), + clipped_dest.size.height.as_value()}; + geometry::PointF const source_origin{ + clipped_dest.top_left.x.as_value() - renderable->screen_position().top_left.x.as_value(), + clipped_dest.top_left.y.as_value() - renderable->screen_position().top_left.y.as_value() + }; + + framebuffers.emplace_back(mg::DisplayElement{ + renderable->screen_position(), + geometry::RectangleF{source_origin, source_size}, + std::move(fb) + }); + } + + if (framebuffers.size() == renderable_list.size() && display_buffer.overlay(framebuffers)) { report->renderables_in_frame(this, renderable_list); renderer->suspend(); @@ -74,7 +113,8 @@ void mc::DefaultDisplayBufferCompositor::composite(mc::SceneElementSequence&& sc { renderer->set_output_transform(display_buffer.transformation()); renderer->set_viewport(view_area); - renderer->render(renderable_list); + + display_buffer.set_next_image(renderer->render(renderable_list)); report->renderables_in_frame(this, renderable_list); report->rendered_frame(this); diff --git a/src/server/compositor/default_display_buffer_compositor.h b/src/server/compositor/default_display_buffer_compositor.h index ed4afd4d020..d83c531a3ca 100644 --- a/src/server/compositor/default_display_buffer_compositor.h +++ b/src/server/compositor/default_display_buffer_compositor.h @@ -18,11 +18,15 @@ #define MIR_COMPOSITOR_DEFAULT_DISPLAY_BUFFER_COMPOSITOR_H_ #include "mir/compositor/display_buffer_compositor.h" -#include "mir/compositor/compositor_report.h" +#include "mir/graphics/platform.h" #include namespace mir { +namespace compositor +{ +class CompositorReport; +} namespace graphics { class DisplayBuffer; @@ -41,15 +45,17 @@ class DefaultDisplayBufferCompositor : public DisplayBufferCompositor public: DefaultDisplayBufferCompositor( graphics::DisplayBuffer& display_buffer, + graphics::GLRenderingProvider& gl_provider, std::shared_ptr const& renderer, - std::shared_ptr const& report); + std::shared_ptr const& report); void composite(SceneElementSequence&& scene_sequence) override; private: graphics::DisplayBuffer& display_buffer; std::shared_ptr const renderer; - std::shared_ptr const report; + std::unique_ptr const fb_adaptor; + std::shared_ptr const report; }; } diff --git a/src/server/compositor/default_display_buffer_compositor_factory.cpp b/src/server/compositor/default_display_buffer_compositor_factory.cpp index cfb9cf2f569..dea8137c949 100644 --- a/src/server/compositor/default_display_buffer_compositor_factory.cpp +++ b/src/server/compositor/default_display_buffer_compositor_factory.cpp @@ -19,6 +19,9 @@ #include "mir/renderer/renderer.h" #include "mir/graphics/display_buffer.h" #include "mir/renderer/gl/render_target.h" +#include "mir/graphics/platform.h" +#include "mir/renderer/gl/gl_surface.h" +#include "mir/graphics/gl_config.h" #include "default_display_buffer_compositor.h" @@ -28,10 +31,14 @@ namespace mc = mir::compositor; namespace mg = mir::graphics; mc::DefaultDisplayBufferCompositorFactory::DefaultDisplayBufferCompositorFactory( + std::shared_ptr render_platform, + std::shared_ptr gl_config, std::shared_ptr const& renderer_factory, std::shared_ptr const& report) : - renderer_factory{renderer_factory}, - report{report} + allocator{std::move(render_platform)}, + gl_config{std::move(gl_config)}, + renderer_factory{renderer_factory}, + report{report} { } @@ -39,13 +46,18 @@ std::unique_ptr mc::DefaultDisplayBufferCompositorFactory::create_compositor_for( mg::DisplayBuffer& display_buffer) { - auto const render_target = dynamic_cast(display_buffer.native_display_buffer()); - if (!render_target) - { - BOOST_THROW_EXCEPTION(std::logic_error("DisplayBuffer does not support GL rendering")); - } - auto renderer = renderer_factory->create_renderer_for(*render_target); + /* TODO: There's scope for (GPU) memory optimisation here: + * We unconditionally allocate a GL rendering surface for the renderer, + * but with a different interface the DisplayBufferCompositor could choose + * not to allocate a GL surface if everything is working with overlays. + * + * For simple cases, such as those targetted by Ubuntu Frame, not needing the + * GL surface could be the common case, and not allocating it would save a + * potentially-significant amount of GPU memory. + */ + auto output_surface = allocator->surface_for_output(display_buffer, *gl_config); + auto renderer = renderer_factory->create_renderer_for(std::move(output_surface), allocator); renderer->set_viewport(display_buffer.view_area()); return std::make_unique( - display_buffer, std::move(renderer), report); + display_buffer, *allocator, std::move(renderer), report); } diff --git a/src/server/compositor/default_display_buffer_compositor_factory.h b/src/server/compositor/default_display_buffer_compositor_factory.h index 1659293be1c..91cd017bc56 100644 --- a/src/server/compositor/default_display_buffer_compositor_factory.h +++ b/src/server/compositor/default_display_buffer_compositor_factory.h @@ -22,10 +22,20 @@ namespace mir { +namespace graphics +{ +class RenderingPlatform; +} namespace renderer { class RendererFactory; } + +namespace graphics +{ +class GLRenderingProvider; +class GLConfig; +} /// Compositing. Combining renderables into a display image. namespace compositor { @@ -34,12 +44,17 @@ class DefaultDisplayBufferCompositorFactory : public DisplayBufferCompositorFact { public: DefaultDisplayBufferCompositorFactory( + std::shared_ptr render_platform, + std::shared_ptr gl_config, std::shared_ptr const& renderer_factory, std::shared_ptr const& report); - std::unique_ptr create_compositor_for(graphics::DisplayBuffer& display_buffer); + std::unique_ptr create_compositor_for(graphics::DisplayBuffer& display_buffer) override; private: + // TODO: This should be a per-DisplayBuffer decision + std::shared_ptr const allocator; + std::shared_ptr const gl_config; std::shared_ptr const renderer_factory; std::shared_ptr const report; }; @@ -47,4 +62,5 @@ class DefaultDisplayBufferCompositorFactory : public DisplayBufferCompositorFact } } + #endif /* MIR_COMPOSITOR_DEFAULT_DISPLAY_BUFFER_COMPOSITOR_FACTORY_H_ */ diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index 1942db6df3f..a4e11ee9ab8 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -201,13 +201,28 @@ auto mir::DefaultServerConfiguration::the_display_platforms() -> std::vectorload_function( "describe_graphics_module", MIR_SERVER_GRAPHICS_PLATFORM_VERSION); - + auto description = describe_module(); - mir::log_info("Selected display driver: %s (version %d.%d.%d)", - description->name, - description->major_version, - description->minor_version, - description->micro_version); + if (!device.device) + { + mir::log_info( + "Selected display driver: %s (version %d.%d.%d) for platform", + description->name, + description->major_version, + description->minor_version, + description->micro_version); + } + else + { + mir::log_info( + "Selected display driver: %s (version %d.%d.%d) for device (%s: %s)", + description->name, + description->major_version, + description->minor_version, + description->micro_version, + device.device->driver(), + device.device->devnode()); + } // TODO: Do we want to be able to continue on partial failure here? display_platforms.push_back( @@ -306,11 +321,26 @@ auto mir::DefaultServerConfiguration::the_rendering_platforms() -> MIR_SERVER_GRAPHICS_PLATFORM_VERSION); auto description = describe_module(); - mir::log_info("Selected rendering driver: %s (version %d.%d.%d)", - description->name, - description->major_version, - description->minor_version, - description->micro_version); + if (!device.device) + { + mir::log_info( + "Selected rendering driver: %s (version %d.%d.%d) for platform", + description->name, + description->major_version, + description->minor_version, + description->micro_version); + } + else + { + mir::log_info( + "Selected rendering driver: %s (version %d.%d.%d) for device (%s: %s)", + description->name, + description->major_version, + description->minor_version, + description->micro_version, + device.device->driver(), + device.device->devnode()); + } // TODO: Do we want to be able to continue on partial failure here? rendering_platforms.push_back( @@ -352,7 +382,7 @@ mir::DefaultServerConfiguration::the_buffer_allocator() [&]() -> std::shared_ptr { // TODO: More than one BufferAllocator - return the_rendering_platforms().front()->create_buffer_allocator(*the_display()); + return the_rendering_platforms().back()->create_buffer_allocator(*the_display()); }); } @@ -362,7 +392,7 @@ mir::DefaultServerConfiguration::the_display() return display( [this]() -> std::shared_ptr { - return the_display_platforms().front()->create_display( + return the_display_platforms().back()->create_display( the_display_configuration_policy(), the_gl_config()); }); diff --git a/src/server/graphics/multiplexing_display.cpp b/src/server/graphics/multiplexing_display.cpp index 80510ea5edf..7966392edba 100644 --- a/src/server/graphics/multiplexing_display.cpp +++ b/src/server/graphics/multiplexing_display.cpp @@ -316,9 +316,3 @@ auto mg::MultiplexingDisplay::create_hardware_cursor() -> std::shared_ptr std::unique_ptr -{ - // This will disappear in the New Platform API™; just return the first underlying Display's context for now. - return displays[0]->create_gl_context(); -} diff --git a/src/server/graphics/multiplexing_display.h b/src/server/graphics/multiplexing_display.h index 7fce7a9ef03..eb648f234b0 100644 --- a/src/server/graphics/multiplexing_display.h +++ b/src/server/graphics/multiplexing_display.h @@ -47,8 +47,6 @@ class MultiplexingDisplay : public Display void resume() override; auto create_hardware_cursor() -> std::shared_ptr override; - - auto create_gl_context() const -> std::unique_ptr override; private: std::vector> const displays; }; diff --git a/src/server/server.cpp b/src/server/server.cpp index 1f0ff102a80..f3d1f6f99c6 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -24,7 +24,6 @@ #include "mir/input/composite_event_filter.h" #include "mir/input/event_filter.h" #include "mir/options/default_configuration.h" -#include "mir/renderer/gl/render_target.h" #include "mir/default_server_configuration.h" #include "mir/logging/logger.h" #include "mir/log.h" diff --git a/tests/acceptance-tests/platforms/CMakeLists.txt b/tests/acceptance-tests/platforms/CMakeLists.txt index c7cf1a9fc12..2e910a3f242 100644 --- a/tests/acceptance-tests/platforms/CMakeLists.txt +++ b/tests/acceptance-tests/platforms/CMakeLists.txt @@ -36,8 +36,20 @@ target_compile_definitions( PRIVATE MIR_SERVER_GRAPHICS_PLATFORM_VERSION="${MIR_SERVER_GRAPHICS_PLATFORM_VERSION}") +target_include_directories( + mirplatformtest + PRIVATE + ${PROJECT_SOURCE_DIR}/tests/include +) + target_link_libraries(mirplatformtest PUBLIC mirplatform mircommon + mir-test-doubles-static ) + +target_include_directories(mirplatformtest + PRIVATE + ${PROJECT_SOURCE_DIR}/tests/include/ +) \ No newline at end of file diff --git a/tests/acceptance-tests/platforms/eglstream-kms_platform.cpp b/tests/acceptance-tests/platforms/eglstream-kms_platform.cpp index 3878b6f4cc4..0ff79f9a12f 100644 --- a/tests/acceptance-tests/platforms/eglstream-kms_platform.cpp +++ b/tests/acceptance-tests/platforms/eglstream-kms_platform.cpp @@ -44,4 +44,3 @@ EGLStreamKMSPlatformEnv platform_harness; } INSTANTIATE_TEST_SUITE_P(EGLStreamKMS, RenderingPlatformTest, testing::Values(&platform_harness)); -INSTANTIATE_TEST_SUITE_P(EGLStreamKMS, DisplayPlatformTest, testing::Values(&platform_harness)); diff --git a/tests/acceptance-tests/platforms/test_rendering_platform.cpp b/tests/acceptance-tests/platforms/test_rendering_platform.cpp index 4948c1cf89e..48d778a92f6 100644 --- a/tests/acceptance-tests/platforms/test_rendering_platform.cpp +++ b/tests/acceptance-tests/platforms/test_rendering_platform.cpp @@ -21,8 +21,14 @@ #include #include "mir/graphics/platform.h" +#include "mir/graphics/graphic_buffer_allocator.h" +#include "mir/options/program_option.h" +#include "mir/emergency_cleanup_registry.h" +#include "mir/udev/wrapper.h" +#include "mir/test/doubles/null_console_services.h" namespace mg = mir::graphics; +namespace mtd = mir::test::doubles; RenderingPlatformTest::RenderingPlatformTest() { @@ -36,11 +42,11 @@ RenderingPlatformTest::~RenderingPlatformTest() TEST_P(RenderingPlatformTest, has_render_platform_entrypoints) { - auto const module = GetParam()->platform_module(); + platform_module = GetParam()->platform_module(); try { - module->load_function( + platform_module->load_function( "describe_graphics_module", MIR_SERVER_GRAPHICS_PLATFORM_VERSION); } @@ -53,7 +59,7 @@ TEST_P(RenderingPlatformTest, has_render_platform_entrypoints) try { - module->load_function( + platform_module->load_function( "create_rendering_platform", MIR_SERVER_GRAPHICS_PLATFORM_VERSION); } @@ -66,7 +72,7 @@ TEST_P(RenderingPlatformTest, has_render_platform_entrypoints) try { - module->load_function( + platform_module->load_function( "probe_rendering_platform", MIR_SERVER_GRAPHICS_PLATFORM_VERSION); } @@ -78,3 +84,57 @@ TEST_P(RenderingPlatformTest, has_render_platform_entrypoints) } } +namespace +{ +class NullEmergencyCleanup : public mir::EmergencyCleanupRegistry +{ +public: + void add(mir::EmergencyCleanupHandler const&) override + { + } + + void add(mir::ModuleEmergencyCleanupHandler) override + { + } +}; +} + +/* + * DISABLED, as this requires more thought + */ +TEST_P(RenderingPlatformTest, DISABLED_supports_gl_rendering) +{ + using namespace testing; + + platform_module = GetParam()->platform_module(); + + auto const platform_loader = platform_module->load_function( + "create_rendering_platform", + MIR_SERVER_GRAPHICS_PLATFORM_VERSION); + auto const platform_probe = platform_module->load_function( + "probe_rendering_platform", + MIR_SERVER_GRAPHICS_PLATFORM_VERSION); + + mir::options::ProgramOption empty_options{}; + NullEmergencyCleanup emergency_cleanup{}; + auto const console_services = std::make_shared(); + + auto supported_devices = platform_probe( + console_services, + std::make_shared(), + empty_options); + + ASSERT_THAT(supported_devices, Not(IsEmpty())); + + for (auto const& device : supported_devices) + { + std::shared_ptr const platform = platform_loader( + device, + {}, // Hopefully the platform can handle not pre-linking with a DisplayPlatform + empty_options, + emergency_cleanup); + + auto const gl_interface = platform->acquire_interface(nullptr); + EXPECT_THAT(gl_interface, testing::NotNull()); + } +} diff --git a/tests/acceptance-tests/platforms/test_rendering_platform.h b/tests/acceptance-tests/platforms/test_rendering_platform.h index c4771f0ef06..80f628985a1 100644 --- a/tests/acceptance-tests/platforms/test_rendering_platform.h +++ b/tests/acceptance-tests/platforms/test_rendering_platform.h @@ -21,11 +21,19 @@ #include "platform_test_harness.h" +namespace mir +{ +class SharedLibrary; +} + class RenderingPlatformTest : public testing::TestWithParam { public: RenderingPlatformTest(); virtual ~RenderingPlatformTest() override; + +protected: + std::shared_ptr platform_module; }; #endif //MIR_TEST_RENDERING_PLATFORM_H diff --git a/tests/include/mir/test/doubles/mock_display.h b/tests/include/mir/test/doubles/mock_display.h index 99fa95d1512..6b2ce95b05a 100644 --- a/tests/include/mir/test/doubles/mock_display.h +++ b/tests/include/mir/test/doubles/mock_display.h @@ -40,7 +40,6 @@ struct MockDisplay : public graphics::Display MOCK_METHOD(void, register_configuration_change_handler, (graphics::EventHandlerRegister&, graphics::DisplayConfigurationChangeHandler const&), (override)); MOCK_METHOD(void, pause, (), (override)); MOCK_METHOD(void, resume, (), (override)); - MOCK_METHOD(std::unique_ptr, create_gl_context, (), (const override)); MOCK_METHOD(std::shared_ptr, create_hardware_cursor, (), (override)); MOCK_METHOD(graphics::Frame, last_frame_on, (unsigned), (const override)); }; diff --git a/tests/include/mir/test/doubles/mock_display_buffer.h b/tests/include/mir/test/doubles/mock_display_buffer.h index 2702e830706..e31f5fdbfbe 100644 --- a/tests/include/mir/test/doubles/mock_display_buffer.h +++ b/tests/include/mir/test/doubles/mock_display_buffer.h @@ -18,6 +18,7 @@ #define MIR_TEST_DOUBLES_MOCK_DISPLAY_BUFFER_H_ #include +#include #include @@ -28,8 +29,7 @@ namespace test namespace doubles { -class MockDisplayBuffer : public graphics::DisplayBuffer, - public graphics::NativeDisplayBuffer +class MockDisplayBuffer : public graphics::DisplayBuffer { public: MockDisplayBuffer() @@ -37,13 +37,12 @@ class MockDisplayBuffer : public graphics::DisplayBuffer, using namespace testing; ON_CALL(*this, view_area()) .WillByDefault(Return(geometry::Rectangle{{0,0},{0,0}})); - ON_CALL(*this, native_display_buffer()) - .WillByDefault(Return(this)); } - MOCK_CONST_METHOD0(view_area, geometry::Rectangle()); - MOCK_METHOD1(overlay, bool(graphics::RenderableList const&)); - MOCK_CONST_METHOD0(transformation, glm::mat2()); - MOCK_METHOD0(native_display_buffer, graphics::NativeDisplayBuffer*()); + MOCK_METHOD(geometry::Rectangle, view_area, (), (const override)); + MOCK_METHOD(bool, overlay, (std::vector const&), (override)); + MOCK_METHOD(void, set_next_image, (std::unique_ptr), (override)); + MOCK_METHOD(glm::mat2, transformation, (), (const override)); + MOCK_METHOD(std::shared_ptr, owner, (), (const override)); }; } diff --git a/tests/include/mir/test/doubles/mock_drm.h b/tests/include/mir/test/doubles/mock_drm.h index aeba8c7d05d..453c95c1fa7 100644 --- a/tests/include/mir/test/doubles/mock_drm.h +++ b/tests/include/mir/test/doubles/mock_drm.h @@ -17,6 +17,7 @@ #ifndef MIR_TEST_DOUBLES_MOCK_DRM_H_ #define MIR_TEST_DOUBLES_MOCK_DRM_H_ +#include "mir_test_framework/mmap_wrapper.h" #include "mir_test_framework/open_wrapper.h" #include "mir/geometry/forward.h" @@ -162,6 +163,9 @@ class MockDRM MOCK_METHOD1(drmCheckModesettingSupported, int(char const*)); + MOCK_METHOD(void*, mmap, (void* addr, size_t length, int prot, int flags, int fd, off_t offset)); + MOCK_METHOD(int, munmap, (void* addr, size_t length)); + void add_crtc( char const* device, uint32_t id, @@ -194,8 +198,30 @@ class MockDRM private: std::unordered_map fake_drms; std::unordered_map fd_to_drm; + + class TransparentUPtrComparator + { + public: + auto operator()(std::unique_ptr const& a, void* b) const -> bool + { + return a.get() < b; + } + auto operator()(void*a, std::unique_ptr const& b) const -> bool + { + return a < b.get(); + } + auto operator()(std::unique_ptr const& a, std::unique_ptr const& b) const -> bool + { + return a.get() < b.get(); + } + struct is_transparent; + }; + + std::map, size_t, TransparentUPtrComparator> mmapings; drmModeObjectProperties empty_object_props; - mir_test_framework::OpenHandlerHandle open_interposer; + mir_test_framework::OpenHandlerHandle const open_interposer; + mir_test_framework::MmapHandlerHandle const mmap_interposer; + mir_test_framework::MunmapHandlerHandle const munmap_interposer; }; testing::Matcher IsFdOfDevice(char const* device); diff --git a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h new file mode 100644 index 00000000000..fbb09f38469 --- /dev/null +++ b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2021 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Christopher James Halse Rogers + */ + +#ifndef MIR_TEST_DOUBLES_MOCK_GL_RENDERING_PROVIDER_H_ +#define MIR_TEST_DOUBLES_MOCK_GL_RENDERING_PROVIDER_H_ + +#include + +#include "mir/graphics/platform.h" + +namespace mir::test::doubles +{ +class MockGlRenderingPlatform : public graphics::GLRenderingProvider +{ +public: + MOCK_METHOD(std::shared_ptr, as_texture, (std::shared_ptr), (override)); + MOCK_METHOD(std::unique_ptr, surface_for_output, (graphics::DisplayBuffer&), (override)); +}; +} + +#endif //MIR_TEST_DOUBLES_MOCK_GL_RENDERING_PROVIDER_H_ diff --git a/tests/include/mir/test/as_render_target.h b/tests/include/mir/test/doubles/mock_output_surface.h similarity index 50% rename from tests/include/mir/test/as_render_target.h rename to tests/include/mir/test/doubles/mock_output_surface.h index ad1d260d7bc..b467060ed13 100644 --- a/tests/include/mir/test/as_render_target.h +++ b/tests/include/mir/test/doubles/mock_output_surface.h @@ -14,29 +14,24 @@ * along with this program. If not, see . */ -#ifndef MIR_TEST_AS_RENDER_TARGET_H_ -#define MIR_TEST_AS_RENDER_TARGET_H_ +#ifndef MIR_TEST_DOUBLES_MOCK_OUTPUT_SURFACE_H_ +#define MIR_TEST_DOUBLES_MOCK_OUTPUT_SURFACE_H_ -#include "mir/graphics/display_buffer.h" -#include "mir/renderer/gl/render_target.h" +#include -#include +#include "mir/renderer/gl/gl_surface.h" -namespace mir +namespace mir::test::doubles { -namespace test +class MockOutputSurface : public mir::graphics::gl::OutputSurface { - -inline auto as_render_target(graphics::DisplayBuffer& display_buffer) -{ - auto const render_target = - dynamic_cast(display_buffer.native_display_buffer()); - if (!render_target) - throw std::logic_error{"DisplayBuffer doesn't support GL rendering"}; - return render_target; -} - -} +public: + MOCK_METHOD(void, bind, (), (override)); + MOCK_METHOD(void, make_current, (), (override)); + MOCK_METHOD(std::unique_ptr, commit, (), (override)); + MOCK_METHOD(mir::geometry::Size, size, (), (const override)); + MOCK_METHOD(Layout, layout, (), (const override)); +}; } -#endif +#endif //MIR_TEST_DOUBLES_MOCK_OUTPUT_SURFACE_H_ diff --git a/tests/include/mir/test/doubles/mock_renderer.h b/tests/include/mir/test/doubles/mock_renderer.h index 201ae907d31..13ec511419c 100644 --- a/tests/include/mir/test/doubles/mock_renderer.h +++ b/tests/include/mir/test/doubles/mock_renderer.h @@ -17,6 +17,7 @@ #define MIR_TEST_DOUBLES_MOCK_RENDERER_H_ #include "mir/renderer/renderer.h" +#include "mir/graphics/platform.h" #include @@ -31,7 +32,7 @@ struct MockRenderer : public renderer::Renderer { MOCK_METHOD1(set_viewport, void(geometry::Rectangle const&)); MOCK_METHOD1(set_output_transform, void(glm::mat2 const&)); - MOCK_CONST_METHOD1(render, void(graphics::RenderableList const&)); + MOCK_METHOD(std::unique_ptr, render, (graphics::RenderableList const&), (const override)); MOCK_METHOD0(suspend, void()); ~MockRenderer() noexcept {} diff --git a/tests/include/mir/test/doubles/null_display.h b/tests/include/mir/test/doubles/null_display.h index 116f4ee5711..ca5f49542b7 100644 --- a/tests/include/mir/test/doubles/null_display.h +++ b/tests/include/mir/test/doubles/null_display.h @@ -60,11 +60,6 @@ class NullDisplay : public graphics::Display { return {}; } - std::unique_ptr create_gl_context() const override - { - return std::unique_ptr{new NullGLContext()}; - } - NullDisplaySyncGroup group; }; diff --git a/tests/include/mir/test/doubles/null_display_buffer.h b/tests/include/mir/test/doubles/null_display_buffer.h index 39a4af171eb..ec81aeedde4 100644 --- a/tests/include/mir/test/doubles/null_display_buffer.h +++ b/tests/include/mir/test/doubles/null_display_buffer.h @@ -26,13 +26,14 @@ namespace test namespace doubles { -class NullDisplayBuffer : public graphics::DisplayBuffer, public graphics::NativeDisplayBuffer +class NullDisplayBuffer : public graphics::DisplayBuffer { public: geometry::Rectangle view_area() const override { return geometry::Rectangle(); } - bool overlay(graphics::RenderableList const&) override { return false; } + bool overlay(std::vector const&) override { return false; } + void set_next_image(std::unique_ptr) override { } glm::mat2 transformation() const override { return glm::mat2(1); } - NativeDisplayBuffer* native_display_buffer() override { return this; } + auto owner() const -> std::shared_ptr override { return {};} }; } diff --git a/tests/include/mir/test/doubles/null_display_sync_group.h b/tests/include/mir/test/doubles/null_display_sync_group.h index a7072477bab..944d9319758 100644 --- a/tests/include/mir/test/doubles/null_display_sync_group.h +++ b/tests/include/mir/test/doubles/null_display_sync_group.h @@ -19,7 +19,7 @@ #include "mir/graphics/display.h" #include "mir/geometry/size.h" -#include "mir/test/doubles/stub_gl_display_buffer.h" +#include "mir/test/doubles/stub_display_buffer.h" #include namespace mir @@ -59,7 +59,7 @@ struct StubDisplaySyncGroup : graphics::DisplaySyncGroup private: std::vector const output_rects; - std::vector display_buffers; + std::vector display_buffers; }; struct NullDisplaySyncGroup : graphics::DisplaySyncGroup @@ -79,7 +79,7 @@ struct NullDisplaySyncGroup : graphics::DisplaySyncGroup return std::chrono::milliseconds::zero(); } - StubGLDisplayBuffer db{{{}, {1, 1}}}; + StubDisplayBuffer db{{{}, {1, 1}}}; }; } diff --git a/tests/include/mir/test/doubles/null_platform.h b/tests/include/mir/test/doubles/null_platform.h index b2e9c4d7697..f13f23ab024 100644 --- a/tests/include/mir/test/doubles/null_platform.h +++ b/tests/include/mir/test/doubles/null_platform.h @@ -36,6 +36,12 @@ class NullDisplayPlatform : public graphics::DisplayPlatform { return mir::make_module_ptr(); } + + auto maybe_create_interface(graphics::DisplayInterfaceBase::Tag const&) + -> std::shared_ptr override + { + return nullptr; + } }; class NullRenderingPlatform : public graphics::RenderingPlatform @@ -46,6 +52,13 @@ class NullRenderingPlatform : public graphics::RenderingPlatform { return nullptr; } + +protected: + auto maybe_create_interface( + graphics::RendererInterfaceBase::Tag const&) -> std::shared_ptr override + { + return nullptr; + } }; } } diff --git a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h new file mode 100644 index 00000000000..eeb6c3e068b --- /dev/null +++ b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h @@ -0,0 +1,77 @@ +/* + * Copyright © 2021 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authored by: Christopher James Halse Rogers + */ + +#ifndef MIR_TEST_DOUBLES_STUB_GL_RENDERING_PROVIDER_H_ +#define MIR_TEST_DOUBLES_STUB_GL_RENDERING_PROVIDER_H_ + +#include "mir/graphics/platform.h" +#include "mir/test/doubles/mock_gl_buffer.h" +#include "mir/test/doubles/mock_output_surface.h" + +namespace mir::test::doubles +{ +class StubGlRenderingPlatform : public graphics::GLRenderingProvider +{ +public: + auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override + { + if (auto existing_buf = std::dynamic_pointer_cast(std::move(buffer))) + { + return existing_buf; + } + auto tex_buf = std::make_shared>( + geometry::Size{800, 500}, + geometry::Stride{-1}, + mir_pixel_format_argb_8888); + + ON_CALL(*tex_buf, shader(testing::_)) + .WillByDefault(testing::Invoke( + [](auto& factory) -> mir::graphics::gl::Program& + { + static int unused = 1; + return factory.compile_fragment_shader(&unused, "extension code", "fragment code"); + })); + + return tex_buf; + } + + auto surface_for_output( + graphics::DisplayBuffer&, + graphics::GLConfig const&) -> std::unique_ptr override + { + return std::make_unique>(); + } + + auto make_framebuffer_provider(graphics::DisplayBuffer const& /*target*/) + -> std::unique_ptr override + { + class NullFramebufferProvider : public FramebufferProvider + { + public: + auto buffer_to_framebuffer(std::shared_ptr) + -> std::unique_ptr override + { + return {}; + } + }; + return std::make_unique(); + } +}; +} + +#endif //MIR_TEST_DOUBLES_STUB_GL_RENDERING_PROVIDER_H_ diff --git a/tests/include/mir/test/doubles/stub_renderer.h b/tests/include/mir/test/doubles/stub_renderer.h index ad7d0d34f67..54ec64b7cb0 100644 --- a/tests/include/mir/test/doubles/stub_renderer.h +++ b/tests/include/mir/test/doubles/stub_renderer.h @@ -19,6 +19,7 @@ #include "mir/renderer/renderer.h" #include "mir/graphics/renderable.h" +#include "mir/graphics/buffer.h" #include namespace mir @@ -35,12 +36,14 @@ class StubRenderer : public renderer::Renderer void set_output_transform(glm::mat2 const&) override {} void suspend() override {} - void render(graphics::RenderableList const& renderables) const override + auto render(graphics::RenderableList const& renderables) const -> std::unique_ptr override { for (auto const& r : renderables) r->buffer(); // We need to consume a buffer to unblock client tests // Yield to reduce runtime under valgrind std::this_thread::yield(); + + return {}; } }; diff --git a/tests/integration-tests/test_surface_stack_with_compositor.cpp b/tests/integration-tests/test_surface_stack_with_compositor.cpp index fabee098056..0411cd3d832 100644 --- a/tests/integration-tests/test_surface_stack_with_compositor.cpp +++ b/tests/integration-tests/test_surface_stack_with_compositor.cpp @@ -31,6 +31,10 @@ #include "mir/test/doubles/null_display_sync_group.h" #include "mir/test/doubles/mock_event_sink.h" #include "mir/test/doubles/stub_buffer_allocator.h" +#include "mir/test/doubles/mock_gl_buffer.h" +#include "mir/test/doubles/mock_output_surface.h" +#include "mir/test/doubles/stub_gl_rendering_provider.h" +#include "mir/test/doubles/null_gl_config.h" #include #include @@ -54,8 +58,9 @@ namespace class StubRendererFactory : public mir::renderer::RendererFactory { public: - std::unique_ptr - create_renderer_for(mir::renderer::gl::RenderTarget&) override + auto create_renderer_for( + std::unique_ptr, + std::shared_ptr) const -> std::unique_ptr override { return std::unique_ptr(new mtd::StubRenderer); } @@ -120,6 +125,27 @@ struct StubDisplayListener : mc::DisplayListener virtual void remove_display(geom::Rectangle const& /*area*/) override {} }; +class StubTexture : public testing::NiceMock +{ +public: + StubTexture() + { + ON_CALL(*this, shader(_)) + .WillByDefault( + Invoke( + [](auto& factory) -> mg::gl::Program& + { + static int yo; + return factory.compile_fragment_shader( + &yo, + "extension fragment", + "shader code"); + })); + ON_CALL(*this, layout) + .WillByDefault(Return(mg::gl::Texture::Layout::GL)); + } +}; + struct SurfaceStackCompositor : public Test { SurfaceStackCompositor() : @@ -155,7 +181,10 @@ struct SurfaceStackCompositor : public Test CountingDisplaySyncGroup stub_secondary_db; StubDisplay stub_display{stub_primary_db, stub_secondary_db}; StubDisplayListener stub_display_listener; + mc::DefaultDisplayBufferCompositorFactory dbc_factory{ + std::make_shared(), + std::make_shared(), mt::fake_shared(renderer_factory), null_comp_report}; }; diff --git a/tests/mir_test_doubles/CMakeLists.txt b/tests/mir_test_doubles/CMakeLists.txt index 41d4f828fd2..285c2f07181 100644 --- a/tests/mir_test_doubles/CMakeLists.txt +++ b/tests/mir_test_doubles/CMakeLists.txt @@ -27,6 +27,9 @@ set( ${PROJECT_SOURCE_DIR}/tests/include/mir/test/doubles/null_display_configuration_policy.h ${PROJECT_SOURCE_DIR}/tests/include/mir/test/doubles/stub_buffer_allocator.h stub_buffer_allocator.cpp + ${PROJECT_SOURCE_DIR}/tests/include/mir/test/doubles/stub_gl_rendering_provider.h + ${PROJECT_SOURCE_DIR}/tests/include/mir/test/doubles/mock_gl_rendering_provider.h + ${PROJECT_SOURCE_DIR}/tests/include/mir/test/doubles/mock_output_surface.h ) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -106,6 +109,12 @@ add_library(mir-test-doubles-static STATIC ${TEST_UTILS_SRCS} ) +target_include_directories( + mir-test-doubles-static + PUBLIC + ${PROJECT_SOURCE_DIR}/tests/include +) + add_dependencies(mir-test-doubles-static GMock) target_link_libraries(mir-test-doubles-static diff --git a/tests/mir_test_doubles/mock_drm.cpp b/tests/mir_test_doubles/mock_drm.cpp index d3d5840b7a0..157998ecca1 100644 --- a/tests/mir_test_doubles/mock_drm.cpp +++ b/tests/mir_test_doubles/mock_drm.cpp @@ -15,6 +15,7 @@ */ #include "mir/test/doubles/mock_drm.h" +#include "mir_test_framework/mmap_wrapper.h" #include "mir_test_framework/open_wrapper.h" #include "mir/geometry/size.h" #include @@ -241,6 +242,34 @@ mtd::MockDRM::MockDRM() return this->open(path, flags); } return std::nullopt; + })}, + mmap_interposer{mir_test_framework::add_mmap_handler( + [this](void* addr, size_t length, int prot, int flags, int fd, off_t offset) -> std::optional + { + // The dumb buffer API uses a call of mmap() on the DRM fd, so we want to handle that. + if (this->fd_to_drm.contains(fd)) + { + return this->mmap(addr, length, prot, flags, fd, offset); + } + return std::nullopt; + })}, + munmap_interposer{mir_test_framework::add_munmap_handler( + [this](void* addr, size_t length) -> std::optional + { + auto maybe_mapping = mmapings.find(addr); + if (maybe_mapping != mmapings.end()) + { + if (maybe_mapping->second != length) + { + /* It's technically possible to unmap only *part* of the mapping + * A more fancy mock would check whether addr + length overlaps + * any existing mappings and punch holes, but 🤷‍♀️ + */ + BOOST_THROW_EXCEPTION((std::runtime_error{"munmap mock does not support partial unmappings"})); + } + return this->munmap(addr, length); + } + return std::nullopt; })} { using namespace testing; @@ -358,6 +387,27 @@ mtd::MockDRM::MockDRM() .WillByDefault(Return(0)); ON_CALL(*this, drmCheckModesettingSupported(IsNull())) .WillByDefault(Return(-EINVAL)); + + ON_CALL(*this, mmap(_, _, _, _, _, _)) + .WillByDefault( + WithArg<1>(Invoke( + [this](auto size) -> void* + { + auto allocation = std::make_unique(size); + void* const address = allocation.get(); + mmapings.insert(std::make_pair(std::move(allocation), size)); + return address; + }))); + + ON_CALL(*this, munmap(_, _)) + .WillByDefault( + WithArg<0>(Invoke( + [this](void* addr) -> int + { + auto iter = mmapings.find(addr); + mmapings.erase(iter); + return 0; + }))); } mtd::MockDRM::~MockDRM() noexcept diff --git a/tests/mir_test_framework/headless_display_buffer_compositor_factory.cpp b/tests/mir_test_framework/headless_display_buffer_compositor_factory.cpp index ed46d7426e7..348c8e658bb 100644 --- a/tests/mir_test_framework/headless_display_buffer_compositor_factory.cpp +++ b/tests/mir_test_framework/headless_display_buffer_compositor_factory.cpp @@ -16,9 +16,10 @@ #include "mir_test_framework/headless_display_buffer_compositor_factory.h" #include "mir_test_framework/passthrough_tracker.h" -#include "mir/renderer/gl/render_target.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/texture.h" +#include "mir/graphics/platform.h" +#include "mir/renderer/gl/gl_surface.h" #include "mir/compositor/display_buffer_compositor.h" #include "mir/compositor/scene_element.h" #include "mir/graphics/buffer.h" @@ -26,18 +27,25 @@ namespace mtf = mir_test_framework; namespace mg = mir::graphics; -namespace mrg = mir::renderer::gl; namespace mc = mir::compositor; namespace geom = mir::geometry; -mtf::HeadlessDisplayBufferCompositorFactory::HeadlessDisplayBufferCompositorFactory() : - tracker(nullptr) +mtf::HeadlessDisplayBufferCompositorFactory::HeadlessDisplayBufferCompositorFactory( + std::shared_ptr render_platform, + std::shared_ptr gl_config) + : render_platform{std::move(render_platform)}, + gl_config{std::move(gl_config)}, + tracker(nullptr) { } mtf::HeadlessDisplayBufferCompositorFactory::HeadlessDisplayBufferCompositorFactory( - std::shared_ptr const& tracker) : - tracker(tracker) + std::shared_ptr render_platform, + std::shared_ptr gl_config, + std::shared_ptr const& tracker) + : render_platform{std::move(render_platform)}, + gl_config{std::move(gl_config)}, + tracker(tracker) { } @@ -48,11 +56,13 @@ mtf::HeadlessDisplayBufferCompositorFactory::create_compositor_for(mg::DisplayBu { HeadlessDBC( mg::DisplayBuffer& db, + std::unique_ptr output, + std::shared_ptr render_platform, std::shared_ptr const& tracker) : - db(db), - tracker(tracker), - render_target(dynamic_cast( - db.native_display_buffer())) + db{db}, + output{std::move(output)}, + render_platform{std::move(render_platform)}, + tracker(tracker) { } @@ -86,34 +96,29 @@ mtf::HeadlessDisplayBufferCompositorFactory::create_compositor_for(mg::DisplayBu void composite(mir::compositor::SceneElementSequence&& seq) override { auto renderlist = filter(seq, db.view_area()); - if (db.overlay(renderlist)) - { - if (tracker) - tracker->note_passthrough(); - return; - } - // Invoke GL renderer specific functions if the DisplayBuffer supports them - if (render_target) - render_target->make_current(); + // TODO: Might need to attempt to overlay in order to test those codepaths? + + output->bind(); // We need to consume a buffer to unblock client tests for (auto const& renderable : renderlist) { auto buf = renderable->buffer(); - if (auto tex = dynamic_cast(buf->native_buffer_base())) + if (auto tex = render_platform->as_texture(buf)) { // bind() is what drives the Wayland frame event. tex->bind(); } } - if (render_target) - render_target->swap_buffers(); + output->commit(); } mg::DisplayBuffer& db; + std::unique_ptr const output; + std::shared_ptr const render_platform; std::shared_ptr const tracker; - mrg::RenderTarget* const render_target; }; - return std::make_unique(db, tracker); + auto output_surface = render_platform->surface_for_output(db, *gl_config); + return std::make_unique(db, std::move(output_surface), render_platform, tracker); } diff --git a/tests/mir_test_framework/headless_test.cpp b/tests/mir_test_framework/headless_test.cpp index 178d88291d6..a01645f244a 100644 --- a/tests/mir_test_framework/headless_test.cpp +++ b/tests/mir_test_framework/headless_test.cpp @@ -27,6 +27,7 @@ namespace geom = mir::geometry; namespace mtf = mir_test_framework; +namespace mg = mir::graphics; namespace { @@ -42,11 +43,20 @@ mtf::HeadlessTest::HeadlessTest() add_to_environment("MIR_SERVER_PLATFORM_INPUT_LIB", mtf::server_platform("input-stub.so").c_str()); add_to_environment("MIR_SERVER_ENABLE_KEY_REPEAT", "false"); add_to_environment("MIR_SERVER_CONSOLE_PROVIDER", "none"); - server.override_the_display_buffer_compositor_factory([] + + server.override_the_display_buffer_compositor_factory( + [this]() -> std::shared_ptr { - return std::make_shared(); + auto first_platform = server.the_rendering_platforms().front(); + auto gl_platform = mg::RenderingPlatform::acquire_interface(std::move(first_platform)); + if (gl_platform) + { + return std::make_shared(std::move(gl_platform), server.the_gl_config()); + } + BOOST_THROW_EXCEPTION((std::runtime_error{"Test RenderingPlatform does not support GL interface"})); }); + server.add_init_callback( [server = &server] { diff --git a/tests/mir_test_framework/mmap_wrapper.cpp b/tests/mir_test_framework/mmap_wrapper.cpp index c63591fa7b2..af0197c32c7 100644 --- a/tests/mir_test_framework/mmap_wrapper.cpp +++ b/tests/mir_test_framework/mmap_wrapper.cpp @@ -31,7 +31,7 @@ namespace mtf = mir_test_framework; namespace { using MmapInterposer = mtf::InterposerHandlers; -using MunmapInterposer = mtf::InterposerHandlers; +using MunmapInterposer = mtf::InterposerHandlers; } mtf::MmapHandlerHandle mtf::add_mmap_handler(MmapHandler handler) @@ -101,4 +101,3 @@ int munmap(void *addr, size_t length) } return (*real_munmap)(addr, length); } - diff --git a/tests/mir_test_framework/platform_graphics_throw.cpp b/tests/mir_test_framework/platform_graphics_throw.cpp index db18b4ada50..f61a08f2cc8 100644 --- a/tests/mir_test_framework/platform_graphics_throw.cpp +++ b/tests/mir_test_framework/platform_graphics_throw.cpp @@ -60,6 +60,21 @@ class ExceptionThrowingPlatform : public mg::DisplayPlatform, public mg::Renderi stub_display_platform = create_stub_display_platform(device, nullptr, nullptr, nullptr, nullptr); } +protected: + auto maybe_create_interface( + mg::RendererInterfaceBase::Tag const&) -> std::shared_ptr override + { + return nullptr; + } + + auto maybe_create_interface( + mg::DisplayInterfaceBase::Tag const&) -> std::shared_ptr override + { + return nullptr; + } + +public: + mir::UniqueModulePtr create_buffer_allocator(mg::Display const& output) override { diff --git a/tests/mir_test_framework/stubbed_graphics_platform.cpp b/tests/mir_test_framework/stubbed_graphics_platform.cpp index 26ce2c68a64..9b2036d62d8 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.cpp +++ b/tests/mir_test_framework/stubbed_graphics_platform.cpp @@ -21,6 +21,7 @@ #include "mir_toolkit/common.h" #include "mir/test/doubles/stub_buffer_allocator.h" +#include "mir/test/doubles/stub_gl_rendering_provider.h" #include "mir/test/doubles/fake_display.h" #include "mir/fd.h" #include "mir/assert_module_entry_point.h" @@ -90,6 +91,24 @@ mir::UniqueModulePtr mtf::StubGraphicPlatform::create_display( return mir::make_module_ptr(display_rects); } +auto mtf::StubGraphicPlatform::maybe_create_interface( + mir::graphics::RendererInterfaceBase::Tag const& tag) + -> std::shared_ptr +{ + if (dynamic_cast(&tag)) + { + return std::make_shared(); + } + return nullptr; +} + +auto mtf::StubGraphicPlatform::maybe_create_interface( + mg::DisplayInterfaceBase::Tag const& /*tag*/) + -> std::shared_ptr +{ + return nullptr; +} + namespace { std::unique_ptr> chosen_display_rects; diff --git a/tests/mir_test_framework/stubbed_graphics_platform.h b/tests/mir_test_framework/stubbed_graphics_platform.h index 354f84724da..997ee55a7c0 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.h +++ b/tests/mir_test_framework/stubbed_graphics_platform.h @@ -35,6 +35,15 @@ class StubGraphicPlatform : std::shared_ptr const&, std::shared_ptr const&) override; +protected: + auto maybe_create_interface( + mir::graphics::RendererInterfaceBase::Tag const& tag) + -> std::shared_ptr override; + + auto maybe_create_interface( + mir::graphics::DisplayInterfaceBase::Tag const& tag) + -> std::shared_ptr override; + private: std::vector const display_rects; }; diff --git a/tests/mir_test_framework/stubbed_server_configuration.cpp b/tests/mir_test_framework/stubbed_server_configuration.cpp index ee8579c62b2..e720c6a627d 100644 --- a/tests/mir_test_framework/stubbed_server_configuration.cpp +++ b/tests/mir_test_framework/stubbed_server_configuration.cpp @@ -46,7 +46,10 @@ namespace class StubRendererFactory : public mir::renderer::RendererFactory { public: - std::unique_ptr create_renderer_for(mir::renderer::gl::RenderTarget&) + auto create_renderer_for( + std::unique_ptr, + std::shared_ptr /*platform*/) + const -> std::unique_ptr override { return std::unique_ptr(new mtd::StubRenderer()); } diff --git a/tests/mir_test_framework/test_display_server.cpp b/tests/mir_test_framework/test_display_server.cpp index eb21f371621..4fd2352abd0 100644 --- a/tests/mir_test_framework/test_display_server.cpp +++ b/tests/mir_test_framework/test_display_server.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ +#include "src/server/compositor/default_display_buffer_compositor_factory.h" #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #include @@ -39,6 +41,7 @@ namespace msh = mir::shell; namespace ml = mir::logging; namespace mo = mir::options; namespace mtd = mir::test::doubles; +namespace mg = mir::graphics; namespace { @@ -101,11 +104,24 @@ void miral::TestDisplayServer::start_server() }); }); - server.override_the_display_buffer_compositor_factory([] + + server.override_the_display_buffer_compositor_factory( + [&server]() -> std::shared_ptr { - return std::make_shared(); + auto first_rendering_platform = server.the_rendering_platforms().front(); + auto gl_platform = + mg::RenderingPlatform::acquire_interface( + std::move(first_rendering_platform)); + if (gl_platform) + { + return std::make_shared( + std::move(gl_platform), + server.the_gl_config()); + } + BOOST_THROW_EXCEPTION((std::runtime_error{"Platform does not support GL interface"})); }); + server.override_the_logger([&]() { std::shared_ptr result{}; diff --git a/tests/platform_test_harness/graphics_platform_test_harness.cpp b/tests/platform_test_harness/graphics_platform_test_harness.cpp index 804df868873..4e17ffc0f6b 100644 --- a/tests/platform_test_harness/graphics_platform_test_harness.cpp +++ b/tests/platform_test_harness/graphics_platform_test_harness.cpp @@ -26,6 +26,7 @@ #include "mir/renderer/renderer_factory.h" #include "mir/renderer/renderer.h" #include "mir/main_loop.h" +#include "mir/renderer/gl/gl_surface.h" #include "../../src/include/platform/mir/graphics/pixel_format_utils.h" #include "mir/default_server_configuration.h" @@ -37,7 +38,6 @@ #include #include #include -#include #include #include @@ -240,29 +240,6 @@ auto test_display_has_at_least_one_enabled_output(mg::Display& display) -> bool return output_count > 0; } -auto test_display_buffers_support_gl(mg::Display& display) -> bool -{ - bool all_support_gl{true}; - for_each_display_buffer( - display, - [&all_support_gl](mg::DisplayBuffer& db) - { - all_support_gl &= - dynamic_cast(db.native_display_buffer()) != nullptr; - }); - - if (all_support_gl) - { - std::cout << "DisplayBuffers support GL rendering" << std::endl; - } - else - { - std::cout << "DisplayBuffers do *not* support GL rendering" << std::endl; - } - - return all_support_gl; -} - auto dump_egl_config(mg::Display& display) -> bool { auto& context_source = dynamic_cast(display); @@ -279,7 +256,7 @@ auto dump_egl_config(mg::Display& display) -> bool return true; } - +/* auto hex_to_gl(unsigned char colour) -> GLclampf { return static_cast(colour) / 255; @@ -322,7 +299,7 @@ void basic_display_swapping(mg::Display& display) } }); } - +*/ std::shared_ptr alloc_and_fill_sw_buffer( mg::GraphicBufferAllocator& allocator, mir::geometry::Size size, @@ -391,8 +368,9 @@ auto bounce_within( [[maybe_unused]] void basic_software_buffer_drawing( mg::Display& display, + std::shared_ptr platform, mg::GraphicBufferAllocator& allocator, - mir::renderer::RendererFactory& factory) + mir::renderer::RendererFactory& /*factory*/) { uint32_t const transparent_orange = 0x002054e9; uint32_t const translucent_aubergine = 0xaa77216f; @@ -407,17 +385,19 @@ void basic_software_buffer_drawing( int min_height{std::numeric_limits::max()}, min_width{std::numeric_limits::max()}; for_each_display_buffer( display, - [&renderers, &factory, &min_height, &min_width](mg::DisplayBuffer& db) + [platform, /*&renderers, &factory,*/ &min_height, &min_width](mg::DisplayBuffer& db) { - auto const render_target = dynamic_cast(db.native_display_buffer()); - if (!render_target) + if (auto gl_interface = mg::RenderingPlatform::acquire_interface(platform)) + { +// auto output_surface = gl_interface->surface_for_output(db, ); +// renderers.push_back(factory.create_renderer_for(std::move(output_surface), gl_interface)); + min_height = std::min(min_height, db.view_area().bottom().as_int()); + min_width = std::min(min_width, db.view_area().right().as_int()); + } + else { - BOOST_THROW_EXCEPTION(std::logic_error("DisplayBuffer does not support GL rendering")); + BOOST_THROW_EXCEPTION((std::runtime_error{"Platform does not support GL"})); } - renderers.push_back(factory.create_renderer_for(*render_target)); - renderers.back()->set_viewport(db.view_area()); - min_height = std::min(min_height, db.view_area().bottom().as_int()); - min_width = std::min(min_width, db.view_area().right().as_int()); }); class DummyRenderable : public mg::Renderable @@ -564,8 +544,8 @@ int main(int argc, char const** argv) { success &= dump_egl_config(*display); success &= test_display_has_at_least_one_enabled_output(*display); - success &= test_display_buffers_support_gl(*display); - basic_display_swapping(*display); +// success &= test_display_buffers_support_gl(*display); +// basic_display_swapping(*display); // TODO: Update for display/render platform split // auto buffer_allocator = platform->create_buffer_allocator(*display); diff --git a/tests/unit-tests/compositor/CMakeLists.txt b/tests/unit-tests/compositor/CMakeLists.txt index d7fb0fad55b..7b25c2e34ed 100644 --- a/tests/unit-tests/compositor/CMakeLists.txt +++ b/tests/unit-tests/compositor/CMakeLists.txt @@ -6,7 +6,7 @@ list(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_multi_monitor_arbiter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_dropping_schedule.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_queueing_schedule.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_screen_shooter.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_screen_shooter.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) diff --git a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp index f5d3b169970..5bcf9ca144a 100644 --- a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp +++ b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp @@ -43,7 +43,7 @@ using namespace std::chrono_literals; namespace { -class MockBufferRenderTarget: public mrg::BufferRenderTarget +class MockBufferRenderTarget: public mrg::BufferOutputSurface { public: MOCK_METHOD(void, set_buffer, (std::shared_ptr const& buffer), (override)); @@ -88,7 +88,7 @@ struct BasicScreenShooter : Test mt::fake_shared(scene), mt::fake_shared(clock), executor, - std::unique_ptr{&render_target}, + std::unique_ptr{&render_target}, std::unique_ptr{&renderer}}; mtd::StubBuffer buffer; geom::Rectangle const viewport_rect{{20, 30}, {40, 50}}; diff --git a/tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp b/tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp index 2bfc5351b1c..3d72935bfd0 100644 --- a/tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp +++ b/tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp @@ -25,6 +25,7 @@ #include "mir/test/doubles/fake_renderable.h" #include "mir/test/doubles/mock_compositor_report.h" #include "mir/test/doubles/stub_scene_element.h" +#include "mir/test/doubles/stub_gl_rendering_provider.h" #include #include @@ -95,6 +96,7 @@ struct DefaultDisplayBufferCompositor : public testing::Test testing::NiceMock mock_renderer; geom::Rectangle screen{{0, 0}, {1366, 768}}; testing::NiceMock display_buffer; + mtd::StubGlRenderingPlatform gl_provider; std::shared_ptr small; std::shared_ptr big; std::shared_ptr fullscreen; @@ -111,6 +113,7 @@ TEST_F(DefaultDisplayBufferCompositor, render) mc::DefaultDisplayBufferCompositor compositor( display_buffer, + gl_provider, mt::fake_shared(mock_renderer), mr::null_compositor_report()); compositor.composite(make_scene_elements({})); @@ -141,6 +144,7 @@ TEST_F(DefaultDisplayBufferCompositor, optimization_skips_composition) mc::DefaultDisplayBufferCompositor compositor( display_buffer, + gl_provider, mt::fake_shared(mock_renderer), report); compositor.composite(make_scene_elements({})); @@ -169,6 +173,7 @@ TEST_F(DefaultDisplayBufferCompositor, rendering_reports_everything) mc::DefaultDisplayBufferCompositor compositor( display_buffer, + gl_provider, mt::fake_shared(mock_renderer), report); compositor.composite(make_scene_elements({})); @@ -191,6 +196,7 @@ TEST_F(DefaultDisplayBufferCompositor, calls_renderer_in_sequence) mc::DefaultDisplayBufferCompositor compositor( display_buffer, + gl_provider, mt::fake_shared(mock_renderer), mr::null_compositor_report()); @@ -228,6 +234,7 @@ TEST_F(DefaultDisplayBufferCompositor, rotates_viewport) mc::DefaultDisplayBufferCompositor compositor( display_buffer, + gl_provider, mt::fake_shared(mock_renderer), mr::null_compositor_report()); @@ -278,6 +285,7 @@ TEST_F(DefaultDisplayBufferCompositor, optimization_toggles_seamlessly) mc::DefaultDisplayBufferCompositor compositor( display_buffer, + gl_provider, mt::fake_shared(mock_renderer), mr::null_compositor_report()); @@ -309,6 +317,7 @@ TEST_F(DefaultDisplayBufferCompositor, occluded_surfaces_are_not_rendered) mc::DefaultDisplayBufferCompositor compositor( display_buffer, + gl_provider, mt::fake_shared(mock_renderer), mr::null_compositor_report()); compositor.composite(make_scene_elements({ @@ -349,6 +358,7 @@ TEST_F(DefaultDisplayBufferCompositor, marks_rendered_scene_elements) mc::DefaultDisplayBufferCompositor compositor( display_buffer, + gl_provider, mt::fake_shared(mock_renderer), mr::null_compositor_report()); @@ -372,6 +382,7 @@ TEST_F(DefaultDisplayBufferCompositor, marks_occluded_scene_elements) mc::DefaultDisplayBufferCompositor compositor( display_buffer, + gl_provider, mt::fake_shared(mock_renderer), mr::null_compositor_report()); diff --git a/tests/unit-tests/graphics/test_multiplexing_display.cpp b/tests/unit-tests/graphics/test_multiplexing_display.cpp index a1c472c32d3..dfa83582d96 100644 --- a/tests/unit-tests/graphics/test_multiplexing_display.cpp +++ b/tests/unit-tests/graphics/test_multiplexing_display.cpp @@ -123,12 +123,6 @@ auto make_safe_mock_display() -> std::unique_ptr { return std::make_unique(); }); - ON_CALL(*display, create_gl_context()) - .WillByDefault( - []() - { - return std::make_unique(); - }); return display; } } diff --git a/tests/unit-tests/graphics/test_shm_buffer.cpp b/tests/unit-tests/graphics/test_shm_buffer.cpp index 8e4f6ffc9db..9ac627ca5b0 100644 --- a/tests/unit-tests/graphics/test_shm_buffer.cpp +++ b/tests/unit-tests/graphics/test_shm_buffer.cpp @@ -57,6 +57,16 @@ class DumbGLContext : public mir::renderer::gl::Context eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } + auto make_share_context() const -> std::unique_ptr override + { + return std::make_unique(ctx); + } + + explicit operator EGLContext() override + { + return ctx; + } + private: EGLDisplay const dpy{reinterpret_cast(0xdeebbeed)}; EGLContext const ctx; diff --git a/tests/unit-tests/platforms/eglstream-kms/CMakeLists.txt b/tests/unit-tests/platforms/eglstream-kms/CMakeLists.txt index 1516e4b6797..c1073395a58 100644 --- a/tests/unit-tests/platforms/eglstream-kms/CMakeLists.txt +++ b/tests/unit-tests/platforms/eglstream-kms/CMakeLists.txt @@ -1,5 +1,4 @@ add_subdirectory("server") -add_subdirectory("client") mir_add_wrapped_executable(mir_unit_tests_eglstream-kms NOINSTALL ${EGLSTREAM_KMS_UNIT_TEST_SOURCES} diff --git a/tests/unit-tests/platforms/eglstream-kms/server/CMakeLists.txt b/tests/unit-tests/platforms/eglstream-kms/server/CMakeLists.txt index f3501451141..8d2e2f5bc0a 100644 --- a/tests/unit-tests/platforms/eglstream-kms/server/CMakeLists.txt +++ b/tests/unit-tests/platforms/eglstream-kms/server/CMakeLists.txt @@ -1,6 +1,5 @@ list(APPEND EGLSTREAM_KMS_UNIT_TEST_SOURCES $ - ${CMAKE_CURRENT_SOURCE_DIR}/test_threaded_drm_event_handler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_utils.cpp ) set(EGLSTREAM_KMS_UNIT_TEST_SOURCES ${EGLSTREAM_KMS_UNIT_TEST_SOURCES} PARENT_SCOPE) diff --git a/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt b/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt index 3d03cf55049..0a1a362c09c 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt +++ b/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt @@ -1,7 +1,7 @@ mir_add_wrapped_executable(mir_unit_tests_gbm-kms NOINSTALL ${CMAKE_CURRENT_SOURCE_DIR}/test_platform.cpp # ${CMAKE_CURRENT_SOURCE_DIR}/test_graphics_platform.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/test_display.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/test_display.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_generic.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_multi_monitor.cpp @@ -43,6 +43,7 @@ target_link_libraries( server_platform_common PkgConfig::DRM + PkgConfig::GBM ) if (MIR_RUN_UNIT_TESTS) diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_buffer_allocator.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_buffer_allocator.cpp new file mode 100644 index 00000000000..6782e5935d9 --- /dev/null +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_buffer_allocator.cpp @@ -0,0 +1,137 @@ +/* + * Copyright © 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "mir/test/doubles/null_emergency_cleanup.h" +#include "src/server/report/null_report_factory.h" +#include "mir/test/doubles/stub_console_services.h" +#include "src/platforms/gbm-kms/server/kms/platform.h" +#include "src/platforms/gbm-kms/server/kms/quirks.h" +#include "src/platforms/gbm-kms/server/buffer_allocator.h" +#include "mir/graphics/buffer_properties.h" +#include "mir/graphics/display.h" +#include "mir/options/program_option.h" +#include "mir/renderer/gl/context.h" + +#include "mir/test/doubles/mock_drm.h" +#include "mir/test/doubles/mock_gbm.h" +#include "mir/test/doubles/mock_egl.h" +#include "mir/test/doubles/mock_gl.h" +#include "mir/test/doubles/null_gl_config.h" +#include "mir/test/doubles/null_display_configuration_policy.h" +#include "mir_test_framework/udev_environment.h" + +#include +#include +#include +#include + +#include + +namespace mg = mir::graphics; +namespace mgg = mir::graphics::gbm; +namespace geom = mir::geometry; +namespace mtd = mir::test::doubles; +namespace mtf = mir_test_framework; +namespace mo = mir::options; + +class MesaBufferAllocatorTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + using namespace testing; + + fake_devices.add_standard_device("standard-drm-devices"); + + size = geom::Size{300, 200}; + pf = mir_pixel_format_argb_8888; + + ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) + .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), + SetArgPointee<4>(1), + Return(EGL_TRUE))); + + ON_CALL(mock_egl, eglGetConfigAttrib(_, mock_egl.fake_configs[0], EGL_NATIVE_VISUAL_ID, _)) + .WillByDefault( + DoAll( + SetArgPointee<3>(GBM_FORMAT_XRGB8888), + Return(EGL_TRUE))); + + // We sometimes want to copy the config of an existing context. + // Since our mocks don't check what config attribs we're asking for, + // we can just make something up. + ON_CALL(mock_egl, eglQueryContext(_,_,EGL_CONFIG_ID,_)) + .WillByDefault( + DoAll( + SetArgPointee<3>(0xabadbaab), + Return(EGL_TRUE))); + + mock_egl.provide_egl_extensions(); + mock_gl.provide_gles_extensions(); + + ON_CALL(mock_gbm, gbm_bo_get_handle(_)) + .WillByDefault(Return(mock_gbm.fake_gbm.bo_handle)); + + EGLDisplay dpy; + EGLContext ctx; + allocator = std::make_unique(dpy, ctx); + } + + // Defaults + geom::Size size; + MirPixelFormat pf; + + ::testing::NiceMock mock_drm; + ::testing::NiceMock mock_gbm; + ::testing::NiceMock mock_egl; + ::testing::NiceMock mock_gl; + std::shared_ptr platform; + std::unique_ptr display; + std::unique_ptr allocator; + mtf::UdevEnvironment fake_devices; +}; + +TEST_F(MesaBufferAllocatorTest, creates_software_buffer_without_utilizing_gbm) +{ + using namespace testing; + EXPECT_CALL(mock_gbm, gbm_bo_create(_,_,_,_,_)).Times(0); + allocator->alloc_software_buffer( { 1000, 1000}, mir_pixel_format_abgr_8888); +} + +TEST_F(MesaBufferAllocatorTest, supported_pixel_formats_contain_common_formats) +{ + auto supported_pixel_formats = allocator->supported_pixel_formats(); + + auto argb_8888_count = std::count(supported_pixel_formats.begin(), + supported_pixel_formats.end(), + mir_pixel_format_argb_8888); + + auto xrgb_8888_count = std::count(supported_pixel_formats.begin(), + supported_pixel_formats.end(), + mir_pixel_format_xrgb_8888); + + EXPECT_EQ(1, argb_8888_count); + EXPECT_EQ(1, xrgb_8888_count); +} + +TEST_F(MesaBufferAllocatorTest, supported_pixel_formats_have_sane_default_in_first_position) +{ + auto supported_pixel_formats = allocator->supported_pixel_formats(); + + ASSERT_FALSE(supported_pixel_formats.empty()); + EXPECT_EQ(mir_pixel_format_argb_8888, supported_pixel_formats[0]); +} + diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display.cpp index c54b016fd95..6ceb813b7ca 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include "mir/test/doubles/null_gl_config.h" #include "src/platforms/gbm-kms/server/kms/platform.h" #include "src/platforms/gbm-kms/server/kms/display.h" #include "src/platforms/gbm-kms/server/kms/quirks.h" @@ -22,7 +23,6 @@ #include "mir/logging/logger.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/default_display_configuration_policy.h" -#include "mir/renderer/gl/render_target.h" #include "mir/time/steady_clock.h" #include "mir/glib_main_loop.h" #include "mir/fatal.h" @@ -46,7 +46,6 @@ #include "mir/test/fake_shared.h" #include "mir/test/auto_unblock_thread.h" #include "mir/test/signal.h" -#include "mir/test/as_render_target.h" #include #include @@ -117,24 +116,55 @@ class MesaDisplayTest : public ::testing::Test std::shared_ptr create_platform() { + mir::udev::Context ctx; + // Caution: non-local state! + // This works because standard-drm-devices contains a udev device with 226:0 and devnode /dev/dri/card0 + auto device = ctx.char_device_from_devnum(makedev(226, 0)); + return std::make_shared( +<<<<<<< HEAD mir::report::null_display_report(), *std::make_shared(), *std::make_shared(), mgg::BypassOption::allowed, std::make_unique(mir::options::ProgramOption{})); +||||||| da02aa60d3 + mir::report::null_display_report(), + std::make_shared(), + *std::make_shared(), + mgg::BypassOption::allowed, + std::make_unique(mir::options::ProgramOption{})); +======= + *device, + mir::report::null_display_report(), + std::make_shared(), + *std::make_shared(), + mgg::BypassOption::allowed); +>>>>>>> new-platform-API } std::shared_ptr create_display( std::shared_ptr const& platform) { +<<<<<<< HEAD + return std::make_shared( + platform->drm, + platform->gbm, + platform->bypass_option(), +||||||| da02aa60d3 return std::make_shared( platform->drm, platform->gbm, + platform->vt, platform->bypass_option(), +======= + std::shared_ptr display = platform->create_display( +>>>>>>> new-platform-API std::make_shared(), - std::make_shared(), - null_report); + std::make_shared() + ); + + return std::dynamic_pointer_cast(display); } void setup_post_update_expectations() @@ -535,8 +565,8 @@ TEST_F(MesaDisplayTest, post_update) auto display = create_display(create_platform()); display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) { - group.for_each_display_buffer([](mg::DisplayBuffer& db) { - mt::as_render_target(db)->swap_buffers(); + group.for_each_display_buffer([](mg::DisplayBuffer&) { + // Do thing that submits framebuffer here }); group.post(); }); @@ -594,8 +624,8 @@ TEST_F(MesaDisplayTest, post_update_flip_failure) { auto display = create_display(create_platform()); display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) { - group.for_each_display_buffer([](mg::DisplayBuffer& db) { - mt::as_render_target(db)->swap_buffers(); + group.for_each_display_buffer([](mg::DisplayBuffer&) { + // Do whatever Framebuffer stuff is necessary… }); group.post(); }); @@ -631,12 +661,29 @@ TEST_F(MesaDisplayTest, successful_creation_of_display_reports_successful_setup_ auto platform = create_platform(); auto display = std::make_shared( +<<<<<<< HEAD platform->drm, platform->gbm, platform->bypass_option(), std::make_shared(), std::make_shared(), mock_report); +||||||| da02aa60d3 + platform->drm, + platform->gbm, + platform->vt, + platform->bypass_option(), + std::make_shared(), + std::make_shared(), + mock_report); +======= + nullptr, + platform->drm, + platform->vt, + platform->bypass_option(), + std::make_shared(), + mock_report); +>>>>>>> new-platform-API } TEST_F(MesaDisplayTest, outputs_correct_string_for_successful_setup_of_native_resources) @@ -803,6 +850,7 @@ TEST_F(MesaDisplayTest, drm_device_change_event_triggers_handler) EXPECT_EQ(expected_call_count, call_count); } +<<<<<<< HEAD TEST_F(MesaDisplayTest, respects_gl_config) { using namespace testing; @@ -859,6 +907,66 @@ TEST_F(MesaDisplayTest, respects_gl_config) null_report}; } +||||||| da02aa60d3 +TEST_F(MesaDisplayTest, respects_gl_config) +{ + using namespace testing; + + mtd::MockGLConfig mock_gl_config; + EGLint const depth_bits{24}; + EGLint const stencil_bits{8}; + + EXPECT_CALL(mock_gl_config, depth_buffer_bits()) + .Times(AtLeast(1)) + .WillRepeatedly(Return(depth_bits)); + EXPECT_CALL(mock_gl_config, stencil_buffer_bits()) + .Times(AtLeast(1)) + .WillRepeatedly(Return(stencil_bits)); + + // We create at least one rendering context, with the requested attributes… + EXPECT_CALL(mock_egl, + eglChooseConfig( + _, + AllOf(mtd::EGLConfigContainsAttrib(EGL_DEPTH_SIZE, depth_bits), + mtd::EGLConfigContainsAttrib(EGL_STENCIL_SIZE, stencil_bits)), + NotNull(),_,_)) + .Times(AtLeast(1)) + .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), + SetArgPointee<4>(1), + Return(EGL_TRUE))); + //…we *also* create zero-or-more non-rendering contexts; we don't care what they ask for + EXPECT_CALL(mock_egl, + eglChooseConfig( + _, + Pointee(EGL_NONE), + NotNull(),_,_)) + .Times(AnyNumber()) + .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), + SetArgPointee<4>(1), + Return(EGL_TRUE))); + /* We actually want the default behaviour here, but because we've made an + * EXPECT_CALL for eglChooseConfig GMock will ignore the ON_CALL behaviour + */ + EXPECT_CALL(mock_egl, eglChooseConfig(_,_,nullptr,_,_)) + .Times(AnyNumber()) + .WillRepeatedly( + DoAll( + SetArgPointee<4>(1), + Return(EGL_TRUE))); + + auto platform = create_platform(); + mgg::Display display{ + platform->drm, + platform->gbm, + platform->vt, + platform->bypass_option(), + std::make_shared(), + mir::test::fake_shared(mock_gl_config), + null_report}; +} + +======= +>>>>>>> new-platform-API TEST_F(MesaDisplayTest, uses_xrgb8888_framebuffer_when_argb8888_is_not_supported_by_EGL) { using namespace testing; @@ -878,11 +986,18 @@ TEST_F(MesaDisplayTest, uses_xrgb8888_framebuffer_when_argb8888_is_not_supported auto platform = create_platform(); mgg::Display display{ + {}, // Hopefully DisplayPlatform{nullptr} is enough for now? platform->drm, +<<<<<<< HEAD platform->gbm, +||||||| da02aa60d3 + platform->gbm, + platform->vt, +======= + platform->vt, +>>>>>>> new-platform-API platform->bypass_option(), std::make_shared(), - std::make_shared>(), null_report}; } @@ -905,11 +1020,18 @@ TEST_F(MesaDisplayTest, uses_argb8888_framebuffer_when_xrgb8888_is_not_supported auto platform = create_platform(); mgg::Display display{ + {}, // Hopefully DisplayPlatform{nullptr} is enough for now? platform->drm, +<<<<<<< HEAD + platform->gbm, +||||||| da02aa60d3 platform->gbm, + platform->vt, +======= + platform->vt, +>>>>>>> new-platform-API platform->bypass_option(), std::make_shared(), - std::make_shared>(), null_report}; } diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp index 8d677202f82..c392f03ac48 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp @@ -29,6 +29,7 @@ #include "mir/graphics/transformation.h" #include "mock_kms_output.h" +#include #include #include #include @@ -53,6 +54,17 @@ class MockDMABufBuffer : public DMABufBuffer MOCK_CONST_METHOD0(planes, std::vector const&()); MOCK_CONST_METHOD0(size, geometry::Size()); }; + +class MockKMSFramebuffer : public FBHandle +{ +public: + MOCK_METHOD(uint32_t, operator_uint32_thunk, (), (const override)); + + operator uint32_t() const override + { + return operator_uint32_thunk(); + } +}; } class MesaDisplayBufferTest : public Test @@ -68,7 +80,17 @@ class MesaDisplayBufferTest : public Test std::make_shared(display_area)} , fake_software_renderable{ std::make_shared(display_area)} - , bypassable_list{fake_bypassable_renderable} + , bypass_framebuffer{std::make_shared>()} + , bypassable_list{ + mir::graphics::DisplayElement{ + display_area, + { + {0, 0}, + {display_area.size.width.as_value(), display_area.size.height.as_value()} + }, + bypass_framebuffer} + } + , parent_platform{nullptr} { ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), @@ -88,6 +110,7 @@ class MesaDisplayBufferTest : public Test .WillByDefault(Return(456)); fake_devices.add_standard_device("standard-drm-devices"); + drm_fd = mir::Fd{::open("/dev/dri/card0", O_RDWR | O_CLOEXEC)}; mock_kms_output = std::make_shared>(); ON_CALL(*mock_kms_output, set_crtc_thunk(_)) @@ -124,22 +147,11 @@ class MesaDisplayBufferTest : public Test } protected: - GBMOutputSurface make_output_surface() - { - helpers::EGLHelper egl{gl_config}; - return GBMOutputSurface{ - mir::Fd{} , - GBMSurfaceUPtr{nullptr}, - static_cast(width), - static_cast(height), - std::move(egl) - }; - } - int const width{56}; int const height{78}; mir::geometry::Rectangle const display_area{{12,34}, {width,height}}; glm::mat2 const identity; + mir::Fd drm_fd; NiceMock mock_gbm; NiceMock mock_egl; NiceMock mock_gl; @@ -154,79 +166,41 @@ class MesaDisplayBufferTest : public Test UdevEnvironment fake_devices; std::shared_ptr mock_kms_output; StubGLConfig gl_config; - mir::graphics::RenderableList const bypassable_list; + std::shared_ptr const bypass_framebuffer; + std::vector const bypassable_list; + std::shared_ptr const parent_platform; }; TEST_F(MesaDisplayBufferTest, unrotated_view_area_is_untouched) { graphics::gbm::DisplayBuffer db( + parent_platform, + drm_fd, graphics::gbm::BypassOption::allowed, null_display_report(), {mock_kms_output}, - make_output_surface(), display_area, identity); EXPECT_EQ(display_area, db.view_area()); } -TEST_F(MesaDisplayBufferTest, bypass_buffer_is_held_for_full_frame) -{ - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - identity); - - auto original_count = mock_bypassable_buffer.use_count(); - - EXPECT_TRUE(db.overlay(bypassable_list)); - EXPECT_EQ(original_count+1, mock_bypassable_buffer.use_count()); - - // Switch back to normal compositing - db.make_current(); - db.swap_buffers(); - db.post(); - - // Bypass buffer should no longer be held by db - EXPECT_EQ(original_count, mock_bypassable_buffer.use_count()); -} - -TEST_F(MesaDisplayBufferTest, predictive_bypass_is_throttled) -{ - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - identity); - - for (int frame = 0; frame < 5; ++frame) - { - ASSERT_TRUE(db.overlay(bypassable_list)); - db.post(); - - // Cast to a simple int type so that test failures are readable - int milliseconds_per_frame = 1000 / mock_refresh_rate; - ASSERT_THAT(db.recommended_sleep().count(), - Ge(milliseconds_per_frame/2)); - } -} - TEST_F(MesaDisplayBufferTest, frames_requiring_gl_are_not_throttled) { - graphics::RenderableList non_bypassable_list{ - std::make_shared(geometry::Rectangle{{12, 34}, {1, 1}}) + std::vector const non_bypassable_list{ + { + {{12, 34}, {1, 1}}, + {{12, 34}, {1, 1}}, + nullptr + } }; graphics::gbm::DisplayBuffer db( + parent_platform, + drm_fd, graphics::gbm::BypassOption::allowed, null_display_report(), {mock_kms_output}, - make_output_surface(), display_area, identity); @@ -240,34 +214,14 @@ TEST_F(MesaDisplayBufferTest, frames_requiring_gl_are_not_throttled) } } -TEST_F(MesaDisplayBufferTest, bypass_buffer_only_referenced_once_by_db) -{ - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - identity); - - auto original_count = mock_bypassable_buffer.use_count(); - - EXPECT_TRUE(db.overlay(bypassable_list)); - EXPECT_EQ(original_count+1, mock_bypassable_buffer.use_count()); - - db.post(); - - // Bypass buffer still held by DB only one ref above the original - EXPECT_EQ(original_count+1, mock_bypassable_buffer.use_count()); -} - TEST_F(MesaDisplayBufferTest, untransformed_with_bypassable_list_can_bypass) { graphics::gbm::DisplayBuffer db( + parent_platform, + drm_fd, graphics::gbm::BypassOption::allowed, null_display_report(), {mock_kms_output}, - make_output_surface(), display_area, identity); @@ -283,201 +237,19 @@ auto fake_shared_ptr(intptr_t value) -> std::shared_ptr } } -TEST_F(MesaDisplayBufferTest, failed_bypass_falls_back_gracefully) -{ // Regression test for LP: #1398296 - EXPECT_CALL(*mock_kms_output, fb_for(A())) - .WillOnce(Return(fake_shared_ptr(0xaabb))); // During the DisplayBuffer constructor - EXPECT_CALL(*mock_kms_output, fb_for(A())) - .WillOnce(Return(nullptr)) // Fail first bypass attempt - .WillOnce(Return(fake_shared_ptr(0xbbcc))); // Succeed second bypass attempt - - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - identity); - - EXPECT_FALSE(db.overlay(bypassable_list)); - // And then we recover. DRM finds enough resources to AddFB ... - EXPECT_TRUE(db.overlay(bypassable_list)); -} - -TEST_F(MesaDisplayBufferTest, skips_bypass_because_of_lagging_resize) -{ // Another regression test for LP: #1398296 - auto fullscreen = std::make_shared(display_area); - auto nonbypassable = std::make_shared>(); - ON_CALL(*nonbypassable, native_buffer_base()) - .WillByDefault(Return(&mock_dmabuf_buffer)); - ON_CALL(*nonbypassable, size()) - .WillByDefault(Return(mir::geometry::Size{12,34})); - - fullscreen->set_buffer(nonbypassable); - graphics::RenderableList list{fullscreen}; - - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - identity); - - EXPECT_FALSE(db.overlay(list)); -} - -TEST_F(MesaDisplayBufferTest, rotated_cannot_bypass) -{ - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - transformation(mir_orientation_right)); - - EXPECT_FALSE(db.overlay(bypassable_list)); -} - -TEST_F(MesaDisplayBufferTest, fullscreen_software_buffer_cannot_bypass) -{ - graphics::RenderableList const list{fake_software_renderable}; - - // Passes the bypass candidate test: - EXPECT_EQ(fake_software_renderable->buffer()->size(), display_area.size); - - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - identity); - - EXPECT_FALSE(db.overlay(list)); -} - -TEST_F(MesaDisplayBufferTest, fullscreen_software_buffer_not_used_as_gbm_bo) -{ // Also checks it doesn't crash (LP: #1493721) - graphics::RenderableList const list{fake_software_renderable}; - - // Passes the bypass candidate test: - EXPECT_EQ(fake_software_renderable->buffer()->size(), display_area.size); - - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - identity); - - // If you find yourself using gbm_ functions on a Shm buffer then you're - // asking for a crash (LP: #1493721) ... - EXPECT_CALL(mock_gbm, gbm_bo_get_user_data(_)).Times(0); - db.overlay(list); -} - TEST_F(MesaDisplayBufferTest, transformation_not_implemented_internally) { glm::mat2 const rotate_left = transformation(mir_orientation_left); graphics::gbm::DisplayBuffer db( + parent_platform, + drm_fd, graphics::gbm::BypassOption::allowed, null_display_report(), {mock_kms_output}, - make_output_surface(), display_area, rotate_left); EXPECT_EQ(rotate_left, db.transformation()); } -TEST_F(MesaDisplayBufferTest, clone_mode_first_flip_flips_but_no_wait) -{ - // Ensure clone mode can do multiple page flips in parallel without - // blocking on either (at least till the second post) - EXPECT_CALL(*mock_kms_output, schedule_page_flip_thunk(_)) - .Times(2); - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) - .Times(0); - - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output, mock_kms_output}, - make_output_surface(), - display_area, - identity); - - db.swap_buffers(); - db.post(); -} - -TEST_F(MesaDisplayBufferTest, single_mode_first_post_flips_with_wait) -{ - EXPECT_CALL(*mock_kms_output, schedule_page_flip_thunk(_)) - .Times(1); - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) - .Times(1); - - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - identity); - - db.swap_buffers(); - db.post(); -} - -TEST_F(MesaDisplayBufferTest, clone_mode_waits_for_page_flip_on_second_flip) -{ - InSequence seq; - - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) - .Times(0); - EXPECT_CALL(*mock_kms_output, schedule_page_flip_thunk(_)) - .Times(2); - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) - .Times(2); - EXPECT_CALL(*mock_kms_output, schedule_page_flip_thunk(_)) - .Times(2); - EXPECT_CALL(*mock_kms_output, wait_for_page_flip()) - .Times(0); - - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output, mock_kms_output}, - make_output_surface(), - display_area, - identity); - - db.swap_buffers(); - db.post(); - - db.swap_buffers(); - db.post(); -} - -TEST_F(MesaDisplayBufferTest, skips_bypass_because_of_incompatible_list) -{ - graphics::RenderableList list{ - std::make_shared(display_area), - std::make_shared(geometry::Rectangle{{12, 34}, {1, 1}}) - }; - - graphics::gbm::DisplayBuffer db( - graphics::gbm::BypassOption::allowed, - null_display_report(), - {mock_kms_output}, - make_output_surface(), - display_area, - identity); - - EXPECT_FALSE(db.overlay(list)); -} diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp index eb3657fea24..52d24f43047 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp @@ -124,12 +124,17 @@ class MesaDisplayConfigurationTest : public ::testing::Test std::shared_ptr create_platform() { + mir::udev::Context ctx; + // Caution: non-local state! + // This works because standard-drm-devices contains a udev device with 226:0 and devnode /dev/dri/card0 + auto device = ctx.char_device_from_devnum(makedev(226, 0)); + return std::make_shared( - mir::report::null_display_report(), - *std::make_shared(), - *std::make_shared(), - mgg::BypassOption::allowed, - std::make_unique(mir::options::ProgramOption{})); + *device, + mir::report::null_display_report(), + *std::make_shared(), + *std::make_shared(), + mgg::BypassOption::allowed); } std::shared_ptr create_display( diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp index 184a44c5a23..d611b7aac78 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp @@ -83,12 +83,17 @@ class DisplayTestGeneric : public ::testing::Test std::shared_ptr create_display() { - auto const platform = std::make_shared( - mir::report::null_display_report(), - *std::make_shared(), - *std::make_shared(), - mgg::BypassOption::allowed, - std::make_unique(mir::options::ProgramOption{})); + mir::udev::Context ctx; + // Caution: non-local state! + // This works because standard-drm-devices contains a udev device with 226:0 and devnode /dev/dri/card0 + auto device = ctx.char_device_from_devnum(makedev(226, 0)); + + auto platform = std::make_shared( + *device, + mir::report::null_display_report(), + *std::make_shared(), + *std::make_shared(), + mgg::BypassOption::allowed); return platform->create_display( std::make_shared(), std::make_shared()); diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp index 0e08fb535f8..82f676eee71 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp @@ -147,12 +147,17 @@ class MesaDisplayMultiMonitorTest : public ::testing::Test std::shared_ptr create_platform() { + mir::udev::Context ctx; + // Caution: non-local state! + // This works because standard-drm-devices contains a udev device with 226:0 and devnode /dev/dri/card0 + auto device = ctx.char_device_from_devnum(makedev(226, 0)); + return std::make_shared( - mir::report::null_display_report(), - *std::make_shared(), - *std::make_shared(), - mgg::BypassOption::allowed, - std::make_unique(mir::options::ProgramOption{})); + *device, + mir::report::null_display_report(), + *std::make_shared(), + *std::make_shared(), + mgg::BypassOption::allowed); } std::shared_ptr create_display_cloned( @@ -301,39 +306,6 @@ TEST_F(MesaDisplayMultiMonitorTest, create_display_sets_all_connected_crtcs) auto display = create_display_cloned(create_platform()); } -TEST_F(MesaDisplayMultiMonitorTest, create_display_creates_shared_egl_contexts) -{ - using namespace testing; - - int const num_connected_outputs{3}; - int const num_disconnected_outputs{2}; - EGLContext const shared_context{reinterpret_cast(0x77)}; - - setup_outputs(num_connected_outputs, num_disconnected_outputs); - - /* Will create only one shared context */ - EXPECT_CALL(mock_egl, eglCreateContext(_, _, EGL_NO_CONTEXT, _)) - .WillOnce(Return(shared_context)); - - /* Will use the shared context when creating other contexts */ - EXPECT_CALL(mock_egl, eglCreateContext(_, _, shared_context, _)) - .Times(AtLeast(1)); - - { - InSequence s; - - /* Contexts are made current to initialize DisplayBuffers */ - EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,Ne(shared_context))) - .Times(AtLeast(1)); - - /* Contexts are released at teardown */ - EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,EGL_NO_CONTEXT)) - .Times(AtLeast(1)); - } - - auto display = create_display_cloned(create_platform()); -} - namespace { @@ -384,20 +356,38 @@ TEST_F(MesaDisplayMultiMonitorTest, flip_flips_all_connected_crtcs) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[1]), Return(0))) .WillOnce(DoAll(InvokePageFlipHandler(&user_data[2]), Return(0))); - auto display = create_display_cloned(create_platform()); + auto platform = create_platform(); + auto display = create_display_cloned(platform); + auto allocator_provider = mg::DisplayPlatform::acquire_interface(std::move(platform)); /* First frame: Page flips are scheduled, but not waited for */ - display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) - { - group.post(); - }); + display->for_each_display_sync_group( + [allocator_provider](mg::DisplaySyncGroup& group) + { + group.for_each_display_buffer( + [allocator_provider](mg::DisplayBuffer& db) + { + auto allocator = allocator_provider->allocator_for_db(db); + auto fb = allocator->acquire(); + db.set_next_image(std::move(fb)); + }); + group.post(); + }); /* Second frame: Previous page flips finish (drmHandleEvent) and new ones are scheduled */ - display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) - { - group.post(); - }); + display->for_each_display_sync_group( + [allocator_provider](mg::DisplaySyncGroup& group) + { + group.for_each_display_buffer( + [allocator_provider](mg::DisplayBuffer& db) + { + auto allocator = allocator_provider->allocator_for_db(db); + auto fb = allocator->acquire(); + db.set_next_image(std::move(fb)); + }); + group.post(); + }); } namespace diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_graphics_platform.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_graphics_platform.cpp index 63646011cf7..06c895e5eab 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_graphics_platform.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_graphics_platform.cpp @@ -80,12 +80,17 @@ class GraphicsPlatform : public ::testing::Test std::shared_ptr create_platform() { - return std::make_shared( - mir::report::null_display_report(), - std::make_shared(), - *std::make_shared(), - mgg::BypassOption::allowed, - std::make_unique(mtd::MockOption{})); + mir::udev::Context ctx; + // Caution: non-local state! + // This works because standard-drm-devices contains a udev device with 226:0 and devnode /dev/dri/card0 + auto device = ctx.char_device_from_devnum(makedev(226, 0)); + + return std::make_shared( + *device, + mir::report::null_display_report(), + std::make_shared(), + *std::make_shared(), + mgg::BypassOption::allowed); } std::shared_ptr logger; diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_platform.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_platform.cpp index c30671a1e10..fd5a84d2016 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_platform.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_platform.cpp @@ -15,6 +15,7 @@ */ #include "mir/graphics/event_handler_register.h" +#include "platform_common.h" #include "src/platforms/gbm-kms/server/kms/platform.h" #include "src/platforms/gbm-kms/server/kms/quirks.h" #include "src/server/report/null_report_factory.h" @@ -76,16 +77,6 @@ class MesaGraphicsPlatform : public ::testing::Test fake_devices.add_standard_device("standard-drm-devices"); } - std::shared_ptr create_platform() - { - return std::make_shared( - mir::report::null_display_report(), - *std::make_shared(), - *std::make_shared(), - mgg::BypassOption::allowed, - std::make_unique(mir::options::ProgramOption{})); - } - auto parsed_options_from_args( std::initializer_list const& options, boost::program_options::options_description const& description) const @@ -114,12 +105,33 @@ TEST_F(MesaGraphicsPlatform, a_failure_while_creating_a_platform_results_in_an_e { using namespace ::testing; + mtf::UdevEnvironment udev_environment; + boost::program_options::options_description po; + mir::options::ProgramOption options; + auto const stub_vt = std::make_shared(); + auto const udev = std::make_shared(); + + udev_environment.add_standard_device("standard-drm-devices"); + + mir::SharedLibrary platform_lib{mtf::server_platform("graphics-gbm-kms")}; + auto probe = platform_lib.load_function(display_platform_probe_symbol); + auto supported_devices = probe(stub_vt, udev, options); + EXPECT_CALL(mock_drm, open(_,_)) - .WillRepeatedly(SetErrnoAndReturn(EINVAL, -1)); + .WillRepeatedly(SetErrnoAndReturn(EINVAL, -1)); try { - auto platform = create_platform(); + for (auto& device : supported_devices) + { + mgg::Platform{ + *device.device, + mir::report::null_display_report(), + *std::make_shared(), + *std::make_shared(), + mgg::BypassOption::allowed + }; + } } catch(std::exception const&) { return; diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_real_kms_output.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_real_kms_output.cpp index fdf6abf7886..ab8c8665b9f 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_real_kms_output.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_real_kms_output.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ +#include "kms/kms_framebuffer.h" #include "src/platforms/gbm-kms/server/kms/real_kms_output.h" #include "src/platforms/gbm-kms/server/kms/page_flipper.h" #include "mir/fatal.h" @@ -54,6 +55,23 @@ class MockPageFlipper : public mgg::PageFlipper MOCK_METHOD1(wait_for_flip, mg::Frame(uint32_t)); }; +class MockKMSFramebuffer : public mgg::FBHandle +{ +public: + MockKMSFramebuffer(uint32_t fb_id) + : fb_id{fb_id} + { + } + + operator uint32_t() const override + { + return fb_id; + } + +private: + uint32_t const fb_id; +}; + class RealKMSOutputTest : public ::testing::Test { public: @@ -164,7 +182,6 @@ class RealKMSOutputTest : public ::testing::Test char const* const drm_device = "/dev/dri/card0"; int const drm_fd; - gbm_bo* const fake_bo{reinterpret_cast(0x123ba)}; uint32_t const invalid_id; std::vector const crtc_ids; std::vector const encoder_ids; @@ -182,7 +199,7 @@ TEST_F(RealKMSOutputTest, operations_use_existing_crtc) setup_outputs_connected_crtc(); uint32_t const fb_id{42}; - append_fb_id(fb_id); + auto const fb = std::make_shared(fb_id); { InSequence s; @@ -209,8 +226,6 @@ TEST_F(RealKMSOutputTest, operations_use_existing_crtc) mg::kms::get_connector(drm_fd, connector_ids[0]), mt::fake_shared(mock_page_flipper)}; - auto fb = output.fb_for(fake_bo); - EXPECT_TRUE(output.set_crtc(*fb)); EXPECT_TRUE(output.schedule_page_flip(*fb)); output.wait_for_page_flip(); @@ -221,6 +236,7 @@ TEST_F(RealKMSOutputTest, operations_use_possible_crtc) using namespace testing; uint32_t const fb_id{67}; + auto const fb = std::make_shared(fb_id); setup_outputs_no_connected_crtc(); @@ -244,15 +260,11 @@ TEST_F(RealKMSOutputTest, operations_use_possible_crtc) .Times(1); } - append_fb_id(fb_id); - mgg::RealKMSOutput output{ drm_fd, mg::kms::get_connector(drm_fd, connector_ids[0]), mt::fake_shared(mock_page_flipper)}; - auto fb = output.fb_for(fake_bo); - EXPECT_TRUE(output.set_crtc(*fb)); EXPECT_TRUE(output.schedule_page_flip(*fb)); output.wait_for_page_flip(); @@ -264,6 +276,7 @@ TEST_F(RealKMSOutputTest, set_crtc_failure_is_handled_gracefully) using namespace testing; uint32_t const fb_id{67}; + auto const fb = std::make_shared(fb_id); setup_outputs_connected_crtc(); @@ -284,15 +297,11 @@ TEST_F(RealKMSOutputTest, set_crtc_failure_is_handled_gracefully) .Times(0); } - append_fb_id(fb_id); - mgg::RealKMSOutput output{ drm_fd, mg::kms::get_connector(drm_fd, connector_ids[0]), mt::fake_shared(mock_page_flipper)}; - auto fb = output.fb_for(fake_bo); - EXPECT_FALSE(output.set_crtc(*fb)); EXPECT_NO_THROW({ @@ -372,7 +381,7 @@ TEST_F(RealKMSOutputTest, cursor_move_permission_failure_is_non_fatal) mg::kms::get_connector(drm_fd, connector_ids[0]), mt::fake_shared(mock_page_flipper)}; - auto fb = output.fb_for(fake_bo); + auto const fb = std::make_shared(4); EXPECT_TRUE(output.set_crtc(*fb)); EXPECT_NO_THROW({ @@ -400,7 +409,7 @@ TEST_F(RealKMSOutputTest, cursor_set_permission_failure_is_non_fatal) mg::kms::get_connector(drm_fd, connector_ids[0]), mt::fake_shared(mock_page_flipper)}; - auto fb = output.fb_for(fake_bo); + auto const fb = std::make_shared(0x42); EXPECT_TRUE(output.set_crtc(*fb)); struct gbm_bo *dummy = reinterpret_cast(0x1234567); @@ -429,7 +438,7 @@ TEST_F(RealKMSOutputTest, has_no_cursor_if_no_hardware_support) mg::kms::get_connector(drm_fd, connector_ids[0]), mt::fake_shared(mock_page_flipper)}; - auto fb = output.fb_for(fake_bo); + auto const fb = std::make_shared(42); EXPECT_TRUE(output.set_crtc(*fb)); struct gbm_bo *dummy = reinterpret_cast(0x1234567); @@ -507,9 +516,7 @@ TEST_F(RealKMSOutputTest, drm_set_gamma) const_cast(gamma.blue.data()))) .Times(1); - append_fb_id(fb_id); - - auto fb = output.fb_for(fake_bo); + auto const fb = std::make_shared(fb_id); EXPECT_TRUE(output.set_crtc(*fb)); @@ -538,9 +545,7 @@ TEST_F(RealKMSOutputTest, drm_set_gamma_failure_does_not_throw) const_cast(gamma.blue.data()))) .WillOnce(Return(-ENOSYS)); - append_fb_id(fb_id); - - auto fb = output.fb_for(fake_bo); + auto const fb = std::make_shared(fb_id); EXPECT_TRUE(output.set_crtc(*fb)); diff --git a/tests/unit-tests/platforms/test_display.h b/tests/unit-tests/platforms/test_display.h index 613a9a95cfa..cf34a330354 100644 --- a/tests/unit-tests/platforms/test_display.h +++ b/tests/unit-tests/platforms/test_display.h @@ -22,17 +22,6 @@ #include "mir/renderer/gl/context_source.h" #include "mir/test/display_config_matchers.h" -namespace -{ -auto as_context_source(mg::Display* display) -{ - auto const ctx = dynamic_cast(display); - if (!ctx) - BOOST_THROW_EXCEPTION(std::logic_error("Display does not support GL rendering")); - return ctx; -} -} - TEST_F(DisplayTestGeneric, configure_disallows_invalid_configuration) { using namespace testing; @@ -48,77 +37,6 @@ TEST_F(DisplayTestGeneric, configure_disallows_invalid_configuration) // platform-dependent exercise, so won't be tested here. } -#ifdef MIR_DISABLE_TESTS_ON_X11 -TEST_F(DisplayTestGeneric, DISABLED_gl_context_make_current_uses_shared_context) -#else -TEST_F(DisplayTestGeneric, gl_context_make_current_uses_shared_context) -#endif -{ - using namespace testing; - EGLContext const shared_context{reinterpret_cast(0x111)}; - EGLContext const display_buffer_context{reinterpret_cast(0x222)}; - EGLContext const new_context{reinterpret_cast(0x333)}; - - EXPECT_CALL(mock_egl, eglCreateContext(_,_,EGL_NO_CONTEXT,_)) - .WillOnce(Return(shared_context)); - EXPECT_CALL(mock_egl, eglCreateContext(_,_,shared_context,_)) - .WillOnce(Return(display_buffer_context)); - - auto display = create_display(); - - Mock::VerifyAndClearExpectations(&mock_egl); - - { - InSequence s; - EXPECT_CALL(mock_egl, eglCreateContext(_,_,shared_context,_)) - .WillOnce(Return(new_context)); - EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,new_context)); - EXPECT_CALL(mock_egl, eglGetCurrentContext()) - .WillOnce(Return(new_context)); - EXPECT_CALL(mock_egl, eglMakeCurrent(_,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)); - - auto const gl_ctx = as_context_source(display.get())->create_gl_context(); - - ASSERT_NE(nullptr, gl_ctx); - - gl_ctx->make_current(); - } - - Mock::VerifyAndClearExpectations(&mock_egl); - - /* Possible display shutdown sequence, depending on the platform */ - EXPECT_CALL(mock_egl, eglGetCurrentContext()) - .Times(AtLeast(0)); - EXPECT_CALL(mock_egl, eglMakeCurrent(_,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)) - .Times(AtLeast(0)); -} - -TEST_F(DisplayTestGeneric, gl_context_releases_context) -{ - using namespace testing; - - auto display = create_display(); - - { - InSequence s; - EXPECT_CALL(mock_egl, eglMakeCurrent(_,_,_,Ne(EGL_NO_CONTEXT))); - EXPECT_CALL(mock_egl, eglMakeCurrent(_,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)); - - auto const gl_ctx = as_context_source(display.get())->create_gl_context(); - - ASSERT_NE(nullptr, gl_ctx); - - gl_ctx->make_current(); - gl_ctx->release_current(); - - Mock::VerifyAndClearExpectations(&mock_egl); - } - - /* Possible display shutdown sequence, depending on the platform */ - EXPECT_CALL(mock_egl, eglMakeCurrent(_,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT)) - .Times(AtLeast(0)); -} - #ifdef MIR_DISABLE_TESTS_ON_X11 TEST_F(DisplayTestGeneric, DISABLED_does_not_expose_display_buffer_for_output_with_power_mode_off) #else diff --git a/tests/unit-tests/platforms/test_graphics_platform.h b/tests/unit-tests/platforms/test_graphics_platform.h deleted file mode 100644 index 1ed3f415216..00000000000 --- a/tests/unit-tests/platforms/test_graphics_platform.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef TEST_GRAPHICS_PLATFORM_H_ -#define TEST_GRAPHICS_PLATFORM_H_ - -#include "mir/graphics/display_configuration_policy.h" -#include "mir/graphics/display.h" - -#include "mir/test/doubles/stub_gl_config.h" -#include "mir/test/doubles/null_display_configuration_policy.h" - -namespace mtd = mir::test::doubles; - -TEST_F(GraphicsPlatform, buffer_allocator_creation) -{ - using namespace testing; - - EXPECT_NO_THROW ( - auto platform = create_platform(); - auto display = platform->create_display( - std::make_shared(), - std::make_shared()); - auto allocator = platform->create_buffer_allocator(*display); - - EXPECT_TRUE(allocator.get()); - ); -} - -TEST_F(GraphicsPlatform, buffer_creation) -{ - auto platform = create_platform(); - auto display = platform->create_display( - std::make_shared(), - std::make_shared()); - auto allocator = platform->create_buffer_allocator(*display); - auto supported_pixel_formats = allocator->supported_pixel_formats(); - - ASSERT_NE(0u, supported_pixel_formats.size()); - - geom::Size size{320, 240}; - MirPixelFormat const pf{supported_pixel_formats[0]}; - mg::BufferUsage usage{mg::BufferUsage::hardware}; - mg::BufferProperties buffer_properties{size, pf, usage}; - - auto buffer = allocator->alloc_software_buffer(size, pf); - - ASSERT_TRUE(buffer.get() != NULL); - - EXPECT_EQ(buffer->size(), size); - EXPECT_EQ(buffer->pixel_format(), pf); -} - -#endif // TEST_GRAPHICS_PLATFORM_H_ diff --git a/tests/unit-tests/platforms/x11/CMakeLists.txt b/tests/unit-tests/platforms/x11/CMakeLists.txt index b5af5ab79d1..a3e727633af 100644 --- a/tests/unit-tests/platforms/x11/CMakeLists.txt +++ b/tests/unit-tests/platforms/x11/CMakeLists.txt @@ -1,15 +1,13 @@ mir_add_wrapped_executable(mir_unit_tests_x11 NOINSTALL ${CMAKE_CURRENT_SOURCE_DIR}/test_platform.cpp -# ${CMAKE_CURRENT_SOURCE_DIR}/test_graphics_platform.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/test_display_generic.cpp $ $ $ # Sub-optimal. We really want to link a lib ) set_property( - SOURCE test_platform.cpp test_graphics_platform.cpp test_display_generic.cpp + SOURCE test_platform.cpp PROPERTY COMPILE_OPTIONS -Wno-variadic-macros) add_dependencies(mir_unit_tests_x11 GMock) diff --git a/tests/unit-tests/platforms/x11/test_display.cpp b/tests/unit-tests/platforms/x11/test_display.cpp index f115154f08f..5f95e4232bc 100644 --- a/tests/unit-tests/platforms/x11/test_display.cpp +++ b/tests/unit-tests/platforms/x11/test_display.cpp @@ -17,6 +17,7 @@ #include #include +#include "mir/graphics/platform.h" #include "src/platforms/x11/graphics/display.h" #include "src/platforms/x11/graphics/platform.h" #include "src/server/report/null/display_report.h" @@ -100,11 +101,11 @@ class X11DisplayTest : public ::testing::Test std::shared_ptr create_display() { return std::make_shared( + nullptr, mt::fake_shared(x11_resources), "Mir on X", sizes, mt::fake_shared(null_display_configuration_policy), - mt::fake_shared(mock_gl_config), std::make_shared()); } @@ -112,34 +113,11 @@ class X11DisplayTest : public ::testing::Test mtd::NullDisplayConfigurationPolicy null_display_configuration_policy; ::testing::NiceMock mock_egl; ::testing::NiceMock mock_x11; - mtd::MockGLConfig mock_gl_config; + ::testing::NiceMock mock_gl_config; }; } -TEST_F(X11DisplayTest, respects_gl_config) -{ - EGLint const depth_bits{24}; - EGLint const stencil_bits{8}; - - EXPECT_CALL(mock_gl_config, depth_buffer_bits()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(depth_bits)); - EXPECT_CALL(mock_gl_config, stencil_buffer_bits()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(stencil_bits)); - - EXPECT_CALL(mock_egl, - eglChooseConfig( - _, - AllOf(mtd::EGLConfigContainsAttrib(EGL_DEPTH_SIZE, depth_bits), - mtd::EGLConfigContainsAttrib(EGL_STENCIL_SIZE, stencil_bits)), - _,_,_)) - .Times(AtLeast(1)); - - auto display = create_display(); -} - TEST_F(X11DisplayTest, calculates_physical_size_of_display_based_on_default_screen) { auto const pixel = geom::Size{2880, 1800}; diff --git a/tests/unit-tests/platforms/x11/test_display_generic.cpp b/tests/unit-tests/platforms/x11/test_display_generic.cpp deleted file mode 100644 index 8489f318120..00000000000 --- a/tests/unit-tests/platforms/x11/test_display_generic.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "mir/graphics/display.h" -#include "mir/graphics/display_configuration.h" - -#include "mir/test/doubles/mock_egl.h" -#include "mir/test/doubles/mock_gl.h" -#include "mir/test/doubles/stub_gl_config.h" -#include "mir/test/doubles/null_emergency_cleanup.h" -#include "src/server/report/null/display_report.h" -#include "mir/graphics/default_display_configuration_policy.h" -#include "src/platforms/x11/graphics/platform.h" -#include "src/platforms/x11/x11_resources.h" -#include "mir/test/doubles/mock_x11_resources.h" -#include "mir/test/doubles/mock_x11.h" - -#include -#include - -namespace mg = mir::graphics; -namespace mtd = mir::test::doubles; - -class DisplayTestGeneric : public ::testing::Test -{ -public: - DisplayTestGeneric() - { - using namespace testing; - - EGLint const client_version = 2; - - ON_CALL(mock_egl, eglQueryContext(mock_egl.fake_egl_display, - mock_egl.fake_egl_context, - EGL_CONTEXT_CLIENT_VERSION, - _)) - .WillByDefault(DoAll(SetArgPointee<3>(client_version), - Return(EGL_TRUE))); - - ON_CALL(mock_egl, eglQuerySurface(mock_egl.fake_egl_display, - mock_egl.fake_egl_surface, - EGL_WIDTH, - _)) - .WillByDefault(DoAll(SetArgPointee<3>(1280), - Return(EGL_TRUE))); - - ON_CALL(mock_egl, eglQuerySurface(mock_egl.fake_egl_display, - mock_egl.fake_egl_surface, - EGL_HEIGHT, - _)) - .WillByDefault(DoAll(SetArgPointee<3>(1024), - Return(EGL_TRUE))); - - ON_CALL(mock_egl, eglGetConfigAttrib(mock_egl.fake_egl_display, - _, - _, - _)) - .WillByDefault(DoAll(SetArgPointee<3>(EGL_WINDOW_BIT), - Return(EGL_TRUE))); - - ON_CALL(mock_x11, XNextEvent(mock_x11.fake_x11.display, - _)) - .WillByDefault(DoAll(SetArgPointee<1>(mock_x11.fake_x11.expose_event_return), - Return(1))); - - ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) - .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), - SetArgPointee<4>(1), - Return(EGL_TRUE))); - - mock_egl.provide_egl_extensions(); - mock_gl.provide_gles_extensions(); - } - - std::shared_ptr create_display() - { - auto const platform = std::make_shared( - std::make_shared(), - "Mir on X", - std::vector{{{1280, 1024}}}, - std::make_shared()); - return platform->create_display( - std::make_shared(), - std::make_shared()); - } - - ::testing::NiceMock mock_egl; - ::testing::NiceMock mock_gl; - ::testing::NiceMock mock_x11; -}; - -#define MIR_DISABLE_TESTS_ON_X11 -#include "../test_display.h" -#undef MIR_DISABLE_TESTS_ON_X11 diff --git a/tests/unit-tests/platforms/x11/test_graphics_platform.cpp b/tests/unit-tests/platforms/x11/test_graphics_platform.cpp deleted file mode 100644 index 83453ed0438..00000000000 --- a/tests/unit-tests/platforms/x11/test_graphics_platform.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "mir/graphics/platform.h" -#include "mir/graphics/graphic_buffer_allocator.h" -#include "mir/graphics/buffer_properties.h" -#include "mir/test/doubles/mock_egl.h" -#include "mir/test/doubles/mock_gl.h" -#include "mir/test/doubles/null_emergency_cleanup.h" -#include "src/server/report/null/display_report.h" -#include "mir/test/doubles/null_console_services.h" -#include "mir/options/program_option.h" -#include "src/platforms/x11/graphics/platform.h" -#include "src/platforms/x11/x11_resources.h" -#include "mir/test/doubles/mock_x11.h" -#include "mir/test/doubles/mock_x11_resources.h" - -#include "mir/logging/dumb_console_logger.h" - -#include - -namespace mg = mir::graphics; -namespace ml = mir::logging; -namespace geom = mir::geometry; -namespace mtd = mir::test::doubles; -namespace mo = mir::options; - -class GraphicsPlatform : public ::testing::Test -{ -public: - GraphicsPlatform() : logger(std::make_shared()) - { - using namespace testing; - - ON_CALL(mock_x11, XNextEvent(_, _)) - .WillByDefault( - DoAll( - Invoke( - [](auto, XEvent* ev) - { - ev->type = Expose; - }), - Return(true))); - ON_CALL(mock_egl, eglQueryString(_, EGL_EXTENSIONS)) - .WillByDefault(Return("")); - } - - std::shared_ptr create_platform() - { - return std::make_shared( - std::make_shared(), - std::vector{{{1280, 1024}}}, - std::make_shared()); - } - - std::shared_ptr logger; - - ::testing::NiceMock mock_egl; - ::testing::NiceMock mock_gl; - ::testing::NiceMock mock_x11; -}; - -#include "../test_graphics_platform.h" diff --git a/tests/unit-tests/renderers/gl/CMakeLists.txt b/tests/unit-tests/renderers/gl/CMakeLists.txt index 180ad6ee1cc..1adaf2679d9 100644 --- a/tests/unit-tests/renderers/gl/CMakeLists.txt +++ b/tests/unit-tests/renderers/gl/CMakeLists.txt @@ -1,6 +1,6 @@ list(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_gl_renderer.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_buffer_render_target.cpp +# ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_buffer_render_target.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) diff --git a/tests/unit-tests/renderers/gl/test_basic_buffer_render_target.cpp b/tests/unit-tests/renderers/gl/test_basic_buffer_render_target.cpp index 09d7fda0ada..ca517b58d3d 100644 --- a/tests/unit-tests/renderers/gl/test_basic_buffer_render_target.cpp +++ b/tests/unit-tests/renderers/gl/test_basic_buffer_render_target.cpp @@ -53,7 +53,7 @@ struct BasicBufferRenderTarget : Test TEST_F(BasicBufferRenderTarget, set_buffer_always_allocs_correct_storage) { - mrg::BasicBufferRenderTarget render_target{mt::fake_shared(ctx)}; + mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; EXPECT_CALL(mock_gl, glRenderbufferStorage(_, _, reasonable_width, reasonable_height)); render_target.set_buffer(mt::fake_shared(reasonable_buffer)); render_target.swap_buffers(); @@ -70,7 +70,7 @@ TEST_F(BasicBufferRenderTarget, set_buffer_always_allocs_correct_storage) TEST_F(BasicBufferRenderTarget, sets_gl_viewport_on_buffer_size_change) { - mrg::BasicBufferRenderTarget render_target{mt::fake_shared(ctx)}; + mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; EXPECT_CALL(mock_gl, glViewport(0, 0, reasonable_width, reasonable_height)); render_target.set_buffer(mt::fake_shared(reasonable_buffer)); Mock::VerifyAndClearExpectations(&mock_gl); @@ -108,7 +108,7 @@ TEST_F(BasicBufferRenderTarget, cleans_up_framebuffers_and_renderbuffers) renderbuffers.erase(*id); })); { - mrg::BasicBufferRenderTarget render_target{mt::fake_shared(ctx)}; + mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; render_target.make_current(); render_target.set_buffer(mt::fake_shared(reasonable_buffer)); render_target.bind(); @@ -130,15 +130,15 @@ TEST_F(BasicBufferRenderTarget, cleans_up_framebuffers_and_renderbuffers) TEST_F(BasicBufferRenderTarget, reads_pixels) { - mrg::BasicBufferRenderTarget render_target{mt::fake_shared(ctx)}; - render_target.set_buffer(mt::fake_shared(reasonable_buffer)); + mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; + render_target.set_buffer(mt::fake_shared(reasonable_buffer), reasonable_size); EXPECT_CALL(mock_gl, glReadPixels(0, 0, reasonable_width, reasonable_height, _, _, _)); render_target.swap_buffers(); } TEST_F(BasicBufferRenderTarget, throws_on_invalid_buffer) { - mrg::BasicBufferRenderTarget render_target{mt::fake_shared(ctx)}; + mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; EXPECT_THROW({ mtd::StubBuffer buffer({ reasonable_size, diff --git a/tests/unit-tests/renderers/gl/test_gl_renderer.cpp b/tests/unit-tests/renderers/gl/test_gl_renderer.cpp index ae8ae02c2be..2a50a0e0376 100644 --- a/tests/unit-tests/renderers/gl/test_gl_renderer.cpp +++ b/tests/unit-tests/renderers/gl/test_gl_renderer.cpp @@ -25,8 +25,8 @@ #include #include #include -#include -#include +#include +#include using testing::SetArgPointee; using testing::InSequence; @@ -154,11 +154,10 @@ class GLRenderer : testing::NiceMock mock_gl; testing::NiceMock mock_egl; std::shared_ptr mock_buffer; - mtd::StubGLDisplayBuffer display_buffer{{{1, 2}, {3, 4}}}; - testing::NiceMock mock_display_buffer; std::shared_ptr> renderable; mg::RenderableList renderable_list; glm::mat4 trans; + std::shared_ptr const gl_platform{std::make_shared()}; class StubProgram : public mg::gl::Program { @@ -166,6 +165,11 @@ class GLRenderer : StubProgram prog; }; +auto make_output_surface() -> std::unique_ptr +{ + return std::make_unique>(); +} + } TEST_F(GLRenderer, disables_blending_for_rgbx_surfaces) @@ -175,7 +179,7 @@ TEST_F(GLRenderer, disables_blending_for_rgbx_surfaces) .WillOnce(Return(false)); EXPECT_CALL(mock_gl, glDisable(GL_BLEND)); - mrg::Renderer renderer(display_buffer); + mrg::Renderer renderer(gl_platform, make_output_surface()); renderer.render(renderable_list); } @@ -185,7 +189,7 @@ TEST_F(GLRenderer, enables_blending_for_rgba_surfaces) EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0); EXPECT_CALL(mock_gl, glEnable(GL_BLEND)); - mrg::Renderer renderer(display_buffer); + mrg::Renderer renderer(gl_platform, make_output_surface()); renderer.render(renderable_list); } @@ -196,7 +200,7 @@ TEST_F(GLRenderer, enables_blending_for_rgbx_translucent_surfaces) EXPECT_CALL(mock_gl, glDisable(GL_BLEND)).Times(0); EXPECT_CALL(mock_gl, glEnable(GL_BLEND)); - mrg::Renderer renderer(display_buffer); + mrg::Renderer renderer(gl_platform, make_output_surface()); renderer.render(renderable_list); } @@ -208,7 +212,7 @@ TEST_F(GLRenderer, uses_premultiplied_src_alpha_for_rgba_surfaces) EXPECT_CALL(mock_gl, glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - mrg::Renderer renderer(display_buffer); + mrg::Renderer renderer(gl_platform, make_output_surface()); renderer.render(renderable_list); } @@ -222,7 +226,7 @@ TEST_F(GLRenderer, avoids_src_alpha_for_rgbx_blending) // LP: #1423462 glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_CONSTANT_ALPHA, GL_ZERO, GL_ONE)); - mrg::Renderer renderer(display_buffer); + mrg::Renderer renderer(gl_platform, make_output_surface()); renderer.render(renderable_list); } @@ -233,83 +237,43 @@ TEST_F(GLRenderer, clears_to_opaque_black) EXPECT_CALL(mock_gl, glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); EXPECT_CALL(mock_gl, glClear(_)); - mrg::Renderer renderer(display_buffer); + mrg::Renderer renderer(gl_platform, make_output_surface()); renderer.render(renderable_list); } TEST_F(GLRenderer, makes_display_buffer_current_when_created) { - EXPECT_CALL(mock_display_buffer, make_current()); - - mrg::Renderer renderer(mock_display_buffer); - - testing::Mock::VerifyAndClearExpectations(&mock_display_buffer); -} - -TEST_F(GLRenderer, releases_display_buffer_current_when_destroyed) -{ - mrg::Renderer renderer(mock_display_buffer); - - EXPECT_CALL(mock_display_buffer, release_current()); -} - + auto mock_output_surface = make_output_surface(); -TEST_F(GLRenderer, makes_display_buffer_current_before_deleting_programs) -{ - mrg::Renderer renderer(mock_display_buffer); - - testing::Sequence s1, s2; - // We must call MakeCurrent before anything else. - EXPECT_CALL(mock_display_buffer, make_current()) - .InSequence(s1, s2); - /* As an implementation detail ProgramFactory calls - * glDeleteShader during shader compilation; this has to happen - * before rendering (and so, before SwapBuffers) - */ - EXPECT_CALL(mock_gl, glDeleteShader(_)) - .Times(AtLeast(1)) - .InSequence(s1, s2); - EXPECT_CALL(mock_display_buffer, swap_buffers()) - .InSequence(s1, s2); - EXPECT_CALL(mock_display_buffer, make_current()) - .InSequence(s1, s2); - /* We only care that all glDeleteProgram() and glDeleteShader calls - * happen after make_current() and before the final release_current(); - * we don't care what order they happen in otherwise. - */ - EXPECT_CALL(mock_gl, glDeleteProgram(_)) - .Times(AtLeast(1)) - .InSequence(s1); - EXPECT_CALL(mock_gl, glDeleteShader(_)) - .Times(AtLeast(1)) - .InSequence(s2); - EXPECT_CALL(mock_display_buffer, release_current()).InSequence(s1, s2); + EXPECT_CALL(*mock_output_surface, make_current()); - renderer.render(renderable_list); + mrg::Renderer renderer(gl_platform, std::move(mock_output_surface)); } TEST_F(GLRenderer, makes_display_buffer_current_before_rendering) { - mrg::Renderer renderer(mock_display_buffer); + auto mock_output_surface = make_output_surface(); InSequence seq; - EXPECT_CALL(mock_display_buffer, make_current()); + EXPECT_CALL(*mock_output_surface, make_current()); EXPECT_CALL(mock_gl, glClear(_)); - renderer.render(renderable_list); + mrg::Renderer renderer(gl_platform, std::move(mock_output_surface)); - testing::Mock::VerifyAndClearExpectations(&mock_display_buffer); + renderer.render(renderable_list); } TEST_F(GLRenderer, swaps_buffers_after_rendering) { - mrg::Renderer renderer(mock_display_buffer); + auto mock_output_surface = make_output_surface(); InSequence seq; EXPECT_CALL(mock_gl, glDrawArrays(_, _, _)).Times(AnyNumber()); - EXPECT_CALL(mock_display_buffer, swap_buffers()); + EXPECT_CALL(*mock_output_surface, commit()) + .WillRepeatedly(testing::Invoke([]() { return std::unique_ptr(); })); + mrg::Renderer renderer(gl_platform, std::move(mock_output_surface)); renderer.render(renderable_list); } @@ -321,7 +285,7 @@ TEST_F(GLRenderer, sets_scissor_test) EXPECT_CALL(mock_gl, glDisable(GL_SCISSOR_TEST)); EXPECT_CALL(mock_gl, glScissor(-1, 2, 2, 3)); - mrg::Renderer renderer(display_buffer); + mrg::Renderer renderer(gl_platform, make_output_surface()); renderer.set_viewport({{1, 2}, {3, 4}}); renderer.render(renderable_list); @@ -333,7 +297,8 @@ TEST_F(GLRenderer, dont_set_scissor_test_when_unnecessary) EXPECT_CALL(mock_gl, glDisable(GL_SCISSOR_TEST)).Times(0); EXPECT_CALL(mock_gl, glScissor(_, _, _, _)).Times(0); - mrg::Renderer renderer(display_buffer); + mrg::Renderer renderer(gl_platform, make_output_surface()); + renderer.set_viewport(mir::geometry::Rectangle{{0, 0}, {2, 3}}); renderer.render(renderable_list); } @@ -341,14 +306,9 @@ TEST_F(GLRenderer, dont_set_scissor_test_when_unnecessary) TEST_F(GLRenderer, unchanged_viewport_avoids_gl_calls) { - int const screen_width = 1920; - int const screen_height = 1080; mir::geometry::Rectangle const view_area{{0,0}, {1920,1080}}; - ON_CALL(mock_display_buffer, size()) - .WillByDefault(Return(mir::geometry::Size{screen_width, screen_height})); - - mrg::Renderer renderer(mock_display_buffer); + mrg::Renderer renderer(gl_platform, make_output_surface()); renderer.set_viewport(view_area); @@ -363,10 +323,14 @@ TEST_F(GLRenderer, unchanged_viewport_updates_gl_if_rotated) int const screen_height = 1080; mir::geometry::Rectangle const view_area{{0,0}, {1920,1080}}; - ON_CALL(mock_display_buffer, size()) - .WillByDefault(Return(mir::geometry::Size{screen_width, screen_height})); + auto output_surface = make_output_surface(); - mrg::Renderer renderer(mock_display_buffer); + ON_CALL(*output_surface, size()) + .WillByDefault( + Return( + mir::geometry::Size{screen_width, screen_height})); + + mrg::Renderer renderer(gl_platform, std::move(output_surface)); renderer.set_viewport(view_area); @@ -384,12 +348,16 @@ TEST_F(GLRenderer, sets_viewport_unscaled_exact) int const screen_height = 1080; mir::geometry::Rectangle const view_area{{0,0}, {1920,1080}}; - ON_CALL(mock_display_buffer, size()) - .WillByDefault(Return(mir::geometry::Size{screen_width, screen_height})); + auto output_surface = make_output_surface(); + + ON_CALL(*output_surface, size()) + .WillByDefault( + Return( + mir::geometry::Size{screen_width, screen_height})); EXPECT_CALL(mock_gl, glViewport(0, 0, screen_width, screen_height)); - mrg::Renderer renderer(mock_display_buffer); + mrg::Renderer renderer(gl_platform, std::move(output_surface)); renderer.set_viewport(view_area); } @@ -399,12 +367,16 @@ TEST_F(GLRenderer, sets_viewport_upscaled_exact) int const screen_height = 1080; mir::geometry::Rectangle const view_area{{0,0}, {1280,720}}; - ON_CALL(mock_display_buffer, size()) - .WillByDefault(Return(mir::geometry::Size{screen_width, screen_height})); + auto output_surface = make_output_surface(); + + ON_CALL(*output_surface, size()) + .WillByDefault( + Return( + mir::geometry::Size{screen_width, screen_height})); EXPECT_CALL(mock_gl, glViewport(0, 0, screen_width, screen_height)); - mrg::Renderer renderer(mock_display_buffer); + mrg::Renderer renderer(gl_platform, std::move(output_surface)); renderer.set_viewport(view_area); } @@ -414,12 +386,16 @@ TEST_F(GLRenderer, sets_viewport_downscaled_exact) int const screen_height = 720; mir::geometry::Rectangle const view_area{{0,0}, {1920,1080}}; - ON_CALL(mock_display_buffer, size()) - .WillByDefault(Return(mir::geometry::Size{screen_width, screen_height})); + auto output_surface = make_output_surface(); + + ON_CALL(*output_surface, size()) + .WillByDefault( + Return( + mir::geometry::Size{screen_width, screen_height})); EXPECT_CALL(mock_gl, glViewport(0, 0, screen_width, screen_height)); - mrg::Renderer renderer(mock_display_buffer); + mrg::Renderer renderer(gl_platform, std::move(output_surface)); renderer.set_viewport(view_area); } @@ -429,12 +405,16 @@ TEST_F(GLRenderer, sets_viewport_upscaled_narrow) int const screen_height = 1080; mir::geometry::Rectangle const view_area{{0,0}, {640,480}}; - ON_CALL(mock_display_buffer, size()) - .WillByDefault(Return(mir::geometry::Size{screen_width, screen_height})); + auto output_surface = make_output_surface(); + + ON_CALL(*output_surface, size()) + .WillByDefault( + Return( + mir::geometry::Size{screen_width, screen_height})); EXPECT_CALL(mock_gl, glViewport(240, 0, 1440, 1080)); - mrg::Renderer renderer(mock_display_buffer); + mrg::Renderer renderer(gl_platform, std::move(output_surface)); renderer.set_viewport(view_area); } @@ -444,11 +424,15 @@ TEST_F(GLRenderer, sets_viewport_downscaled_wide) int const screen_height = 480; mir::geometry::Rectangle const view_area{{0,0}, {1920,1080}}; - ON_CALL(mock_display_buffer, size()) - .WillByDefault(Return(mir::geometry::Size{screen_width, screen_height})); + auto output_surface = make_output_surface(); + + ON_CALL(*output_surface, size()) + .WillByDefault( + Return( + mir::geometry::Size{screen_width, screen_height})); EXPECT_CALL(mock_gl, glViewport(0, 60, 640, 360)); - mrg::Renderer renderer(mock_display_buffer); + mrg::Renderer renderer(gl_platform, std::move(output_surface)); renderer.set_viewport(view_area); } From 9d1ea5a481ea390de82fa8c1da9a81952f31cc09 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 28 Apr 2023 18:55:27 +1000 Subject: [PATCH 002/108] Aaaaargh --- .../platform/mir/graphics/display_buffer.h | 4 +- include/platform/mir/graphics/platform.h | 104 ++++++------- .../eglstream-kms/server/buffer_allocator.cpp | 23 +-- .../eglstream-kms/server/buffer_allocator.h | 7 +- .../eglstream-kms/server/display.cpp | 4 +- .../eglstream-kms/server/platform_symbols.cpp | 2 +- .../gbm-kms/server/buffer_allocator.cpp | 41 ++++-- .../gbm-kms/server/buffer_allocator.h | 7 +- src/platforms/gbm-kms/server/kms/display.cpp | 63 +++----- src/platforms/gbm-kms/server/kms/display.h | 21 +-- .../gbm-kms/server/kms/display_buffer.cpp | 12 +- .../gbm-kms/server/kms/display_buffer.h | 6 +- src/platforms/gbm-kms/server/kms/platform.cpp | 58 +++++--- src/platforms/gbm-kms/server/kms/platform.h | 10 +- .../gbm-kms/server/kms/platform_symbols.cpp | 138 +++++++++++++++++- .../gbm-kms/server/kms/symbols.map.in | 2 + .../renderer-generic-egl/buffer_allocator.cpp | 38 +++-- .../renderer-generic-egl/buffer_allocator.h | 7 +- .../renderer-generic-egl/platform_symbols.cpp | 4 +- .../rendering_platform.cpp | 6 +- .../renderer-generic-egl/rendering_platform.h | 2 +- src/platforms/wayland/displayclient.cpp | 4 +- src/platforms/wayland/platform.cpp | 9 +- src/platforms/wayland/platform.h | 4 +- src/platforms/x11/graphics/display.cpp | 2 +- src/platforms/x11/graphics/display.h | 5 +- src/platforms/x11/graphics/display_buffer.cpp | 7 +- src/platforms/x11/graphics/display_buffer.h | 7 +- src/platforms/x11/graphics/egl_helper.cpp | 38 ++++- src/platforms/x11/graphics/egl_helper.h | 13 +- src/platforms/x11/graphics/platform.cpp | 94 +++++++++--- src/platforms/x11/graphics/platform.h | 23 +-- .../compositor/basic_screen_shooter.cpp | 1 + src/server/compositor/basic_screen_shooter.h | 14 +- .../compositor/default_configuration.cpp | 41 +++--- .../default_display_buffer_compositor.cpp | 2 +- ...ault_display_buffer_compositor_factory.cpp | 3 +- src/server/graphics/default_configuration.cpp | 9 +- .../mir/test/doubles/mock_display_buffer.h | 2 +- .../test/doubles/mock_gl_rendering_provider.h | 7 +- .../mir/test/doubles/null_display_buffer.h | 2 +- .../include/mir/test/doubles/null_platform.h | 5 +- .../test/doubles/stub_gl_rendering_provider.h | 5 +- ...less_display_buffer_compositor_factory.cpp | 3 +- .../platform_graphics_throw.cpp | 9 +- .../stubbed_graphics_platform.cpp | 18 ++- .../stubbed_graphics_platform.h | 6 +- .../stubbed_server_configuration.cpp | 2 +- .../gbm-kms/kms/test_display_buffer.cpp | 4 +- .../kms/test_display_multi_monitor.cpp | 16 +- .../gbm-kms/kms/test_real_kms_output.cpp | 4 + 51 files changed, 595 insertions(+), 323 deletions(-) diff --git a/include/platform/mir/graphics/display_buffer.h b/include/platform/mir/graphics/display_buffer.h index 793c01741a9..eda8095e65c 100644 --- a/include/platform/mir/graphics/display_buffer.h +++ b/include/platform/mir/graphics/display_buffer.h @@ -30,7 +30,7 @@ namespace graphics { class Framebuffer; -class DisplayPlatform; +class DisplayInterfaceProvider; struct DisplayElement { @@ -95,7 +95,7 @@ class DisplayBuffer */ virtual glm::mat2 transformation() const = 0; - virtual auto owner() const -> std::shared_ptr = 0; + virtual auto display_provider() const -> std::shared_ptr = 0; protected: DisplayBuffer() = default; DisplayBuffer(DisplayBuffer const& c) = delete; diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index b975d6b6d3b..1a41385b0c6 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -62,6 +62,8 @@ class DisplayConfigurationPolicy; class GraphicBufferAllocator; class GLConfig; +class DisplayInterfaceProvider; + class RendererInterfaceBase { public: @@ -90,10 +92,11 @@ class RendererInterfaceBase * \param buffer * \return */ - virtual auto buffer_to_framebuffer(std::shared_ptr buffer) -> std::unique_ptr = 0; + virtual auto buffer_to_framebuffer(std::shared_ptr buffer) + -> std::unique_ptr = 0; }; - virtual auto make_framebuffer_provider(DisplayBuffer const& target) + virtual auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr = 0; }; @@ -112,7 +115,10 @@ class GLRenderingProvider : public RendererInterfaceBase virtual auto as_texture(std::shared_ptr buffer) -> std::shared_ptr = 0; - virtual auto surface_for_output(DisplayBuffer& db, GLConfig const& config) -> std::unique_ptr = 0; + virtual auto surface_for_output( + std::shared_ptr framebuffer_provider, + geometry::Size size, + GLConfig const& config) -> std::unique_ptr = 0; }; /** @@ -211,6 +217,11 @@ class Framebuffer public: Framebuffer() = default; virtual ~Framebuffer() = default; + + /** + * The size of this framebuffer, in pixels + */ + virtual auto size() const -> geometry::Size = 0; }; @@ -230,26 +241,8 @@ class DumbDisplayProvider : public DisplayInterfaceBase virtual ~MappableFB() override = default; }; - class Allocator - { - public: - Allocator() = default; - virtual ~Allocator() = default; - - /** - * Acquire a buffer for rendering into - * - * This - * - * \return - */ - virtual auto acquire() -> std::unique_ptr = 0; - }; - - DumbDisplayProvider() = default; - - virtual auto allocator_for_db(DisplayBuffer const& db) - -> std::unique_ptr = 0; + virtual auto alloc_fb(geometry::Size pixel_size) + -> std::unique_ptr = 0; }; class GBMDisplayProvider : public DisplayInterfaceBase @@ -267,11 +260,6 @@ class GBMDisplayProvider : public DisplayInterfaceBase */ virtual auto is_same_device(mir::udev::Device const& render_device) const -> bool = 0; - /** - * Check if the provided DisplayBuffer is driven by this device - */ - virtual auto is_same_device(DisplayBuffer const& db) const -> bool = 0; - /** * Get the GBM device for this display */ @@ -336,7 +324,7 @@ class EGLStreamDisplayProvider : public DisplayInterfaceBase { }; - virtual auto claim_stream_for_output(DisplayBuffer& db) -> EGLStreamKHR = 0; + virtual auto claim_stream() -> EGLStreamKHR = 0; }; class GenericEGLDisplayProvider : public DisplayInterfaceBase @@ -355,24 +343,17 @@ class GenericEGLDisplayProvider : public DisplayInterfaceBase virtual auto clone_handle() -> std::unique_ptr = 0; }; - virtual auto framebuffer_for_db(DisplayBuffer& db, GLConfig const& config, EGLContext share_context) -> std::unique_ptr = 0; + virtual auto alloc_framebuffer(GLConfig const& config, EGLContext share_context) -> std::unique_ptr = 0; }; -class DisplayPlatform : public std::enable_shared_from_this +class DisplayInterfaceProvider : public std::enable_shared_from_this { public: - DisplayPlatform() = default; - DisplayPlatform(DisplayPlatform const& p) = delete; - DisplayPlatform& operator=(DisplayPlatform const& p) = delete; + DisplayInterfaceProvider() = default; + virtual ~DisplayInterfaceProvider() = default; - virtual ~DisplayPlatform() = default; - - /** - * Creates the display subsystem. - */ - virtual UniqueModulePtr create_display( - std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config) = 0; + DisplayInterfaceProvider(DisplayInterfaceProvider const&) = delete; + auto operator=(DisplayInterfaceProvider const&) -> DisplayInterfaceProvider& = delete; /** * Attempt to acquire a platform-specific interface from this DisplayPlatform @@ -388,13 +369,13 @@ class DisplayPlatform : public std::enable_shared_from_this * On failure: std::shared_ptr{nullptr} */ template - static auto acquire_interface(std::shared_ptr const& platform) -> std::shared_ptr + auto acquire_interface() -> std::shared_ptr { static_assert( std::is_convertible_v, "Can only acquire a Display interface; Interface must implement DisplayInterfaceBase"); - if (auto const base_interface = platform->maybe_create_interface(typename Interface::Tag{})) + if (auto const base_interface = maybe_create_interface(typename Interface::Tag{})) { if (auto const requested_interface = std::dynamic_pointer_cast(base_interface)) { @@ -404,7 +385,7 @@ class DisplayPlatform : public std::enable_shared_from_this "Implementation error! Platform returned object that does not support requested interface"})); } return nullptr; - } + } protected: /** @@ -414,19 +395,44 @@ class DisplayPlatform : public std::enable_shared_from_this * expected to work and return a pointer to an implementation of that interface. * * This function is guaranteed to be called with `this` managed by a `shared_ptr`; if - * the returned value needs to share ownership with `this`, calls to std::shared_from_this + * the returned value needs to share ownership with `this`, calls to `std::shared_from_this` * can be expected to work. * * \param type_tag [in] An instance of the Tag type for the requested interface. * Implementations are expected to dynamic_cast<> this to * discover the specific interface being requested. - * \return A pointer to an implementation of the RenderInterfaceBase-derived + * \return A pointer to an implementation of the DisplayInterfaceBase-derived * interface that corresponds to the most-derived type of tag_type. */ virtual auto maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) -> std::shared_ptr = 0; }; +class DisplayPlatform : public std::enable_shared_from_this +{ +public: + DisplayPlatform() = default; + DisplayPlatform(DisplayPlatform const& p) = delete; + DisplayPlatform& operator=(DisplayPlatform const& p) = delete; + + virtual ~DisplayPlatform() = default; + + /** + * Creates the display subsystem. + */ + virtual UniqueModulePtr create_display( + std::shared_ptr const& initial_conf_policy, + std::shared_ptr const& gl_config) = 0; + + static auto interface_for(std::shared_ptr platform) + -> std::shared_ptr + { + return platform->interface_for(); + } +protected: + virtual auto interface_for() -> std::shared_ptr = 0; +}; + /** * A measure of how well a platform supports a device * @@ -487,7 +493,7 @@ typedef mir::UniqueModulePtr(*CreateDisplayPlatf typedef mir::UniqueModulePtr(*CreateRenderPlatform)( mir::graphics::SupportedDevice const& device, - std::vector> const& displays, + std::vector> const& displays, mir::options::Option const& options, mir::EmergencyCleanupRegistry& emergency_cleanup_registry); @@ -535,7 +541,7 @@ mir::UniqueModulePtr create_display_platform( mir::UniqueModulePtr create_rendering_platform( mir::graphics::SupportedDevice const& device, - std::vector> const& displays, + std::vector> const& displays, mir::options::Option const& options, mir::EmergencyCleanupRegistry& emergency_cleanup_registry); diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index 8b175e725f5..ead959d9b40 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -590,7 +590,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface public: CPUCopyOutputSurface( std::unique_ptr ctx, - std::unique_ptr allocator, + std::shared_ptr allocator, geom::Size size) : allocator{std::move(allocator)}, ctx{std::move(ctx)}, @@ -647,7 +647,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface auto commit() -> std::unique_ptr override { - auto fb = allocator->acquire(); + auto fb = allocator->alloc_fb(size_); glBindFramebuffer(GL_FRAMEBUFFER, fbo); { auto mapping = fb->map_writeable(); @@ -678,7 +678,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface } private: - std::unique_ptr const allocator; + std::shared_ptr const allocator; std::unique_ptr const ctx; geom::Size const size_; RenderbufferHandle const colour_buffer; @@ -814,10 +814,11 @@ auto mir::graphics::eglstream::GLRenderingProvider::as_texture(std::shared_ptr target, + geom::Size size, mg::GLConfig const& gl_config) -> std::unique_ptr { - if (auto stream_platform = DisplayPlatform::acquire_interface(db.owner())) + if (auto stream_platform = target->acquire_interface()) { try { @@ -825,8 +826,8 @@ auto mge::GLRenderingProvider::surface_for_output( dpy, pick_stream_surface_config(dpy, gl_config), static_cast(*ctx), - stream_platform->claim_stream_for_output(db), - db.view_area().size); + stream_platform->claim_stream(), + size); } catch (std::exception const& err) { @@ -835,17 +836,17 @@ auto mge::GLRenderingProvider::surface_for_output( err.what()); } } - auto dumb_display = DisplayPlatform::acquire_interface(db.owner()); + auto dumb_display = target->acquire_interface(); auto fb_context = ctx->make_share_context(); fb_context->make_current(); return std::make_unique( std::move(fb_context), - dumb_display->allocator_for_db(db), - db.view_area().size); + dumb_display, + size); } -auto mge::GLRenderingProvider::make_framebuffer_provider(mir::graphics::DisplayBuffer const& /*target*/) +auto mge::GLRenderingProvider::make_framebuffer_provider(std::shared_ptr /*target*/) -> std::unique_ptr { // TODO: *Can* we provide overlay support? diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.h b/src/platforms/eglstream-kms/server/buffer_allocator.h index 2a23441ee4f..7c70fbf0c91 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.h +++ b/src/platforms/eglstream-kms/server/buffer_allocator.h @@ -99,9 +99,12 @@ class GLRenderingProvider : public graphics::GLRenderingProvider auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; - auto make_framebuffer_provider(DisplayBuffer const& target) -> std::unique_ptr override; + auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr override; - auto surface_for_output(DisplayBuffer& db, GLConfig const& gl_config) -> std::unique_ptr override; + auto surface_for_output( + std::shared_ptr provider, + geometry::Size size, + GLConfig const& gl_config) -> std::unique_ptr override; private: EGLDisplay dpy; std::unique_ptr const ctx; diff --git a/src/platforms/eglstream-kms/server/display.cpp b/src/platforms/eglstream-kms/server/display.cpp index 5a85cc572fc..01a76a8eea1 100644 --- a/src/platforms/eglstream-kms/server/display.cpp +++ b/src/platforms/eglstream-kms/server/display.cpp @@ -125,7 +125,7 @@ class DisplayBuffer { public: DisplayBuffer( - std::shared_ptr owner, + std::shared_ptr owner, mir::Fd drm_node, EGLDisplay dpy, EGLContext ctx, @@ -281,7 +281,7 @@ class DisplayBuffer private: - std::shared_ptr const owner; + std::shared_ptr const owner; EGLDisplay dpy; EGLContext ctx; EGLOutputLayerEXT layer; diff --git a/src/platforms/eglstream-kms/server/platform_symbols.cpp b/src/platforms/eglstream-kms/server/platform_symbols.cpp index 312c5fb9943..32e1c82b8fc 100644 --- a/src/platforms/eglstream-kms/server/platform_symbols.cpp +++ b/src/platforms/eglstream-kms/server/platform_symbols.cpp @@ -47,7 +47,7 @@ namespace mge = mir::graphics::eglstream; auto create_rendering_platform( mg::SupportedDevice const& device, - std::vector> const& /*displays*/, + std::vector> const& /*displays*/, mo::Option const&, mir::EmergencyCleanupRegistry&) -> mir::UniqueModulePtr { diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index 7c5b5ddf538..0d9c1c8faa9 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -366,7 +366,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface CPUCopyOutputSurface( EGLDisplay dpy, EGLContext ctx, - std::unique_ptr allocator, + std::shared_ptr allocator, geom::Size size) : allocator{std::move(allocator)}, dpy{dpy}, @@ -429,7 +429,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface auto commit() -> std::unique_ptr override { - auto fb = allocator->acquire(); + auto fb = allocator->alloc_fb(size_); glBindFramebuffer(GL_FRAMEBUFFER, fbo); { auto mapping = fb->map_writeable(); @@ -460,7 +460,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface } private: - std::unique_ptr const allocator; + std::shared_ptr const allocator; EGLDisplay const dpy; EGLContext const ctx; geom::Size const size_; @@ -674,29 +674,38 @@ class GBMOutputSurface : public mg::gl::OutputSurface }; } -auto mgg::GLRenderingProvider::surface_for_output(DisplayBuffer& db, GLConfig const& config) +auto mgg::GLRenderingProvider::surface_for_output( + std::shared_ptr target, + geom::Size size, + GLConfig const& config) -> std::unique_ptr { - if (bound_display && bound_display->is_same_device(db)) + if (bound_display) { - return std::make_unique( - dpy, - ctx, - config, - *bound_display, - DRMFormat{DRM_FORMAT_XRGB8888}, - db.view_area().size); + if (auto gbm_provider = target->acquire_interface()) + { + if (bound_display->gbm_device() == gbm_provider->gbm_device()) + { + return std::make_unique( + dpy, + ctx, + config, + *bound_display, + DRMFormat{DRM_FORMAT_XRGB8888}, + size); + } + } } - auto dumb_display = DisplayPlatform::acquire_interface(db.owner()); + auto dumb_display = target->acquire_interface(); return std::make_unique( dpy, ctx, - dumb_display->allocator_for_db(db), - db.view_area().size); + std::move(dumb_display), + size); } -auto mgg::GLRenderingProvider::make_framebuffer_provider(DisplayBuffer const& /*target*/) +auto mgg::GLRenderingProvider::make_framebuffer_provider(std::shared_ptr /*target*/) -> std::unique_ptr { // TODO: Make this not a null implementation, so bypass/overlays can work again diff --git a/src/platforms/gbm-kms/server/buffer_allocator.h b/src/platforms/gbm-kms/server/buffer_allocator.h index 4b4aadb7e64..f99b16d7379 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.h +++ b/src/platforms/gbm-kms/server/buffer_allocator.h @@ -89,12 +89,15 @@ class GLRenderingProvider : public graphics::GLRenderingProvider EGLDisplay dpy, EGLContext ctx); - auto make_framebuffer_provider(DisplayBuffer const& target) + auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr override; auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; - auto surface_for_output(DisplayBuffer& db, GLConfig const& config) -> std::unique_ptr override; + auto surface_for_output( + std::shared_ptr framebuffer_provider, + geometry::Size size, + GLConfig const& config) -> std::unique_ptr override; private: udev::Device const& device; diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index 6627197af50..fdf016019ab 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -144,7 +144,7 @@ void log_drm_details(mir::Fd const& drm_fd) } mgg::Display::Display( - std::shared_ptr parent, + std::shared_ptr parent, mir::Fd drm_fd, mgg::BypassOption bypass_option, std::shared_ptr const& initial_conf_policy, @@ -452,44 +452,26 @@ auto drm_get_cap_checked(mir::Fd const& drm_fd, uint64_t cap) -> uint64_t uint64_t value; if (drmGetCap(drm_fd, cap, &value)) { - + BOOST_THROW_EXCEPTION(( + std::system_error{ + errno, + std::system_category(), + "Failed to query DRM capabilities"})); } return value; } - - -class DumbAllocator : public mg::DumbDisplayProvider::Allocator -{ -public: - DumbAllocator(mir::Fd drm_fd, mg::DisplayBuffer const& db) - : drm_fd(std::move(drm_fd)), - supports_modifiers{drm_get_cap_checked(this->drm_fd, DRM_CAP_ADDFB2_MODIFIERS) == 1}, - size{db.view_area().size} - { - } - - auto acquire() -> std::unique_ptr override - { - return std::make_unique(drm_fd, supports_modifiers, size); - } - -private: - mir::Fd const drm_fd; - bool const supports_modifiers; - mir::geometry::Size const size; -}; - } -mgg::DumbDisplayProvider::DumbDisplayProvider() +mgg::DumbDisplayProvider::DumbDisplayProvider(mir::Fd drm_fd) + : drm_fd{std::move(drm_fd)}, + supports_modifiers{drm_get_cap_checked(this->drm_fd, DRM_CAP_ADDFB2_MODIFIERS) == 1} { } -auto mgg::DumbDisplayProvider::allocator_for_db( - mg::DisplayBuffer const& db) -> std::unique_ptr +auto mgg::DumbDisplayProvider::alloc_fb( + geom::Size size) -> std::unique_ptr { - auto const& kms_db = dynamic_cast(db); - return std::make_unique(kms_db.drm_fd(), db); + return std::make_unique(drm_fd, supports_modifiers, size); } namespace @@ -520,11 +502,9 @@ auto gbm_create_device_checked(mir::Fd fd) -> std::shared_ptr } mgg::GBMDisplayProvider::GBMDisplayProvider( - mir::Fd drm_fd, - mg::DisplayPlatform const* parent) + mir::Fd drm_fd) : fd{std::move(drm_fd)}, - gbm{gbm_create_device_checked(fd)}, - parent{parent} + gbm{gbm_create_device_checked(fd)} { } @@ -533,7 +513,6 @@ auto mgg::GBMDisplayProvider::gbm_device() const -> std::shared_ptr bool { #ifndef MIR_DRM_HAS_GET_DEVICE_FROM_DEVID @@ -589,12 +568,6 @@ auto mgg::GBMDisplayProvider::is_same_device(mir::udev::Device const& render_dev #endif } -auto mgg::GBMDisplayProvider::is_same_device(mg::DisplayBuffer const& db) const -> bool -{ - mir::log_debug("Is %p the same as %p?", db.owner().get(), parent); - return db.owner().get() == parent; -} - auto mgg::GBMDisplayProvider::supported_formats() const -> std::vector { // TODO: Pull out of KMS plane info @@ -655,6 +628,14 @@ class GBMBoFramebuffer : public mgg::FBHandle { return *fb_id; } + + auto size() const -> geom::Size override + { + return + geom::Size{ + gbm_bo_get_width(bo.get()), + gbm_bo_get_height(bo.get())}; + } private: GBMBoFramebuffer(LockedFrontBuffer bo, std::shared_ptr fb) : bo{std::move(bo)}, diff --git a/src/platforms/gbm-kms/server/kms/display.h b/src/platforms/gbm-kms/server/kms/display.h index 6e8ae759f8d..df0b8d493bb 100644 --- a/src/platforms/gbm-kms/server/kms/display.h +++ b/src/platforms/gbm-kms/server/kms/display.h @@ -35,7 +35,7 @@ namespace mir namespace graphics { -class DisplayPlatform; +class DisplayInterfaceProvider; class DisplayReport; class DisplayBuffer; class DisplayConfigurationPolicy; @@ -59,7 +59,7 @@ class Display : public graphics::Display { public: Display( - std::shared_ptr parent, + std::shared_ptr parent, mir::Fd drm_fd, BypassOption bypass_option, std::shared_ptr const& initial_conf_policy, @@ -86,7 +86,7 @@ class Display : public graphics::Display private: void clear_connected_unused_outputs(); - std::shared_ptr const owner; + std::shared_ptr const owner; mutable std::mutex configuration_mutex; mir::Fd const drm_fd; std::shared_ptr const listener; @@ -107,21 +107,23 @@ class Display : public graphics::Display class DumbDisplayProvider : public graphics::DumbDisplayProvider { public: - DumbDisplayProvider(); + explicit DumbDisplayProvider(mir::Fd drm_fd); - auto allocator_for_db(graphics::DisplayBuffer const& db) - -> std::unique_ptr override; + auto alloc_fb(geometry::Size pixel_size) + -> std::unique_ptr override; + +private: + mir::Fd const drm_fd; + bool const supports_modifiers; }; class GBMDisplayProvider : public graphics::GBMDisplayProvider { public: - GBMDisplayProvider(mir::Fd drm_fd, DisplayPlatform const* parent); + GBMDisplayProvider(mir::Fd drm_fd); auto is_same_device(mir::udev::Device const& render_device) const -> bool override; - auto is_same_device(graphics::DisplayBuffer const& db) const -> bool override; - auto gbm_device() const -> std::shared_ptr override; auto supported_formats() const -> std::vector override; @@ -132,7 +134,6 @@ class GBMDisplayProvider : public graphics::GBMDisplayProvider private: mir::Fd const fd; std::shared_ptr const gbm; - DisplayPlatform const* const parent; }; } } diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index b8f90ae85dc..752f421908f 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -46,14 +46,14 @@ namespace mgg = mir::graphics::gbm; namespace geom = mir::geometry; mgg::DisplayBuffer::DisplayBuffer( - std::shared_ptr owner, + std::shared_ptr provider, mir::Fd drm_fd, mgg::BypassOption, std::shared_ptr const& listener, std::vector> const& outputs, geom::Rectangle const& area, glm::mat2 const& transformation) - : owner_{std::move(owner)}, + : provider{std::move(provider)}, listener(listener), outputs(outputs), area(area), @@ -77,9 +77,7 @@ mgg::DisplayBuffer::DisplayBuffer( listener->report_successful_display_construction(); } -mgg::DisplayBuffer::~DisplayBuffer() -{ -} +mgg::DisplayBuffer::~DisplayBuffer() = default; geom::Rectangle mgg::DisplayBuffer::view_area() const { @@ -283,9 +281,9 @@ void mgg::DisplayBuffer::schedule_set_crtc() needs_set_crtc = true; } -auto mir::graphics::gbm::DisplayBuffer::owner() const -> std::shared_ptr +auto mir::graphics::gbm::DisplayBuffer::display_provider() const -> std::shared_ptr { - return owner_; + return provider; } auto mgg::DisplayBuffer::drm_fd() const -> mir::Fd diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.h b/src/platforms/gbm-kms/server/kms/display_buffer.h index a5fd6113620..f30523dd728 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.h +++ b/src/platforms/gbm-kms/server/kms/display_buffer.h @@ -49,7 +49,7 @@ class DisplayBuffer : public graphics::DisplayBuffer, { public: DisplayBuffer( - std::shared_ptr owner, + std::shared_ptr provider, mir::Fd drm_fd, BypassOption bypass_options, std::shared_ptr const& listener, @@ -75,14 +75,14 @@ class DisplayBuffer : public graphics::DisplayBuffer, void schedule_set_crtc(); void wait_for_page_flip(); - auto owner() const -> std::shared_ptr override; + auto display_provider() const -> std::shared_ptr override; auto drm_fd() const -> mir::Fd; private: bool schedule_page_flip(FBHandle const& bufobj); void set_crtc(FBHandle const&); - std::shared_ptr const owner_; + std::shared_ptr const provider; bool holding_client_buffers{false}; std::shared_ptr bypass_bufobj{nullptr}; std::shared_ptr const listener; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 63a35a7a027..da3445f5a01 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -75,11 +75,12 @@ mgg::Platform::Platform( listener{listener}, device_handle{std::move(std::get<0>(drm))}, drm_fd{std::move(std::get<1>(drm))}, + provider{std::make_shared(drm_fd)}, bypass_option_{bypass_option} { if (drm_fd == mir::Fd::invalid) { - BOOST_THROW_EXCEPTION((std::runtime_error{"WTF?"})); + BOOST_THROW_EXCEPTION((std::logic_error{"Invalid DRM device FD"})); } } @@ -87,7 +88,7 @@ namespace { auto gbm_device_for_udev_device( mir::udev::Device const& device, - std::vector> const& displays) + std::vector> const& displays) -> std::variant, std::shared_ptr> { /* First check to see whether our device exactly matches a display device. @@ -95,7 +96,7 @@ auto gbm_device_for_udev_device( */ for(auto const& display_device : displays) { - if (auto gbm_display = mg::DisplayPlatform::acquire_interface(display_device)) + if (auto gbm_display = display_device->acquire_interface()) { if (gbm_display->is_same_device(device)) { @@ -219,7 +220,7 @@ struct gbm_device_from_hw mgg::RenderingPlatform::RenderingPlatform( mir::udev::Device const& device, - std::vector> const& displays) + std::vector> const& displays) : RenderingPlatform(device.clone(), gbm_device_for_udev_device(device, displays)) { } @@ -229,7 +230,6 @@ mgg::RenderingPlatform::RenderingPlatform( std::variant, std::shared_ptr> hw) : udev_device{std::move(udev_device)}, device{std::visit(gbm_device_from_hw{}, hw)}, - bound_display{std::visit(display_provider_or_nothing{}, hw)}, dpy{initialise_egl(dpy_for_gbm_device(device.get()), 1, 4)}, share_ctx{make_share_only_context(dpy)} { @@ -251,35 +251,51 @@ auto mgg::RenderingPlatform::maybe_create_interface( } return nullptr; } +class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfaceProvider +{ +public: + KMSDisplayInterfaceProvider(mir::Fd drm_fd) + : drm_fd{std::move(drm_fd)} + { + } + +protected: + auto maybe_create_interface(mg::DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr + { + if (dynamic_cast(&type_tag)) + { + mir::log_debug("Using GBMDisplayProvider"); + return std::make_shared(drm_fd); + } + if (dynamic_cast(&type_tag)) + { + mir::log_debug("Using DumbDisplayProvider"); + return std::make_shared(drm_fd); + } + return {}; + } +private: + mir::Fd const drm_fd; +}; mir::UniqueModulePtr mgg::Platform::create_display( std::shared_ptr const& initial_conf_policy, std::shared_ptr const&) { return make_module_ptr( - shared_from_this(), + std::make_shared(drm_fd), drm_fd, bypass_option_, initial_conf_policy, listener); } -mgg::BypassOption mgg::Platform::bypass_option() const +auto mgg::Platform::interface_for() -> std::shared_ptr { - return bypass_option_; + return provider; } -auto mgg::Platform::maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr +mgg::BypassOption mgg::Platform::bypass_option() const { - if (dynamic_cast(&type_tag)) - { - mir::log_debug("Using GBMDisplayProvider"); - return std::make_shared(drm_fd, this); - } - if (dynamic_cast(&type_tag)) - { - mir::log_debug("Using DumbDisplayProvider"); - return std::make_shared(); - } - return {}; + return bypass_option_; } diff --git a/src/platforms/gbm-kms/server/kms/platform.h b/src/platforms/gbm-kms/server/kms/platform.h index c36ff1484ae..f6f69df4311 100644 --- a/src/platforms/gbm-kms/server/kms/platform.h +++ b/src/platforms/gbm-kms/server/kms/platform.h @@ -56,8 +56,7 @@ class Platform : public graphics::DisplayPlatform std::shared_ptr const listener; protected: - auto maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr override; + auto interface_for() -> std::shared_ptr override; public: BypassOption bypass_option() const; @@ -70,6 +69,9 @@ class Platform : public graphics::DisplayPlatform std::unique_ptr const device_handle; mir::Fd const drm_fd; + + class KMSDisplayInterfaceProvider; + std::shared_ptr const provider; BypassOption const bypass_option_; }; @@ -77,7 +79,9 @@ class Platform : public graphics::DisplayPlatform class RenderingPlatform : public graphics::RenderingPlatform { public: - RenderingPlatform(udev::Device const& device, std::vector> const& displays); + RenderingPlatform( + udev::Device const& device, + std::vector> const& displays); auto create_buffer_allocator( graphics::Display const&) -> UniqueModulePtr override; diff --git a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp index 4b18efd85fd..aa4593d1be2 100644 --- a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp +++ b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp @@ -77,7 +77,7 @@ mir::UniqueModulePtr create_display_platform( auto create_rendering_platform( mg::SupportedDevice const& device, - std::vector> const& displays, + std::vector> const& displays, mo::Option const&, mir::EmergencyCleanupRegistry&) -> mir::UniqueModulePtr { @@ -306,6 +306,141 @@ auto probe_display_platform( return supported_devices; } +auto probe_rendering_platform( + std::shared_ptr const&, + std::shared_ptr const& udev, + mir::options::ProgramOption const& options) -> std::vector +{ + mir::assert_entry_point_signature(&probe_rendering_platform); + + mgg::Quirks quirks{options}; + + mir::udev::Enumerator drm_devices{udev}; + drm_devices.match_subsystem("drm"); + drm_devices.match_sysname("card[0-9]*"); + drm_devices.match_sysname("renderD[0-9]*"); + drm_devices.scan_devices(); + + if (drm_devices.begin() == drm_devices.end()) + { + mir::log_info("Unsupported: No DRM devices detected"); + return {}; + } + + // We also require GBM EGL platform + auto const* client_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (!client_extensions) + { + // Doesn't support EGL client extensions; Mesa does, so this is unlikely to be gbm-kms. + mir::log_info("Unsupported: EGL platform does not support client extensions."); + return {}; + } + if (strstr(client_extensions, "EGL_KHR_platform_gbm") == nullptr) + { + // Doesn't support the Khronos-standardised GBM platform… + mir::log_info("EGL platform does not support EGL_KHR_platform_gbm extension"); + // …maybe we support the old pre-standardised Mesa GBM platform? + if (strstr(client_extensions, "EGL_MESA_platform_gbm") == nullptr) + { + mir::log_info( + "Unsupported: EGL platform supports neither EGL_KHR_platform_gbm nor EGL_MESA_platform_gbm"); + return {}; + } + } + + // Check for suitability + std::vector supported_devices; + for (auto& device : drm_devices) + { + if (quirks.should_skip(device)) + { + mir::log_info("Not probing device %s due to specified quirk", device.devnode()); + continue; + } + auto const device_node = device.devnode(); + if (device_node == nullptr) + { + /* The display connectors attached to the card appear as subdevices + * of the card[0-9] node. + * These won't have a device node, so pass on anything that doesn't have + * a /dev/dri/card* node + */ + continue; + } + + try + { + // Open the device node directly; we don't need DRM master + mir::Fd tmp_fd{::open(device_node, O_RDWR | O_CLOEXEC)}; + + /* We prefer render nodes for the RenderingPlatform. + * Check if this device has an associated render node, + * and skip it if it does. + */ + std::unique_ptr> const render_node{ + drmGetRenderDeviceNameFromFd(tmp_fd), + &free + }; + if (render_node) + { + // We might already be probing a render node + if (strcmp(device_node, render_node.get())) + { + // The render node of this device is not *this* device, + // so let's pass and let us open the render node later. + continue; + } + } + + // We know we've got a device that we *might* be able to use + // Mark it as “supported” because we should *at least* be able to get a software context on it. + supported_devices.emplace_back(mg::SupportedDevice{device.clone(), mg::PlatformPriority::supported, nullptr}); + if (tmp_fd != mir::Fd::invalid) + { + mgg::helpers::GBMHelper gbm_device{tmp_fd}; + mgg::helpers::EGLHelper egl{MinimalGLConfig()}; + + egl.setup(gbm_device); + + egl.make_current(); + + auto const renderer_string = reinterpret_cast(glGetString(GL_RENDERER)); + if (!renderer_string) + { + throw mg::gl_error( + "Probe failed to query GL renderer"); + } + + // TODO: Probe for necessary EGL extensions (ie: at least one of WL_bind_display / dmabuf_import + + if (strncmp( + "llvmpipe", + renderer_string, + strlen("llvmpipe")) == 0) + { + mir::log_info("Detected software renderer: %s", renderer_string); + // Leave the priority at ::supported; if we've got a software renderer then + // we *don't* support *this* device very well. + } + else + { + // We've got a non-software renderer. That's our cue! + supported_devices.back().support_level = mg::PlatformPriority::best; + } + } + } + catch (std::exception const& e) + { + mir::log( + mir::logging::Severity::informational, + MIR_LOG_COMPONENT, + std::current_exception(), + "Failed to probe DRM device"); + } + } + return supported_devices; +} + namespace { mir::ModuleProperties const description = { @@ -322,3 +457,4 @@ mir::ModuleProperties const* describe_graphics_module() mir::assert_entry_point_signature(&describe_graphics_module); return &description; } + diff --git a/src/platforms/gbm-kms/server/kms/symbols.map.in b/src/platforms/gbm-kms/server/kms/symbols.map.in index 30dad63f1b7..59fbd10490d 100644 --- a/src/platforms/gbm-kms/server/kms/symbols.map.in +++ b/src/platforms/gbm-kms/server/kms/symbols.map.in @@ -4,6 +4,8 @@ probe_display_platform; describe_graphics_module; create_display_platform; + probe_rendering_platform; + create_rendering_platform; local: *; }; diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index 6416d9ae27d..1c9e02b361d 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -366,12 +366,12 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface CPUCopyOutputSurface( EGLDisplay dpy, EGLContext ctx, - std::unique_ptr allocator, + std::shared_ptr allocator, geom::Size size) : allocator{std::move(allocator)}, dpy{dpy}, ctx{ctx}, - size_{std::move(size)} + size_{size} { if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE) { @@ -429,7 +429,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface auto commit() -> std::unique_ptr override { - auto fb = allocator->acquire(); + auto fb = allocator->alloc_fb(size()); glBindFramebuffer(GL_FRAMEBUFFER, fbo); { auto mapping = fb->map_writeable(); @@ -460,7 +460,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface } private: - std::unique_ptr const allocator; + std::shared_ptr const allocator; EGLDisplay const dpy; EGLContext const ctx; geom::Size const size_; @@ -472,10 +472,8 @@ class EGLOutputSurface : public mg::gl::OutputSurface { public: EGLOutputSurface( - std::unique_ptr fb, - geom::Size size) - : fb{std::move(fb)}, - size_{size} + std::unique_ptr fb) + : fb{std::move(fb)} { } @@ -495,7 +493,7 @@ class EGLOutputSurface : public mg::gl::OutputSurface auto size() const -> geom::Size override { - return size_; + return fb->size(); } auto layout() const -> Layout override @@ -505,32 +503,32 @@ class EGLOutputSurface : public mg::gl::OutputSurface private: std::unique_ptr const fb; - geom::Size const size_; }; } -auto mge::GLRenderingProvider::surface_for_output(DisplayBuffer& db, GLConfig const& config) +auto mge::GLRenderingProvider::surface_for_output( + std::shared_ptr framebuffer_provider, + geometry::Size size, + GLConfig const& config) -> std::unique_ptr { - if (auto egl_display = DisplayPlatform::acquire_interface(db.owner())) + if (auto egl_display = framebuffer_provider->acquire_interface()) { - return std::make_unique( - egl_display->framebuffer_for_db(db, config, ctx), - db.view_area().size); + return std::make_unique(egl_display->alloc_framebuffer(config, ctx)); } - auto dumb_display = DisplayPlatform::acquire_interface(db.owner()); + auto dumb_display = framebuffer_provider->acquire_interface(); return std::make_unique( dpy, ctx, - dumb_display->allocator_for_db(db), - db.view_area().size); + std::move(dumb_display), + size); } -auto mge::GLRenderingProvider::make_framebuffer_provider(DisplayBuffer const& /*target*/) +auto mge::GLRenderingProvider::make_framebuffer_provider(std::shared_ptr /*target*/) -> std::unique_ptr { - // TODO: Make this not a null implementation, so bypass/overlays can work again + // TODO: Work out under what circumstances the EGL renderer *can* provide overlayable framebuffers class NullFramebufferProvider : public FramebufferProvider { public: diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.h b/src/platforms/renderer-generic-egl/buffer_allocator.h index 4533b6372c1..dfbd9a91157 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.h +++ b/src/platforms/renderer-generic-egl/buffer_allocator.h @@ -88,12 +88,15 @@ class GLRenderingProvider : public graphics::GLRenderingProvider EGLDisplay dpy, EGLContext ctx); - auto make_framebuffer_provider(DisplayBuffer const& target) + auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr override; auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; - auto surface_for_output(DisplayBuffer& db, GLConfig const& config) -> std::unique_ptr override; + auto surface_for_output( + std::shared_ptr framebuffer_provider, + geometry::Size size, + GLConfig const& config) -> std::unique_ptr override; private: EGLDisplay const dpy; diff --git a/src/platforms/renderer-generic-egl/platform_symbols.cpp b/src/platforms/renderer-generic-egl/platform_symbols.cpp index 85746ade4e0..a1d197c43a2 100644 --- a/src/platforms/renderer-generic-egl/platform_symbols.cpp +++ b/src/platforms/renderer-generic-egl/platform_symbols.cpp @@ -35,11 +35,11 @@ namespace mge = mg::egl::generic; auto create_rendering_platform( mg::SupportedDevice const&, - std::vector> const& displays, + std::vector> const& displays, mo::Option const&, mir::EmergencyCleanupRegistry&) -> mir::UniqueModulePtr { - mir::assert_entry_point_signature(&create_rendering_platform); + mir::assert_entry_point_signature(&create_rendering_platform); return mir::make_module_ptr(displays); } diff --git a/src/platforms/renderer-generic-egl/rendering_platform.cpp b/src/platforms/renderer-generic-egl/rendering_platform.cpp index 65ea99a869f..9a06b3fc2d3 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.cpp +++ b/src/platforms/renderer-generic-egl/rendering_platform.cpp @@ -27,11 +27,11 @@ namespace mge = mg::egl::generic; namespace { -auto egl_display_from_platforms(std::vector> const& displays) -> EGLDisplay +auto egl_display_from_platforms(std::vector> const& displays) -> EGLDisplay { for (auto const& display : displays) { - if (auto egl_provider = mg::DisplayPlatform::acquire_interface(display)) + if (auto egl_provider = display->acquire_interface()) { return egl_provider->get_egl_display(); } @@ -78,7 +78,7 @@ auto make_share_only_context(EGLDisplay dpy) -> EGLContext } } -mge::RenderingPlatform::RenderingPlatform(std::vector> const& displays) +mge::RenderingPlatform::RenderingPlatform(std::vector> const& displays) : dpy{egl_display_from_platforms(displays)}, ctx{make_share_only_context(dpy)} { diff --git a/src/platforms/renderer-generic-egl/rendering_platform.h b/src/platforms/renderer-generic-egl/rendering_platform.h index af2a387961e..171df8273e4 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.h +++ b/src/platforms/renderer-generic-egl/rendering_platform.h @@ -30,7 +30,7 @@ namespace graphics::egl::generic class RenderingPlatform : public graphics::RenderingPlatform { public: - explicit RenderingPlatform(std::vector> const& displays); + explicit RenderingPlatform(std::vector> const& displays); auto create_buffer_allocator( graphics::Display const& output) -> UniqueModulePtr override; diff --git a/src/platforms/wayland/displayclient.cpp b/src/platforms/wayland/displayclient.cpp index 9f8b4d619fd..b396a4f4971 100644 --- a/src/platforms/wayland/displayclient.cpp +++ b/src/platforms/wayland/displayclient.cpp @@ -100,7 +100,7 @@ class mgw::DisplayClient::Output : auto view_area() const -> geometry::Rectangle override; bool overlay(std::vector const& renderlist) override; auto transformation() const -> glm::mat2 override; - auto owner() const -> std::shared_ptr override; + auto display_provider() const -> std::shared_ptr override; void set_next_image(std::unique_ptr content) override; private: @@ -429,7 +429,7 @@ auto mgw::DisplayClient::Output::transformation() const -> glm::mat2 return glm::mat2{1}; } -auto mgw::DisplayClient::Output::owner() const -> std::shared_ptr +auto mgw::DisplayClient::Output::display_provider() const -> std::shared_ptr { return {}; } diff --git a/src/platforms/wayland/platform.cpp b/src/platforms/wayland/platform.cpp index 35668efb594..d3f7041d3e8 100644 --- a/src/platforms/wayland/platform.cpp +++ b/src/platforms/wayland/platform.cpp @@ -16,6 +16,7 @@ #include "platform.h" #include "display.h" +#include "mir/graphics/platform.h" namespace mg = mir::graphics; namespace mgw = mir::graphics::wayland; @@ -31,11 +32,11 @@ mir::UniqueModulePtr mgw::Platform::create_display( std::shared_ptr const&, std::shared_ptr const& gl_config) { - return mir::make_module_ptr(wl_display, gl_config, report); + return mir::make_module_ptr(wl_display, gl_config, report); } -auto mgw::Platform::maybe_create_interface( - const DisplayInterfaceBase::Tag& /*tag*/) -> std::shared_ptr +auto mgw::Platform::interface_for() -> std::shared_ptr { - return {}; + return nullptr; } + diff --git a/src/platforms/wayland/platform.h b/src/platforms/wayland/platform.h index 6544c02ac7f..1ac260d8a53 100644 --- a/src/platforms/wayland/platform.h +++ b/src/platforms/wayland/platform.h @@ -46,9 +46,9 @@ class Platform : public graphics::DisplayPlatform std::shared_ptr const& initial_conf_policy, std::shared_ptr const& gl_config) override; +protected: + auto interface_for() -> std::shared_ptr override; private: - auto maybe_create_interface(DisplayInterfaceBase::Tag const& tag) - -> std::shared_ptr override; struct wl_display* const wl_display; std::shared_ptr const report; diff --git a/src/platforms/x11/graphics/display.cpp b/src/platforms/x11/graphics/display.cpp index 8c7ca86d8c3..cb80d393975 100644 --- a/src/platforms/x11/graphics/display.cpp +++ b/src/platforms/x11/graphics/display.cpp @@ -103,7 +103,7 @@ mgx::X11Window::operator xcb_window_t() const } mgx::Display::Display( - std::shared_ptr parent, + std::shared_ptr parent, std::shared_ptr const& x11_resources, std::string const title, std::vector const& requested_sizes, diff --git a/src/platforms/x11/graphics/display.h b/src/platforms/x11/graphics/display.h index 2e270b0c43b..502e755913b 100644 --- a/src/platforms/x11/graphics/display.h +++ b/src/platforms/x11/graphics/display.h @@ -43,6 +43,7 @@ namespace X class DisplayBuffer; class X11OutputConfig; +class Platform; class X11Window { @@ -63,7 +64,7 @@ class Display : public graphics::Display { public: Display( - std::shared_ptr parent, + std::shared_ptr parent, std::shared_ptr const& x11_resources, std::string const title, std::vector const& requested_size, @@ -107,7 +108,7 @@ class Display : public graphics::Display std::shared_ptr const config; }; - std::shared_ptr const parent; + std::shared_ptr const parent; std::shared_ptr const x11_resources; geometry::SizeF pixel_size_mm; std::shared_ptr const report; diff --git a/src/platforms/x11/graphics/display_buffer.cpp b/src/platforms/x11/graphics/display_buffer.cpp index a8d10823758..6813d013439 100644 --- a/src/platforms/x11/graphics/display_buffer.cpp +++ b/src/platforms/x11/graphics/display_buffer.cpp @@ -15,6 +15,7 @@ */ #include "mir/fatal.h" +#include "platform.h" #include "display_buffer.h" #include "display_configuration.h" #include "mir/graphics/display_report.h" @@ -25,7 +26,7 @@ namespace mg=mir::graphics; namespace mgx=mg::X; namespace geom=mir::geometry; -mgx::DisplayBuffer::DisplayBuffer(std::shared_ptr parent, +mgx::DisplayBuffer::DisplayBuffer(std::shared_ptr parent, xcb_window_t win, geometry::Rectangle const& view_area) : parent{std::move(parent)}, @@ -78,9 +79,9 @@ void mgx::DisplayBuffer::set_view_area(geom::Rectangle const& a) area = a; } -auto mgx::DisplayBuffer::owner() const -> std::shared_ptr +auto mgx::DisplayBuffer::display_provider() const -> std::shared_ptr { - return parent; + return parent->provider_for_window(x_win); } void mgx::DisplayBuffer::set_transformation(glm::mat2 const& t) diff --git a/src/platforms/x11/graphics/display_buffer.h b/src/platforms/x11/graphics/display_buffer.h index f9278faf635..44118a0bd7a 100644 --- a/src/platforms/x11/graphics/display_buffer.h +++ b/src/platforms/x11/graphics/display_buffer.h @@ -37,13 +37,14 @@ class DisplayReport; namespace X { +class Platform; class DisplayBuffer : public graphics::DisplayBuffer, public graphics::DisplaySyncGroup { public: DisplayBuffer( - std::shared_ptr parent, + std::shared_ptr parent, xcb_window_t win, geometry::Rectangle const& view_area); @@ -54,7 +55,7 @@ class DisplayBuffer : public graphics::DisplayBuffer, glm::mat2 transformation() const override; - auto owner() const -> std::shared_ptr override; + auto display_provider() const -> std::shared_ptr override; void set_view_area(geometry::Rectangle const& a); void set_transformation(glm::mat2 const& t); @@ -66,7 +67,7 @@ class DisplayBuffer : public graphics::DisplayBuffer, auto x11_window() const -> xcb_window_t; private: - std::shared_ptr const parent; + std::shared_ptr const parent; std::shared_ptr next_frame; geometry::Rectangle area; glm::mat2 transform; diff --git a/src/platforms/x11/graphics/egl_helper.cpp b/src/platforms/x11/graphics/egl_helper.cpp index c44bbc9617a..4f57d9d002d 100644 --- a/src/platforms/x11/graphics/egl_helper.cpp +++ b/src/platforms/x11/graphics/egl_helper.cpp @@ -48,16 +48,22 @@ class mgxh::Framebuffer::EGLState EGLSurface const surf; }; -mgxh::Framebuffer::Framebuffer(EGLDisplay dpy, EGLContext ctx, EGLSurface surf) - : Framebuffer(std::make_shared(dpy, ctx, surf)) +mgxh::Framebuffer::Framebuffer(EGLDisplay dpy, EGLContext ctx, EGLSurface surf, geometry::Size size) + : Framebuffer(std::make_shared(dpy, ctx, surf), size) { } -mgxh::Framebuffer::Framebuffer(std::shared_ptr state) - : state{std::move(state)} +mgxh::Framebuffer::Framebuffer(std::shared_ptr state, geometry::Size size) + : state{std::move(state)}, + size_{size} { } +auto mgxh::Framebuffer::size() const -> geometry::Size +{ + return size_; +} + void mgxh::Framebuffer::make_current() { if (eglMakeCurrent(state->dpy, state->surf, state->surf, state->ctx) != EGL_TRUE) @@ -76,7 +82,7 @@ void mgxh::Framebuffer::swap_buffers() auto mgxh::Framebuffer::clone_handle() -> std::unique_ptr { - return std::unique_ptr{new Framebuffer(state)}; + return std::unique_ptr{new Framebuffer(state, size_)}; } mgxh::EGLHelper::EGLHelper(::Display* const x_dpy) @@ -153,8 +159,24 @@ mgxh::EGLHelper::EGLHelper(int stencil_bits, int depth_bits) { } +namespace +{ +auto size_for_x_win(xcb_connection_t* xcb_conn, xcb_window_t win) -> mir::geometry::Size +{ + auto cookie = xcb_get_geometry(xcb_conn, win); + if (auto reply = xcb_get_geometry_reply(xcb_conn, cookie, nullptr)) + { + mir::geometry::Size const window_size{reply->width, reply->height}; + free(reply); + return window_size; + } + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to get X11 window size"})); +} +} + auto mgxh::EGLHelper::framebuffer_for_window( GLConfig const& conf, + xcb_connection_t* xcb_conn, xcb_window_t win, EGLContext shared_context) -> std::unique_ptr { @@ -192,7 +214,11 @@ auto mgxh::EGLHelper::framebuffer_for_window( if (egl_context == EGL_NO_CONTEXT) BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); - return std::make_unique(egl_display, egl_context, egl_surface); + return std::make_unique( + egl_display, + egl_context, + egl_surface, + size_for_x_win(xcb_conn, win)); } void mgxh::EGLHelper::setup_internal(::Display* x_dpy, bool initialize) diff --git a/src/platforms/x11/graphics/egl_helper.h b/src/platforms/x11/graphics/egl_helper.h index a7d1dfb38a6..24d1faf42ad 100644 --- a/src/platforms/x11/graphics/egl_helper.h +++ b/src/platforms/x11/graphics/egl_helper.h @@ -48,7 +48,9 @@ class Framebuffer : public GenericEGLDisplayProvider::EGLFramebuffer * final handle generated from this Framebuffer is released, * the EGL resources \param ctx and \param surff will be freed. */ - Framebuffer(EGLDisplay dpy, EGLContext ctx, EGLSurface surf); + Framebuffer(EGLDisplay dpy, EGLContext ctx, EGLSurface surf, geometry::Size size); + + auto size() const -> geometry::Size override; void make_current() override; auto clone_handle() -> std::unique_ptr override; @@ -56,9 +58,10 @@ class Framebuffer : public GenericEGLDisplayProvider::EGLFramebuffer void swap_buffers(); private: class EGLState; - Framebuffer(std::shared_ptr surf); + Framebuffer(std::shared_ptr surf, geometry::Size size); std::shared_ptr const state; + geometry::Size const size_; }; class EGLHelper @@ -74,7 +77,11 @@ class EGLHelper EGLHelper(GLConfig const& gl_config, ::Display* const x_dpy, xcb_window_t win, EGLContext shared_context); ~EGLHelper() noexcept; - auto framebuffer_for_window(GLConfig const& conf, xcb_window_t win, EGLContext shared_context) + auto framebuffer_for_window( + GLConfig const& conf, + xcb_connection_t* xcb_conn, + xcb_window_t win, + EGLContext shared_context) -> std::unique_ptr; EGLDisplay display() const { return egl_display; } diff --git a/src/platforms/x11/graphics/platform.cpp b/src/platforms/x11/graphics/platform.cpp index bb29ee1dfb7..9e1e72056f3 100644 --- a/src/platforms/x11/graphics/platform.cpp +++ b/src/platforms/x11/graphics/platform.cpp @@ -21,9 +21,9 @@ #include "mir/graphics/egl_error.h" #include "mir/graphics/platform.h" #include "mir/options/option.h" +#include -namespace mo = mir::options; namespace mg = mir::graphics; namespace mgx = mg::X; namespace geom = mir::geometry; @@ -112,7 +112,8 @@ mgx::Platform::Platform(std::shared_ptr const& x11_resourc std::string title, std::vector output_sizes, std::shared_ptr const& report) - : x11_resources{x11_resources}, + : egl_provider{std::make_shared(x11_resources->xlib_dpy)}, + x11_resources{x11_resources}, title{std::move(title)}, report{report}, output_sizes{std::move(output_sizes)} @@ -125,41 +126,86 @@ mir::UniqueModulePtr mgx::Platform::create_display( std::shared_ptr const& initial_conf_policy, std::shared_ptr const& /*gl_config*/) { - return make_module_ptr(shared_from_this(), x11_resources, title, output_sizes, initial_conf_policy, report); + return make_module_ptr( + std::dynamic_pointer_cast(shared_from_this()), + x11_resources, + title, + output_sizes, + initial_conf_policy, + report); } -class mgx::Platform::EGLDisplayProvider : public mg::GenericEGLDisplayProvider +class mgx::Platform::InterfaceProvider : public mg::DisplayInterfaceProvider { public: - EGLDisplayProvider(::Display* const x_dpy) - : egl_helper{x_dpy} + InterfaceProvider(::Display* x_dpy) + : egl{std::make_shared(x_dpy)}, + connection{nullptr}, + win{std::nullopt} { } - auto get_egl_display() -> EGLDisplay + InterfaceProvider( + InterfaceProvider const& from, + std::shared_ptr connection, + xcb_window_t win) + : egl{from.egl}, + connection{std::move(connection)}, + win{win} { - return egl_helper.display(); } - auto framebuffer_for_db(mg::DisplayBuffer& db, mg::GLConfig const& config, EGLContext share_context) - -> std::unique_ptr + class EGLDisplayProvider : public mg::GenericEGLDisplayProvider { - auto& x11db = dynamic_cast(db); - return egl_helper.framebuffer_for_window(config, x11db.x11_window(), share_context); - } + public: + EGLDisplayProvider( + std::shared_ptr egl, + std::shared_ptr connection, + std::optional x_win) + : egl{std::move(egl)}, + connection{std::move(connection)}, + x_win{std::move(x_win)} + { + } + + auto get_egl_display() -> EGLDisplay + { + return egl->display(); + } + + auto alloc_framebuffer(mg::GLConfig const& config, EGLContext share_context) + -> std::unique_ptr + { + return egl->framebuffer_for_window(config, connection->conn->connection(), x_win.value(), share_context); + } + private: + std::shared_ptr const egl; + std::shared_ptr const connection; + std::optional const x_win; + }; + + auto maybe_create_interface(mir::graphics::DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr + { + if (dynamic_cast(&type_tag)) + { + return std::make_shared(egl, connection, win); + } + return {}; + } + private: - mgx::helpers::EGLHelper egl_helper; + std::shared_ptr const egl; + std::shared_ptr const connection; + std::optional const win; }; -auto mgx::Platform::maybe_create_interface(mir::graphics::DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr +auto mgx::Platform::interface_for() -> std::shared_ptr { - if (dynamic_cast(&type_tag)) - { - std::call_once( - provider_constructed, - [this]() { egl_provider = std::make_shared(x11_resources->xlib_dpy); }); - return egl_provider; - } - return {}; + return egl_provider; } + +auto mgx::Platform::provider_for_window(xcb_window_t x_win) -> std::shared_ptr +{ + return std::make_shared(*egl_provider, x11_resources, x_win); +} \ No newline at end of file diff --git a/src/platforms/x11/graphics/platform.h b/src/platforms/x11/graphics/platform.h index 6ed98d26be5..abbebba8206 100644 --- a/src/platforms/x11/graphics/platform.h +++ b/src/platforms/x11/graphics/platform.h @@ -21,6 +21,7 @@ #include "mir/graphics/platform.h" #include "mir/geometry/size.h" +#include #include namespace mir @@ -60,25 +61,25 @@ class Platform : public graphics::DisplayPlatform // Parses colon separated list of sizes in the form WIDTHxHEIGHT^SCALE (^SCALE is optional) static auto parse_output_sizes(std::string output_sizes) -> std::vector; - explicit Platform(std::shared_ptr const& x11_resources, - std::string const title, - std::vector output_sizes, - std::shared_ptr const& report); + Platform( + std::shared_ptr const& x11_resources, + std::string const title, + std::vector output_sizes, + std::shared_ptr const& report); ~Platform() = default; /* From Platform */ - UniqueModulePtr create_display( + auto create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config) override; + std::shared_ptr const& gl_config) -> UniqueModulePtr override; + auto provider_for_window(xcb_window_t x_win) -> std::shared_ptr; protected: - auto maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr override; + auto interface_for() -> std::shared_ptr override; private: - class EGLDisplayProvider; - std::once_flag provider_constructed; - std::shared_ptr egl_provider; + class InterfaceProvider; + std::shared_ptr const egl_provider; std::shared_ptr const x11_resources; std::string const title; std::shared_ptr const report; diff --git a/src/server/compositor/basic_screen_shooter.cpp b/src/server/compositor/basic_screen_shooter.cpp index 9910c4aee93..deedd6e3b34 100644 --- a/src/server/compositor/basic_screen_shooter.cpp +++ b/src/server/compositor/basic_screen_shooter.cpp @@ -22,6 +22,7 @@ #include "mir/compositor/scene.h" #include "mir/log.h" #include "mir/executor.h" +#include "mir/graphics/platform.h" namespace mc = mir::compositor; namespace mr = mir::renderer; diff --git a/src/server/compositor/basic_screen_shooter.h b/src/server/compositor/basic_screen_shooter.h index 5212ebd4915..5201abde83f 100644 --- a/src/server/compositor/basic_screen_shooter.h +++ b/src/server/compositor/basic_screen_shooter.h @@ -18,6 +18,8 @@ #define MIR_COMPOSITOR_BASIC_SCREEN_SHOOTER_H_ #include "mir/compositor/screen_shooter.h" +#include "mir/graphics/platform.h" +#include "mir/renderer/renderer_factory.h" #include "mir/time/clock.h" #include @@ -28,6 +30,7 @@ class Executor; namespace renderer { class Renderer; +class RendererFactory; namespace gl { class BufferOutputSurface; @@ -44,8 +47,8 @@ class BasicScreenShooter: public ScreenShooter std::shared_ptr const& scene, std::shared_ptr const& clock, Executor& executor, - std::unique_ptr&& render_target, - std::unique_ptr&& renderer); + graphics::GLRenderingProvider& platform, + renderer::RendererFactory& render_factory); void capture( std::shared_ptr const& buffer, @@ -58,18 +61,19 @@ class BasicScreenShooter: public ScreenShooter Self( std::shared_ptr const& scene, std::shared_ptr const& clock, - std::unique_ptr&& render_target, - std::unique_ptr&& renderer); + std::unique_ptr renderer); auto render( std::shared_ptr const& buffer, geometry::Rectangle const& area) -> time::Timestamp; + class HeadlessDisplay; + std::mutex mutex; std::shared_ptr const scene; - std::unique_ptr const render_target; std::unique_ptr const renderer; std::shared_ptr const clock; + std::shared_ptr const output; }; std::shared_ptr const self; Executor& executor; diff --git a/src/server/compositor/default_configuration.cpp b/src/server/compositor/default_configuration.cpp index 6c9e80e0aa5..a59a8f0ec95 100644 --- a/src/server/compositor/default_configuration.cpp +++ b/src/server/compositor/default_configuration.cpp @@ -98,29 +98,26 @@ std::shared_ptr mir::DefaultServerConfiguration: auto mir::DefaultServerConfiguration::the_screen_shooter() -> std::shared_ptr { return screen_shooter( - []() -> std::shared_ptr + [/*this*/]() -> std::shared_ptr { -/* try - { - auto render_target = std::make_unique(the_display()->create_gl_context()); - auto renderer = the_renderer_factory()->create_renderer_for(*render_target); - return std::make_shared( - the_scene(), - the_clock(), - thread_pool_executor, - std::move(render_target), - ); // TODO: WE'VE BROKEN THE SCREENSHOOTER FOR NOW - } - catch (...) - { - mir::log( - ::mir::logging::Severity::error, - "", - std::current_exception(), - "failed to create BasicScreenShooter"); - return std::make_shared(thread_pool_executor); - } -*/ + // try + // { + // return std::make_shared( + // the_scene(), + // the_clock(), + // thread_pool_executor, + // the_renderer_factory()); + // } + // catch (...) + // { + // mir::log( + // ::mir::logging::Severity::error, + // "", + // std::current_exception(), + // "failed to create BasicScreenShooter"); + // return std::make_shared(thread_pool_executor); + // } + return std::make_shared(thread_pool_executor); }); } diff --git a/src/server/compositor/default_display_buffer_compositor.cpp b/src/server/compositor/default_display_buffer_compositor.cpp index 585c59aa0ea..6d0cb241b52 100644 --- a/src/server/compositor/default_display_buffer_compositor.cpp +++ b/src/server/compositor/default_display_buffer_compositor.cpp @@ -37,7 +37,7 @@ mc::DefaultDisplayBufferCompositor::DefaultDisplayBufferCompositor( std::shared_ptr const& report) : display_buffer(display_buffer), renderer(renderer), - fb_adaptor{gl_provider.make_framebuffer_provider(display_buffer)}, + fb_adaptor{gl_provider.make_framebuffer_provider(display_buffer.display_provider())}, report(report) { } diff --git a/src/server/compositor/default_display_buffer_compositor_factory.cpp b/src/server/compositor/default_display_buffer_compositor_factory.cpp index dea8137c949..c2a70b40bfb 100644 --- a/src/server/compositor/default_display_buffer_compositor_factory.cpp +++ b/src/server/compositor/default_display_buffer_compositor_factory.cpp @@ -55,7 +55,8 @@ mc::DefaultDisplayBufferCompositorFactory::create_compositor_for( * GL surface could be the common case, and not allocating it would save a * potentially-significant amount of GPU memory. */ - auto output_surface = allocator->surface_for_output(display_buffer, *gl_config); + auto output_surface = allocator->surface_for_output( + display_buffer.display_provider(), display_buffer.view_area().size, *gl_config); auto renderer = renderer_factory->create_renderer_for(std::move(output_surface), allocator); renderer->set_viewport(display_buffer.view_area()); return std::make_unique( diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index a4e11ee9ab8..09f384118df 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -310,6 +310,13 @@ auto mir::DefaultServerConfiguration::the_rendering_platforms() -> platform_modules = mir::graphics::rendering_modules_for_device(platforms, dynamic_cast(*the_options()), the_console_services()); } + std::vector> display_interfaces; + display_interfaces.reserve(the_display_platforms().size()); + + for (auto& display : the_display_platforms()) + { + display_interfaces.push_back(mg::DisplayPlatform::interface_for(display)); + } for (auto const& [device, platform]: platform_modules) { auto create_rendering_platform = platform->load_function( @@ -346,7 +353,7 @@ auto mir::DefaultServerConfiguration::the_rendering_platforms() -> rendering_platforms.push_back( create_rendering_platform( device, - the_display_platforms(), + display_interfaces, *the_options(), *the_emergency_cleanup())); // Add this module to the list searched by the input stack later diff --git a/tests/include/mir/test/doubles/mock_display_buffer.h b/tests/include/mir/test/doubles/mock_display_buffer.h index e31f5fdbfbe..5803580fb9c 100644 --- a/tests/include/mir/test/doubles/mock_display_buffer.h +++ b/tests/include/mir/test/doubles/mock_display_buffer.h @@ -42,7 +42,7 @@ class MockDisplayBuffer : public graphics::DisplayBuffer MOCK_METHOD(bool, overlay, (std::vector const&), (override)); MOCK_METHOD(void, set_next_image, (std::unique_ptr), (override)); MOCK_METHOD(glm::mat2, transformation, (), (const override)); - MOCK_METHOD(std::shared_ptr, owner, (), (const override)); + MOCK_METHOD(std::shared_ptr, display_provider, (), (const override)); }; } diff --git a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h index fbb09f38469..12fc08afe09 100644 --- a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h @@ -21,6 +21,7 @@ #include +#include "mir/graphics/gl_config.h" #include "mir/graphics/platform.h" namespace mir::test::doubles @@ -29,7 +30,11 @@ class MockGlRenderingPlatform : public graphics::GLRenderingProvider { public: MOCK_METHOD(std::shared_ptr, as_texture, (std::shared_ptr), (override)); - MOCK_METHOD(std::unique_ptr, surface_for_output, (graphics::DisplayBuffer&), (override)); + MOCK_METHOD( + std::unique_ptr, + surface_for_output, + (std::shared_ptr, geometry::Size, graphics::GLConfig const&), + (override)); }; } diff --git a/tests/include/mir/test/doubles/null_display_buffer.h b/tests/include/mir/test/doubles/null_display_buffer.h index ec81aeedde4..ee61d69ccc2 100644 --- a/tests/include/mir/test/doubles/null_display_buffer.h +++ b/tests/include/mir/test/doubles/null_display_buffer.h @@ -33,7 +33,7 @@ class NullDisplayBuffer : public graphics::DisplayBuffer bool overlay(std::vector const&) override { return false; } void set_next_image(std::unique_ptr) override { } glm::mat2 transformation() const override { return glm::mat2(1); } - auto owner() const -> std::shared_ptr override { return {};} + auto display_provider() const -> std::shared_ptr override { return {};} }; } diff --git a/tests/include/mir/test/doubles/null_platform.h b/tests/include/mir/test/doubles/null_platform.h index f13f23ab024..0d805043288 100644 --- a/tests/include/mir/test/doubles/null_platform.h +++ b/tests/include/mir/test/doubles/null_platform.h @@ -37,8 +37,9 @@ class NullDisplayPlatform : public graphics::DisplayPlatform return mir::make_module_ptr(); } - auto maybe_create_interface(graphics::DisplayInterfaceBase::Tag const&) - -> std::shared_ptr override +protected: + auto interface_for() + -> std::shared_ptr override { return nullptr; } diff --git a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h index eeb6c3e068b..dfb030d63ac 100644 --- a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h @@ -51,13 +51,14 @@ class StubGlRenderingPlatform : public graphics::GLRenderingProvider } auto surface_for_output( - graphics::DisplayBuffer&, + std::shared_ptr, + geometry::Size, graphics::GLConfig const&) -> std::unique_ptr override { return std::make_unique>(); } - auto make_framebuffer_provider(graphics::DisplayBuffer const& /*target*/) + auto make_framebuffer_provider(std::shared_ptr /*target*/) -> std::unique_ptr override { class NullFramebufferProvider : public FramebufferProvider diff --git a/tests/mir_test_framework/headless_display_buffer_compositor_factory.cpp b/tests/mir_test_framework/headless_display_buffer_compositor_factory.cpp index 348c8e658bb..93c92f18967 100644 --- a/tests/mir_test_framework/headless_display_buffer_compositor_factory.cpp +++ b/tests/mir_test_framework/headless_display_buffer_compositor_factory.cpp @@ -119,6 +119,7 @@ mtf::HeadlessDisplayBufferCompositorFactory::create_compositor_for(mg::DisplayBu std::shared_ptr const render_platform; std::shared_ptr const tracker; }; - auto output_surface = render_platform->surface_for_output(db, *gl_config); + auto output_surface = + render_platform->surface_for_output(db.display_provider(), db.view_area().size, *gl_config); return std::make_unique(db, std::move(output_surface), render_platform, tracker); } diff --git a/tests/mir_test_framework/platform_graphics_throw.cpp b/tests/mir_test_framework/platform_graphics_throw.cpp index f61a08f2cc8..ebea709cd38 100644 --- a/tests/mir_test_framework/platform_graphics_throw.cpp +++ b/tests/mir_test_framework/platform_graphics_throw.cpp @@ -67,10 +67,9 @@ class ExceptionThrowingPlatform : public mg::DisplayPlatform, public mg::Renderi return nullptr; } - auto maybe_create_interface( - mg::DisplayInterfaceBase::Tag const&) -> std::shared_ptr override + auto interface_for() -> std::shared_ptr override { - return nullptr; + return mg::DisplayPlatform::interface_for(stub_display_platform); } public: @@ -123,7 +122,7 @@ class ExceptionThrowingPlatform : public mg::DisplayPlatform, public mg::Renderi std::unordered_map> const should_throw; mir::UniqueModulePtr stub_render_platform; - mir::UniqueModulePtr stub_display_platform; + std::shared_ptr stub_display_platform; }; } @@ -181,7 +180,7 @@ void add_graphics_platform_options(boost::program_options::options_description&) mir::UniqueModulePtr create_rendering_platform( mg::SupportedDevice const&, - std::vector> const&, + std::vector> const&, mo::Option const&, mir::EmergencyCleanupRegistry&) { diff --git a/tests/mir_test_framework/stubbed_graphics_platform.cpp b/tests/mir_test_framework/stubbed_graphics_platform.cpp index 9b2036d62d8..8bfaf7e7d57 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.cpp +++ b/tests/mir_test_framework/stubbed_graphics_platform.cpp @@ -102,11 +102,19 @@ auto mtf::StubGraphicPlatform::maybe_create_interface( return nullptr; } -auto mtf::StubGraphicPlatform::maybe_create_interface( - mg::DisplayInterfaceBase::Tag const& /*tag*/) - -> std::shared_ptr +auto mtf::StubGraphicPlatform::interface_for() + -> std::shared_ptr { - return nullptr; + class NullInterfaceProvider : public mg::DisplayInterfaceProvider + { + protected: + auto maybe_create_interface(mg::DisplayInterfaceBase::Tag const&) + -> std::shared_ptr + { + return nullptr; + } + }; + return std::make_shared(); } namespace @@ -156,7 +164,7 @@ mir::UniqueModulePtr create_display_platform( mir::UniqueModulePtr create_rendering_platform( mg::SupportedDevice const&, - std::vector> const&, + std::vector> const&, mo::Option const&, mir::EmergencyCleanupRegistry&) { diff --git a/tests/mir_test_framework/stubbed_graphics_platform.h b/tests/mir_test_framework/stubbed_graphics_platform.h index 997ee55a7c0..70b93f3e0a2 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.h +++ b/tests/mir_test_framework/stubbed_graphics_platform.h @@ -40,10 +40,8 @@ class StubGraphicPlatform : mir::graphics::RendererInterfaceBase::Tag const& tag) -> std::shared_ptr override; - auto maybe_create_interface( - mir::graphics::DisplayInterfaceBase::Tag const& tag) - -> std::shared_ptr override; - + auto interface_for() + -> std::shared_ptr override; private: std::vector const display_rects; }; diff --git a/tests/mir_test_framework/stubbed_server_configuration.cpp b/tests/mir_test_framework/stubbed_server_configuration.cpp index e720c6a627d..a4e9d2b1589 100644 --- a/tests/mir_test_framework/stubbed_server_configuration.cpp +++ b/tests/mir_test_framework/stubbed_server_configuration.cpp @@ -83,7 +83,7 @@ mtf::StubbedServerConfiguration::StubbedServerConfiguration( mtf::StubbedServerConfiguration::~StubbedServerConfiguration() = default; auto mtf::StubbedServerConfiguration::the_display_platforms() --> std::vector> const& + -> std::vector> const& { if (display_platform.empty()) { diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp index c392f03ac48..358e4a9fc7d 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp @@ -64,6 +64,8 @@ class MockKMSFramebuffer : public FBHandle { return operator_uint32_thunk(); } + + MOCK_METHOD(mir::geometry::Size, size, (), (const override)); }; } @@ -168,7 +170,7 @@ class MesaDisplayBufferTest : public Test StubGLConfig gl_config; std::shared_ptr const bypass_framebuffer; std::vector const bypassable_list; - std::shared_ptr const parent_platform; + std::shared_ptr const parent_platform; }; TEST_F(MesaDisplayBufferTest, unrotated_view_area_is_untouched) diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp index 82f676eee71..a3e64b1afdc 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp @@ -358,17 +358,16 @@ TEST_F(MesaDisplayMultiMonitorTest, flip_flips_all_connected_crtcs) auto platform = create_platform(); auto display = create_display_cloned(platform); - auto allocator_provider = mg::DisplayPlatform::acquire_interface(std::move(platform)); + auto provider = mg::DisplayPlatform::interface_for(std::move(platform))->acquire_interface(); /* First frame: Page flips are scheduled, but not waited for */ display->for_each_display_sync_group( - [allocator_provider](mg::DisplaySyncGroup& group) + [provider](mg::DisplaySyncGroup& group) { group.for_each_display_buffer( - [allocator_provider](mg::DisplayBuffer& db) + [provider](mg::DisplayBuffer& db) { - auto allocator = allocator_provider->allocator_for_db(db); - auto fb = allocator->acquire(); + auto fb = provider->alloc_fb(db.view_area().size); db.set_next_image(std::move(fb)); }); group.post(); @@ -377,13 +376,12 @@ TEST_F(MesaDisplayMultiMonitorTest, flip_flips_all_connected_crtcs) /* Second frame: Previous page flips finish (drmHandleEvent) and new ones are scheduled */ display->for_each_display_sync_group( - [allocator_provider](mg::DisplaySyncGroup& group) + [provider](mg::DisplaySyncGroup& group) { group.for_each_display_buffer( - [allocator_provider](mg::DisplayBuffer& db) + [provider](mg::DisplayBuffer& db) { - auto allocator = allocator_provider->allocator_for_db(db); - auto fb = allocator->acquire(); + auto fb = provider->alloc_fb(db.view_area().size); db.set_next_image(std::move(fb)); }); group.post(); diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_real_kms_output.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_real_kms_output.cpp index ab8c8665b9f..fe7583f8c69 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_real_kms_output.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_real_kms_output.cpp @@ -68,6 +68,10 @@ class MockKMSFramebuffer : public mgg::FBHandle return fb_id; } + auto size() const -> geom::Size override + { + return {}; + } private: uint32_t const fb_id; }; From ec7d148c702712ef25b5414882ed75743b558ce5 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 16 May 2023 16:12:30 +1000 Subject: [PATCH 003/108] tests/x11: Test refusal to start at a higher level The `mgx::Platform` constructor has a precondition that it gets passed a valid `std::shared_ptr`. The new `DisplayProvider` code makes use of that precondition, so we can't easily test for it in the constructor anymore and bail on failure. But (a) we're allowed to misbehave on precondition failures, and (b) the only way for the rest of Mir to get into the `mgx::Platform` constructor is via `create_display_platform`, which already checks this condition before constructing the `mgx::Platform`. So move the test up there, too. --- tests/unit-tests/platforms/x11/test_platform.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/unit-tests/platforms/x11/test_platform.cpp b/tests/unit-tests/platforms/x11/test_platform.cpp index f5158b230be..2198a45a78b 100644 --- a/tests/unit-tests/platforms/x11/test_platform.cpp +++ b/tests/unit-tests/platforms/x11/test_platform.cpp @@ -17,6 +17,7 @@ #include #include +#include "mir/graphics/platform.h" #include "mir/options/program_option.h" #include "src/platforms/x11/graphics/platform.h" #include "src/platforms/x11/x11_resources.h" @@ -83,12 +84,16 @@ TEST_F(X11GraphicsPlatformTest, failure_to_open_x11_display_results_in_an_error) EXPECT_CALL(mock_x11, XOpenDisplay(_)) .WillRepeatedly(Return(nullptr)); + mir::SharedLibrary platform_lib{mtf::server_platform("server-x11")}; + auto create_platform = platform_lib.load_function("create_display_platform"); + EXPECT_THROW( { - std::make_shared( + create_platform( + mg::SupportedDevice{}, + nullptr, + nullptr, nullptr, - "Mir on X", - std::vector{{{1280, 1024}}}, std::make_shared()); }, std::exception); } From 16623a43fde12b0987c8232524b4721b2824bc12 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 16 May 2023 16:13:52 +1000 Subject: [PATCH 004/108] platforms/X11: Drop precondition check This will now never fire, because we've already dereferenced `x11_resources` by the time we get here. This only fired in tests, anyway, as the only way to get here is through `create_display_platform` which already checks this. --- src/platforms/x11/graphics/platform.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platforms/x11/graphics/platform.cpp b/src/platforms/x11/graphics/platform.cpp index 9e1e72056f3..16bd5fe4348 100644 --- a/src/platforms/x11/graphics/platform.cpp +++ b/src/platforms/x11/graphics/platform.cpp @@ -118,8 +118,6 @@ mgx::Platform::Platform(std::shared_ptr const& x11_resourc report{report}, output_sizes{std::move(output_sizes)} { - if (!x11_resources) - BOOST_THROW_EXCEPTION(std::runtime_error("Need valid x11 display")); } mir::UniqueModulePtr mgx::Platform::create_display( From 1fee101fc74e6e70e6e6266b09a91b0e588ef644 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 19 May 2023 17:52:30 +1000 Subject: [PATCH 005/108] inclusive-naming: platform: Dumb -> CPUAddressable "CPUAddressable" is more descriptive than "Dumb" (people had guessed "Stub"). Places where this is a technical term ("dumb buffer" is a part of the KMS API) are retained, with additional context to make it clearer that it is a technical term. --- include/platform/mir/graphics/platform.h | 2 +- .../eglstream-kms/server/buffer_allocator.cpp | 8 ++-- .../eglstream-kms/server/egl_output.cpp | 16 ++++---- .../gbm-kms/server/buffer_allocator.cpp | 8 ++-- .../gbm-kms/server/kms/CMakeLists.txt | 4 +- .../{dumb_fb.cpp => cpu_addressable_fb.cpp} | 38 +++++++++---------- .../kms/{dumb_fb.h => cpu_addressable_fb.h} | 18 ++++----- src/platforms/gbm-kms/server/kms/display.cpp | 8 ++-- src/platforms/gbm-kms/server/kms/display.h | 4 +- .../gbm-kms/server/kms/display_buffer.cpp | 4 +- src/platforms/gbm-kms/server/kms/platform.cpp | 4 +- .../renderer-generic-egl/buffer_allocator.cpp | 8 ++-- .../kms/test_display_multi_monitor.cpp | 2 +- 13 files changed, 62 insertions(+), 62 deletions(-) rename src/platforms/gbm-kms/server/kms/{dumb_fb.cpp => cpu_addressable_fb.cpp} (87%) rename src/platforms/gbm-kms/server/kms/{dumb_fb.h => cpu_addressable_fb.h} (71%) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index 1a41385b0c6..d4303f18ffa 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -225,7 +225,7 @@ class Framebuffer }; -class DumbDisplayProvider : public DisplayInterfaceBase +class CPUAddressableDisplayProvider : public DisplayInterfaceBase { public: class Tag : public DisplayInterfaceBase::Tag diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index ead959d9b40..ea7c9876fc2 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -590,7 +590,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface public: CPUCopyOutputSurface( std::unique_ptr ctx, - std::shared_ptr allocator, + std::shared_ptr allocator, geom::Size size) : allocator{std::move(allocator)}, ctx{std::move(ctx)}, @@ -678,7 +678,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface } private: - std::shared_ptr const allocator; + std::shared_ptr const allocator; std::unique_ptr const ctx; geom::Size const size_; RenderbufferHandle const colour_buffer; @@ -836,13 +836,13 @@ auto mge::GLRenderingProvider::surface_for_output( err.what()); } } - auto dumb_display = target->acquire_interface(); + auto cpu_provider = target->acquire_interface(); auto fb_context = ctx->make_share_context(); fb_context->make_current(); return std::make_unique( std::move(fb_context), - dumb_display, + cpu_provider, size); } diff --git a/src/platforms/eglstream-kms/server/egl_output.cpp b/src/platforms/eglstream-kms/server/egl_output.cpp index e263ef151b6..56b0ecf7505 100644 --- a/src/platforms/eglstream-kms/server/egl_output.cpp +++ b/src/platforms/eglstream-kms/server/egl_output.cpp @@ -39,10 +39,10 @@ namespace { namespace { -class DumbFb +class CPUAddressableFb { public: - DumbFb(int drm_fd, uint32_t width, uint32_t height) + CPUAddressableFb(int drm_fd, uint32_t width, uint32_t height) : drm_fd{drm_fd} { struct drm_mode_create_dumb params = {}; @@ -53,7 +53,7 @@ class DumbFb if (ioctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, ¶ms) != 0) { - BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to create dumb buffer"})); + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to create KMS dumb buffer"})); } gem_handle = params.handle; @@ -62,7 +62,7 @@ class DumbFb auto ret = drmModeAddFB(drm_fd, width, height, 24, 32, params.pitch, params.handle, &fb_id); if (ret) { - BOOST_THROW_EXCEPTION((std::system_error{-ret, std::system_category(), "Failed to attach dumb buffer to FB"})); + BOOST_THROW_EXCEPTION((std::system_error{-ret, std::system_category(), "Failed to attach KMS dumb buffer to FB"})); } struct drm_mode_map_dumb map_request = {}; @@ -72,7 +72,7 @@ class DumbFb ret = drmIoctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_request); if (ret) { - BOOST_THROW_EXCEPTION((std::system_error{-ret, std::system_category(), "Failed to map dumb buffer"})); + BOOST_THROW_EXCEPTION((std::system_error{-ret, std::system_category(), "Failed to map KMS dumb buffer"})); } auto map = mmap(0, params.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, map_request.offset); @@ -83,7 +83,7 @@ class DumbFb ::memset(map, 0, pitch_ * height); } - ~DumbFb() noexcept(false) + ~CPUAddressableFb() noexcept(false) { struct drm_mode_destroy_dumb params = { gem_handle }; @@ -91,7 +91,7 @@ class DumbFb { if (!std::uncaught_exceptions()) { - BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to destroy dumb buffer"})); + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to destroy KMS dumb buffer"})); } } } @@ -233,7 +233,7 @@ void mgek::EGLOutput::configure(size_t kms_mode_index) std::system_error(-ret, std::system_category(), "Failed to create DRM Mode property blob")); } - DumbFb dummy{drm_fd, width, height}; + CPUAddressableFb dummy{drm_fd, width, height}; mgk::ObjectProperties crtc_props{drm_fd, crtc_id, DRM_MODE_OBJECT_CRTC}; diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index 0d9c1c8faa9..d4e518f8fb7 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -366,7 +366,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface CPUCopyOutputSurface( EGLDisplay dpy, EGLContext ctx, - std::shared_ptr allocator, + std::shared_ptr allocator, geom::Size size) : allocator{std::move(allocator)}, dpy{dpy}, @@ -460,7 +460,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface } private: - std::shared_ptr const allocator; + std::shared_ptr const allocator; EGLDisplay const dpy; EGLContext const ctx; geom::Size const size_; @@ -696,12 +696,12 @@ auto mgg::GLRenderingProvider::surface_for_output( } } } - auto dumb_display = target->acquire_interface(); + auto cpu_provider = target->acquire_interface(); return std::make_unique( dpy, ctx, - std::move(dumb_display), + std::move(cpu_provider), size); } diff --git a/src/platforms/gbm-kms/server/kms/CMakeLists.txt b/src/platforms/gbm-kms/server/kms/CMakeLists.txt index 12822675cf1..fac0b24655f 100644 --- a/src/platforms/gbm-kms/server/kms/CMakeLists.txt +++ b/src/platforms/gbm-kms/server/kms/CMakeLists.txt @@ -38,8 +38,8 @@ add_library( quirks.cpp quirks.h kms_framebuffer.h - dumb_fb.cpp - dumb_fb.h + cpu_addressable_fb.cpp + cpu_addressable_fb.h ) target_link_libraries( diff --git a/src/platforms/gbm-kms/server/kms/dumb_fb.cpp b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp similarity index 87% rename from src/platforms/gbm-kms/server/kms/dumb_fb.cpp rename to src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp index 6f9e2cdca97..6d1f2435831 100644 --- a/src/platforms/gbm-kms/server/kms/dumb_fb.cpp +++ b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp @@ -16,7 +16,7 @@ * Authored by: Christopher James Halse Rogers */ -#include "dumb_fb.h" +#include "cpu_addressable_fb.h" #include "mir/geometry/forward.h" #include "mir/log.h" @@ -30,7 +30,7 @@ namespace mg = mir::graphics; namespace mgg = mg::gbm; -class mgg::DumbFB::DumbBuffer : public mir::renderer::software::RWMappableBuffer +class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappableBuffer { template class Mapping : public mir::renderer::software::Mapping @@ -95,7 +95,7 @@ class mgg::DumbFB::DumbBuffer : public mir::renderer::software::RWMappableBuffer }; public: - ~DumbBuffer() + ~Buffer() { struct drm_mode_destroy_dumb params = { gem_handle }; @@ -105,8 +105,8 @@ class mgg::DumbFB::DumbBuffer : public mir::renderer::software::RWMappableBuffer } } - static auto create_dumb_buffer(mir::Fd drm_fd, mir::geometry::Size const& size) -> - std::unique_ptr + static auto create_kms_dumb_buffer(mir::Fd drm_fd, mir::geometry::Size const& size) -> + std::unique_ptr { struct drm_mode_create_dumb params = {}; @@ -123,8 +123,8 @@ class mgg::DumbFB::DumbBuffer : public mir::renderer::software::RWMappableBuffer "Failed to allocate CPU-accessible buffer"})); } - return std::unique_ptr{ - new DumbBuffer{std::move(drm_fd), params}}; + return std::unique_ptr{ + new Buffer{std::move(drm_fd), params}}; } auto map_writeable() -> std::unique_ptr> override @@ -191,7 +191,7 @@ class mgg::DumbFB::DumbBuffer : public mir::renderer::software::RWMappableBuffer return geometry::Size{width_, height_}; } private: - DumbBuffer( + Buffer( mir::Fd fd, struct drm_mode_create_dumb const& params) : drm_fd{std::move(fd)}, @@ -239,52 +239,52 @@ class mgg::DumbFB::DumbBuffer : public mir::renderer::software::RWMappableBuffer size_t const size_; }; -mgg::DumbFB::DumbFB(mir::Fd const& drm_fd, bool supports_modifiers, mir::geometry::Size const& size) - : DumbFB(drm_fd, supports_modifiers, DumbBuffer::create_dumb_buffer(drm_fd, size)) +mgg::CPUAddressableFB::CPUAddressableFB(mir::Fd const& drm_fd, bool supports_modifiers, mir::geometry::Size const& size) + : CPUAddressableFB(drm_fd, supports_modifiers, Buffer::create_kms_dumb_buffer(drm_fd, size)) { } -mgg::DumbFB::~DumbFB() +mgg::CPUAddressableFB::~CPUAddressableFB() { drmModeRmFB(drm_fd, fb_id); } -mgg::DumbFB::DumbFB( +mgg::CPUAddressableFB::CPUAddressableFB( mir::Fd drm_fd, bool supports_modifiers, - std::unique_ptr buffer) + std::unique_ptr buffer) : drm_fd{std::move(drm_fd)}, fb_id{fb_id_for_buffer(this->drm_fd, supports_modifiers, *buffer)}, buffer{std::move(buffer)} { } -auto mgg::DumbFB::map_writeable() -> std::unique_ptr> +auto mgg::CPUAddressableFB::map_writeable() -> std::unique_ptr> { return buffer->map_writeable(); } -auto mgg::DumbFB::format() const -> MirPixelFormat +auto mgg::CPUAddressableFB::format() const -> MirPixelFormat { return buffer->format(); } -auto mgg::DumbFB::stride() const -> geometry::Stride +auto mgg::CPUAddressableFB::stride() const -> geometry::Stride { return buffer->stride(); } -auto mgg::DumbFB::size() const -> geometry::Size +auto mgg::CPUAddressableFB::size() const -> geometry::Size { return buffer->size(); } -mgg::DumbFB::operator uint32_t() const +mgg::CPUAddressableFB::operator uint32_t() const { return fb_id; } -auto mgg::DumbFB::fb_id_for_buffer(mir::Fd const &drm_fd, bool supports_modifiers, DumbBuffer const& buf) -> uint32_t +auto mgg::CPUAddressableFB::fb_id_for_buffer(mir::Fd const &drm_fd, bool supports_modifiers, Buffer const& buf) -> uint32_t { uint32_t fb_id; uint32_t const pitches[4] = { buf.pitch(), 0, 0, 0 }; diff --git a/src/platforms/gbm-kms/server/kms/dumb_fb.h b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h similarity index 71% rename from src/platforms/gbm-kms/server/kms/dumb_fb.h rename to src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h index 28f9b1b6bd1..8d609d93fe5 100644 --- a/src/platforms/gbm-kms/server/kms/dumb_fb.h +++ b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h @@ -26,11 +26,11 @@ namespace mir::graphics::gbm { -class DumbFB : public FBHandle, public DumbDisplayProvider::MappableFB +class CPUAddressableFB : public FBHandle, public CPUAddressableDisplayProvider::MappableFB { public: - DumbFB(mir::Fd const& drm_fd, bool supports_modifiers, mir::geometry::Size const& size); - ~DumbFB() override; + CPUAddressableFB(mir::Fd const& drm_fd, bool supports_modifiers, mir::geometry::Size const& size); + ~CPUAddressableFB() override; auto map_writeable() -> std::unique_ptr> override; @@ -40,17 +40,17 @@ class DumbFB : public FBHandle, public DumbDisplayProvider::MappableFB operator uint32_t() const override; - DumbFB(DumbFB const&) = delete; - DumbFB& operator=(DumbFB const&) = delete; + CPUAddressableFB(CPUAddressableFB const&) = delete; + CPUAddressableFB& operator=(CPUAddressableFB const&) = delete; private: - class DumbBuffer; + class Buffer; - DumbFB(mir::Fd drm_fd, bool supports_modifiers, std::unique_ptr buffer); - static auto fb_id_for_buffer(mir::Fd const& drm_fd, bool supports_modifiers, DumbBuffer const& buf) -> uint32_t; + CPUAddressableFB(mir::Fd drm_fd, bool supports_modifiers, std::unique_ptr buffer); + static auto fb_id_for_buffer(mir::Fd const& drm_fd, bool supports_modifiers, Buffer const& buf) -> uint32_t; mir::Fd const drm_fd; uint32_t const fb_id; - std::unique_ptr const buffer; + std::unique_ptr const buffer; }; } diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index fdf016019ab..cffb902c81e 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -24,7 +24,7 @@ #include "kms_output.h" #include "kms_page_flipper.h" #include "kms_framebuffer.h" -#include "dumb_fb.h" +#include "cpu_addressable_fb.h" #include "mir/console_services.h" #include "mir/graphics/overlapping_output_grouping.h" #include "mir/graphics/event_handler_register.h" @@ -462,16 +462,16 @@ auto drm_get_cap_checked(mir::Fd const& drm_fd, uint64_t cap) -> uint64_t } } -mgg::DumbDisplayProvider::DumbDisplayProvider(mir::Fd drm_fd) +mgg::CPUAddressableDisplayProvider::CPUAddressableDisplayProvider(mir::Fd drm_fd) : drm_fd{std::move(drm_fd)}, supports_modifiers{drm_get_cap_checked(this->drm_fd, DRM_CAP_ADDFB2_MODIFIERS) == 1} { } -auto mgg::DumbDisplayProvider::alloc_fb( +auto mgg::CPUAddressableDisplayProvider::alloc_fb( geom::Size size) -> std::unique_ptr { - return std::make_unique(drm_fd, supports_modifiers, size); + return std::make_unique(drm_fd, supports_modifiers, size); } namespace diff --git a/src/platforms/gbm-kms/server/kms/display.h b/src/platforms/gbm-kms/server/kms/display.h index df0b8d493bb..41e365036bd 100644 --- a/src/platforms/gbm-kms/server/kms/display.h +++ b/src/platforms/gbm-kms/server/kms/display.h @@ -104,10 +104,10 @@ class Display : public graphics::Display std::weak_ptr cursor; }; -class DumbDisplayProvider : public graphics::DumbDisplayProvider +class CPUAddressableDisplayProvider : public graphics::CPUAddressableDisplayProvider { public: - explicit DumbDisplayProvider(mir::Fd drm_fd); + explicit CPUAddressableDisplayProvider(mir::Fd drm_fd); auto alloc_fb(geometry::Size pixel_size) -> std::unique_ptr override; diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index 752f421908f..d3fb0ac27bb 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -16,7 +16,7 @@ #include "display_buffer.h" #include "kms_output.h" -#include "dumb_fb.h" +#include "cpu_addressable_fb.h" #include "mir/fd.h" #include "mir/graphics/display_report.h" #include "mir/graphics/transformation.h" @@ -63,7 +63,7 @@ mgg::DisplayBuffer::DisplayBuffer( { listener->report_successful_setup_of_native_resources(); - auto initial_fb = std::make_shared(std::move(drm_fd), false, area.size); + auto initial_fb = std::make_shared(std::move(drm_fd), false, area.size); auto mapping = initial_fb->map_writeable(); ::memset(mapping->data(), 24, mapping->len()); diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index da3445f5a01..fff7ff288f5 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -268,10 +268,10 @@ class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfacePr mir::log_debug("Using GBMDisplayProvider"); return std::make_shared(drm_fd); } - if (dynamic_cast(&type_tag)) + if (dynamic_cast(&type_tag)) { mir::log_debug("Using DumbDisplayProvider"); - return std::make_shared(drm_fd); + return std::make_shared(drm_fd); } return {}; } diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index 1c9e02b361d..4c45b9cd3a9 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -366,7 +366,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface CPUCopyOutputSurface( EGLDisplay dpy, EGLContext ctx, - std::shared_ptr allocator, + std::shared_ptr allocator, geom::Size size) : allocator{std::move(allocator)}, dpy{dpy}, @@ -460,7 +460,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface } private: - std::shared_ptr const allocator; + std::shared_ptr const allocator; EGLDisplay const dpy; EGLContext const ctx; geom::Size const size_; @@ -516,12 +516,12 @@ auto mge::GLRenderingProvider::surface_for_output( { return std::make_unique(egl_display->alloc_framebuffer(config, ctx)); } - auto dumb_display = framebuffer_provider->acquire_interface(); + auto cpu_provider = framebuffer_provider->acquire_interface(); return std::make_unique( dpy, ctx, - std::move(dumb_display), + std::move(cpu_provider), size); } diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp index a3e64b1afdc..b41f9873b9a 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp @@ -358,7 +358,7 @@ TEST_F(MesaDisplayMultiMonitorTest, flip_flips_all_connected_crtcs) auto platform = create_platform(); auto display = create_display_cloned(platform); - auto provider = mg::DisplayPlatform::interface_for(std::move(platform))->acquire_interface(); + auto provider = mg::DisplayPlatform::interface_for(std::move(platform))->acquire_interface(); /* First frame: Page flips are scheduled, but not waited for */ display->for_each_display_sync_group( From a5cc801e8df074d666458af6875c0577ff7467d8 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 22 May 2023 11:39:26 +1000 Subject: [PATCH 006/108] Yo! Give this a better commit message when it's done --- include/platform/mir/graphics/platform.h | 33 +++++++++++++++++- .../eglstream-kms/server/buffer_allocator.cpp | 14 ++++++++ .../eglstream-kms/server/buffer_allocator.h | 2 ++ .../gbm-kms/server/buffer_allocator.cpp | 26 ++++++++++++++ .../gbm-kms/server/buffer_allocator.h | 2 ++ .../renderer-generic-egl/buffer_allocator.cpp | 24 +++++++++++++ .../renderer-generic-egl/buffer_allocator.h | 2 ++ .../compositor/default_configuration.cpp | 20 +++++++---- ...ault_display_buffer_compositor_factory.cpp | 34 ++++++++++++++++--- ...efault_display_buffer_compositor_factory.h | 5 ++- .../test/doubles/stub_gl_rendering_provider.h | 6 ++++ .../test_surface_stack_with_compositor.cpp | 3 +- 12 files changed, 155 insertions(+), 16 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index d4303f18ffa..ad46480f6ae 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -64,6 +64,24 @@ class GLConfig; class DisplayInterfaceProvider; +namespace probe +{ + using Result = uint32_t; + Result const unsupported = 0; /**< Unable to function at all on this device */ + Result const dummy = 1; /**< Used only for dummy or stub platforms. + */ + Result const supported = 128; /**< Capable of providing a functionality on this device; + * possibly with degraded performance or features. + */ + Result const hosted = 192; /**< Capable of providing fully-featured functionality on this device; + * running nested under some other display server rather than with + * exclusive hardware access. + */ + Result const best = 256; /**< Capable of providing the best features and performance this device + * is capable of. + */ +} + class RendererInterfaceBase { public: @@ -86,16 +104,29 @@ class RendererInterfaceBase auto operator=(FramebufferProvider const&) = delete; /** + * Get a directly-displayable handle for a buffer, if cheaply possible + * + * Some buffer types can be passed as-is to the display hardware. If this + * buffer can be used in this way (on the DisplayInterfaceProvider associated + * with this FramebufferProvider), this method creates a handle that can be + * passed to the overlay method of an associated DisplayBuffer. * * \note The returned Framebuffer may share ownership of the provided Buffer. * It is not necessary for calling code to retain a reference to the Buffer. * \param buffer - * \return + * \return A handle to a directly submittable buffer, or nullptr if this buffer + is not pasable as-is to the display hardware. */ virtual auto buffer_to_framebuffer(std::shared_ptr buffer) -> std::unique_ptr = 0; }; + /** + * Check how well this Renderer can support a particular display target + */ + virtual auto suitability_for_display(std::shared_ptr const& target) + -> probe::Result = 0; + virtual auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr = 0; }; diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index ea7c9876fc2..aa53cf0a114 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -846,6 +846,20 @@ auto mge::GLRenderingProvider::surface_for_output( size); } +auto mge::GLRenderingProvider::suitability_for_display( + std::shared_ptr const& target) -> probe::Result +{ + if (target->acquire_interface()) + { + return probe::best; + } + if (target->acquire_interface()) + { + return probe::supported; + } + return probe::unsupported; +} + auto mge::GLRenderingProvider::make_framebuffer_provider(std::shared_ptr /*target*/) -> std::unique_ptr { diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.h b/src/platforms/eglstream-kms/server/buffer_allocator.h index 7c70fbf0c91..d5f5cbf12cc 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.h +++ b/src/platforms/eglstream-kms/server/buffer_allocator.h @@ -99,6 +99,8 @@ class GLRenderingProvider : public graphics::GLRenderingProvider auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; + auto suitability_for_display(std::shared_ptr const& target) -> probe::Result override; + auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr override; auto surface_for_output( diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index d4e518f8fb7..0f77378c858 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -674,6 +674,32 @@ class GBMOutputSurface : public mg::gl::OutputSurface }; } +auto mgg::GLRenderingProvider::suitability_for_display( + std::shared_ptr const& target) -> probe::Result +{ + if (bound_display) + { + if (auto gbm_provider = target->acquire_interface()) + { + if (bound_display->gbm_device() == gbm_provider->gbm_device()) + { + /* We're rendering on the same device as display; + * it doesn't get better than this! + */ + return probe::best; + } + } + } + + if (target->acquire_interface()) + { + // We *can* render to CPU buffers, but if anyone can do better, let them. + return probe::supported; + } + + return probe::unsupported; +} + auto mgg::GLRenderingProvider::surface_for_output( std::shared_ptr target, geom::Size size, diff --git a/src/platforms/gbm-kms/server/buffer_allocator.h b/src/platforms/gbm-kms/server/buffer_allocator.h index f99b16d7379..96e9e83ada7 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.h +++ b/src/platforms/gbm-kms/server/buffer_allocator.h @@ -94,6 +94,8 @@ class GLRenderingProvider : public graphics::GLRenderingProvider auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; + auto suitability_for_display(std::shared_ptr const& target) -> probe::Result override; + auto surface_for_output( std::shared_ptr framebuffer_provider, geometry::Size size, diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index 4c45b9cd3a9..132ab788fb4 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -506,6 +506,30 @@ class EGLOutputSurface : public mg::gl::OutputSurface }; } +auto mge::GLRenderingProvider::suitability_for_display( + std::shared_ptr const& target) -> probe::Result +{ + if (target->acquire_interface()) + { + /* We're effectively hosted on an underlying EGL platform. + * + * We'll work fine, but if there's a hardware-specific platform + * let it take over. + */ + return probe::hosted; + } + + if (target->acquire_interface()) + { + /* We can *work* on a CPU-backed surface, but if anything's better + * we should use something else! + */ + return probe::supported; + } + + return probe::unsupported; +} + auto mge::GLRenderingProvider::surface_for_output( std::shared_ptr framebuffer_provider, geometry::Size size, diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.h b/src/platforms/renderer-generic-egl/buffer_allocator.h index dfbd9a91157..5f278bfdcd4 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.h +++ b/src/platforms/renderer-generic-egl/buffer_allocator.h @@ -93,6 +93,8 @@ class GLRenderingProvider : public graphics::GLRenderingProvider auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; + auto suitability_for_display(std::shared_ptr const& target) -> probe::Result override; + auto surface_for_output( std::shared_ptr framebuffer_provider, geometry::Size size, diff --git a/src/server/compositor/default_configuration.cpp b/src/server/compositor/default_configuration.cpp index a59a8f0ec95..c8fb2e42808 100644 --- a/src/server/compositor/default_configuration.cpp +++ b/src/server/compositor/default_configuration.cpp @@ -48,14 +48,22 @@ mir::DefaultServerConfiguration::the_display_buffer_compositor_factory() return display_buffer_compositor_factory( [this]() { - auto rendering_platform = the_rendering_platforms().back(); - if (auto gl_provider = - mg::RenderingPlatform::acquire_interface(std::move(rendering_platform))) + std::vector> providers; + providers.reserve(the_rendering_platforms().size()); + for (auto const& platform : the_rendering_platforms()) { - return wrap_display_buffer_compositor_factory(std::make_shared( - std::move(gl_provider), the_gl_config(), the_renderer_factory(), the_compositor_report())); + if (auto gl_provider = mg::RenderingPlatform::acquire_interface(platform)) + { + providers.push_back(gl_provider); + } } - BOOST_THROW_EXCEPTION((std::runtime_error{"Selected rendering platform does not support GL"})); + if (providers.empty()) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Selected rendering platform does not support GL"})); + } + return wrap_display_buffer_compositor_factory( + std::make_shared( + std::move(providers), the_gl_config(), the_renderer_factory(), the_compositor_report())); }); } diff --git a/src/server/compositor/default_display_buffer_compositor_factory.cpp b/src/server/compositor/default_display_buffer_compositor_factory.cpp index c2a70b40bfb..9fab34870fa 100644 --- a/src/server/compositor/default_display_buffer_compositor_factory.cpp +++ b/src/server/compositor/default_display_buffer_compositor_factory.cpp @@ -31,11 +31,11 @@ namespace mc = mir::compositor; namespace mg = mir::graphics; mc::DefaultDisplayBufferCompositorFactory::DefaultDisplayBufferCompositorFactory( - std::shared_ptr render_platform, + std::vector> render_platforms, std::shared_ptr gl_config, std::shared_ptr const& renderer_factory, std::shared_ptr const& report) : - allocator{std::move(render_platform)}, + platforms{std::move(render_platforms)}, gl_config{std::move(gl_config)}, renderer_factory{renderer_factory}, report{report} @@ -55,10 +55,34 @@ mc::DefaultDisplayBufferCompositorFactory::create_compositor_for( * GL surface could be the common case, and not allocating it would save a * potentially-significant amount of GPU memory. */ - auto output_surface = allocator->surface_for_output( + + auto const display_provider = display_buffer.display_provider(); + + /* In a heterogeneous system, different providers may be better at driving a specific + * display. Select the best one. + */ + std::pair> best_provider = std::make_pair(mg::probe::unsupported, nullptr); + for (auto const& provider : platforms) + { + auto suitability = provider->suitability_for_display(display_provider); + if (suitability > best_provider.first) + { + best_provider = std::make_pair(suitability, provider); + } + } + if (best_provider.first == mg::probe::unsupported) + { + // We should not get here; the rendering platforms have already had + // an opportunity to claim they don't support any present hardware + BOOST_THROW_EXCEPTION((std::logic_error{"No rendering platform claims to support this output"})); + } + + auto const chosen_allocator = best_provider.second; + + auto output_surface = chosen_allocator->surface_for_output( display_buffer.display_provider(), display_buffer.view_area().size, *gl_config); - auto renderer = renderer_factory->create_renderer_for(std::move(output_surface), allocator); + auto renderer = renderer_factory->create_renderer_for(std::move(output_surface), chosen_allocator); renderer->set_viewport(display_buffer.view_area()); return std::make_unique( - display_buffer, *allocator, std::move(renderer), report); + display_buffer, *chosen_allocator, std::move(renderer), report); } diff --git a/src/server/compositor/default_display_buffer_compositor_factory.h b/src/server/compositor/default_display_buffer_compositor_factory.h index 91cd017bc56..c1b00f8f05b 100644 --- a/src/server/compositor/default_display_buffer_compositor_factory.h +++ b/src/server/compositor/default_display_buffer_compositor_factory.h @@ -44,7 +44,7 @@ class DefaultDisplayBufferCompositorFactory : public DisplayBufferCompositorFact { public: DefaultDisplayBufferCompositorFactory( - std::shared_ptr render_platform, + std::vector> render_platforms, std::shared_ptr gl_config, std::shared_ptr const& renderer_factory, std::shared_ptr const& report); @@ -52,8 +52,7 @@ class DefaultDisplayBufferCompositorFactory : public DisplayBufferCompositorFact std::unique_ptr create_compositor_for(graphics::DisplayBuffer& display_buffer) override; private: - // TODO: This should be a per-DisplayBuffer decision - std::shared_ptr const allocator; + std::vector> const platforms; std::shared_ptr const gl_config; std::shared_ptr const renderer_factory; std::shared_ptr const report; diff --git a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h index dfb030d63ac..f1b7c61025d 100644 --- a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h @@ -58,6 +58,12 @@ class StubGlRenderingPlatform : public graphics::GLRenderingProvider return std::make_unique>(); } + auto suitability_for_display(std::shared_ptr const& /*target*/) + -> graphics::probe::Result override + { + return graphics::probe::dummy; + } + auto make_framebuffer_provider(std::shared_ptr /*target*/) -> std::unique_ptr override { diff --git a/tests/integration-tests/test_surface_stack_with_compositor.cpp b/tests/integration-tests/test_surface_stack_with_compositor.cpp index 0411cd3d832..e4aa7a321c2 100644 --- a/tests/integration-tests/test_surface_stack_with_compositor.cpp +++ b/tests/integration-tests/test_surface_stack_with_compositor.cpp @@ -15,6 +15,7 @@ */ #include "mir/compositor/display_listener.h" +#include "mir/graphics/platform.h" #include "mir/renderer/renderer_factory.h" #include "src/server/report/null_report_factory.h" #include "src/server/scene/surface_stack.h" @@ -183,7 +184,7 @@ struct SurfaceStackCompositor : public Test StubDisplayListener stub_display_listener; mc::DefaultDisplayBufferCompositorFactory dbc_factory{ - std::make_shared(), + std::vector>{std::make_shared()}, std::make_shared(), mt::fake_shared(renderer_factory), null_comp_report}; From 190e5ed4ce094a9b4f26d78dc340ef47eb4eb858 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 26 May 2023 17:22:28 +1000 Subject: [PATCH 007/108] graphics/probe: Rework `probe_module` helper. `probe_module` doesn't need to do any processing of the arguments to the `probe` function, so we can choose instead to pass in a nullary functor with those arguments already bound. This will allow the arguments to `probe_display_platform` and `probe_rendering_platform` to diverge without needing to edit the helper. --- src/server/graphics/platform_probe.cpp | 36 ++++++++++++++------------ 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/server/graphics/platform_probe.cpp b/src/server/graphics/platform_probe.cpp index d484147abee..e1d414d2d52 100644 --- a/src/server/graphics/platform_probe.cpp +++ b/src/server/graphics/platform_probe.cpp @@ -25,11 +25,9 @@ namespace mg = mir::graphics; namespace { auto probe_module( - mir::graphics::PlatformProbe const& probe, + std::function()> const& probe, mir::SharedLibrary& module, - char const* platform_type_name, - mir::options::ProgramOption const& options, - std::shared_ptr const& console) -> std::vector + char const* platform_type_name) -> std::vector { auto describe = module.load_function( "describe_graphics_module", @@ -43,7 +41,7 @@ auto probe_module( desc->minor_version, desc->micro_version); - auto supported_devices = probe(console, std::make_shared(), options); + auto supported_devices = probe(); if (supported_devices.empty()) { mir::log_info("(Unsupported by system environment)"); @@ -80,13 +78,15 @@ auto mir::graphics::probe_display_module( std::shared_ptr const& console) -> std::vector { return probe_module( - module.load_function( - "probe_display_platform", - MIR_SERVER_GRAPHICS_PLATFORM_VERSION), + [&console, &options, &module]() -> std::vector + { + auto probe = module.load_function( + "probe_display_platform", + MIR_SERVER_GRAPHICS_PLATFORM_VERSION); + return probe(console, std::make_shared(), options); + }, module, - "display", - options, - console); + "display"); } auto mir::graphics::probe_rendering_module( @@ -95,13 +95,15 @@ auto mir::graphics::probe_rendering_module( std::shared_ptr const& console) -> std::vector { return probe_module( - module.load_function( - "probe_rendering_platform", - MIR_SERVER_GRAPHICS_PLATFORM_VERSION), + [&console, &options, &module]() -> std::vector + { + auto probe = module.load_function( + "probe_rendering_platform", + MIR_SERVER_GRAPHICS_PLATFORM_VERSION); + return probe(console, std::make_shared(), options); + }, module, - "rendering", - options, - console); + "rendering"); } namespace From e72fb690c64dfeba6909adaf3616cdd5d6897cc5 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 29 May 2023 16:25:10 +1000 Subject: [PATCH 008/108] platform/graphics: Improve RenderingPlatform selection. Passing in the set of `DisplayInterfaceProvider`s to the probe lets us discard rendering platforms that cannot work earlier. This *shouldn't* be necessary, but this helps avoid a problem: we need whatever platform is chosen to provide `the_buffer_allocator()` to integrate with each platform providing a `GLRenderingProvider` to the renderer. The infrastructure for that is there, but not yet hooked up, and this lets us handle everything that currently works without needed the full solution implemented now. --- include/platform/mir/graphics/platform.h | 9 +++- .../eglstream-kms/server/platform_symbols.cpp | 34 ++++++++++-- .../gbm-kms/server/kms/platform_symbols.cpp | 38 ++++++++++++-- .../renderer-generic-egl/platform_symbols.cpp | 22 ++++++-- ...ault_display_buffer_compositor_factory.cpp | 2 +- src/server/graphics/default_configuration.cpp | 18 ++++--- src/server/graphics/platform_probe.cpp | 52 ++++++++----------- src/server/graphics/platform_probe.h | 6 ++- .../platforms/test_rendering_platform.cpp | 2 +- .../platform_graphics_dummy.cpp | 5 +- .../platform_graphics_throw.cpp | 5 +- 11 files changed, 136 insertions(+), 57 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index ad46480f6ae..e78e493f815 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -536,6 +536,12 @@ typedef std::vector(*PlatformProbe)( std::shared_ptr const&, mir::options::ProgramOption const& options); +typedef std::vector(*RenderProbe)( + std::span> const&, + mir::ConsoleServices&, + std::shared_ptr const&, + mir::options::ProgramOption const&); + typedef mir::ModuleProperties const*(*DescribeModule)(); } } @@ -595,7 +601,8 @@ auto probe_display_platform( mir::options::ProgramOption const& options) -> std::vector; auto probe_rendering_platform( - std::shared_ptr const& console, + std::span> const& display_providers, + mir::ConsoleServices& console, std::shared_ptr const& udev, mir::options::ProgramOption const& options) -> std::vector; diff --git a/src/platforms/eglstream-kms/server/platform_symbols.cpp b/src/platforms/eglstream-kms/server/platform_symbols.cpp index 32e1c82b8fc..443393369b6 100644 --- a/src/platforms/eglstream-kms/server/platform_symbols.cpp +++ b/src/platforms/eglstream-kms/server/platform_symbols.cpp @@ -80,11 +80,39 @@ void add_graphics_platform_options(boost::program_options::options_description& } auto probe_rendering_platform( - std::shared_ptr const& /*console*/, + std::span> const& displays, + mir::ConsoleServices& /*console*/, std::shared_ptr const& udev, mo::ProgramOption const& /*options*/) -> std::vector { - mir::assert_entry_point_signature(&probe_rendering_platform); + mir::assert_entry_point_signature(&probe_rendering_platform); + + mg::PlatformPriority maximum_suitability = mg::PlatformPriority::unsupported; + // First check if there are any displays we can possibly drive + for (auto const& display_provider : displays) + { + if (display_provider->acquire_interface()) + { + // We can optimally drive an EGLStream display + mir::log_debug("EGLStream-capable display found"); + maximum_suitability = mg::PlatformPriority::best; + break; + } + if (display_provider->acquire_interface()) + { + /* We *can* support this output, but with slower buffer copies + * If another platform supports this device better, let it. + */ + maximum_suitability = mg::PlatformPriority::supported; + } + } + + if (maximum_suitability == mg::PlatformPriority::unsupported) + { + mir::log_debug("No outputs capable of accepting EGLStream input detected"); + mir::log_debug("Probing will be skipped"); + return {}; + } std::vector missing_extensions; for (char const* extension : { @@ -192,7 +220,7 @@ auto probe_rendering_platform( if (missing_extensions.empty()) { // We've got EGL, and we've got the necessary EGL extensions. We're good. - supported_devices.back().support_level = mg::PlatformPriority::best; + supported_devices.back().support_level = maximum_suitability; } } } diff --git a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp index aa4593d1be2..20fe033803f 100644 --- a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp +++ b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp @@ -307,11 +307,39 @@ auto probe_display_platform( } auto probe_rendering_platform( - std::shared_ptr const&, + std::span> const& displays, + mir::ConsoleServices&, std::shared_ptr const& udev, mir::options::ProgramOption const& options) -> std::vector { - mir::assert_entry_point_signature(&probe_rendering_platform); + mir::assert_entry_point_signature(&probe_rendering_platform); + + mg::PlatformPriority maximum_suitability = mg::PlatformPriority::unsupported; + // First check if there are any displays we can possibly drive + for (auto const& display_provider : displays) + { + if (display_provider->acquire_interface()) + { + // We can optimally drive a GBM-backed display + mir::log_debug("GBM-capable display found"); + maximum_suitability = mg::PlatformPriority::best; + break; + } + if (display_provider->acquire_interface()) + { + /* We *can* support this output, but with slower buffer copies + * If another platform supports this device better, let it. + */ + maximum_suitability = mg::PlatformPriority::supported; + } + } + + if (maximum_suitability == mg::PlatformPriority::unsupported) + { + mir::log_debug("No outputs capable of accepting GBM input detected"); + mir::log_debug("Probing will be skipped"); + return {}; + } mgg::Quirks quirks{options}; @@ -393,8 +421,7 @@ auto probe_rendering_platform( } // We know we've got a device that we *might* be able to use - // Mark it as “supported” because we should *at least* be able to get a software context on it. - supported_devices.emplace_back(mg::SupportedDevice{device.clone(), mg::PlatformPriority::supported, nullptr}); + supported_devices.emplace_back(mg::SupportedDevice{device.clone(), mg::PlatformPriority::unsupported, nullptr}); if (tmp_fd != mir::Fd::invalid) { mgg::helpers::GBMHelper gbm_device{tmp_fd}; @@ -421,11 +448,12 @@ auto probe_rendering_platform( mir::log_info("Detected software renderer: %s", renderer_string); // Leave the priority at ::supported; if we've got a software renderer then // we *don't* support *this* device very well. + supported_devices.back().support_level = std::min(maximum_suitability, mg::PlatformPriority::supported); } else { // We've got a non-software renderer. That's our cue! - supported_devices.back().support_level = mg::PlatformPriority::best; + supported_devices.back().support_level = maximum_suitability; } } } diff --git a/src/platforms/renderer-generic-egl/platform_symbols.cpp b/src/platforms/renderer-generic-egl/platform_symbols.cpp index a1d197c43a2..86d63bee2df 100644 --- a/src/platforms/renderer-generic-egl/platform_symbols.cpp +++ b/src/platforms/renderer-generic-egl/platform_symbols.cpp @@ -50,18 +50,34 @@ void add_graphics_platform_options(boost::program_options::options_description&) } auto probe_rendering_platform( - std::shared_ptr const&, + std::span> const& displays, + mir::ConsoleServices&, std::shared_ptr const&, mo::ProgramOption const&) -> std::vector { - mir::assert_entry_point_signature(&probe_rendering_platform); + mir::assert_entry_point_signature(&probe_rendering_platform); + + mg::PlatformPriority maximum_suitability = mg::PlatformPriority::unsupported; + // First check if there are any displays we can possibly drive + for (auto const& display_provider : displays) + { + if (display_provider->acquire_interface()) + { + maximum_suitability = mg::PlatformPriority::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. + */ + } std::vector supported_devices; supported_devices.push_back( mg::SupportedDevice { nullptr, // We aren't associated with any particular device - mg::PlatformPriority::supported, // We should be fully-functional, but let any hardware-specific + maximum_suitability, // We should be fully-functional, but let any hardware-specific // platform claim a higher priority, if it exists. nullptr // No platform-specific data diff --git a/src/server/compositor/default_display_buffer_compositor_factory.cpp b/src/server/compositor/default_display_buffer_compositor_factory.cpp index 9fab34870fa..737dc1ff6f0 100644 --- a/src/server/compositor/default_display_buffer_compositor_factory.cpp +++ b/src/server/compositor/default_display_buffer_compositor_factory.cpp @@ -80,7 +80,7 @@ mc::DefaultDisplayBufferCompositorFactory::create_compositor_for( auto const chosen_allocator = best_provider.second; auto output_surface = chosen_allocator->surface_for_output( - display_buffer.display_provider(), display_buffer.view_area().size, *gl_config); + display_provider, display_buffer.view_area().size, *gl_config); auto renderer = renderer_factory->create_renderer_for(std::move(output_surface), chosen_allocator); renderer->set_viewport(display_buffer.view_area()); return std::make_unique( diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index 09f384118df..892618d338e 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -271,6 +271,14 @@ auto mir::DefaultServerConfiguration::the_rendering_platforms() -> throw std::runtime_error(msg.c_str()); } + std::vector> display_interfaces; + display_interfaces.reserve(the_display_platforms().size()); + + for (auto& display : the_display_platforms()) + { + display_interfaces.push_back(mg::DisplayPlatform::interface_for(display)); + } + if (the_options()->is_set(options::platform_rendering_libs)) { auto const manually_selected_platforms = @@ -280,6 +288,7 @@ auto mir::DefaultServerConfiguration::the_rendering_platforms() -> { auto supported_devices = graphics::probe_rendering_module( + display_interfaces, *platform, dynamic_cast(*the_options()), the_console_services()); @@ -307,16 +316,9 @@ auto mir::DefaultServerConfiguration::the_rendering_platforms() -> } else { - platform_modules = mir::graphics::rendering_modules_for_device(platforms, dynamic_cast(*the_options()), the_console_services()); + platform_modules = mir::graphics::rendering_modules_for_device(platforms, display_interfaces, dynamic_cast(*the_options()), the_console_services()); } - std::vector> display_interfaces; - display_interfaces.reserve(the_display_platforms().size()); - - for (auto& display : the_display_platforms()) - { - display_interfaces.push_back(mg::DisplayPlatform::interface_for(display)); - } for (auto const& [device, platform]: platform_modules) { auto create_rendering_platform = platform->load_function( diff --git a/src/server/graphics/platform_probe.cpp b/src/server/graphics/platform_probe.cpp index e1d414d2d52..e53a3a7f051 100644 --- a/src/server/graphics/platform_probe.cpp +++ b/src/server/graphics/platform_probe.cpp @@ -16,6 +16,7 @@ #include "mir/log.h" #include "mir/graphics/platform.h" +#include "mir/shared_library.h" #include "platform_probe.h" #include @@ -26,7 +27,7 @@ namespace { auto probe_module( std::function()> const& probe, - mir::SharedLibrary& module, + mir::SharedLibrary const& module, char const* platform_type_name) -> std::vector { auto describe = module.load_function( @@ -73,7 +74,7 @@ auto probe_module( } auto mir::graphics::probe_display_module( - SharedLibrary& module, + SharedLibrary const& module, options::ProgramOption const& options, std::shared_ptr const& console) -> std::vector { @@ -90,17 +91,18 @@ auto mir::graphics::probe_display_module( } auto mir::graphics::probe_rendering_module( - SharedLibrary& module, + std::span> const& displays, + SharedLibrary const& module, options::ProgramOption const& options, std::shared_ptr const& console) -> std::vector { return probe_module( - [&console, &options, &module]() -> std::vector + [&console, &options, &module, &displays]() -> std::vector { - auto probe = module.load_function( + auto probe = module.load_function( "probe_rendering_platform", MIR_SERVER_GRAPHICS_PLATFORM_VERSION); - return probe(console, std::make_shared(), options); + return probe(displays, *console, std::make_shared(), options); }, module, "rendering"); @@ -130,27 +132,16 @@ enum class ModuleType }; auto modules_for_device( - ModuleType type, - std::vector> const& modules, - mir::options::ProgramOption const& options, - std::shared_ptr const& console) --> std::vector>> + std::function(mir::SharedLibrary const&)> const& probe, + std::vector> const& modules) + -> std::vector>> { std::vector>> best_modules_so_far; for (auto& module : modules) { try { - std::vector supported_devices; - switch (type) - { - case ModuleType::Rendering: - supported_devices = mg::probe_rendering_module(*module, options, console); - break; - case ModuleType::Display: - supported_devices = mg::probe_display_module(*module, options, console); - break; - } + auto supported_devices = probe(*module); for (auto& device : supported_devices) { if (device.device) @@ -221,20 +212,23 @@ auto mir::graphics::display_modules_for_device( std::shared_ptr const& console) -> std::vector>> { return modules_for_device( - ModuleType::Display, - modules, - options, - console); + [&options, &console](mir::SharedLibrary const& module) -> std::vector + { + return mg::probe_display_module(module, options, console); + }, + modules); } auto mir::graphics::rendering_modules_for_device( std::vector> const& modules, + std::span> const& displays, options::ProgramOption const& options, std::shared_ptr const& console) -> std::vector>> { return modules_for_device( - ModuleType::Rendering, - modules, - options, - console); + [&displays, &options, &console](SharedLibrary const& module) -> std::vector + { + return probe_rendering_module(displays, module, options, console); + }, + modules); } diff --git a/src/server/graphics/platform_probe.h b/src/server/graphics/platform_probe.h index 8a9aef20182..88b177fb623 100644 --- a/src/server/graphics/platform_probe.h +++ b/src/server/graphics/platform_probe.h @@ -31,12 +31,13 @@ class ConsoleServices; namespace graphics { auto probe_display_module( - SharedLibrary& module, + SharedLibrary const& module, options::ProgramOption const& options, std::shared_ptr const& console) -> std::vector; auto probe_rendering_module( - SharedLibrary& module, + std::span> const& displays, + SharedLibrary const& module, options::ProgramOption const& options, std::shared_ptr const& console) -> std::vector; @@ -48,6 +49,7 @@ auto display_modules_for_device( auto rendering_modules_for_device( std::vector> const& modules, + std::span> const& displays, options::ProgramOption const& options, std::shared_ptr const& console) -> std::vector>>; diff --git a/tests/acceptance-tests/platforms/test_rendering_platform.cpp b/tests/acceptance-tests/platforms/test_rendering_platform.cpp index 48d778a92f6..f3fa8142d36 100644 --- a/tests/acceptance-tests/platforms/test_rendering_platform.cpp +++ b/tests/acceptance-tests/platforms/test_rendering_platform.cpp @@ -72,7 +72,7 @@ TEST_P(RenderingPlatformTest, has_render_platform_entrypoints) try { - platform_module->load_function( + platform_module->load_function( "probe_rendering_platform", MIR_SERVER_GRAPHICS_PLATFORM_VERSION); } diff --git a/tests/mir_test_framework/platform_graphics_dummy.cpp b/tests/mir_test_framework/platform_graphics_dummy.cpp index cf034d28de4..95c1d5d7064 100644 --- a/tests/mir_test_framework/platform_graphics_dummy.cpp +++ b/tests/mir_test_framework/platform_graphics_dummy.cpp @@ -45,11 +45,12 @@ auto probe_display_platform( } auto probe_rendering_platform( - std::shared_ptr const&, + std::span> const&, + mir::ConsoleServices&, std::shared_ptr const&, mir::options::ProgramOption const&) -> std::vector { - mir::assert_entry_point_signature(&probe_rendering_platform); + mir::assert_entry_point_signature(&probe_rendering_platform); std::vector result; result.emplace_back( mg::SupportedDevice { diff --git a/tests/mir_test_framework/platform_graphics_throw.cpp b/tests/mir_test_framework/platform_graphics_throw.cpp index ebea709cd38..7d84d014761 100644 --- a/tests/mir_test_framework/platform_graphics_throw.cpp +++ b/tests/mir_test_framework/platform_graphics_throw.cpp @@ -144,11 +144,12 @@ auto probe_display_platform( } auto probe_rendering_platform( - std::shared_ptr const&, + std::span> const&, + mir::ConsoleServices&, std::shared_ptr const&, mir::options::ProgramOption const&) -> std::vector { - mir::assert_entry_point_signature(&probe_rendering_platform); + mir::assert_entry_point_signature(&probe_rendering_platform); std::vector result; result.emplace_back( mg::SupportedDevice { From d2572a04fb4b599633f7679eaea1b13e407fdc2a Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 29 May 2023 16:35:48 +1000 Subject: [PATCH 009/108] Fix typo --- include/platform/mir/graphics/graphic_buffer_allocator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/platform/mir/graphics/graphic_buffer_allocator.h b/include/platform/mir/graphics/graphic_buffer_allocator.h index 3c5c4694f55..85831b6ac93 100644 --- a/include/platform/mir/graphics/graphic_buffer_allocator.h +++ b/include/platform/mir/graphics/graphic_buffer_allocator.h @@ -75,7 +75,7 @@ class GraphicBufferAllocator * Deinitialise the BufferAllocator for this Wayland display * * This should do whatever is required to clean up before the Wayland loop is stopped. For - * example, calling eglUnindWaylandDisplayWL. + * example, calling eglUnbindWaylandDisplayWL. * * \param display [in] The Wayland display to unbind from */ From d393a683200021207039ca463919b98316370f7c Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 1 Jun 2023 12:33:56 +1000 Subject: [PATCH 010/108] compositor/BasicScreenShooter: Update for new Platform API --- src/server/compositor/CMakeLists.txt | 2 +- .../compositor/basic_screen_shooter.cpp | 157 ++++++++++++++++-- src/server/compositor/basic_screen_shooter.h | 29 +++- .../compositor/default_configuration.cpp | 53 +++--- 4 files changed, 198 insertions(+), 43 deletions(-) diff --git a/src/server/compositor/CMakeLists.txt b/src/server/compositor/CMakeLists.txt index bdbadb66b3f..092b29f21c3 100644 --- a/src/server/compositor/CMakeLists.txt +++ b/src/server/compositor/CMakeLists.txt @@ -17,7 +17,7 @@ set( multi_monitor_arbiter.cpp dropping_schedule.cpp queueing_schedule.cpp -# basic_screen_shooter.cpp + basic_screen_shooter.cpp null_screen_shooter.cpp ) diff --git a/src/server/compositor/basic_screen_shooter.cpp b/src/server/compositor/basic_screen_shooter.cpp index deedd6e3b34..5cbe96ec6cd 100644 --- a/src/server/compositor/basic_screen_shooter.cpp +++ b/src/server/compositor/basic_screen_shooter.cpp @@ -15,6 +15,7 @@ */ #include "basic_screen_shooter.h" +#include "mir/graphics/gl_config.h" #include "mir/renderer/gl/buffer_render_target.h" #include "mir/renderer/renderer.h" #include "mir/renderer/gl/context.h" @@ -23,6 +24,8 @@ #include "mir/log.h" #include "mir/executor.h" #include "mir/graphics/platform.h" +#include "mir/renderer/renderer_factory.h" +#include "mir/renderer/sw/pixel_source.h" namespace mc = mir::compositor; namespace mr = mir::renderer; @@ -31,15 +34,90 @@ namespace mrg = mir::renderer::gl; namespace mrs = mir::renderer::software; namespace geom = mir::geometry; +class mc::BasicScreenShooter::Self::OneShotBufferDisplayProvider : public mg::CPUAddressableDisplayProvider +{ +public: + OneShotBufferDisplayProvider() = default; + + class FB : public mg::CPUAddressableDisplayProvider::MappableFB + { + public: + FB(std::shared_ptr buffer) + : buffer{std::move(buffer)} + { + } + + auto map_writeable() -> std::unique_ptr> override + { + return buffer->map_writeable(); + } + auto size() const -> geom::Size override + { + return buffer->size(); + } + auto format() const -> MirPixelFormat override + { + return buffer->format(); + } + auto stride() const -> geom::Stride override + { + return buffer->stride(); + } + private: + std::shared_ptr const buffer; + }; + + auto alloc_fb(geom::Size pixel_size) -> std::unique_ptr override + { + if (pixel_size != next_buffer->size()) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Mismatched buffer sizes?"})); + } + return std::make_unique(std::exchange(next_buffer, nullptr)); + } + + void set_next_buffer(std::shared_ptr buffer) + { + if (next_buffer) + { + BOOST_THROW_EXCEPTION((std::logic_error{"Attempt to set next buffer with a buffer already pending"})); + } + next_buffer = std::move(buffer); + } +private: + std::shared_ptr next_buffer; +}; + +class InterfaceProvider : public mg::DisplayInterfaceProvider +{ +public: + InterfaceProvider(std::shared_ptr provider) + : provider {std::move(provider)} + { + } +protected: + auto maybe_create_interface(mg::DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr override + { + if (dynamic_cast(&type_tag)) + { + return provider; + } + return nullptr; + } +private: + std::shared_ptr const provider; +}; + mc::BasicScreenShooter::Self::Self( std::shared_ptr const& scene, std::shared_ptr const& clock, - std::unique_ptr&& render_target, - std::unique_ptr&& renderer) + std::shared_ptr render_provider, + std::shared_ptr renderer_factory) : scene{scene}, - render_target{std::move(render_target)}, - renderer{std::move(renderer)}, - clock{clock} + clock{clock}, + render_provider{std::move(render_provider)}, + renderer_factory{std::move(renderer_factory)} { } @@ -59,25 +137,72 @@ auto mc::BasicScreenShooter::Self::render( } scene_elements.clear(); - render_target->make_current(); - render_target->set_buffer(buffer); + auto& renderer = renderer_for_buffer(buffer); + renderer.set_viewport(area); + /* We don't need the result of this `render` call, as we know it's + * going into the buffer we just set + */ + renderer.render(renderable_list); - render_target->bind(); - renderer->set_viewport(area); -// renderer->render(renderable_list); + return captured_time; +} - renderable_list.clear(); +auto mc::BasicScreenShooter::Self::renderer_for_buffer(std::shared_ptr buffer) + -> mr::Renderer& +{ + if (buffer->size() != last_rendered_size) + { + // We need to build a new Renderer, at the new size + class NoAuxConfig : public graphics::GLConfig + { + public: + auto depth_buffer_bits() const -> int override + { + return 0; + } + auto stencil_buffer_bits() const -> int override + { + return 0; + } + }; + auto interface_provider = std::make_shared(output); + auto gl_surface = render_provider->surface_for_output(interface_provider, buffer->size(), NoAuxConfig{}); + current_renderer = renderer_factory->create_renderer_for(std::move(gl_surface), render_provider); + } - return captured_time; + return *current_renderer; +} + +auto mc::BasicScreenShooter::select_provider( + std::span> const& providers) + -> std::shared_ptr +{ + auto display_provider = std::make_shared(); + auto interface_provider = std::make_shared(display_provider); + + for (auto const& render_provider : providers) + { + /* TODO: There might be more sensible ways to select a provider + * (one in use by at least one DisplayBuffer, the only one in use, the lowest-powered one,...) + * That will be a job for a policy object, later. + * + * For now, just use the first that claims to work. + */ + if (render_provider->suitability_for_display(interface_provider) >= mg::probe::supported) + { + return render_provider; + } + } + BOOST_THROW_EXCEPTION((std::runtime_error{"No rendering provider claims to support a CPU addressable target"})); } mc::BasicScreenShooter::BasicScreenShooter( std::shared_ptr const& scene, std::shared_ptr const& clock, Executor& executor, - std::unique_ptr&& render_target, - std::unique_ptr&& renderer) - : self{std::make_shared(scene, clock, std::move(render_target), std::move(renderer))}, + std::span> const& providers, + std::shared_ptr render_factory) + : self{std::make_shared(scene, clock, select_provider(providers), std::move(render_factory))}, executor{executor} { } @@ -89,7 +214,7 @@ void mc::BasicScreenShooter::capture( { // TODO: use an atomic to keep track of number of in-flight captures, and error if it's too many - executor.spawn([weak_self=std::weak_ptr{self}, buffer, area, callback=std::move(callback)] + executor.spawn([weak_self=std::weak_ptr{self}, buffer, area, callback=std::move(callback)] { if (auto const self = weak_self.lock()) { diff --git a/src/server/compositor/basic_screen_shooter.h b/src/server/compositor/basic_screen_shooter.h index 5201abde83f..d5bc343e4ae 100644 --- a/src/server/compositor/basic_screen_shooter.h +++ b/src/server/compositor/basic_screen_shooter.h @@ -20,6 +20,7 @@ #include "mir/compositor/screen_shooter.h" #include "mir/graphics/platform.h" #include "mir/renderer/renderer_factory.h" +#include "mir/renderer/sw/pixel_source.h" #include "mir/time/clock.h" #include @@ -47,8 +48,8 @@ class BasicScreenShooter: public ScreenShooter std::shared_ptr const& scene, std::shared_ptr const& clock, Executor& executor, - graphics::GLRenderingProvider& platform, - renderer::RendererFactory& render_factory); + std::span> const& providers, + std::shared_ptr render_factory); void capture( std::shared_ptr const& buffer, @@ -58,25 +59,41 @@ class BasicScreenShooter: public ScreenShooter private: struct Self { + class OneShotBufferDisplayProvider; + Self( std::shared_ptr const& scene, std::shared_ptr const& clock, - std::unique_ptr renderer); + std::shared_ptr provider, + std::shared_ptr render_factory); auto render( std::shared_ptr const& buffer, geometry::Rectangle const& area) -> time::Timestamp; - class HeadlessDisplay; + auto renderer_for_buffer(std::shared_ptr buffer) + -> renderer::Renderer&; std::mutex mutex; std::shared_ptr const scene; - std::unique_ptr const renderer; std::shared_ptr const clock; - std::shared_ptr const output; + std::shared_ptr const render_provider; + std::shared_ptr const renderer_factory; + + /* The Renderer instantiation is tied to a particular output size, and + * and requires enough setup to make it worth keeping around as a consumer + * is likely to be taking screenshots of consistent size + */ + std::unique_ptr current_renderer; + geometry::Size last_rendered_size; + + std::shared_ptr const output; }; std::shared_ptr const self; Executor& executor; + + static auto select_provider(std::span> const& providers) + -> std::shared_ptr; }; } } diff --git a/src/server/compositor/default_configuration.cpp b/src/server/compositor/default_configuration.cpp index c8fb2e42808..9e48b026d85 100644 --- a/src/server/compositor/default_configuration.cpp +++ b/src/server/compositor/default_configuration.cpp @@ -16,6 +16,7 @@ #include "mir/default_server_configuration.h" +#include "mir/log.h" #include "mir/shell/shell.h" #include "buffer_stream_factory.h" #include "default_display_buffer_compositor_factory.h" @@ -106,26 +107,38 @@ std::shared_ptr mir::DefaultServerConfiguration: auto mir::DefaultServerConfiguration::the_screen_shooter() -> std::shared_ptr { return screen_shooter( - [/*this*/]() -> std::shared_ptr + [this]() -> std::shared_ptr { - // try - // { - // return std::make_shared( - // the_scene(), - // the_clock(), - // thread_pool_executor, - // the_renderer_factory()); - // } - // catch (...) - // { - // mir::log( - // ::mir::logging::Severity::error, - // "", - // std::current_exception(), - // "failed to create BasicScreenShooter"); - // return std::make_shared(thread_pool_executor); - // } - - return std::make_shared(thread_pool_executor); + try + { + std::vector> providers; + providers.reserve(the_rendering_platforms().size()); + for (auto& platform : the_rendering_platforms()) + { + if (auto gl_provider = mg::RenderingPlatform::acquire_interface(platform)) + { + providers.push_back(gl_provider); + } + } + if (providers.empty()) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"No platform provides GL rendering support"})); + } + return std::make_shared( + the_scene(), + the_clock(), + thread_pool_executor, + providers, + the_renderer_factory()); + } + catch (...) + { + mir::log( + ::mir::logging::Severity::error, + "", + std::current_exception(), + "failed to create BasicScreenShooter"); + return std::make_shared(thread_pool_executor); + } }); } From 9a201a6ff96d89efb89bd6267852ad6b587d0f63 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 9 Jun 2023 10:25:02 +1000 Subject: [PATCH 011/108] platform/DRMFormat: Add from_mir_format conversion In order to more pervasively use `DRMFormat` as our format mechanism we temporarily need to also convert from `MirPixelFormat` at some interface boundaries. Add that helper. --- include/platform/mir/graphics/drm_formats.h | 2 + src/platform/graphics/drm_formats.cpp | 45 ++++++++++++++++++++- src/platform/symbols.map | 7 ++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/include/platform/mir/graphics/drm_formats.h b/include/platform/mir/graphics/drm_formats.h index b8a50ee7133..851c5271087 100644 --- a/include/platform/mir/graphics/drm_formats.h +++ b/include/platform/mir/graphics/drm_formats.h @@ -50,6 +50,8 @@ class DRMFormat operator uint32_t() const; auto as_mir_format() const -> std::optional; + static auto from_mir_format(MirPixelFormat format) -> DRMFormat; + struct FormatInfo; private: FormatInfo const* info; diff --git a/src/platform/graphics/drm_formats.cpp b/src/platform/graphics/drm_formats.cpp index 257c26dc623..d1923a2b383 100644 --- a/src/platform/graphics/drm_formats.cpp +++ b/src/platform/graphics/drm_formats.cpp @@ -15,6 +15,7 @@ */ #include "mir/graphics/drm_formats.h" +#include "mir_toolkit/common.h" #include #include @@ -528,7 +529,49 @@ auto mg::DRMFormat::as_mir_format() const -> std::optional default: return std::nullopt; } -} +} + +auto mg::DRMFormat::from_mir_format(MirPixelFormat format) + -> DRMFormat +{ + switch (format) + { + case mir_pixel_format_argb_8888: + return DRMFormat{DRM_FORMAT_ARGB8888}; + case mir_pixel_format_xrgb_8888: + return DRMFormat{DRM_FORMAT_XRGB8888}; + case mir_pixel_format_abgr_8888: + return DRMFormat{DRM_FORMAT_ABGR8888}; + case mir_pixel_format_xbgr_8888: + return DRMFormat{DRM_FORMAT_XBGR8888}; + case mir_pixel_format_bgr_888: + return DRMFormat{DRM_FORMAT_BGR888}; + case mir_pixel_format_rgb_888: + return DRMFormat{DRM_FORMAT_RGB888}; + case mir_pixel_format_rgb_565: + return DRMFormat{DRM_FORMAT_RGB565}; + case mir_pixel_format_rgba_5551: + return DRMFormat{DRM_FORMAT_RGBA5551}; + case mir_pixel_format_rgba_4444: + return DRMFormat{DRM_FORMAT_RGBA4444}; + case mir_pixel_format_invalid: + /* We *could* do something with DRM_FORMAT_INVALID here, but + * let's not. Let's maintain that DRMFormat is always valid + */ + BOOST_THROW_EXCEPTION((std::runtime_error{"Attempt to look up DRM format info of invalid pixel format"})); + case mir_pixel_formats: + BOOST_THROW_EXCEPTION((std::logic_error{"Attempt to look up format info of sentinel pixel format"})); +#ifndef __clang__ + /* Clang accepts that the above cases are exhaustive, gcc does not + * Have a default clause here to pacify gcc, but omit it for clang + * so that we fail to build if the above is *not* exhaustive. + */ + default: + BOOST_THROW_EXCEPTION((std::logic_error{"Exhaustive switch needs updating"})); +#endif + } +} + auto mg::drm_modifier_to_string(uint64_t modifier) -> std::string { diff --git a/src/platform/symbols.map b/src/platform/symbols.map index 0187aed2796..4961ca78429 100644 --- a/src/platform/symbols.map +++ b/src/platform/symbols.map @@ -220,3 +220,10 @@ MIR_PLATFORM_2.11 { mir::graphics::DRMFormat::as_mir_format*; }; } MIR_PLATFORM_2.8; + +MIR_PLATFORM_2.13 { + global: + extern "C++" { + mir::graphics::DRMFormat::from_mir_format*; + }; +} MIR_PLATFORM_2.11; From d0133e9801cbb82cae8ee6e26b108b1e3b149f1d Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 9 Jun 2023 10:49:18 +1000 Subject: [PATCH 012/108] renderer::gl::OutputSurface: Add `release_current` It *was* the case that `gl::OutputSurface`s were only ever accessed on a single thread; our `MultiThreadedCompositor` makes that guarantee. That meant that `OutputSurface` could be made current exactly once and never needed to be released. Unfortunately, with `BasicScreenShooter` we don't have that guarantee; rendering is done on a threadpool, so unless we have a thread-local `Renderer`, we need to be able to release the underlying EGL context in order that it can be made current on another thread. --- include/platform/mir/graphics/platform.h | 1 + include/platform/mir/renderer/gl/gl_surface.h | 1 + .../eglstream-kms/server/buffer_allocator.cpp | 13 +++++++++++++ src/platforms/gbm-kms/server/buffer_allocator.cpp | 13 +++++++++++++ .../renderer-generic-egl/buffer_allocator.cpp | 10 ++++++++++ src/platforms/x11/graphics/egl_helper.cpp | 8 ++++++++ src/platforms/x11/graphics/egl_helper.h | 1 + .../include/mir/test/doubles/mock_output_surface.h | 1 + 8 files changed, 48 insertions(+) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index e78e493f815..ee38f6413cb 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -371,6 +371,7 @@ class GenericEGLDisplayProvider : public DisplayInterfaceBase { public: virtual void make_current() = 0; + virtual void release_current() = 0; virtual auto clone_handle() -> std::unique_ptr = 0; }; diff --git a/include/platform/mir/renderer/gl/gl_surface.h b/include/platform/mir/renderer/gl/gl_surface.h index fa14c751449..d2dbabe3b20 100644 --- a/include/platform/mir/renderer/gl/gl_surface.h +++ b/include/platform/mir/renderer/gl/gl_surface.h @@ -38,6 +38,7 @@ class OutputSurface virtual void bind() = 0; virtual void make_current() = 0; + virtual void release_current() = 0; // Naming: SwapBuffers? Commit? Claim current buffer? virtual auto commit() -> std::unique_ptr = 0; diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index aa53cf0a114..7b209ae9838 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -645,6 +645,11 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface ctx->make_current(); } + void release_current() override + { + ctx->release_current(); + } + auto commit() -> std::unique_ptr override { auto fb = allocator->alloc_fb(size_); @@ -749,6 +754,14 @@ class EGLStreamOutputSurface : public mg::gl::OutputSurface } } + void release_current() override + { + if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to release EGL context")); + } + } + auto commit() -> std::unique_ptr override { if (eglSwapBuffers(dpy, surface) != EGL_TRUE) diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index 0f77378c858..8e27da933b3 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -427,6 +427,11 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface 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_); @@ -501,6 +506,14 @@ class GBMOutputSurface : public mg::gl::OutputSurface } } + void release_current() override + { + if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to release EGL context")); + } + } + auto commit() -> std::unique_ptr override { if (eglSwapBuffers(dpy, egl_surf) != EGL_TRUE) diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index 132ab788fb4..8680a7c9901 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -427,6 +427,11 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface 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()); @@ -486,6 +491,11 @@ class EGLOutputSurface : public mg::gl::OutputSurface fb->make_current(); } + void release_current() override + { + fb->release_current(); + } + auto commit() -> std::unique_ptr override { return fb->clone_handle(); diff --git a/src/platforms/x11/graphics/egl_helper.cpp b/src/platforms/x11/graphics/egl_helper.cpp index 4f57d9d002d..df7536309df 100644 --- a/src/platforms/x11/graphics/egl_helper.cpp +++ b/src/platforms/x11/graphics/egl_helper.cpp @@ -72,6 +72,14 @@ void mgxh::Framebuffer::make_current() } } +void mgxh::Framebuffer::release_current() +{ + if (eglMakeCurrent(state->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to release EGL context")); + } +} + void mgxh::Framebuffer::swap_buffers() { if (eglSwapBuffers(state->dpy, state->surf) != EGL_TRUE) diff --git a/src/platforms/x11/graphics/egl_helper.h b/src/platforms/x11/graphics/egl_helper.h index 24d1faf42ad..80e5a7e1bce 100644 --- a/src/platforms/x11/graphics/egl_helper.h +++ b/src/platforms/x11/graphics/egl_helper.h @@ -53,6 +53,7 @@ class Framebuffer : public GenericEGLDisplayProvider::EGLFramebuffer auto size() const -> geometry::Size override; void make_current() override; + void release_current() override; auto clone_handle() -> std::unique_ptr override; void swap_buffers(); diff --git a/tests/include/mir/test/doubles/mock_output_surface.h b/tests/include/mir/test/doubles/mock_output_surface.h index b467060ed13..88e3e5f88ea 100644 --- a/tests/include/mir/test/doubles/mock_output_surface.h +++ b/tests/include/mir/test/doubles/mock_output_surface.h @@ -28,6 +28,7 @@ class MockOutputSurface : public mir::graphics::gl::OutputSurface public: MOCK_METHOD(void, bind, (), (override)); MOCK_METHOD(void, make_current, (), (override)); + MOCK_METHOD(void, release_current, (), (override)); MOCK_METHOD(std::unique_ptr, commit, (), (override)); MOCK_METHOD(mir::geometry::Size, size, (), (const override)); MOCK_METHOD(Layout, layout, (), (const override)); From 3a41bea8439d3de2b6e0bf677d7fffa376137d83 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 9 Jun 2023 17:18:03 +1000 Subject: [PATCH 013/108] renderers::gl::Renderer: Hook up `make_current()`/`release_current()` `BasicScreenShooter` wants be able to use the same `gl::Renderer` on an arbitrary thread - not simultaneously, but not always on the same thread. That means that thread-local state - such as the current EGL context - cannot be assumed to be constant across calls to `render()`. Also allow users to explicitly release the current context, via the pleasantly-already-existing-`suspend()`. --- src/renderers/gl/renderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderers/gl/renderer.cpp b/src/renderers/gl/renderer.cpp index a80dbf1ad87..160e7824e29 100644 --- a/src/renderers/gl/renderer.cpp +++ b/src/renderers/gl/renderer.cpp @@ -353,6 +353,7 @@ void mrg::Renderer::tessellate(std::vector& primitives, auto mrg::Renderer::render(mg::RenderableList const& renderables) const -> std::unique_ptr { + output_surface->make_current(); output_surface->bind(); glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); @@ -639,4 +640,5 @@ void mrg::Renderer::set_output_transform(glm::mat2 const& t) void mrg::Renderer::suspend() { + output_surface->release_current(); } From 3ca7d91ae3dcec31e48e052ebcdac1e56c1898b0 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 9 Jun 2023 17:33:55 +1000 Subject: [PATCH 014/108] CPUAddressableDisplayProvider: Expose supported pixel formats. `DisplayProviders` almost certainly have a limited set of pixel formats they can output, and renderers need to care about that in order for colours to be correct (or in order to not be garbled, in the case of BPP differences). Add a `supported_formats()` query, and accept a `DRMFormat` in the `alloc_fb()` call. --- include/platform/mir/graphics/platform.h | 5 ++- .../eglstream-kms/server/buffer_allocator.cpp | 45 +++++++++++++++++-- .../gbm-kms/server/buffer_allocator.cpp | 42 +++++++++++++++-- .../gbm-kms/server/kms/cpu_addressable_fb.cpp | 41 ++++++++++++----- .../gbm-kms/server/kms/cpu_addressable_fb.h | 18 ++++++-- src/platforms/gbm-kms/server/kms/display.cpp | 11 ++++- src/platforms/gbm-kms/server/kms/display.h | 5 ++- .../gbm-kms/server/kms/display_buffer.cpp | 7 ++- .../renderer-generic-egl/buffer_allocator.cpp | 45 +++++++++++++++++-- .../compositor/basic_screen_shooter.cpp | 16 ++++++- .../kms/test_display_multi_monitor.cpp | 6 ++- 11 files changed, 209 insertions(+), 32 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index ee38f6413cb..4f325d66e0f 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -272,7 +272,10 @@ class CPUAddressableDisplayProvider : public DisplayInterfaceBase virtual ~MappableFB() override = default; }; - virtual auto alloc_fb(geometry::Size pixel_size) + virtual auto supported_formats() const + -> std::vector = 0; + + virtual auto alloc_fb(geometry::Size pixel_size, DRMFormat format) -> std::unique_ptr = 0; }; diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index 7b209ae9838..c5bc5813845 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -20,6 +20,7 @@ #include "buffer_allocator.h" #include "mir/anonymous_shm_file.h" #include "mir/graphics/display_buffer.h" +#include "mir/graphics/drm_formats.h" #include "mir/graphics/egl_resources.h" #include "mir/graphics/gl_config.h" #include "mir/graphics/platform.h" @@ -47,6 +48,7 @@ #include #include +#include #include #include @@ -585,6 +587,31 @@ 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: @@ -594,7 +621,8 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface geom::Size size) : allocator{std::move(allocator)}, ctx{std::move(ctx)}, - size_{std::move(size)} + 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()); @@ -652,9 +680,19 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface auto commit() -> std::unique_ptr override { - auto fb = allocator->alloc_fb(size_); + 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 @@ -667,7 +705,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface glReadPixels( 0, 0, size_.width.as_int(), size_.height.as_int(), - GL_RGBA, GL_UNSIGNED_BYTE, mapping->data()); + pixel_layout, GL_UNSIGNED_BYTE, mapping->data()); } return fb; } @@ -686,6 +724,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface 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; }; diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index 8e27da933b3..0de430a0541 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -359,6 +359,30 @@ class GLHandle 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"})); +} class CPUCopyOutputSurface : public mg::gl::OutputSurface { @@ -371,7 +395,8 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface : allocator{std::move(allocator)}, dpy{dpy}, ctx{ctx}, - size_{std::move(size)} + size_{std::move(size)}, + format{select_format_from(*this->allocator)} { if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE) { @@ -434,9 +459,19 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface auto commit() -> std::unique_ptr override { - auto fb = allocator->alloc_fb(size_); + 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 @@ -449,7 +484,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface glReadPixels( 0, 0, size_.width.as_int(), size_.height.as_int(), - GL_RGBA, GL_UNSIGNED_BYTE, mapping->data()); + pixel_layout, GL_UNSIGNED_BYTE, mapping->data()); } return fb; } @@ -469,6 +504,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface EGLDisplay const dpy; EGLContext const ctx; geom::Size const size_; + mg::DRMFormat const format; RenderbufferHandle const colour_buffer; FramebufferHandle const fbo; }; diff --git a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp index 6d1f2435831..d3c32459d58 100644 --- a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp +++ b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp @@ -39,10 +39,12 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable Mapping( uint32_t width, uint32_t height, uint32_t pitch, + MirPixelFormat format, T* data, size_t len) : size_{width, height}, stride_{pitch}, + format_{format}, data_{data}, len_{len} { @@ -60,7 +62,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable [[nodiscard]] auto format() const -> MirPixelFormat { - return mir_pixel_format_xrgb_8888; + return format_; } [[nodiscard]] @@ -90,6 +92,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable private: mir::geometry::Size const size_; mir::geometry::Stride const stride_; + MirPixelFormat const format_; T* const data_; size_t const len_; }; @@ -105,7 +108,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable } } - static auto create_kms_dumb_buffer(mir::Fd drm_fd, mir::geometry::Size const& size) -> + static auto create_kms_dumb_buffer(mir::Fd drm_fd, DRMFormat format, mir::geometry::Size const& size) -> std::unique_ptr { struct drm_mode_create_dumb params = {}; @@ -124,7 +127,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable } return std::unique_ptr{ - new Buffer{std::move(drm_fd), params}}; + new Buffer{std::move(drm_fd), params, format}}; } auto map_writeable() -> std::unique_ptr> override @@ -133,6 +136,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable return std::make_unique>( width(), height(), pitch(), + format(), static_cast(data), size_); } @@ -140,7 +144,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable { auto const data = mmap_buffer(PROT_READ); return std::make_unique>( - width(), height(),pitch(), + width(), height(), pitch(), format(), static_cast(data), size_); } @@ -151,6 +155,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable return std::make_unique>( width(), height(), pitch(), + format(), static_cast(data), size_); } @@ -178,7 +183,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable auto format() const -> MirPixelFormat override { - return mir_pixel_format_xrgb_8888; + return format_.as_mir_format().value_or(mir_pixel_format_invalid); } auto stride() const -> geometry::Stride override @@ -193,11 +198,13 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable private: Buffer( mir::Fd fd, - struct drm_mode_create_dumb const& params) + struct drm_mode_create_dumb const& params, + DRMFormat format) : drm_fd{std::move(fd)}, width_{params.width}, height_{params.height}, pitch_{params.pitch}, + format_{format}, gem_handle{params.handle}, size_{params.size} { @@ -235,12 +242,17 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable uint32_t const width_; uint32_t const height_; uint32_t const pitch_; + DRMFormat const format_; uint32_t const gem_handle; size_t const size_; }; -mgg::CPUAddressableFB::CPUAddressableFB(mir::Fd const& drm_fd, bool supports_modifiers, mir::geometry::Size const& size) - : CPUAddressableFB(drm_fd, supports_modifiers, Buffer::create_kms_dumb_buffer(drm_fd, size)) +mgg::CPUAddressableFB::CPUAddressableFB( + mir::Fd const& drm_fd, + bool supports_modifiers, + DRMFormat format, + mir::geometry::Size const& size) + : CPUAddressableFB(drm_fd, supports_modifiers, format, Buffer::create_kms_dumb_buffer(drm_fd, format, size)) { } @@ -252,9 +264,10 @@ mgg::CPUAddressableFB::~CPUAddressableFB() mgg::CPUAddressableFB::CPUAddressableFB( mir::Fd drm_fd, bool supports_modifiers, + DRMFormat format, std::unique_ptr buffer) : drm_fd{std::move(drm_fd)}, - fb_id{fb_id_for_buffer(this->drm_fd, supports_modifiers, *buffer)}, + fb_id{fb_id_for_buffer(this->drm_fd, supports_modifiers, format, *buffer)}, buffer{std::move(buffer)} { } @@ -284,7 +297,11 @@ mgg::CPUAddressableFB::operator uint32_t() const return fb_id; } -auto mgg::CPUAddressableFB::fb_id_for_buffer(mir::Fd const &drm_fd, bool supports_modifiers, Buffer const& buf) -> uint32_t +auto mgg::CPUAddressableFB::fb_id_for_buffer( + mir::Fd const &drm_fd, + bool supports_modifiers, + DRMFormat format, + Buffer const& buf) -> uint32_t { uint32_t fb_id; uint32_t const pitches[4] = { buf.pitch(), 0, 0, 0 }; @@ -297,7 +314,7 @@ auto mgg::CPUAddressableFB::fb_id_for_buffer(mir::Fd const &drm_fd, bool support drm_fd, buf.width(), buf.height(), - DRM_FORMAT_XRGB8888, + format, handles, pitches, offsets, @@ -317,7 +334,7 @@ auto mgg::CPUAddressableFB::fb_id_for_buffer(mir::Fd const &drm_fd, bool support drm_fd, buf.width(), buf.height(), - DRM_FORMAT_XRGB8888, + format, handles, pitches, offsets, diff --git a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h index 8d609d93fe5..949a035269a 100644 --- a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h +++ b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h @@ -29,7 +29,11 @@ namespace mir::graphics::gbm class CPUAddressableFB : public FBHandle, public CPUAddressableDisplayProvider::MappableFB { public: - CPUAddressableFB(mir::Fd const& drm_fd, bool supports_modifiers, mir::geometry::Size const& size); + CPUAddressableFB( + mir::Fd const& drm_fd, + bool supports_modifiers, + DRMFormat format, + mir::geometry::Size const& size); ~CPUAddressableFB() override; auto map_writeable() -> std::unique_ptr> override; @@ -45,8 +49,16 @@ class CPUAddressableFB : public FBHandle, public CPUAddressableDisplayProvider:: private: class Buffer; - CPUAddressableFB(mir::Fd drm_fd, bool supports_modifiers, std::unique_ptr buffer); - static auto fb_id_for_buffer(mir::Fd const& drm_fd, bool supports_modifiers, Buffer const& buf) -> uint32_t; + CPUAddressableFB( + mir::Fd drm_fd, + bool supports_modifiers, + DRMFormat format, + std::unique_ptr buffer); + static auto fb_id_for_buffer( + mir::Fd const& drm_fd, + bool supports_modifiers, + DRMFormat format, + Buffer const& buf) -> uint32_t; mir::Fd const drm_fd; uint32_t const fb_id; diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index cffb902c81e..b9c80c198c1 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -468,10 +468,17 @@ mgg::CPUAddressableDisplayProvider::CPUAddressableDisplayProvider(mir::Fd drm_fd { } +auto mgg::CPUAddressableDisplayProvider::supported_formats() const + -> std::vector +{ + // TODO: Pull out of DRM info + return {mg::DRMFormat{DRM_FORMAT_XRGB8888}, mg::DRMFormat{DRM_FORMAT_ARGB8888}}; +} + auto mgg::CPUAddressableDisplayProvider::alloc_fb( - geom::Size size) -> std::unique_ptr + geom::Size size, DRMFormat format) -> std::unique_ptr { - return std::make_unique(drm_fd, supports_modifiers, size); + return std::make_unique(drm_fd, supports_modifiers, format, size); } namespace diff --git a/src/platforms/gbm-kms/server/kms/display.h b/src/platforms/gbm-kms/server/kms/display.h index 41e365036bd..be6cf74ac86 100644 --- a/src/platforms/gbm-kms/server/kms/display.h +++ b/src/platforms/gbm-kms/server/kms/display.h @@ -109,7 +109,10 @@ class CPUAddressableDisplayProvider : public graphics::CPUAddressableDisplayProv public: explicit CPUAddressableDisplayProvider(mir::Fd drm_fd); - auto alloc_fb(geometry::Size pixel_size) + auto supported_formats() const + -> std::vector override; + + auto alloc_fb(geometry::Size pixel_size, DRMFormat format) -> std::unique_ptr override; private: diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index d3fb0ac27bb..6e03181a8f6 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -63,7 +63,12 @@ mgg::DisplayBuffer::DisplayBuffer( { listener->report_successful_setup_of_native_resources(); - auto initial_fb = std::make_shared(std::move(drm_fd), false, area.size); + // TODO: Pull a supported format out of KMS rather than assuming XRGB8888 + auto initial_fb = std::make_shared( + std::move(drm_fd), + false, + DRMFormat{DRM_FORMAT_XRGB8888}, + area.size); auto mapping = initial_fb->map_writeable(); ::memset(mapping->data(), 24, mapping->len()); diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index 8680a7c9901..50e81f7c67e 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -44,6 +44,8 @@ #include #include +#include + #include #include #include @@ -359,6 +361,31 @@ class GLHandle 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"})); +} + class CPUCopyOutputSurface : public mg::gl::OutputSurface { @@ -371,7 +398,8 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface : allocator{std::move(allocator)}, dpy{dpy}, ctx{ctx}, - size_{size} + size_{size}, + format{select_format_from(*this->allocator)} { if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE) { @@ -434,9 +462,19 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface auto commit() -> std::unique_ptr override { - auto fb = allocator->alloc_fb(size()); + 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 @@ -449,7 +487,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface glReadPixels( 0, 0, size_.width.as_int(), size_.height.as_int(), - GL_RGBA, GL_UNSIGNED_BYTE, mapping->data()); + pixel_layout, GL_UNSIGNED_BYTE, mapping->data()); } return fb; } @@ -469,6 +507,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface EGLDisplay const dpy; EGLContext const ctx; geom::Size const size_; + mg::DRMFormat const format; RenderbufferHandle const colour_buffer; FramebufferHandle const fbo; }; diff --git a/src/server/compositor/basic_screen_shooter.cpp b/src/server/compositor/basic_screen_shooter.cpp index 5cbe96ec6cd..4c2443cf3ff 100644 --- a/src/server/compositor/basic_screen_shooter.cpp +++ b/src/server/compositor/basic_screen_shooter.cpp @@ -15,6 +15,7 @@ */ #include "basic_screen_shooter.h" +#include "mir/graphics/drm_formats.h" #include "mir/graphics/gl_config.h" #include "mir/renderer/gl/buffer_render_target.h" #include "mir/renderer/renderer.h" @@ -67,12 +68,25 @@ class mc::BasicScreenShooter::Self::OneShotBufferDisplayProvider : public mg::CP std::shared_ptr const buffer; }; - auto alloc_fb(geom::Size pixel_size) -> std::unique_ptr override + auto supported_formats() const -> std::vector override + { + if (!next_buffer) + { + BOOST_THROW_EXCEPTION((std::logic_error{"Attempted to query supported_formats before assigning a buffer"})); + } + return {mg::DRMFormat::from_mir_format(next_buffer->format())}; + } + + auto alloc_fb(geom::Size pixel_size, mg::DRMFormat format) -> std::unique_ptr override { if (pixel_size != next_buffer->size()) { BOOST_THROW_EXCEPTION((std::runtime_error{"Mismatched buffer sizes?"})); } + if (format.as_mir_format().value_or(mir_pixel_format_invalid) != next_buffer->format()) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Mismatched pixel formats"})); + } return std::make_unique(std::exchange(next_buffer, nullptr)); } diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp index b41f9873b9a..8f34ccd195e 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp @@ -17,6 +17,7 @@ #include "mir/graphics/display.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/display_configuration.h" +#include "mir/graphics/drm_formats.h" #include "mir/graphics/platform.h" #include "src/platforms/gbm-kms/server/kms/platform.h" @@ -36,6 +37,7 @@ #include "mir/test/doubles/mock_drm.h" #include "mir/test/doubles/mock_gbm.h" +#include #include #include @@ -367,7 +369,7 @@ TEST_F(MesaDisplayMultiMonitorTest, flip_flips_all_connected_crtcs) group.for_each_display_buffer( [provider](mg::DisplayBuffer& db) { - auto fb = provider->alloc_fb(db.view_area().size); + auto fb = provider->alloc_fb(db.view_area().size, mg::DRMFormat{DRM_FORMAT_ABGR8888}); db.set_next_image(std::move(fb)); }); group.post(); @@ -381,7 +383,7 @@ TEST_F(MesaDisplayMultiMonitorTest, flip_flips_all_connected_crtcs) group.for_each_display_buffer( [provider](mg::DisplayBuffer& db) { - auto fb = provider->alloc_fb(db.view_area().size); + auto fb = provider->alloc_fb(db.view_area().size, mg::DRMFormat{DRM_FORMAT_ARGB8888}); db.set_next_image(std::move(fb)); }); group.post(); From 9abffdfeae50ced4461e75b2950f964a5cdd7f8a Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 9 Jun 2023 17:47:27 +1000 Subject: [PATCH 015/108] platforms/gbm-kms: Fix GBMSurface lifetime I was getting a crash on display hot-unplug; this turns out to be because the `gbm_surface` was being destroyed before the frontbuffer derived from it. Fix this by `shared_ptr`ing the `gbm_surface` into the `LockedFrontBuffer`. --- src/platforms/gbm-kms/server/kms/display.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index b9c80c198c1..810edf5a19b 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -666,7 +666,8 @@ class GBMSurfaceImpl : public mgg::GBMDisplayProvider::GBMSurface format, modifiers.empty() ? nullptr : modifiers.data(), modifiers.size(), - GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT)} + GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT), + [](auto surface) { gbm_surface_destroy(surface); }} { if (!surface) { @@ -676,7 +677,6 @@ class GBMSurfaceImpl : public mgg::GBMDisplayProvider::GBMSurface ~GBMSurfaceImpl() { - gbm_surface_destroy(surface); } GBMSurfaceImpl(GBMSurfaceImpl const&) = delete; @@ -684,12 +684,12 @@ class GBMSurfaceImpl : public mgg::GBMDisplayProvider::GBMSurface operator gbm_surface*() const override { - return surface; + return surface.get(); } auto claim_framebuffer() -> std::unique_ptr override { - if (!gbm_surface_has_free_buffers(surface)) + if (!gbm_surface_has_free_buffers(surface.get())) { BOOST_THROW_EXCEPTION(( std::system_error{ @@ -699,8 +699,8 @@ class GBMSurfaceImpl : public mgg::GBMDisplayProvider::GBMSurface } LockedFrontBuffer bo{ - gbm_surface_lock_front_buffer(surface), - [this](gbm_bo* bo) { gbm_surface_release_buffer(surface, bo); }}; + gbm_surface_lock_front_buffer(surface.get()), + [shared_surface = surface](gbm_bo* bo) { gbm_surface_release_buffer(shared_surface.get(), bo); }}; if (!bo) { @@ -711,7 +711,7 @@ class GBMSurfaceImpl : public mgg::GBMDisplayProvider::GBMSurface } private: mir::Fd const drm_fd; - gbm_surface* const surface; + std::shared_ptr const surface; }; } From 71675b04fee2fb69cc6ce4136fa3f60458c6353f Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 9 Jun 2023 17:50:35 +1000 Subject: [PATCH 016/108] multiple/CPUCopyOutputSurface: Fix EGL context We need the EGL context to be current *before* allocating the FBO. Rather than make that a precondition of calling the constructor, make the context current during construction. --- .../gbm-kms/server/buffer_allocator.cpp | 17 +++++++++++------ .../renderer-generic-egl/buffer_allocator.cpp | 11 ++++++++++- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index 0de430a0541..6aa595aeeb7 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -384,6 +384,16 @@ auto select_format_from(mg::CPUAddressableDisplayProvider const& provider) -> mg 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: @@ -394,15 +404,10 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface geom::Size size) : allocator{std::move(allocator)}, dpy{dpy}, - ctx{ctx}, + ctx{ensure_context_current(dpy, ctx)}, size_{std::move(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()); diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index 50e81f7c67e..dfa84353f32 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -386,6 +386,15 @@ auto select_format_from(mg::CPUAddressableDisplayProvider const& provider) -> mg 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 { @@ -397,7 +406,7 @@ class CPUCopyOutputSurface : public mg::gl::OutputSurface geom::Size size) : allocator{std::move(allocator)}, dpy{dpy}, - ctx{ctx}, + ctx{ensure_context_current(dpy, ctx)}, size_{size}, format{select_format_from(*this->allocator)} { From 1e919fde3f7ffcdd01dd74e7cfb2624ff1225110 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 9 Jun 2023 17:54:27 +1000 Subject: [PATCH 017/108] platforms/gbm-kms: Fix KMSDisplayProvider Users of the GBMDisplayProvider require that the `gbm_device` is stable across probing and subsequent display. Pre-construct one in the `KMSDisplayProvider` constructor, and return it when requested. Also make sure to use the *same* `KMSDisplayProvider`, rather than constructing new ones all the time. --- src/platforms/gbm-kms/server/kms/platform.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index fff7ff288f5..72eae5e3711 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -230,6 +230,7 @@ mgg::RenderingPlatform::RenderingPlatform( std::variant, std::shared_ptr> hw) : udev_device{std::move(udev_device)}, device{std::visit(gbm_device_from_hw{}, hw)}, + bound_display{std::visit(display_provider_or_nothing{}, hw)}, dpy{initialise_egl(dpy_for_gbm_device(device.get()), 1, 4)}, share_ctx{make_share_only_context(dpy)} { @@ -251,11 +252,13 @@ auto mgg::RenderingPlatform::maybe_create_interface( } return nullptr; } + class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfaceProvider { public: - KMSDisplayInterfaceProvider(mir::Fd drm_fd) - : drm_fd{std::move(drm_fd)} + explicit KMSDisplayInterfaceProvider(mir::Fd drm_fd) + : drm_fd{std::move(drm_fd)}, + gbm_provider{std::make_shared(this->drm_fd)} { } @@ -265,25 +268,26 @@ class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfacePr { if (dynamic_cast(&type_tag)) { - mir::log_debug("Using GBMDisplayProvider"); - return std::make_shared(drm_fd); + return gbm_provider; } if (dynamic_cast(&type_tag)) { - mir::log_debug("Using DumbDisplayProvider"); return std::make_shared(drm_fd); } return {}; } private: mir::Fd const drm_fd; + // We rely on the GBM provider being stable over probe, so we construct one at startup + // and reuse it. + std::shared_ptr const gbm_provider; }; mir::UniqueModulePtr mgg::Platform::create_display( std::shared_ptr const& initial_conf_policy, std::shared_ptr const&) { return make_module_ptr( - std::make_shared(drm_fd), + provider, drm_fd, bypass_option_, initial_conf_policy, From 16e71612d73c59693689b5c277ad95d4b24e4c22 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 9 Jun 2023 17:58:25 +1000 Subject: [PATCH 018/108] =?UTF-8?q?compositor/BasicScreenShooter:=20Fix=20?= =?UTF-8?q?it!=20=E2=98=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we don't keep the EGL context current on a previous thread when processing a new call; make sure we've loaded a buffer into the `OneShotBufferDisplayProvider`. Now actually works! --- src/server/compositor/basic_screen_shooter.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/server/compositor/basic_screen_shooter.cpp b/src/server/compositor/basic_screen_shooter.cpp index 4c2443cf3ff..ac24bf63fcd 100644 --- a/src/server/compositor/basic_screen_shooter.cpp +++ b/src/server/compositor/basic_screen_shooter.cpp @@ -31,7 +31,6 @@ namespace mc = mir::compositor; namespace mr = mir::renderer; namespace mg = mir::graphics; -namespace mrg = mir::renderer::gl; namespace mrs = mir::renderer::software; namespace geom = mir::geometry; @@ -131,7 +130,8 @@ mc::BasicScreenShooter::Self::Self( : scene{scene}, clock{clock}, render_provider{std::move(render_provider)}, - renderer_factory{std::move(renderer_factory)} + renderer_factory{std::move(renderer_factory)}, + output{std::make_shared()} { } @@ -158,13 +158,18 @@ auto mc::BasicScreenShooter::Self::render( */ renderer.render(renderable_list); + // Because we might be called on a different thread next time we need to + // ensure the renderer doesn't keep the EGL context current + renderer.suspend(); return captured_time; } auto mc::BasicScreenShooter::Self::renderer_for_buffer(std::shared_ptr buffer) -> mr::Renderer& { - if (buffer->size() != last_rendered_size) + auto const buffer_size = buffer->size(); + output->set_next_buffer(std::move(buffer)); + if (buffer_size != last_rendered_size) { // We need to build a new Renderer, at the new size class NoAuxConfig : public graphics::GLConfig @@ -180,10 +185,9 @@ auto mc::BasicScreenShooter::Self::renderer_for_buffer(std::shared_ptr(output); - auto gl_surface = render_provider->surface_for_output(interface_provider, buffer->size(), NoAuxConfig{}); + auto gl_surface = render_provider->surface_for_output(interface_provider, buffer_size, NoAuxConfig{}); current_renderer = renderer_factory->create_renderer_for(std::move(gl_surface), render_provider); } - return *current_renderer; } From 8d1c7360565bde056e98493871c31c357fc05327 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 9 Jun 2023 17:59:27 +1000 Subject: [PATCH 019/108] graphics::DefaultServerConfiguration: Use MultiplexingDisplay. Merge all the display platforms into a single, mega Voltron DisplayPlatform! --- src/server/graphics/default_configuration.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index 892618d338e..57002edf494 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -20,6 +20,7 @@ #include "mir/graphics/default_display_configuration_policy.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/display.h" +#include "multiplexing_display.h" #include "null_cursor.h" #include "software_cursor.h" #include "platform_probe.h" @@ -401,9 +402,18 @@ mir::DefaultServerConfiguration::the_display() return display( [this]() -> std::shared_ptr { - return the_display_platforms().back()->create_display( - the_display_configuration_policy(), - the_gl_config()); + std::vector> displays; + displays.reserve(the_display_platforms().size()); + for (auto const& platform : the_display_platforms()) + { + displays.push_back( + platform->create_display( + the_display_configuration_policy(), + the_gl_config())); + } + return std::make_shared( + std::move(displays), + *the_display_configuration_policy()); }); } From 81f1e365014b49bab278336205bf851890d6e3bf Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 13 Jun 2023 17:23:25 +1000 Subject: [PATCH 020/108] platforms/common: Pull out CPUCopyOutputSurface These platforms all have (effectively) a single implementation of `CPUCopyOutputSurface`. Pull it into the common platforms code to make it easier to fix up over time. --- src/platforms/common/server/CMakeLists.txt | 2 + .../common/server/cpu_copy_output_surface.cpp | 281 ++++++++++++++++++ .../common/server/cpu_copy_output_surface.h | 63 ++++ .../eglstream-kms/server/buffer_allocator.cpp | 150 +--------- .../gbm-kms/server/buffer_allocator.cpp | 194 +----------- .../renderer-generic-egl/buffer_allocator.cpp | 198 +----------- 6 files changed, 354 insertions(+), 534 deletions(-) create mode 100644 src/platforms/common/server/cpu_copy_output_surface.cpp create mode 100644 src/platforms/common/server/cpu_copy_output_surface.h 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), From cb697bca1e2bdb6bc1e5224c6b77cceb82339778 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 13 Jun 2023 17:35:39 +1000 Subject: [PATCH 021/108] tests/GLRenderer: Fix make_current test --- tests/unit-tests/renderers/gl/test_gl_renderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit-tests/renderers/gl/test_gl_renderer.cpp b/tests/unit-tests/renderers/gl/test_gl_renderer.cpp index 2a50a0e0376..8ab477b6bdf 100644 --- a/tests/unit-tests/renderers/gl/test_gl_renderer.cpp +++ b/tests/unit-tests/renderers/gl/test_gl_renderer.cpp @@ -256,7 +256,7 @@ TEST_F(GLRenderer, makes_display_buffer_current_before_rendering) auto mock_output_surface = make_output_surface(); InSequence seq; - EXPECT_CALL(*mock_output_surface, make_current()); + EXPECT_CALL(*mock_output_surface, make_current()).Times(AnyNumber()); EXPECT_CALL(mock_gl, glClear(_)); mrg::Renderer renderer(gl_platform, std::move(mock_output_surface)); From 112ad64a739ad7fbc4ecfbc7bf6709a770e37b14 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 20 Jun 2023 11:25:34 +1000 Subject: [PATCH 022/108] GBMHelper: Drop dead code --- .../gbm-kms/server/display_helpers.cpp | 24 ------------------- .../gbm-kms/server/display_helpers.h | 2 -- 2 files changed, 26 deletions(-) diff --git a/src/platforms/gbm-kms/server/display_helpers.cpp b/src/platforms/gbm-kms/server/display_helpers.cpp index 1f0fa74978c..2ad592eb741 100644 --- a/src/platforms/gbm-kms/server/display_helpers.cpp +++ b/src/platforms/gbm-kms/server/display_helpers.cpp @@ -234,30 +234,6 @@ mgmh::GBMHelper::GBMHelper(mir::Fd const& drm_fd) std::runtime_error("Failed to create GBM device")); } -mgg::GBMSurfaceUPtr mgmh::GBMHelper::create_scanout_surface( - uint32_t width, - uint32_t height, - uint32_t gbm_format, - bool sharable) const -{ - auto format_flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; - - if (sharable) - { - format_flags |= GBM_BO_USE_LINEAR; - } - - auto surface_raw = gbm_surface_create(device, width, height, gbm_format, format_flags); - - auto gbm_surface_deleter = [](gbm_surface *p) { if (p) gbm_surface_destroy(p); }; - GBMSurfaceUPtr surface{surface_raw, gbm_surface_deleter}; - - if (!surface) - BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create GBM scanout surface")); - - return surface; -} - mgmh::GBMHelper::~GBMHelper() { if (device) diff --git a/src/platforms/gbm-kms/server/display_helpers.h b/src/platforms/gbm-kms/server/display_helpers.h index 4438846d710..27c789474d0 100644 --- a/src/platforms/gbm-kms/server/display_helpers.h +++ b/src/platforms/gbm-kms/server/display_helpers.h @@ -79,8 +79,6 @@ class GBMHelper GBMHelper(const GBMHelper&) = delete; GBMHelper& operator=(const GBMHelper&) = delete; - GBMSurfaceUPtr create_scanout_surface(uint32_t width, uint32_t height, uint32_t gbm_format, bool sharable) const; - gbm_device* const device; }; From 3119e749766e2b9dab7aa90751337d3a392921ac Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 20 Jun 2023 11:27:06 +1000 Subject: [PATCH 023/108] gbm-kms: Also populate offset for KMS FB I don't know of a case where this would *not* be zero, but it's cheap to be prepared in case it is! --- src/platforms/gbm-kms/server/kms/display.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index 810edf5a19b..50ea21e0345 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -613,7 +613,7 @@ class GBMBoFramebuffer : public mgg::FBHandle }}; uint32_t handles[4] = {gbm_bo_get_handle(bo.get()).u32, 0, 0, 0}; uint32_t strides[4] = {gbm_bo_get_stride(bo.get()), 0, 0, 0}; - uint32_t offsets[4] = {0, 0, 0, 0}; + uint32_t offsets[4] = {gbm_bo_get_offset(bo.get(), 0), 0, 0, 0}; auto format = gbm_bo_get_format(bo.get()); From f7e27c71f2843b4dfdb45430021af039cf197b41 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 20 Jun 2023 11:29:45 +1000 Subject: [PATCH 024/108] gbm-kms: factor out gbm_surface creation --- src/platforms/gbm-kms/server/kms/display.cpp | 63 ++++++++++++++------ 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index 50ea21e0345..698904e79fd 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -654,31 +654,60 @@ class GBMBoFramebuffer : public mgg::FBHandle std::shared_ptr const fb_id; }; +namespace +{ +auto create_gbm_surface(gbm_device* gbm, geom::Size size, mg::DRMFormat format, std::span modifiers) + -> std::shared_ptr +{ + auto const surface = + [&]() + { + if (modifiers.empty()) + { + // If we have no no modifiers don't use the with-modifiers creation path. + auto foo = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; + return gbm_surface_create( + gbm, + size.width.as_uint32_t(), size.height.as_uint32_t(), + format, + foo); + } + else + { + return gbm_surface_create_with_modifiers2( + gbm, + size.width.as_uint32_t(), size.height.as_uint32_t(), + format, + modifiers.data(), + modifiers.size(), + GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT); + } + }(); + + if (!surface) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + errno, + std::system_category(), + "Failed to create GBM surface"})); + + } + return std::shared_ptr{ + surface, + [](auto surface) { gbm_surface_destroy(surface); }}; +} +} + class GBMSurfaceImpl : public mgg::GBMDisplayProvider::GBMSurface { public: GBMSurfaceImpl(mir::Fd drm_fd, gbm_device* gbm, geom::Size size, mg::DRMFormat const format, std::span modifiers) : drm_fd{std::move(drm_fd)}, - surface{ - gbm_surface_create_with_modifiers2( - gbm, - size.width.as_uint32_t(), size.height.as_uint32_t(), - format, - modifiers.empty() ? nullptr : modifiers.data(), - modifiers.size(), - GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT), - [](auto surface) { gbm_surface_destroy(surface); }} + surface{create_gbm_surface(gbm, size, format, modifiers)} { - if (!surface) - { - BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to create GBM surface"})); - } } - ~GBMSurfaceImpl() - { - } - GBMSurfaceImpl(GBMSurfaceImpl const&) = delete; auto operator=(GBMSurfaceImpl const&) -> GBMSurfaceImpl const& = delete; From d1b7e4debd5d3dffbe81259414833e3ea5370810 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 20 Jun 2023 11:46:01 +1000 Subject: [PATCH 025/108] gbm-kms: Throw on KMS FB creation failure, rather than returning garbage --- src/platforms/gbm-kms/server/kms/display.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index 698904e79fd..e250fb1b522 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -735,8 +735,13 @@ class GBMSurfaceImpl : public mgg::GBMDisplayProvider::GBMSurface { BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to acquire GBM front buffer"})); } - - return GBMBoFramebuffer::framebuffer_for_frontbuffer(drm_fd, std::move(bo)); + + auto fb = GBMBoFramebuffer::framebuffer_for_frontbuffer(drm_fd, std::move(bo)); + if (!fb) + { + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to make DRM FB"})); + } + return fb; } private: mir::Fd const drm_fd; From 131b3c8768d3e5046211e3d4f85d8a58601a45e3 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 20 Jun 2023 14:03:15 +1000 Subject: [PATCH 026/108] unit-tests/gbm-kms/CMake: Stop double-linking server_platform_common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'm not sure why CMake (in CI, but *not* on my machine) doesn't see that we've already linked in `server_platform_common` through transitive dependencies, but it's causing a build failure (again, in CI, not my machine). 🤷‍♀️ --- tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt b/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt index 0a1a362c09c..90ddd36fb74 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt +++ b/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt @@ -40,8 +40,6 @@ target_link_libraries( mir-test-doubles-platform-static mirsharedgbmservercommon-static - server_platform_common - PkgConfig::DRM PkgConfig::GBM ) From bb1abcc2adfa0eb173f040c0d7cab39d13f3b281 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 20 Jun 2023 15:51:12 +1000 Subject: [PATCH 027/108] gbm-kms: Fix build where sizof(u64) != sizeof(size_t) --- src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp index d3c32459d58..fa6e0d1e150 100644 --- a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp +++ b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp @@ -206,7 +206,10 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable pitch_{params.pitch}, format_{format}, gem_handle{params.handle}, - size_{params.size} + size_{static_cast(params.size)} /* params.size is a u64, but the kernel cannot possibly return a value outside the range of size_t + * On 64bit systems this is trivial, as sizeof(size_t) == sizeof(u64) + * On 32bit systems sizeof(size_t) < sizeof(u64), so this cast is necessary. + */ { } From c1610cdb5202e32b780243b62c8e045d7abcba25 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 20 Jun 2023 16:22:23 +1000 Subject: [PATCH 028/108] gbm-kms: Drop unused udev::Device members --- src/platforms/gbm-kms/server/buffer_allocator.cpp | 4 +--- src/platforms/gbm-kms/server/buffer_allocator.h | 8 +++----- src/platforms/gbm-kms/server/kms/platform.cpp | 8 +++----- src/platforms/gbm-kms/server/kms/platform.h | 2 -- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index 112c2f75980..e81992aab8b 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -613,12 +613,10 @@ auto mgg::GLRenderingProvider::make_framebuffer_provider(std::shared_ptr associated_display, EGLDisplay dpy, EGLContext ctx) - : device{device}, - bound_display{std::move(associated_display)}, + : bound_display{std::move(associated_display)}, dpy{dpy}, ctx{ctx} { diff --git a/src/platforms/gbm-kms/server/buffer_allocator.h b/src/platforms/gbm-kms/server/buffer_allocator.h index 96e9e83ada7..a88d0089912 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.h +++ b/src/platforms/gbm-kms/server/buffer_allocator.h @@ -84,10 +84,9 @@ class GLRenderingProvider : public graphics::GLRenderingProvider { public: GLRenderingProvider( - udev::Device const& device, - std::shared_ptr associated_display, - EGLDisplay dpy, - EGLContext ctx); + std::shared_ptr associated_display, + EGLDisplay dpy, + EGLContext ctx); auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr override; @@ -102,7 +101,6 @@ class GLRenderingProvider : public graphics::GLRenderingProvider GLConfig const& config) -> std::unique_ptr override; private: - udev::Device const& device; std::shared_ptr const bound_display; ///< Associated Display provider (if any - null is valid) EGLDisplay const dpy; EGLContext const ctx; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 72eae5e3711..331075b9eeb 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -221,15 +221,13 @@ struct gbm_device_from_hw mgg::RenderingPlatform::RenderingPlatform( mir::udev::Device const& device, std::vector> const& displays) - : RenderingPlatform(device.clone(), gbm_device_for_udev_device(device, displays)) + : RenderingPlatform(gbm_device_for_udev_device(device, displays)) { } mgg::RenderingPlatform::RenderingPlatform( - std::unique_ptr udev_device, std::variant, std::shared_ptr> hw) - : udev_device{std::move(udev_device)}, - device{std::visit(gbm_device_from_hw{}, hw)}, + : device{std::visit(gbm_device_from_hw{}, hw)}, bound_display{std::visit(display_provider_or_nothing{}, hw)}, dpy{initialise_egl(dpy_for_gbm_device(device.get()), 1, 4)}, share_ctx{make_share_only_context(dpy)} @@ -248,7 +246,7 @@ auto mgg::RenderingPlatform::maybe_create_interface( { if (dynamic_cast(&type_tag)) { - return std::make_shared(*udev_device, bound_display, dpy, share_ctx); + return std::make_shared(bound_display, dpy, share_ctx); } return nullptr; } diff --git a/src/platforms/gbm-kms/server/kms/platform.h b/src/platforms/gbm-kms/server/kms/platform.h index f6f69df4311..70c845d6a5d 100644 --- a/src/platforms/gbm-kms/server/kms/platform.h +++ b/src/platforms/gbm-kms/server/kms/platform.h @@ -92,10 +92,8 @@ class RenderingPlatform : public graphics::RenderingPlatform private: RenderingPlatform( - std::unique_ptr udev_device, std::variant, std::shared_ptr> hw); - std::unique_ptr const udev_device; std::shared_ptr const device; ///< gbm_device this platform is created on, always valid. std::shared_ptr const bound_display; ///< Associated Display, if any (nullptr is valid) EGLDisplay const dpy; From c8170e940bd07d41ac511094b7d44d7df940c09c Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 4 Jul 2023 18:00:13 +1000 Subject: [PATCH 029/108] Clean up dropped BasicBufferRenderTarget detritus --- .../renderer/gl/basic_buffer_render_target.h | 77 -------- src/renderers/gl/CMakeLists.txt | 1 - .../gl/basic_buffer_render_target.cpp | 173 ------------------ tests/unit-tests/renderers/gl/CMakeLists.txt | 1 - .../gl/test_basic_buffer_render_target.cpp | 150 --------------- 5 files changed, 402 deletions(-) delete mode 100644 include/renderers/gl/mir/renderer/gl/basic_buffer_render_target.h delete mode 100644 src/renderers/gl/basic_buffer_render_target.cpp delete mode 100644 tests/unit-tests/renderers/gl/test_basic_buffer_render_target.cpp diff --git a/include/renderers/gl/mir/renderer/gl/basic_buffer_render_target.h b/include/renderers/gl/mir/renderer/gl/basic_buffer_render_target.h deleted file mode 100644 index a3f36653252..00000000000 --- a/include/renderers/gl/mir/renderer/gl/basic_buffer_render_target.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef MIR_RENDERER_GL_BASIC_BUFFER_RENDER_TARGET_H_ -#define MIR_RENDERER_GL_BASIC_BUFFER_RENDER_TARGET_H_ - -#include "buffer_render_target.h" - -#include -#include - -namespace mir -{ -namespace renderer -{ -namespace gl -{ -class Context; - -/// Not threadsafe, do not use concurrently -class BasicBufferOutputSurface: public BufferOutputSurface -{ -public: - BasicBufferOutputSurface(std::shared_ptr const& ctx); - - void set_buffer(std::shared_ptr const& buffer) override; - - auto size() const -> geometry::Size override; - void make_current() override; - auto commit() -> std::unique_ptr override; - void bind() override; - - auto layout() const -> Layout override; - -private: - class Framebuffer - { - public: - Framebuffer(geometry::Size const& size); - ~Framebuffer(); - void copy_to(software::WriteMappableBuffer& buffer); - void bind(); - - geometry::Size const size; - - private: - Framebuffer(Framebuffer const&) = delete; - Framebuffer& operator=(Framebuffer const&) = delete; - - GLuint colour_buffer; - GLuint fbo; - }; - - std::shared_ptr const ctx; - - std::shared_ptr buffer{nullptr}; - std::optional framebuffer; -}; - -} -} -} - -#endif // MIR_RENDERER_GL_BASIC_BUFFER_RENDER_TARGET_H_ diff --git a/src/renderers/gl/CMakeLists.txt b/src/renderers/gl/CMakeLists.txt index 25f829275db..5b50becf9df 100644 --- a/src/renderers/gl/CMakeLists.txt +++ b/src/renderers/gl/CMakeLists.txt @@ -19,7 +19,6 @@ ADD_LIBRARY( renderer.cpp renderer_factory.cpp -# basic_buffer_render_target.cpp ) target_include_directories( diff --git a/src/renderers/gl/basic_buffer_render_target.cpp b/src/renderers/gl/basic_buffer_render_target.cpp deleted file mode 100644 index 2891474b72a..00000000000 --- a/src/renderers/gl/basic_buffer_render_target.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "mir/renderer/gl/basic_buffer_render_target.h" -#include "mir/renderer/gl/context.h" -#include "mir/renderer/sw/pixel_source.h" -#include "mir/graphics/egl_error.h" - -#include -#include - -namespace mg = mir::graphics; -namespace mrg = mir::renderer::gl; -namespace mrs = mir::renderer::software; - -mrg::BasicBufferOutputSurface::Framebuffer::Framebuffer(geometry::Size const& size) - : size{size} -{ - glGenRenderbuffers(1, &colour_buffer); - glGenFramebuffers(1, &fbo); - - 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)})); - } - - // gl::Renderer can only set glViewport if there is a current EGL surface to get the size from. Since we don't bind - // an EGL surface when rendering to a buffer, we have to set the viewport ourselves. - glViewport(0, 0, size.width.as_int(), size.height.as_int()); -} - -mrg::BasicBufferOutputSurface::Framebuffer::~Framebuffer() -{ - glDeleteFramebuffers(1, &fbo); - glDeleteRenderbuffers(1, &colour_buffer); -} - -void mrg::BasicBufferOutputSurface::Framebuffer::copy_to(software::WriteMappableBuffer& buffer) -{ - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - auto mapping = buffer.map_writeable(); - if (mapping->size() != size) - { - BOOST_THROW_EXCEPTION(std::logic_error("given size does not match buffer size")); - } - if (mapping->stride() != geometry::Stride{size.width.as_int() * 4}) - { - BOOST_THROW_EXCEPTION(std::logic_error("invalid buffer stride " + std::to_string(mapping->stride().as_int()))); - } - if (mapping->format() != mir_pixel_format_argb_8888) - { - BOOST_THROW_EXCEPTION(std::logic_error("invalid pixel format " + std::to_string(mapping->format()))); - } - glReadPixels( - 0, 0, - size.width.as_int(), size.height.as_int(), - GL_BGRA_EXT, GL_UNSIGNED_BYTE, mapping->data()); -} - -void mrg::BasicBufferOutputSurface::Framebuffer::bind() -{ - glBindFramebuffer(GL_FRAMEBUFFER, fbo); -} - -mrg::BasicBufferOutputSurface::BasicBufferOutputSurface(std::shared_ptr const& ctx) - : ctx{ctx} -{ -} - -void mrg::BasicBufferOutputSurface::set_buffer( - std::shared_ptr const& buffer) -{ - this->buffer = buffer; - if (framebuffer && framebuffer->size == buffer->size()) - { - return; - } - framebuffer.reset(); - framebuffer.emplace(buffer->size()); -} - -auto mrg::BasicBufferOutputSurface::size() const -> geometry::Size -{ - if (framebuffer) - { - return framebuffer.value().size; - } - else - { - return {}; - } -} - -void mrg::BasicBufferOutputSurface::make_current() -{ - ctx->make_current(); -} - -auto mrg::BasicBufferOutputSurface::commit() -> std::unique_ptr -{ - if (!framebuffer || !buffer) - { - BOOST_THROW_EXCEPTION(std::logic_error("swap_buffers() called when buffer unset")); - } - framebuffer->copy_to(*buffer); - /* Because of the way the screencopy extension is set up we don't really need to return anything - * here. - * - * If the client provided us with a pool or queue of buffers to allocate from, or had asynchronous - * copying then there might be something worth returning here. - */ - return {}; -} - -void mrg::BasicBufferOutputSurface::bind() -{ - if (!framebuffer) - { - BOOST_THROW_EXCEPTION(std::logic_error("bind() called without framebuffer")); - } - framebuffer->bind(); -} - -mir::graphics::gl::OutputSurface::Layout mir::renderer::gl::BasicBufferOutputSurface::layout() const -{ - return Layout::GL; -} diff --git a/tests/unit-tests/renderers/gl/CMakeLists.txt b/tests/unit-tests/renderers/gl/CMakeLists.txt index 1adaf2679d9..ed3b9c14ecf 100644 --- a/tests/unit-tests/renderers/gl/CMakeLists.txt +++ b/tests/unit-tests/renderers/gl/CMakeLists.txt @@ -1,6 +1,5 @@ list(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_gl_renderer.cpp -# ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_buffer_render_target.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) diff --git a/tests/unit-tests/renderers/gl/test_basic_buffer_render_target.cpp b/tests/unit-tests/renderers/gl/test_basic_buffer_render_target.cpp deleted file mode 100644 index ca517b58d3d..00000000000 --- a/tests/unit-tests/renderers/gl/test_basic_buffer_render_target.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "mir/renderer/gl/basic_buffer_render_target.h" - -#include "mir/test/doubles/null_gl_context.h" -#include "mir/test/doubles/mock_gl.h" -#include "mir/test/doubles/stub_buffer.h" -#include "mir/test/fake_shared.h" - -#include -#include - -namespace mr = mir::renderer; -namespace mrg = mir::renderer::gl; -namespace mg = mir::graphics; -namespace geom = mir::geometry; -namespace mt = mir::test; -namespace mtd = mir::test::doubles; - -using namespace testing; - -namespace -{ - -struct BasicBufferRenderTarget : Test -{ - NiceMock mock_gl; - mtd::NullGLContext ctx; - int const reasonable_width = 24, reasonable_height = 32; - geom::Size reasonable_size{reasonable_width, reasonable_height}; - MirPixelFormat const reasonable_pixel_format{mir_pixel_format_argb_8888}; - mtd::StubBuffer reasonable_buffer{{ - reasonable_size, - reasonable_pixel_format, - mg::BufferUsage::software}}; -}; - -} - -TEST_F(BasicBufferRenderTarget, set_buffer_always_allocs_correct_storage) -{ - mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; - EXPECT_CALL(mock_gl, glRenderbufferStorage(_, _, reasonable_width, reasonable_height)); - render_target.set_buffer(mt::fake_shared(reasonable_buffer)); - render_target.swap_buffers(); - Mock::VerifyAndClearExpectations(&mock_gl); - int const other_width = 124, other_height = 88; - mtd::StubBuffer other_buffer{{ - {other_width, other_height}, - reasonable_pixel_format, - mg::BufferUsage::software}}; - EXPECT_CALL(mock_gl, glRenderbufferStorage(_, _, other_width, other_height)); - render_target.set_buffer(mt::fake_shared(other_buffer)); - render_target.swap_buffers(); -} - -TEST_F(BasicBufferRenderTarget, sets_gl_viewport_on_buffer_size_change) -{ - mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; - EXPECT_CALL(mock_gl, glViewport(0, 0, reasonable_width, reasonable_height)); - render_target.set_buffer(mt::fake_shared(reasonable_buffer)); - Mock::VerifyAndClearExpectations(&mock_gl); - int const other_width = 124, other_height = 88; - mtd::StubBuffer other_buffer{{ - {other_width, other_height}, - reasonable_pixel_format, - mg::BufferUsage::software}}; - EXPECT_CALL(mock_gl, glViewport(0, 0, other_width, other_height)); - render_target.set_buffer(mt::fake_shared(other_buffer)); -} - -TEST_F(BasicBufferRenderTarget, cleans_up_framebuffers_and_renderbuffers) -{ - std::set framebuffers; - GLuint framebuffer_count{0}; - ON_CALL(mock_gl, glGenFramebuffers(1, _)).WillByDefault(Invoke([&](GLsizei, GLuint* result) - { - *result = framebuffer_count++; - framebuffers.insert(*result); - })); - ON_CALL(mock_gl, glDeleteFramebuffers(1, _)).WillByDefault(Invoke([&](GLsizei, GLuint const* id) - { - framebuffers.erase(*id); - })); - std::set renderbuffers; - GLuint renderbuffer_count{0}; - ON_CALL(mock_gl, glGenRenderbuffers(1, _)).WillByDefault(Invoke([&](GLsizei, GLuint* result) - { - *result = renderbuffer_count++; - renderbuffers.insert(*result); - })); - ON_CALL(mock_gl, glDeleteRenderbuffers(1, _)).WillByDefault(Invoke([&](GLsizei, GLuint const* id) - { - renderbuffers.erase(*id); - })); - { - mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; - render_target.make_current(); - render_target.set_buffer(mt::fake_shared(reasonable_buffer)); - render_target.bind(); - render_target.swap_buffers(); - int const other_width = 124, other_height = 88; - mtd::StubBuffer other_buffer{{ - {other_width, other_height}, - reasonable_pixel_format, - mg::BufferUsage::software}}; - render_target.set_buffer(mt::fake_shared(other_buffer)); - render_target.bind(); - render_target.swap_buffers(); - } - EXPECT_THAT(framebuffer_count, Gt(0)); - EXPECT_THAT(framebuffers.size(), Eq(0)); - EXPECT_THAT(renderbuffer_count, Gt(0)); - EXPECT_THAT(renderbuffers.size(), Eq(0)); -} - -TEST_F(BasicBufferRenderTarget, reads_pixels) -{ - mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; - render_target.set_buffer(mt::fake_shared(reasonable_buffer), reasonable_size); - EXPECT_CALL(mock_gl, glReadPixels(0, 0, reasonable_width, reasonable_height, _, _, _)); - render_target.swap_buffers(); -} - -TEST_F(BasicBufferRenderTarget, throws_on_invalid_buffer) -{ - mrg::BasicBufferOutputSurface render_target{mt::fake_shared(ctx)}; - EXPECT_THROW({ - mtd::StubBuffer buffer({ - reasonable_size, - mir_pixel_format_abgr_8888, // wrong format - mg::BufferUsage::software}); - render_target.set_buffer(mt::fake_shared(buffer)); - render_target.swap_buffers(); - }, std::logic_error); -} From 949b9a9e4c9e0f44dddb2010c7ba74b270496452 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 13 Jul 2023 14:36:38 +1000 Subject: [PATCH 030/108] mtd::MockGLRenderingPlatform: Mock the other half of GLRenderingProvider --- .../gl/mir/renderer/gl/buffer_render_target.h | 47 ------------------- .../test/doubles/mock_gl_rendering_provider.h | 18 +++++-- 2 files changed, 14 insertions(+), 51 deletions(-) delete mode 100644 include/renderers/gl/mir/renderer/gl/buffer_render_target.h diff --git a/include/renderers/gl/mir/renderer/gl/buffer_render_target.h b/include/renderers/gl/mir/renderer/gl/buffer_render_target.h deleted file mode 100644 index eee19897ba9..00000000000 --- a/include/renderers/gl/mir/renderer/gl/buffer_render_target.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef MIR_RENDERER_GL_BUFFER_RENDER_TARGET_H_ -#define MIR_RENDERER_GL_BUFFER_RENDER_TARGET_H_ - -#include "mir/geometry/size.h" -#include "mir/renderer/gl/gl_surface.h" - -#include - -namespace mir -{ -namespace renderer -{ -namespace software -{ -class WriteMappableBuffer; -} -namespace gl -{ - -/// Not threadsafe, do not use concurrently -class BufferOutputSurface: public graphics::gl::OutputSurface -{ -public: - virtual void set_buffer(std::shared_ptr const& buffer) = 0; -}; - -} -} -} - -#endif // MIR_RENDERER_GL_BUFFER_RENDER_TARGET_H_ diff --git a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h index 12fc08afe09..3afe91b91f8 100644 --- a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h @@ -31,10 +31,20 @@ class MockGlRenderingPlatform : public graphics::GLRenderingProvider public: MOCK_METHOD(std::shared_ptr, as_texture, (std::shared_ptr), (override)); MOCK_METHOD( - std::unique_ptr, - surface_for_output, - (std::shared_ptr, geometry::Size, graphics::GLConfig const&), - (override)); + std::unique_ptr, + surface_for_output, + (std::shared_ptr, geometry::Size, graphics::GLConfig const&), + (override)); + MOCK_METHOD( + graphics::probe::Result, + suitability_for_display, + (std::shared_ptr const&), + (override)); + MOCK_METHOD( + std::unique_ptr, + make_framebuffer_provider, + (std::shared_ptr), + (override)); }; } From eb546674d963db3a347ef30a283542729363efd1 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 13 Jul 2023 14:41:39 +1000 Subject: [PATCH 031/108] Re-enable BasicScreenShooter tests --- tests/unit-tests/compositor/CMakeLists.txt | 2 +- .../compositor/test_basic_screen_shooter.cpp | 129 ++++++++++-------- 2 files changed, 76 insertions(+), 55 deletions(-) diff --git a/tests/unit-tests/compositor/CMakeLists.txt b/tests/unit-tests/compositor/CMakeLists.txt index 7b25c2e34ed..d7fb0fad55b 100644 --- a/tests/unit-tests/compositor/CMakeLists.txt +++ b/tests/unit-tests/compositor/CMakeLists.txt @@ -6,7 +6,7 @@ list(APPEND UNIT_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test_multi_monitor_arbiter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_dropping_schedule.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_queueing_schedule.cpp -# ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_screen_shooter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_screen_shooter.cpp ) set(UNIT_TEST_SOURCES ${UNIT_TEST_SOURCES} PARENT_SCOPE) diff --git a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp index 5bcf9ca144a..8207f56dcee 100644 --- a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp +++ b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp @@ -14,12 +14,16 @@ * along with this program. If not, see . */ +#include "mir/graphics/platform.h" +#include "mir/renderer/gl/gl_surface.h" +#include "mir/test/doubles/stub_gl_rendering_provider.h" #include "src/server/compositor/basic_screen_shooter.h" -#include "mir/renderer/gl/buffer_render_target.h" -#include "mir/test/fake_shared.h" +#include "mir/renderer/renderer_factory.h" #include "mir/test/doubles/mock_scene.h" #include "mir/test/doubles/mock_renderer.h" +#include "mir/test/doubles/mock_gl_rendering_provider.h" +#include "mir/test/doubles/stub_gl_rendering_provider.h" #include "mir/test/doubles/advanceable_clock.h" #include "mir/test/doubles/explicit_executor.h" #include "mir/test/doubles/stub_buffer.h" @@ -30,11 +34,8 @@ namespace mc = mir::compositor; namespace mr = mir::renderer; -namespace mrg = mir::renderer::gl; -namespace mrs = mir::renderer::software; namespace mg = mir::graphics; namespace geom = mir::geometry; -namespace mt = mir::test; namespace mtd = mir::test::doubles; using namespace testing; @@ -42,26 +43,53 @@ using namespace std::chrono_literals; namespace { - -class MockBufferRenderTarget: public mrg::BufferOutputSurface +class MockRendererFactory : public mr::RendererFactory { public: - MOCK_METHOD(void, set_buffer, (std::shared_ptr const& buffer), (override)); - MOCK_METHOD(geom::Size, size, (), (const, override)); - MOCK_METHOD(void, make_current, (), (override)); - MOCK_METHOD(void, release_current, (), (override)); - MOCK_METHOD(void, swap_buffers, (), (override)); - MOCK_METHOD(void, bind, (), (override)); + MOCK_METHOD( + std::unique_ptr, + create_renderer_for, + (std::unique_ptr, std::shared_ptr), + (const override)); }; struct BasicScreenShooter : Test { BasicScreenShooter() { - ON_CALL(scene, scene_elements_for(_)).WillByDefault(Return(scene_elements)); + ON_CALL(*scene, scene_elements_for(_)).WillByDefault(Return(scene_elements)); + ON_CALL(*renderer_factory, create_renderer_for(_,_)).WillByDefault( + InvokeWithoutArgs( + [this]() + { + return std::move(next_renderer); + })); + ON_CALL(*gl_provider, as_texture(_)) + .WillByDefault( + [this](auto buffer) + { + return default_gl_behaviour_provider.as_texture(std::move(buffer)); + }); + ON_CALL(*gl_provider, surface_for_output(_, _, _)) + .WillByDefault( + [this](std::shared_ptr provider, auto size, mg::GLConfig const& config) + { + return default_gl_behaviour_provider.surface_for_output(std::move(provider), size, config); + }); + ON_CALL(*gl_provider, suitability_for_display(_)) + .WillByDefault(Return(mg::probe::supported)); + + shooter = std::make_unique( + scene, + clock, + executor, + gl_providers, + renderer_factory); } - NiceMock scene; + std::unique_ptr next_renderer{std::make_unique>()}; + + std::shared_ptr scene{std::make_shared>()}; mg::RenderableList renderables{[]() { mg::RenderableList renderables; @@ -80,17 +108,14 @@ struct BasicScreenShooter : Test } return elements; }()}; - mtd::MockRenderer& renderer{*new NiceMock}; - MockBufferRenderTarget& render_target{*new NiceMock}; - mtd::AdvanceableClock clock; + mtd::StubGlRenderingPlatform default_gl_behaviour_provider; + std::shared_ptr gl_provider{std::make_shared>()}; + std::vector> gl_providers{gl_provider}; + std::shared_ptr renderer_factory{std::make_shared>()}; + std::shared_ptr clock{std::make_shared()}; mtd::ExplicitExecutor executor; - mc::BasicScreenShooter shooter{ - mt::fake_shared(scene), - mt::fake_shared(clock), - executor, - std::unique_ptr{&render_target}, - std::unique_ptr{&renderer}}; - mtd::StubBuffer buffer; + std::unique_ptr shooter; + std::shared_ptr buffer{std::make_shared(geom::Size{800, 600})}; geom::Rectangle const viewport_rect{{20, 30}, {40, 50}}; StrictMock)>> callback; std::optional const nullopt_time{}; @@ -100,65 +125,60 @@ struct BasicScreenShooter : Test TEST_F(BasicScreenShooter, calls_callback_from_executor) { - shooter.capture(mt::fake_shared(buffer), viewport_rect, [&](auto time) + shooter->capture(buffer, viewport_rect, [&](auto time) { callback.Call(time); }); - clock.advance_by(1s); - EXPECT_CALL(callback, Call(std::make_optional(clock.now()))); + clock->advance_by(1s); + EXPECT_CALL(callback, Call(std::make_optional(clock->now()))); executor.execute(); } TEST_F(BasicScreenShooter, renders_scene_elements) { - shooter.capture(mt::fake_shared(buffer), viewport_rect, [&](auto time) + shooter->capture(buffer, viewport_rect, [&](auto time) { callback.Call(time); }); InSequence seq; - EXPECT_CALL(scene, scene_elements_for(_)).WillOnce(Return(scene_elements)); + EXPECT_CALL(*scene, scene_elements_for(_)).WillOnce(Return(scene_elements)); EXPECT_THAT(renderables.size(), Gt(0)); - EXPECT_CALL(renderer, render(Eq(renderables))); - EXPECT_CALL(callback, Call(std::make_optional(clock.now()))); + EXPECT_CALL(*next_renderer, render(Eq(renderables))); + EXPECT_CALL(callback, Call(std::make_optional(clock->now()))); executor.execute(); } TEST_F(BasicScreenShooter, sets_viewport_correctly_before_render) { - shooter.capture(mt::fake_shared(buffer), viewport_rect, [&](auto time) + shooter->capture(buffer, viewport_rect, [&](auto time) { callback.Call(time); }); Sequence a, b; - EXPECT_CALL(renderer, set_viewport(Eq(viewport_rect))).InSequence(b); - EXPECT_CALL(renderer, render(_)).InSequence(a, b); - EXPECT_CALL(callback, Call(std::make_optional(clock.now()))).InSequence(a, b); + EXPECT_CALL(*next_renderer, set_viewport(Eq(viewport_rect))).InSequence(b); + EXPECT_CALL(*next_renderer, render(_)).InSequence(a, b); + EXPECT_CALL(callback, Call(std::make_optional(clock->now()))).InSequence(a, b); executor.execute(); } -TEST_F(BasicScreenShooter, sets_buffer_before_render) +TEST_F(BasicScreenShooter, graceful_failure_on_zero_sized_buffer) { - shooter.capture(mt::fake_shared(buffer), viewport_rect, [&](auto time) + auto broken_buffer = std::make_shared(geom::Size{0, 0}); + shooter->capture(broken_buffer, viewport_rect, [&](auto time) { callback.Call(time); }); - InSequence seq; - EXPECT_CALL(render_target, make_current()); - EXPECT_CALL(render_target, set_buffer(Eq(mt::fake_shared(buffer)))); - EXPECT_CALL(render_target, bind()); - EXPECT_CALL(renderer, render(_)); - EXPECT_CALL(render_target, release_current()); - EXPECT_CALL(callback, Call(std::make_optional(clock.now()))); + EXPECT_CALL(callback, Call(nullopt_time)); executor.execute(); } TEST_F(BasicScreenShooter, throw_in_scene_elements_for_causes_graceful_failure) { - ON_CALL(scene, scene_elements_for(_)).WillByDefault(Invoke([](auto) -> mc::SceneElementSequence + ON_CALL(*scene, scene_elements_for(_)).WillByDefault(Invoke([](auto) -> mc::SceneElementSequence { throw std::runtime_error{"throw in scene_elements_for()!"}; })); - shooter.capture(mt::fake_shared(buffer), viewport_rect, [&](auto time) + shooter->capture(buffer, viewport_rect, [&](auto time) { callback.Call(time); }); @@ -166,13 +186,14 @@ TEST_F(BasicScreenShooter, throw_in_scene_elements_for_causes_graceful_failure) executor.execute(); } -TEST_F(BasicScreenShooter, throw_in_make_current_causes_graceful_failure) +TEST_F(BasicScreenShooter, throw_in_surface_for_output_handled_gracefully) { - ON_CALL(render_target, make_current()).WillByDefault(Invoke([]() + ON_CALL(*gl_provider, surface_for_output).WillByDefault( + [](auto, auto, auto&) -> std::unique_ptr { - throw std::runtime_error{"throw in make_current()!"}; - })); - shooter.capture(mt::fake_shared(buffer), viewport_rect, [&](auto time) + BOOST_THROW_EXCEPTION((std::runtime_error{"Throw in surface_for_output"})); + }); + shooter->capture(buffer, viewport_rect, [&](auto time) { callback.Call(time); }); @@ -182,11 +203,11 @@ TEST_F(BasicScreenShooter, throw_in_make_current_causes_graceful_failure) TEST_F(BasicScreenShooter, throw_in_render_causes_graceful_failure) { - ON_CALL(renderer, render(_)).WillByDefault(Invoke([](auto) + ON_CALL(*next_renderer, render(_)).WillByDefault(Invoke([](auto) -> std::unique_ptr { throw std::runtime_error{"throw in render()!"}; })); - shooter.capture(mt::fake_shared(buffer), viewport_rect, [&](auto time) + shooter->capture(buffer, viewport_rect, [&](auto time) { callback.Call(time); }); From 1141bb9dd4bf1134a1495c2178a6b1d15382505c Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 13 Jul 2023 14:44:36 +1000 Subject: [PATCH 032/108] BasicScreenShooter: Cleanup --- src/server/compositor/basic_screen_shooter.cpp | 3 +-- src/server/compositor/basic_screen_shooter.h | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/server/compositor/basic_screen_shooter.cpp b/src/server/compositor/basic_screen_shooter.cpp index ac24bf63fcd..4144c830c84 100644 --- a/src/server/compositor/basic_screen_shooter.cpp +++ b/src/server/compositor/basic_screen_shooter.cpp @@ -17,9 +17,8 @@ #include "basic_screen_shooter.h" #include "mir/graphics/drm_formats.h" #include "mir/graphics/gl_config.h" -#include "mir/renderer/gl/buffer_render_target.h" #include "mir/renderer/renderer.h" -#include "mir/renderer/gl/context.h" +#include "mir/renderer/gl/gl_surface.h" #include "mir/compositor/scene_element.h" #include "mir/compositor/scene.h" #include "mir/log.h" diff --git a/src/server/compositor/basic_screen_shooter.h b/src/server/compositor/basic_screen_shooter.h index d5bc343e4ae..3caeaa80779 100644 --- a/src/server/compositor/basic_screen_shooter.h +++ b/src/server/compositor/basic_screen_shooter.h @@ -32,10 +32,6 @@ namespace renderer { class Renderer; class RendererFactory; -namespace gl -{ -class BufferOutputSurface; -} } namespace compositor { From 1e62f8d04a878ff2ff218d1d7250156ed38312c1 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 13 Jul 2023 14:54:17 +1000 Subject: [PATCH 033/108] BasicScreenShooter: Sanity-check destination buffer size --- src/server/compositor/basic_screen_shooter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/server/compositor/basic_screen_shooter.cpp b/src/server/compositor/basic_screen_shooter.cpp index 4144c830c84..6d4cf3cdd49 100644 --- a/src/server/compositor/basic_screen_shooter.cpp +++ b/src/server/compositor/basic_screen_shooter.cpp @@ -167,6 +167,10 @@ auto mc::BasicScreenShooter::Self::renderer_for_buffer(std::shared_ptr mr::Renderer& { auto const buffer_size = buffer->size(); + if (buffer_size.height == geom::Height{0} || buffer_size.width == geom::Width{0}) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Attempt to capture to a zero-sized buffer"})); + } output->set_next_buffer(std::move(buffer)); if (buffer_size != last_rendered_size) { From 73969e99b56fe80a32d13ee7f1b68a8bad518770 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 13 Jul 2023 18:03:44 +1000 Subject: [PATCH 034/108] TestBasicScreenShooter: Make mock Renderer interaction more authentic. --- .../compositor/test_basic_screen_shooter.cpp | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp index 8207f56dcee..c5d818ae45c 100644 --- a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp +++ b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp @@ -59,11 +59,17 @@ struct BasicScreenShooter : Test { ON_CALL(*scene, scene_elements_for(_)).WillByDefault(Return(scene_elements)); ON_CALL(*renderer_factory, create_renderer_for(_,_)).WillByDefault( - InvokeWithoutArgs( - [this]() + [this](auto output_surface, auto) { + ON_CALL(*next_renderer, render(_)) + .WillByDefault( + [surface = std::shared_ptr(std::move(output_surface))]() + { + return surface->commit(); + }); + return std::move(next_renderer); - })); + }); ON_CALL(*gl_provider, as_texture(_)) .WillByDefault( [this](auto buffer) @@ -72,9 +78,22 @@ struct BasicScreenShooter : Test }); ON_CALL(*gl_provider, surface_for_output(_, _, _)) .WillByDefault( - [this](std::shared_ptr provider, auto size, mg::GLConfig const& config) + [](std::shared_ptr provider, auto size, auto const&) + -> std::unique_ptr { - return default_gl_behaviour_provider.surface_for_output(std::move(provider), size, config); + if (auto cpu_provider = provider->acquire_interface()) + { + auto surface = std::make_unique>(); + auto format = cpu_provider->supported_formats().front(); + ON_CALL(*surface, commit()) + .WillByDefault( + [cpu_provider, size, format]() + { + return cpu_provider->alloc_fb(size, format); + }); + return surface; + } + BOOST_THROW_EXCEPTION((std::runtime_error{"CPU output support not available?!"})); }); ON_CALL(*gl_provider, suitability_for_display(_)) .WillByDefault(Return(mg::probe::supported)); From a4a29b83e343682e92ce989f9c4f0487171bee91 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 13 Jul 2023 18:05:07 +1000 Subject: [PATCH 035/108] TestBasicScreenShooter: Fix throw_in_render_causes_graceful_failure Now that we set a default expectation on `Renderer::render()` we should `EXPECT_CALL` in order to override the default expectation. --- tests/unit-tests/compositor/test_basic_screen_shooter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp index c5d818ae45c..ec9a7125538 100644 --- a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp +++ b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp @@ -222,10 +222,10 @@ TEST_F(BasicScreenShooter, throw_in_surface_for_output_handled_gracefully) TEST_F(BasicScreenShooter, throw_in_render_causes_graceful_failure) { - ON_CALL(*next_renderer, render(_)).WillByDefault(Invoke([](auto) -> std::unique_ptr + EXPECT_CALL(*next_renderer, render(_)).WillOnce([](auto) -> std::unique_ptr { throw std::runtime_error{"throw in render()!"}; - })); + }); shooter->capture(buffer, viewport_rect, [&](auto time) { callback.Call(time); From a385de418afbf7891050d6a20371547d465c4df6 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 13 Jul 2023 18:06:52 +1000 Subject: [PATCH 036/108] TestBasicScreenShooter: Test threading requirements. Because the `Renderer` hides thread-local state (in the form of an EGL context) it's important that the `BasicScreenShooter` do the dance required to ensure the `Renderer` tries to be current on at most one thread at a time. --- .../compositor/test_basic_screen_shooter.cpp | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp index ec9a7125538..2db361dce1d 100644 --- a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp +++ b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ +#include "mir/executor.h" #include "mir/graphics/platform.h" #include "mir/renderer/gl/gl_surface.h" #include "mir/test/doubles/stub_gl_rendering_provider.h" @@ -233,3 +234,56 @@ TEST_F(BasicScreenShooter, throw_in_render_causes_graceful_failure) EXPECT_CALL(callback, Call(nullopt_time)); executor.execute(); } + +TEST_F(BasicScreenShooter, ensures_renderer_is_current_on_only_one_thread) +{ + thread_local bool is_current_here = false; + std::atomic is_current = false; + + auto shooter = std::make_unique( + scene, + clock, + mir::thread_pool_executor, + gl_providers, + renderer_factory); + + ON_CALL(*next_renderer, render(_)) + .WillByDefault( + [&](auto) -> std::unique_ptr + { + /* It's OK if we're being made current again on the same thread + * without having been released previously. + * We just need to ensure that, if we are current *anywhere* then + * it's *this* thread that we're current on. + */ + EXPECT_THAT(is_current_here, Eq(is_current.load())); + is_current = true; + is_current_here = true; + return {}; + }); + ON_CALL(*next_renderer, suspend()) + .WillByDefault( + [&]() + { + EXPECT_THAT(is_current_here, Eq(is_current.load())); + is_current = false; + is_current_here = false; + }); + + // This doesn't actually have to be atomic + std::atomic call_count = 0; + auto const spawn_a_capture = + [&]() + { + shooter->capture(buffer, viewport_rect, [&](auto) { call_count++; }); + }; + + auto const expected_call_count = 20; + for (auto i = 0; i < expected_call_count; ++i) + { + mir::thread_pool_executor.spawn(spawn_a_capture); + } + + mir::ThreadPoolExecutor::quiesce(); + EXPECT_THAT(call_count, Eq(expected_call_count)); +} From 87aeeb37c60f00bd814906ccea82c6bf126287f4 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 13 Jul 2023 18:07:46 +1000 Subject: [PATCH 037/108] BasicScreenShooter: Actually cache current_renderer We need to actually *set* `last_rendered_size` somewhere if we want to use it to determine whether we need to construct a new renderer! --- src/server/compositor/basic_screen_shooter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/compositor/basic_screen_shooter.cpp b/src/server/compositor/basic_screen_shooter.cpp index 6d4cf3cdd49..cc3c1076dbc 100644 --- a/src/server/compositor/basic_screen_shooter.cpp +++ b/src/server/compositor/basic_screen_shooter.cpp @@ -190,6 +190,7 @@ auto mc::BasicScreenShooter::Self::renderer_for_buffer(std::shared_ptr(output); auto gl_surface = render_provider->surface_for_output(interface_provider, buffer_size, NoAuxConfig{}); current_renderer = renderer_factory->create_renderer_for(std::move(gl_surface), render_provider); + last_rendered_size = buffer_size; } return *current_renderer; } From d593ce430144a3facb6a99a29799c0d369335316 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 14 Jul 2023 11:43:53 +1000 Subject: [PATCH 038/108] Remove unused remnants of merge-broken file --- .../gbm-kms/server/kms/kms_framebuffer.cpp | 89 ------------------- 1 file changed, 89 deletions(-) delete mode 100644 src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp diff --git a/src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp b/src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp deleted file mode 100644 index 0cd4e102be7..00000000000 --- a/src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* -<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp - * Copyright © Canonical Ltd. -|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp - * Copyright © 2017 Canonical Ltd. -======== - * Copyright © 2021 Canonical Ltd. ->>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp - * - * 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 . - */ - -<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp -#include "rendering_platform.h" -#include "buffer_allocator.h" -|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp -#include "gbm_platform.h" -#include "buffer_allocator.h" -======== -#include "kms_framebuffer.h" - -#include ->>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp - -<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp -namespace mg = mir::graphics; -namespace mge = mg::egl::generic; -|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp -namespace mg = mir::graphics; -namespace mgg = mir::graphics::gbm; -======== -namespace mgg = mir::graphics::gbm; ->>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp - -<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp -mge::RenderingPlatform::RenderingPlatform() -|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp -mgg::GBMPlatform::GBMPlatform( - std::shared_ptr const& udev, - std::shared_ptr const& drm) : - udev(udev), - drm(drm), - gbm{std::make_shared(drm->fd)} -======== -mgg::FBHandle::FBHandle(int drm_fd, uint32_t fb_id) - : drm_fd{drm_fd}, - fb_id{fb_id} ->>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp -{ -} - -<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp -auto mge::RenderingPlatform::create_buffer_allocator( - mg::Display const& output) -> mir::UniqueModulePtr -|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp -mir::UniqueModulePtr mgg::GBMPlatform::create_buffer_allocator( - Display const& output) -======== -mgg::FBHandle::~FBHandle() ->>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp -{ -<<<<<<<< HEAD:src/platforms/renderer-generic-egl/rendering_platform.cpp - return make_module_ptr(output); -|||||||| da02aa60d3:src/platforms/gbm-kms/server/gbm_platform.cpp - return make_module_ptr(output); -======== - // TODO: Some sort of logging on failure? ->>>>>>>> new-platform-API:src/platforms/gbm-kms/server/kms/kms_framebuffer.cpp -} - -auto mgg::FBHandle::get_drm_fb_id() const -> uint32_t -{ - return fb_id; -} - -mgg::FBHandle::operator uint32_t() const -{ - return fb_id; -} \ No newline at end of file From 9d31e2853d9be52ec9a559cddc1f44e266440a4f Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 25 Jul 2023 12:27:54 +1000 Subject: [PATCH 039/108] gbm-kms: Return unsupported for SW-rasterised rendering platforms. If we've tried to create an EGL context on a DRM rendernode and it's returned us a software context, then we clearly can't actually drive that rendernode. Return unsupported, rather than supported, in this case --- src/platforms/gbm-kms/server/kms/platform_symbols.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp index 20fe033803f..33d395895c9 100644 --- a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp +++ b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp @@ -446,9 +446,8 @@ auto probe_rendering_platform( strlen("llvmpipe")) == 0) { mir::log_info("Detected software renderer: %s", renderer_string); - // Leave the priority at ::supported; if we've got a software renderer then - // we *don't* support *this* device very well. - supported_devices.back().support_level = std::min(maximum_suitability, mg::PlatformPriority::supported); + // Leave the priority at ::unsupported; if we've got a software renderer then + // we're not successfully using *this* rendernode. } else { From aa796259aec4593df872560667efb68cbf63ded7 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 25 Jul 2023 12:57:24 +1000 Subject: [PATCH 040/108] gbm-kms: Make GBM optional for KMS output. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are KMS devices that can *only* provide “dumb buffers” and have no associated rendering hardware, like DisplayLink. For these we don't need a GBM device, so make failure to construct one non-fatal --- src/platforms/gbm-kms/server/kms/platform.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 331075b9eeb..f0cb67e0ad2 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -256,7 +256,7 @@ class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfacePr public: explicit KMSDisplayInterfaceProvider(mir::Fd drm_fd) : drm_fd{std::move(drm_fd)}, - gbm_provider{std::make_shared(this->drm_fd)} + gbm_provider{maybe_make_gbm_provider(this->drm_fd)} { } @@ -275,6 +275,20 @@ class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfacePr return {}; } private: + static auto maybe_make_gbm_provider(mir::Fd drm_fd) -> std::shared_ptr + { + try + { + return std::make_shared(std::move(drm_fd)); + } + catch (std::exception const& err) + { + mir::log_info("Failed to create GBM device for direct buffer submission"); + mir::log_info("Output will use CPU buffer copies"); + return {}; + } + } + mir::Fd const drm_fd; // We rely on the GBM provider being stable over probe, so we construct one at startup // and reuse it. From ccbc38c976a0967241501e4214a3ccdbe7bcdad8 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 28 Jul 2023 17:52:46 +1000 Subject: [PATCH 041/108] CPUCopyOutputSurface: Fix EGL context usage. The context we get passed in is the *share* context; we need to contruct our own context to use here. --- .../common/server/cpu_copy_output_surface.cpp | 31 +++++++++++++++---- .../common/server/cpu_copy_output_surface.h | 2 +- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/platforms/common/server/cpu_copy_output_surface.cpp b/src/platforms/common/server/cpu_copy_output_surface.cpp index 9d126d66e23..607ceb19b84 100644 --- a/src/platforms/common/server/cpu_copy_output_surface.cpp +++ b/src/platforms/common/server/cpu_copy_output_surface.cpp @@ -18,6 +18,10 @@ #include #include + +#include +#include + #include #include "mir/graphics/egl_error.h" @@ -67,9 +71,24 @@ class GLHandle using RenderbufferHandle = GLHandle<&glGenRenderbuffers, &glDeleteRenderbuffers>; using FramebufferHandle = GLHandle<&glGenFramebuffers, &glDeleteFramebuffers>; -auto ensure_context_current(EGLDisplay dpy, EGLContext ctx) +auto create_current_context(EGLDisplay dpy, EGLContext share_ctx) -> EGLContext { + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + auto egl_extensions = eglQueryString(dpy, EGL_EXTENSIONS); + if (strstr(egl_extensions, "EGL_KHR_no_config_context")) + { + // We do not *strictly* need this, but it means I don't need to thread a GLConfig all the way through to here. + BOOST_THROW_EXCEPTION((std::runtime_error{"EGL implementation missing necessary EGL_KHR_no_config_context extension"})); + } + + eglBindAPI(EGL_OPENGL_ES_API); + auto ctx = eglCreateContext(dpy, EGL_NO_CONFIG_KHR, share_ctx, context_attr); + if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE) { BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make context current")); @@ -108,7 +127,7 @@ class mgc::CPUCopyOutputSurface::Impl public: Impl( EGLDisplay dpy, - EGLContext ctx, + EGLContext share_ctx, std::shared_ptr allocator, geom::Size size); @@ -134,10 +153,10 @@ class mgc::CPUCopyOutputSurface::Impl mgc::CPUCopyOutputSurface::CPUCopyOutputSurface( EGLDisplay dpy, - EGLContext ctx, + EGLContext share_ctx, std::shared_ptr allocator, geom::Size size) - : impl{std::make_unique(dpy, ctx, std::move(allocator), size)} + : impl{std::make_unique(dpy, share_ctx, std::move(allocator), size)} { } @@ -175,12 +194,12 @@ auto mgc::CPUCopyOutputSurface::layout() const -> Layout mgc::CPUCopyOutputSurface::Impl::Impl( EGLDisplay dpy, - EGLContext ctx, + EGLContext share_ctx, std::shared_ptr allocator, geom::Size size) : allocator{std::move(allocator)}, dpy{dpy}, - ctx{ensure_context_current(dpy, ctx)}, + ctx{create_current_context(dpy, share_ctx)}, size_{size}, format{select_format_from(*this->allocator)} { diff --git a/src/platforms/common/server/cpu_copy_output_surface.h b/src/platforms/common/server/cpu_copy_output_surface.h index 7a24c3369a7..a1d239efa8e 100644 --- a/src/platforms/common/server/cpu_copy_output_surface.h +++ b/src/platforms/common/server/cpu_copy_output_surface.h @@ -36,7 +36,7 @@ class CPUCopyOutputSurface : public gl::OutputSurface public: CPUCopyOutputSurface( EGLDisplay dpy, - EGLContext ctx, + EGLContext share_ctx, std::shared_ptr allocator, geometry::Size size); From d32e78175ad304dbf6933583b94b6b61d401b3f9 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Fri, 28 Jul 2023 10:30:54 +0100 Subject: [PATCH 042/108] Fix typo --- src/platforms/common/server/cpu_copy_output_surface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/common/server/cpu_copy_output_surface.cpp b/src/platforms/common/server/cpu_copy_output_surface.cpp index 607ceb19b84..468cb4feb22 100644 --- a/src/platforms/common/server/cpu_copy_output_surface.cpp +++ b/src/platforms/common/server/cpu_copy_output_surface.cpp @@ -80,7 +80,7 @@ auto create_current_context(EGLDisplay dpy, EGLContext share_ctx) }; auto egl_extensions = eglQueryString(dpy, EGL_EXTENSIONS); - if (strstr(egl_extensions, "EGL_KHR_no_config_context")) + if (strstr(egl_extensions, "EGL_KHR_no_config_context") == nullptr) { // We do not *strictly* need this, but it means I don't need to thread a GLConfig all the way through to here. BOOST_THROW_EXCEPTION((std::runtime_error{"EGL implementation missing necessary EGL_KHR_no_config_context extension"})); From 06f516cb6b8ef268b228a8a8ddf6be6925afa31f Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Wed, 6 Sep 2023 13:51:32 -0400 Subject: [PATCH 043/108] Building implementation of the CRTC-only smooth boot support --- include/miral/miral/smooth_boot_support.h | 46 +++++++++++++++++++ include/platform/mir/graphics/platform.h | 3 +- include/platform/mir/options/configuration.h | 1 + src/miral/CMakeLists.txt | 1 + src/miral/smooth_boot_support.cpp | 46 +++++++++++++++++++ src/miral/symbols.map | 5 ++ .../options/default_configuration.cpp | 1 + src/platform/symbols.map | 1 + src/platforms/gbm-kms/server/kms/display.cpp | 9 ++-- src/platforms/gbm-kms/server/kms/display.h | 4 +- .../gbm-kms/server/kms/display_buffer.cpp | 33 +++++++------ .../gbm-kms/server/kms/display_buffer.h | 3 +- src/platforms/gbm-kms/server/kms/platform.cpp | 10 +++- src/platforms/gbm-kms/server/kms/platform.h | 3 +- src/platforms/wayland/platform.cpp | 3 +- src/platforms/wayland/platform.h | 3 +- src/platforms/x11/graphics/platform.cpp | 3 +- src/platforms/x11/graphics/platform.h | 3 +- src/server/graphics/default_configuration.cpp | 3 +- .../include/mir/test/doubles/null_platform.h | 3 +- .../platform_graphics_throw.cpp | 5 +- .../stubbed_graphics_platform.cpp | 3 +- .../stubbed_graphics_platform.h | 3 +- .../graphics_platform_test_harness.cpp | 9 +++- .../gbm-kms/kms/test_display_buffer.cpp | 12 +++-- .../kms/test_display_configuration.cpp | 4 +- .../gbm-kms/kms/test_display_generic.cpp | 5 +- .../kms/test_display_multi_monitor.cpp | 7 ++- 28 files changed, 189 insertions(+), 43 deletions(-) create mode 100644 include/miral/miral/smooth_boot_support.h create mode 100644 src/miral/smooth_boot_support.cpp diff --git a/include/miral/miral/smooth_boot_support.h b/include/miral/miral/smooth_boot_support.h new file mode 100644 index 00000000000..63a37c5713b --- /dev/null +++ b/include/miral/miral/smooth_boot_support.h @@ -0,0 +1,46 @@ +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MIRAL_SMOOTH_BOOT_SUPPORT_H +#define MIRAL_SMOOTH_BOOT_SUPPORT_H + +#include + +namespace mir { class Server; } + +namespace miral +{ + +/// Provides a smooth boot transition from the prior screen (such as the plymouth +/// splash or any other DRM-based display) to the compositor. +class SmoothBootSupport +{ +public: + void operator()(mir::Server& server) const; + + explicit SmoothBootSupport(); + ~SmoothBootSupport(); + SmoothBootSupport(SmoothBootSupport const&); + auto operator=(SmoothBootSupport const&) -> SmoothBootSupport&; + +private: + struct Self; + std::shared_ptr self; +}; + +} + +#endif \ No newline at end of file diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index 4f325d66e0f..f036276818d 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -457,7 +457,8 @@ class DisplayPlatform : public std::enable_shared_from_this */ virtual UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config) = 0; + std::shared_ptr const& gl_config, + std::shared_ptr const& options) = 0; static auto interface_for(std::shared_ptr platform) -> std::shared_ptr diff --git a/include/platform/mir/options/configuration.h b/include/platform/mir/options/configuration.h index f4af7a7af28..96c20d4db7d 100644 --- a/include/platform/mir/options/configuration.h +++ b/include/platform/mir/options/configuration.h @@ -65,6 +65,7 @@ extern char const* const null_console; extern char const* const auto_console; extern char const* const vt_option_name; +extern char const* const smooth_boot_opt; class Configuration { diff --git a/src/miral/CMakeLists.txt b/src/miral/CMakeLists.txt index 020c47050f1..0f40bfa258f 100644 --- a/src/miral/CMakeLists.txt +++ b/src/miral/CMakeLists.txt @@ -69,6 +69,7 @@ add_library(miral SHARED window_specification.cpp ${miral_include}/miral/window_specification.h internal_client.cpp ${miral_include}/miral/internal_client.h prepend_event_filter.cpp ${miral_include}/miral/prepend_event_filter.h + smooth_boot_support.cpp ${miral_include}/miral/smooth_boot_support.h set_command_line_handler.cpp ${miral_include}/miral/set_command_line_handler.h set_terminator.cpp ${miral_include}/miral/set_terminator.h set_window_management_policy.cpp ${miral_include}/miral/set_window_management_policy.h diff --git a/src/miral/smooth_boot_support.cpp b/src/miral/smooth_boot_support.cpp new file mode 100644 index 00000000000..1d900db60a3 --- /dev/null +++ b/src/miral/smooth_boot_support.cpp @@ -0,0 +1,46 @@ + +/* + * Copyright © Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "miral/smooth_boot_support.h" +#include +#include +#include + +namespace mg = mir::graphics; +namespace mo = mir::options; + +struct miral::SmoothBootSupport::Self +{ +}; + +miral::SmoothBootSupport::SmoothBootSupport() + : self{std::make_shared()} +{ +} + +miral::SmoothBootSupport::~SmoothBootSupport() = default; +miral::SmoothBootSupport::SmoothBootSupport(SmoothBootSupport const& other) = default; +auto miral::SmoothBootSupport::operator=(miral::SmoothBootSupport const& other) -> SmoothBootSupport& = default; + +void miral::SmoothBootSupport::operator()(mir::Server &server) const +{ + server.add_configuration_option( + mo::smooth_boot_opt, + "When set, provides a transition from the previous screen to the compositor", + true + ); +} \ No newline at end of file diff --git a/src/miral/symbols.map b/src/miral/symbols.map index a58dfddb609..d1d0d69bc9e 100644 --- a/src/miral/symbols.map +++ b/src/miral/symbols.map @@ -478,6 +478,11 @@ global: extern "C++" { miral::WaylandExtensions::zwp_input_method_v1*; miral::WaylandExtensions::zwp_input_panel_v1*; + miral::SmoothBootSupport::?SmoothBootSupport*; + miral::SmoothBootSupport::SmoothBootSupport*; + miral::SmoothBootSupport::operator*; + typeinfo?for?miral::SmoothBootSupport; + vtable?for?miral::SmoothBootSupport; }; } MIRAL_4.0; diff --git a/src/platform/options/default_configuration.cpp b/src/platform/options/default_configuration.cpp index e628f614e16..0da896c894b 100644 --- a/src/platform/options/default_configuration.cpp +++ b/src/platform/options/default_configuration.cpp @@ -61,6 +61,7 @@ char const* const mo::null_console = "none"; char const* const mo::auto_console = "auto"; char const* const mo::vt_option_name = "vt"; +char const* const mo::smooth_boot_opt = "smooth-boot"; namespace diff --git a/src/platform/symbols.map b/src/platform/symbols.map index 4961ca78429..e66a50a207f 100644 --- a/src/platform/symbols.map +++ b/src/platform/symbols.map @@ -225,5 +225,6 @@ MIR_PLATFORM_2.13 { global: extern "C++" { mir::graphics::DRMFormat::from_mir_format*; + mir::options::smooth_boot_opt*; }; } MIR_PLATFORM_2.11; diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index e250fb1b522..e0aa3a7641e 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -148,7 +148,8 @@ mgg::Display::Display( mir::Fd drm_fd, mgg::BypassOption bypass_option, std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& listener) + std::shared_ptr const& listener, + bool smooth_transition) : owner{std::move(parent)}, drm_fd{std::move(drm_fd)}, listener(listener), @@ -159,7 +160,8 @@ mgg::Display::Display( std::make_shared(this->drm_fd, listener))}, current_display_configuration{output_container}, dirty_configuration{false}, - bypass_option(bypass_option) + bypass_option(bypass_option), + smooth_transition{smooth_transition} { monitor.filter_by_subsystem_and_type("drm", "drm_minor"); monitor.enable(); @@ -428,7 +430,8 @@ void mgg::Display::configure_locked( listener, kms_outputs, bounding_rect, - transformation); + transformation, + smooth_transition); display_buffers_new.push_back(std::move(db)); } diff --git a/src/platforms/gbm-kms/server/kms/display.h b/src/platforms/gbm-kms/server/kms/display.h index be6cf74ac86..081653256a2 100644 --- a/src/platforms/gbm-kms/server/kms/display.h +++ b/src/platforms/gbm-kms/server/kms/display.h @@ -63,7 +63,8 @@ class Display : public graphics::Display mir::Fd drm_fd, BypassOption bypass_option, std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& listener); + std::shared_ptr const& listener, + bool smooth_boot); ~Display(); geometry::Rectangle view_area() const; @@ -102,6 +103,7 @@ class Display : public graphics::Display BypassOption bypass_option; std::weak_ptr cursor; + bool smooth_transition; }; class CPUAddressableDisplayProvider : public graphics::CPUAddressableDisplayProvider diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index 6e03181a8f6..a06a0148113 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -52,7 +52,8 @@ mgg::DisplayBuffer::DisplayBuffer( std::shared_ptr const& listener, std::vector> const& outputs, geom::Rectangle const& area, - glm::mat2 const& transformation) + glm::mat2 const& transformation, + bool smooth_transition) : provider{std::move(provider)}, listener(listener), outputs(outputs), @@ -63,22 +64,24 @@ mgg::DisplayBuffer::DisplayBuffer( { listener->report_successful_setup_of_native_resources(); - // TODO: Pull a supported format out of KMS rather than assuming XRGB8888 - auto initial_fb = std::make_shared( - std::move(drm_fd), - false, - DRMFormat{DRM_FORMAT_XRGB8888}, - area.size); - - auto mapping = initial_fb->map_writeable(); - ::memset(mapping->data(), 24, mapping->len()); - - visible_fb = std::move(initial_fb); - for (auto& output : outputs) + if (!smooth_transition) { - output->set_crtc(*visible_fb); + // TODO: Pull a supported format out of KMS rather than assuming XRGB8888 + auto initial_fb = std::make_shared( + std::move(drm_fd), + false, + DRMFormat{DRM_FORMAT_XRGB8888}, + area.size); + + auto mapping = initial_fb->map_writeable(); + ::memset(mapping->data(), 24, mapping->len()); + + visible_fb = std::move(initial_fb); + for (auto &output: outputs) { + output->set_crtc(*visible_fb); + } + listener->report_successful_drm_mode_set_crtc_on_construction(); } - listener->report_successful_drm_mode_set_crtc_on_construction(); listener->report_successful_display_construction(); } diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.h b/src/platforms/gbm-kms/server/kms/display_buffer.h index f30523dd728..f363951403d 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.h +++ b/src/platforms/gbm-kms/server/kms/display_buffer.h @@ -55,7 +55,8 @@ class DisplayBuffer : public graphics::DisplayBuffer, std::shared_ptr const& listener, std::vector> const& outputs, geometry::Rectangle const& area, - glm::mat2 const& transformation); + glm::mat2 const& transformation, + bool smooth_transition); ~DisplayBuffer(); geometry::Rectangle view_area() const override; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index f0cb67e0ad2..6f4f760ba20 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -26,6 +26,8 @@ #include "mir/graphics/egl_error.h" #include "mir/graphics/egl_extensions.h" #include "one_shot_device_observer.h" +#include "mir/options/configuration.h" +#include "mir/options/option.h" #include #define MIR_LOG_COMPONENT "platform-graphics-gbm-kms" @@ -296,14 +298,18 @@ class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfacePr }; mir::UniqueModulePtr mgg::Platform::create_display( - std::shared_ptr const& initial_conf_policy, std::shared_ptr const&) + std::shared_ptr const& initial_conf_policy, + std::shared_ptr const&, + std::shared_ptr const& options) { + auto smooth_boot = options->is_set(options::smooth_boot_opt); return make_module_ptr( provider, drm_fd, bypass_option_, initial_conf_policy, - listener); + listener, + smooth_boot); } auto mgg::Platform::interface_for() -> std::shared_ptr diff --git a/src/platforms/gbm-kms/server/kms/platform.h b/src/platforms/gbm-kms/server/kms/platform.h index 70c845d6a5d..d7972df5dbb 100644 --- a/src/platforms/gbm-kms/server/kms/platform.h +++ b/src/platforms/gbm-kms/server/kms/platform.h @@ -49,7 +49,8 @@ class Platform : public graphics::DisplayPlatform /* From Platform */ UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config) override; + std::shared_ptr const& gl_config, + std::shared_ptr const& options) override; std::shared_ptr udev; diff --git a/src/platforms/wayland/platform.cpp b/src/platforms/wayland/platform.cpp index d3f7041d3e8..7ec0dcfdf5d 100644 --- a/src/platforms/wayland/platform.cpp +++ b/src/platforms/wayland/platform.cpp @@ -30,7 +30,8 @@ mgw::Platform::Platform(struct wl_display* const wl_display, std::shared_ptr mgw::Platform::create_display( std::shared_ptr const&, - std::shared_ptr const& gl_config) + std::shared_ptr const& gl_config, + std::shared_ptr const&) { return mir::make_module_ptr(wl_display, gl_config, report); } diff --git a/src/platforms/wayland/platform.h b/src/platforms/wayland/platform.h index 1ac260d8a53..a34cde5a4af 100644 --- a/src/platforms/wayland/platform.h +++ b/src/platforms/wayland/platform.h @@ -44,7 +44,8 @@ class Platform : public graphics::DisplayPlatform UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config) override; + std::shared_ptr const& gl_config, + std::shared_ptr const& options) override; protected: auto interface_for() -> std::shared_ptr override; diff --git a/src/platforms/x11/graphics/platform.cpp b/src/platforms/x11/graphics/platform.cpp index 16bd5fe4348..4e1485cace7 100644 --- a/src/platforms/x11/graphics/platform.cpp +++ b/src/platforms/x11/graphics/platform.cpp @@ -122,7 +122,8 @@ mgx::Platform::Platform(std::shared_ptr const& x11_resourc mir::UniqueModulePtr mgx::Platform::create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& /*gl_config*/) + std::shared_ptr const& /*gl_config*/, + std::shared_ptr const& /*options*/) { return make_module_ptr( std::dynamic_pointer_cast(shared_from_this()), diff --git a/src/platforms/x11/graphics/platform.h b/src/platforms/x11/graphics/platform.h index abbebba8206..9665ef1ad00 100644 --- a/src/platforms/x11/graphics/platform.h +++ b/src/platforms/x11/graphics/platform.h @@ -71,7 +71,8 @@ class Platform : public graphics::DisplayPlatform /* From Platform */ auto create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config) -> UniqueModulePtr override; + std::shared_ptr const& gl_config, + std::shared_ptr const& options) -> UniqueModulePtr override; auto provider_for_window(xcb_window_t x_win) -> std::shared_ptr; protected: diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index 57002edf494..dbf2d8d653b 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -409,7 +409,8 @@ mir::DefaultServerConfiguration::the_display() displays.push_back( platform->create_display( the_display_configuration_policy(), - the_gl_config())); + the_gl_config(), + the_options())); } return std::make_shared( std::move(displays), diff --git a/tests/include/mir/test/doubles/null_platform.h b/tests/include/mir/test/doubles/null_platform.h index 0d805043288..b8239d8b185 100644 --- a/tests/include/mir/test/doubles/null_platform.h +++ b/tests/include/mir/test/doubles/null_platform.h @@ -32,7 +32,8 @@ class NullDisplayPlatform : public graphics::DisplayPlatform public: auto create_display( std::shared_ptr const&, - std::shared_ptr const&) -> mir::UniqueModulePtr override + std::shared_ptr const&, + std::shared_ptr const&) -> mir::UniqueModulePtr override { return mir::make_module_ptr(); } diff --git a/tests/mir_test_framework/platform_graphics_throw.cpp b/tests/mir_test_framework/platform_graphics_throw.cpp index 7d84d014761..95f08d3cc7e 100644 --- a/tests/mir_test_framework/platform_graphics_throw.cpp +++ b/tests/mir_test_framework/platform_graphics_throw.cpp @@ -85,12 +85,13 @@ class ExceptionThrowingPlatform : public mg::DisplayPlatform, public mg::Renderi mir::UniqueModulePtr create_display( std::shared_ptr const& ptr, - std::shared_ptr const& shared_ptr) override + std::shared_ptr const& gl_config, + std::shared_ptr const& options) override { if (should_throw.at(ExceptionLocation::at_create_display)) BOOST_THROW_EXCEPTION(std::runtime_error("Exception during create_display")); - return stub_display_platform->create_display(ptr, shared_ptr); + return stub_display_platform->create_display(ptr, gl_config, options); } private: diff --git a/tests/mir_test_framework/stubbed_graphics_platform.cpp b/tests/mir_test_framework/stubbed_graphics_platform.cpp index 8bfaf7e7d57..5dd28b7b474 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.cpp +++ b/tests/mir_test_framework/stubbed_graphics_platform.cpp @@ -73,7 +73,8 @@ std::unique_ptr display_preset; mir::UniqueModulePtr mtf::StubGraphicPlatform::create_display( std::shared_ptr const&, - std::shared_ptr const&) + std::shared_ptr const&, + std::shared_ptr const&) { if (display_preset) { diff --git a/tests/mir_test_framework/stubbed_graphics_platform.h b/tests/mir_test_framework/stubbed_graphics_platform.h index 70b93f3e0a2..cf3a936600f 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.h +++ b/tests/mir_test_framework/stubbed_graphics_platform.h @@ -33,7 +33,8 @@ class StubGraphicPlatform : mir::UniqueModulePtr create_display( std::shared_ptr const&, - std::shared_ptr const&) override; + std::shared_ptr const&, + std::shared_ptr const&) override; protected: auto maybe_create_interface( diff --git a/tests/platform_test_harness/graphics_platform_test_harness.cpp b/tests/platform_test_harness/graphics_platform_test_harness.cpp index 4e17ffc0f6b..fe21c35825a 100644 --- a/tests/platform_test_harness/graphics_platform_test_harness.cpp +++ b/tests/platform_test_harness/graphics_platform_test_harness.cpp @@ -18,6 +18,7 @@ #include "mir/graphics/display.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/graphic_buffer_allocator.h" +#include "mir/options/option.h" #include "mir/options/program_option.h" #include "mir/shared_library.h" #include "mir/renderer/gl/context_source.h" @@ -103,6 +104,11 @@ class MinimalServerEnvironment : private mir::DefaultServerConfiguration return the_gl_config(); } + auto options() -> std::shared_ptr + { + return the_options(); + } + private: std::thread main_loop_thread; static char const* argv[]; @@ -203,7 +209,8 @@ auto test_display_construction(mir::graphics::DisplayPlatform& platform, Minimal { auto display = platform.create_display( env.initial_display_configuration(), - env.gl_config()); + env.gl_config(), + env.options()); std::cout << "Successfully created display" << std::endl; diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp index 358e4a9fc7d..acd111e934c 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp @@ -182,7 +182,8 @@ TEST_F(MesaDisplayBufferTest, unrotated_view_area_is_untouched) null_display_report(), {mock_kms_output}, display_area, - identity); + identity, + false); EXPECT_EQ(display_area, db.view_area()); } @@ -204,7 +205,8 @@ TEST_F(MesaDisplayBufferTest, frames_requiring_gl_are_not_throttled) null_display_report(), {mock_kms_output}, display_area, - identity); + identity, + false); for (int frame = 0; frame < 5; ++frame) { @@ -225,7 +227,8 @@ TEST_F(MesaDisplayBufferTest, untransformed_with_bypassable_list_can_bypass) null_display_report(), {mock_kms_output}, display_area, - identity); + identity, + false); EXPECT_TRUE(db.overlay(bypassable_list)); } @@ -250,7 +253,8 @@ TEST_F(MesaDisplayBufferTest, transformation_not_implemented_internally) null_display_report(), {mock_kms_output}, display_area, - rotate_left); + rotate_left, + false); EXPECT_EQ(rotate_left, db.transformation()); } diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp index 52d24f43047..78f6d600b55 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp @@ -35,6 +35,7 @@ #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/stub_console_services.h" #include "mir/test/doubles/stub_gl_config.h" +#include "mir/test/doubles/mock_option.h" #include "mir_test_framework/udev_environment.h" @@ -142,7 +143,8 @@ class MesaDisplayConfigurationTest : public ::testing::Test { return platform->create_display( std::make_shared(), - std::make_shared()); + std::make_shared(), + std::make_shared()); } void setup_sample_modes() diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp index d611b7aac78..b2d5307db74 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp @@ -21,6 +21,7 @@ #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/stub_gl_config.h" +#include "mir/test/doubles/mock_option.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/stub_console_services.h" #include "src/server/report/null_report_factory.h" @@ -34,6 +35,7 @@ #include #include #include +#include namespace mg = mir::graphics; namespace mgg = mg::gbm; @@ -96,7 +98,8 @@ class DisplayTestGeneric : public ::testing::Test mgg::BypassOption::allowed); return platform->create_display( std::make_shared(), - std::make_shared()); + std::make_shared(), + std::make_shared()); } ::testing::NiceMock mock_egl; diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp index 8f34ccd195e..6dc831e1fa7 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp @@ -31,6 +31,7 @@ #include "mir/test/doubles/mock_gl.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/test/doubles/stub_gl_config.h" +#include "mir/test/doubles/mock_option.h" #include "mir_test_framework/udev_environment.h" @@ -167,7 +168,8 @@ class MesaDisplayMultiMonitorTest : public ::testing::Test { return platform->create_display( std::make_shared(), - std::make_shared()); + std::make_shared(), + std::make_shared()); } std::shared_ptr create_display_side_by_side( @@ -175,7 +177,8 @@ class MesaDisplayMultiMonitorTest : public ::testing::Test { return platform->create_display( std::make_shared(), - std::make_shared());; + std::make_shared(), + std::make_shared());; } void setup_outputs(int connected, int disconnected) From 2a61739c6f940f11d04e007d2ecf7018b01c5850 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Wed, 6 Sep 2023 14:14:38 -0400 Subject: [PATCH 044/108] Regenerating debian symbols --- debian/libmiral6.symbols | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/debian/libmiral6.symbols b/debian/libmiral6.symbols index aa6b245b2bd..db61512fa3e 100644 --- a/debian/libmiral6.symbols +++ b/debian/libmiral6.symbols @@ -422,5 +422,10 @@ libmiral.so.6 libmiral6 #MINVER# (c++)"vtable for miral::MinimalWindowManager@MIRAL_4.0" 4.0.0 (c++)"vtable for miral::WindowManagementPolicy@MIRAL_4.0" 4.0.0 MIRAL_4.1@MIRAL_4.1 4.1.0 + (c++)"miral::SmoothBootSupport::SmoothBootSupport()@MIRAL_4.1" 4.1.0 + (c++)"miral::SmoothBootSupport::SmoothBootSupport(miral::SmoothBootSupport const&)@MIRAL_4.1" 4.1.0 + (c++)"miral::SmoothBootSupport::operator()(mir::Server&) const@MIRAL_4.1" 4.1.0 + (c++)"miral::SmoothBootSupport::operator=(miral::SmoothBootSupport const&)@MIRAL_4.1" 4.1.0 + (c++)"miral::SmoothBootSupport::~SmoothBootSupport()@MIRAL_4.1" 4.1.0 (c++)"miral::WaylandExtensions::zwp_input_method_v1@MIRAL_4.1" 4.1.0 (c++)"miral::WaylandExtensions::zwp_input_panel_v1@MIRAL_4.1" 4.1.0 From d926d217fba1ecd8c64f2bbc5cd06b209db5c999 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Wed, 6 Sep 2023 17:27:41 -0400 Subject: [PATCH 045/108] Rename variable from smooth_boot to smooth_transition --- src/platforms/gbm-kms/server/kms/display.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/gbm-kms/server/kms/display.h b/src/platforms/gbm-kms/server/kms/display.h index 081653256a2..135cd15620c 100644 --- a/src/platforms/gbm-kms/server/kms/display.h +++ b/src/platforms/gbm-kms/server/kms/display.h @@ -64,7 +64,7 @@ class Display : public graphics::Display BypassOption bypass_option, std::shared_ptr const& initial_conf_policy, std::shared_ptr const& listener, - bool smooth_boot); + bool smooth_transition); ~Display(); geometry::Rectangle view_area() const; From 44d563b69b1c542619e389ae8e620d59cdddc45c Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Thu, 7 Sep 2023 10:49:35 -0400 Subject: [PATCH 046/108] Setting the CRTC when there is a resolution mismatch on startup --- .../gbm-kms/server/kms/display_buffer.cpp | 24 ++++++++++++++++++- src/platforms/gbm-kms/server/kms/kms_output.h | 2 +- .../gbm-kms/server/kms/real_kms_output.cpp | 13 ++++++++++ .../gbm-kms/server/kms/real_kms_output.h | 1 + .../platforms/gbm-kms/kms/mock_kms_output.h | 1 + 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index a06a0148113..17a1a2d3630 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -64,7 +64,29 @@ mgg::DisplayBuffer::DisplayBuffer( { listener->report_successful_setup_of_native_resources(); - if (!smooth_transition) + bool needs_crtc_set = false; + for (auto& output : outputs) + { + try + { + geometry::Rectangle rectangle = output->get_rectangle(); + if (rectangle != area) + { + needs_crtc_set = true; + break; + } + } + catch (std::invalid_argument&) + { + needs_crtc_set = true; + break; + } + } + + if (smooth_transition && needs_crtc_set) + mir::log_warning("smooth_transition was requested but the CRTC requires setting"); + + if (needs_crtc_set) { // TODO: Pull a supported format out of KMS rather than assuming XRGB8888 auto initial_fb = std::make_shared( diff --git a/src/platforms/gbm-kms/server/kms/kms_output.h b/src/platforms/gbm-kms/server/kms/kms_output.h index 98a3713c559..abe6037f1a6 100644 --- a/src/platforms/gbm-kms/server/kms/kms_output.h +++ b/src/platforms/gbm-kms/server/kms/kms_output.h @@ -24,7 +24,6 @@ #include "mir/graphics/frame.h" #include "mir/graphics/dmabuf_buffer.h" #include "mir_toolkit/common.h" - #include "kms-utils/drm_mode_resources.h" #include @@ -64,6 +63,7 @@ class KMSOutput virtual int max_refresh_rate() const = 0; virtual bool set_crtc(FBHandle const& fb) = 0; + virtual auto get_rectangle() -> mir::geometry::Rectangle = 0; virtual void clear_crtc() = 0; virtual bool schedule_page_flip(FBHandle const& fb) = 0; virtual void wait_for_page_flip() = 0; diff --git a/src/platforms/gbm-kms/server/kms/real_kms_output.cpp b/src/platforms/gbm-kms/server/kms/real_kms_output.cpp index 08311467829..7e700dacbb6 100644 --- a/src/platforms/gbm-kms/server/kms/real_kms_output.cpp +++ b/src/platforms/gbm-kms/server/kms/real_kms_output.cpp @@ -155,6 +155,19 @@ bool mgg::RealKMSOutput::set_crtc(FBHandle const& fb) return true; } +auto mgg::RealKMSOutput::get_rectangle() -> mir::geometry::Rectangle +{ + if (!ensure_crtc()) + { + mir::log_error("Output %s has no associated CRTC to get ", mgk::connector_name(connector).c_str()); + BOOST_THROW_EXCEPTION(std::invalid_argument("get_crtc: Output has no associated CRTC to get")); + } + + return mir::geometry::Rectangle( + mir::geometry::Point(current_crtc->x, current_crtc->y), + mir::geometry::Size(current_crtc->width, current_crtc->height)); +} + void mgg::RealKMSOutput::clear_crtc() { try diff --git a/src/platforms/gbm-kms/server/kms/real_kms_output.h b/src/platforms/gbm-kms/server/kms/real_kms_output.h index a9ab4770ba3..de9a24cae75 100644 --- a/src/platforms/gbm-kms/server/kms/real_kms_output.h +++ b/src/platforms/gbm-kms/server/kms/real_kms_output.h @@ -49,6 +49,7 @@ class RealKMSOutput : public KMSOutput int max_refresh_rate() const override; bool set_crtc(FBHandle const& fb) override; + auto get_rectangle() -> mir::geometry::Rectangle override; void clear_crtc() override; bool schedule_page_flip(FBHandle const& fb) override; void wait_for_page_flip() override; diff --git a/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h b/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h index 020b4b56573..b62b87dc77a 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h +++ b/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h @@ -39,6 +39,7 @@ struct MockKMSOutput : public graphics::gbm::KMSOutput return set_crtc_thunk(&fb); } + MOCK_METHOD0(get_rectangle, mir::geometry::Rectangle()); MOCK_METHOD1(set_crtc_thunk, bool(graphics::gbm::FBHandle const*)); MOCK_METHOD0(clear_crtc, void()); From 397ee9ad8fc4c77d82d8117456242fbb2e35e41f Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Thu, 7 Sep 2023 11:11:51 -0400 Subject: [PATCH 047/108] Adding SmoothBootSupport to the demo server --- examples/mir_demo_server/server_example.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/mir_demo_server/server_example.cpp b/examples/mir_demo_server/server_example.cpp index 195f0c2f6d4..828f0406eb7 100644 --- a/examples/mir_demo_server/server_example.cpp +++ b/examples/mir_demo_server/server_example.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "mir/abnormal_exit.h" #include "mir/server.h" @@ -140,6 +141,7 @@ try .enable(miral::WaylandExtensions::zwp_input_method_manager_v2) .enable(miral::WaylandExtensions::zwlr_screencopy_manager_v1) .enable(miral::WaylandExtensions::ext_session_lock_manager_v1), + miral::SmoothBootSupport{}, miral::set_window_management_policy(), me::add_input_device_configuration_options_to, add_timeout_option_to, From deeb09c3409e1443ab2d0be459ac3397a22f718b Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Thu, 7 Sep 2023 11:28:33 -0400 Subject: [PATCH 048/108] Reverted the Smooth Boot flag in favor of always smooth booting --- examples/mir_demo_server/server_example.cpp | 2 - include/miral/miral/smooth_boot_support.h | 46 ------------------- include/platform/mir/graphics/platform.h | 3 +- include/platform/mir/options/configuration.h | 1 - src/miral/CMakeLists.txt | 1 - src/miral/smooth_boot_support.cpp | 46 ------------------- .../options/default_configuration.cpp | 1 - src/platform/symbols.map | 1 - src/platforms/gbm-kms/server/kms/display.cpp | 9 ++-- src/platforms/gbm-kms/server/kms/display.h | 4 +- .../gbm-kms/server/kms/display_buffer.cpp | 7 +-- .../gbm-kms/server/kms/display_buffer.h | 3 +- src/platforms/gbm-kms/server/kms/platform.cpp | 7 +-- src/platforms/gbm-kms/server/kms/platform.h | 3 +- src/platforms/wayland/platform.cpp | 3 +- src/platforms/wayland/platform.h | 3 +- src/platforms/x11/graphics/platform.cpp | 3 +- src/platforms/x11/graphics/platform.h | 3 +- src/server/graphics/default_configuration.cpp | 3 +- .../include/mir/test/doubles/null_platform.h | 3 +- .../platform_graphics_throw.cpp | 5 +- .../stubbed_graphics_platform.cpp | 3 +- .../stubbed_graphics_platform.h | 3 +- .../graphics_platform_test_harness.cpp | 9 +--- .../platforms/gbm-kms/kms/mock_kms_output.h | 2 +- .../gbm-kms/kms/test_display_buffer.cpp | 12 ++--- .../kms/test_display_configuration.cpp | 4 +- .../gbm-kms/kms/test_display_generic.cpp | 5 +- .../kms/test_display_multi_monitor.cpp | 7 +-- 29 files changed, 31 insertions(+), 171 deletions(-) delete mode 100644 include/miral/miral/smooth_boot_support.h delete mode 100644 src/miral/smooth_boot_support.cpp diff --git a/examples/mir_demo_server/server_example.cpp b/examples/mir_demo_server/server_example.cpp index 828f0406eb7..195f0c2f6d4 100644 --- a/examples/mir_demo_server/server_example.cpp +++ b/examples/mir_demo_server/server_example.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include "mir/abnormal_exit.h" #include "mir/server.h" @@ -141,7 +140,6 @@ try .enable(miral::WaylandExtensions::zwp_input_method_manager_v2) .enable(miral::WaylandExtensions::zwlr_screencopy_manager_v1) .enable(miral::WaylandExtensions::ext_session_lock_manager_v1), - miral::SmoothBootSupport{}, miral::set_window_management_policy(), me::add_input_device_configuration_options_to, add_timeout_option_to, diff --git a/include/miral/miral/smooth_boot_support.h b/include/miral/miral/smooth_boot_support.h deleted file mode 100644 index 63a37c5713b..00000000000 --- a/include/miral/miral/smooth_boot_support.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef MIRAL_SMOOTH_BOOT_SUPPORT_H -#define MIRAL_SMOOTH_BOOT_SUPPORT_H - -#include - -namespace mir { class Server; } - -namespace miral -{ - -/// Provides a smooth boot transition from the prior screen (such as the plymouth -/// splash or any other DRM-based display) to the compositor. -class SmoothBootSupport -{ -public: - void operator()(mir::Server& server) const; - - explicit SmoothBootSupport(); - ~SmoothBootSupport(); - SmoothBootSupport(SmoothBootSupport const&); - auto operator=(SmoothBootSupport const&) -> SmoothBootSupport&; - -private: - struct Self; - std::shared_ptr self; -}; - -} - -#endif \ No newline at end of file diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index f036276818d..4f325d66e0f 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -457,8 +457,7 @@ class DisplayPlatform : public std::enable_shared_from_this */ virtual UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config, - std::shared_ptr const& options) = 0; + std::shared_ptr const& gl_config) = 0; static auto interface_for(std::shared_ptr platform) -> std::shared_ptr diff --git a/include/platform/mir/options/configuration.h b/include/platform/mir/options/configuration.h index 96c20d4db7d..f4af7a7af28 100644 --- a/include/platform/mir/options/configuration.h +++ b/include/platform/mir/options/configuration.h @@ -65,7 +65,6 @@ extern char const* const null_console; extern char const* const auto_console; extern char const* const vt_option_name; -extern char const* const smooth_boot_opt; class Configuration { diff --git a/src/miral/CMakeLists.txt b/src/miral/CMakeLists.txt index 0f40bfa258f..020c47050f1 100644 --- a/src/miral/CMakeLists.txt +++ b/src/miral/CMakeLists.txt @@ -69,7 +69,6 @@ add_library(miral SHARED window_specification.cpp ${miral_include}/miral/window_specification.h internal_client.cpp ${miral_include}/miral/internal_client.h prepend_event_filter.cpp ${miral_include}/miral/prepend_event_filter.h - smooth_boot_support.cpp ${miral_include}/miral/smooth_boot_support.h set_command_line_handler.cpp ${miral_include}/miral/set_command_line_handler.h set_terminator.cpp ${miral_include}/miral/set_terminator.h set_window_management_policy.cpp ${miral_include}/miral/set_window_management_policy.h diff --git a/src/miral/smooth_boot_support.cpp b/src/miral/smooth_boot_support.cpp deleted file mode 100644 index 1d900db60a3..00000000000 --- a/src/miral/smooth_boot_support.cpp +++ /dev/null @@ -1,46 +0,0 @@ - -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "miral/smooth_boot_support.h" -#include -#include -#include - -namespace mg = mir::graphics; -namespace mo = mir::options; - -struct miral::SmoothBootSupport::Self -{ -}; - -miral::SmoothBootSupport::SmoothBootSupport() - : self{std::make_shared()} -{ -} - -miral::SmoothBootSupport::~SmoothBootSupport() = default; -miral::SmoothBootSupport::SmoothBootSupport(SmoothBootSupport const& other) = default; -auto miral::SmoothBootSupport::operator=(miral::SmoothBootSupport const& other) -> SmoothBootSupport& = default; - -void miral::SmoothBootSupport::operator()(mir::Server &server) const -{ - server.add_configuration_option( - mo::smooth_boot_opt, - "When set, provides a transition from the previous screen to the compositor", - true - ); -} \ No newline at end of file diff --git a/src/platform/options/default_configuration.cpp b/src/platform/options/default_configuration.cpp index 0da896c894b..e628f614e16 100644 --- a/src/platform/options/default_configuration.cpp +++ b/src/platform/options/default_configuration.cpp @@ -61,7 +61,6 @@ char const* const mo::null_console = "none"; char const* const mo::auto_console = "auto"; char const* const mo::vt_option_name = "vt"; -char const* const mo::smooth_boot_opt = "smooth-boot"; namespace diff --git a/src/platform/symbols.map b/src/platform/symbols.map index e66a50a207f..4961ca78429 100644 --- a/src/platform/symbols.map +++ b/src/platform/symbols.map @@ -225,6 +225,5 @@ MIR_PLATFORM_2.13 { global: extern "C++" { mir::graphics::DRMFormat::from_mir_format*; - mir::options::smooth_boot_opt*; }; } MIR_PLATFORM_2.11; diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index e0aa3a7641e..e250fb1b522 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -148,8 +148,7 @@ mgg::Display::Display( mir::Fd drm_fd, mgg::BypassOption bypass_option, std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& listener, - bool smooth_transition) + std::shared_ptr const& listener) : owner{std::move(parent)}, drm_fd{std::move(drm_fd)}, listener(listener), @@ -160,8 +159,7 @@ mgg::Display::Display( std::make_shared(this->drm_fd, listener))}, current_display_configuration{output_container}, dirty_configuration{false}, - bypass_option(bypass_option), - smooth_transition{smooth_transition} + bypass_option(bypass_option) { monitor.filter_by_subsystem_and_type("drm", "drm_minor"); monitor.enable(); @@ -430,8 +428,7 @@ void mgg::Display::configure_locked( listener, kms_outputs, bounding_rect, - transformation, - smooth_transition); + transformation); display_buffers_new.push_back(std::move(db)); } diff --git a/src/platforms/gbm-kms/server/kms/display.h b/src/platforms/gbm-kms/server/kms/display.h index 135cd15620c..be6cf74ac86 100644 --- a/src/platforms/gbm-kms/server/kms/display.h +++ b/src/platforms/gbm-kms/server/kms/display.h @@ -63,8 +63,7 @@ class Display : public graphics::Display mir::Fd drm_fd, BypassOption bypass_option, std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& listener, - bool smooth_transition); + std::shared_ptr const& listener); ~Display(); geometry::Rectangle view_area() const; @@ -103,7 +102,6 @@ class Display : public graphics::Display BypassOption bypass_option; std::weak_ptr cursor; - bool smooth_transition; }; class CPUAddressableDisplayProvider : public graphics::CPUAddressableDisplayProvider diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index 17a1a2d3630..1d723dc197d 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -52,8 +52,7 @@ mgg::DisplayBuffer::DisplayBuffer( std::shared_ptr const& listener, std::vector> const& outputs, geom::Rectangle const& area, - glm::mat2 const& transformation, - bool smooth_transition) + glm::mat2 const& transformation) : provider{std::move(provider)}, listener(listener), outputs(outputs), @@ -83,11 +82,9 @@ mgg::DisplayBuffer::DisplayBuffer( } } - if (smooth_transition && needs_crtc_set) - mir::log_warning("smooth_transition was requested but the CRTC requires setting"); - if (needs_crtc_set) { + mir::log_info("Clearing screen due to differing encountered and target modes"); // TODO: Pull a supported format out of KMS rather than assuming XRGB8888 auto initial_fb = std::make_shared( std::move(drm_fd), diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.h b/src/platforms/gbm-kms/server/kms/display_buffer.h index f363951403d..f30523dd728 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.h +++ b/src/platforms/gbm-kms/server/kms/display_buffer.h @@ -55,8 +55,7 @@ class DisplayBuffer : public graphics::DisplayBuffer, std::shared_ptr const& listener, std::vector> const& outputs, geometry::Rectangle const& area, - glm::mat2 const& transformation, - bool smooth_transition); + glm::mat2 const& transformation); ~DisplayBuffer(); geometry::Rectangle view_area() const override; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 6f4f760ba20..8ed1efb108f 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -299,17 +299,14 @@ class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfacePr mir::UniqueModulePtr mgg::Platform::create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const&, - std::shared_ptr const& options) + std::shared_ptr const&) { - auto smooth_boot = options->is_set(options::smooth_boot_opt); return make_module_ptr( provider, drm_fd, bypass_option_, initial_conf_policy, - listener, - smooth_boot); + listener); } auto mgg::Platform::interface_for() -> std::shared_ptr diff --git a/src/platforms/gbm-kms/server/kms/platform.h b/src/platforms/gbm-kms/server/kms/platform.h index d7972df5dbb..70c845d6a5d 100644 --- a/src/platforms/gbm-kms/server/kms/platform.h +++ b/src/platforms/gbm-kms/server/kms/platform.h @@ -49,8 +49,7 @@ class Platform : public graphics::DisplayPlatform /* From Platform */ UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config, - std::shared_ptr const& options) override; + std::shared_ptr const& gl_config) override; std::shared_ptr udev; diff --git a/src/platforms/wayland/platform.cpp b/src/platforms/wayland/platform.cpp index 7ec0dcfdf5d..d3f7041d3e8 100644 --- a/src/platforms/wayland/platform.cpp +++ b/src/platforms/wayland/platform.cpp @@ -30,8 +30,7 @@ mgw::Platform::Platform(struct wl_display* const wl_display, std::shared_ptr mgw::Platform::create_display( std::shared_ptr const&, - std::shared_ptr const& gl_config, - std::shared_ptr const&) + std::shared_ptr const& gl_config) { return mir::make_module_ptr(wl_display, gl_config, report); } diff --git a/src/platforms/wayland/platform.h b/src/platforms/wayland/platform.h index a34cde5a4af..1ac260d8a53 100644 --- a/src/platforms/wayland/platform.h +++ b/src/platforms/wayland/platform.h @@ -44,8 +44,7 @@ class Platform : public graphics::DisplayPlatform UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config, - std::shared_ptr const& options) override; + std::shared_ptr const& gl_config) override; protected: auto interface_for() -> std::shared_ptr override; diff --git a/src/platforms/x11/graphics/platform.cpp b/src/platforms/x11/graphics/platform.cpp index 4e1485cace7..16bd5fe4348 100644 --- a/src/platforms/x11/graphics/platform.cpp +++ b/src/platforms/x11/graphics/platform.cpp @@ -122,8 +122,7 @@ mgx::Platform::Platform(std::shared_ptr const& x11_resourc mir::UniqueModulePtr mgx::Platform::create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& /*gl_config*/, - std::shared_ptr const& /*options*/) + std::shared_ptr const& /*gl_config*/) { return make_module_ptr( std::dynamic_pointer_cast(shared_from_this()), diff --git a/src/platforms/x11/graphics/platform.h b/src/platforms/x11/graphics/platform.h index 9665ef1ad00..abbebba8206 100644 --- a/src/platforms/x11/graphics/platform.h +++ b/src/platforms/x11/graphics/platform.h @@ -71,8 +71,7 @@ class Platform : public graphics::DisplayPlatform /* From Platform */ auto create_display( std::shared_ptr const& initial_conf_policy, - std::shared_ptr const& gl_config, - std::shared_ptr const& options) -> UniqueModulePtr override; + std::shared_ptr const& gl_config) -> UniqueModulePtr override; auto provider_for_window(xcb_window_t x_win) -> std::shared_ptr; protected: diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index dbf2d8d653b..57002edf494 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -409,8 +409,7 @@ mir::DefaultServerConfiguration::the_display() displays.push_back( platform->create_display( the_display_configuration_policy(), - the_gl_config(), - the_options())); + the_gl_config())); } return std::make_shared( std::move(displays), diff --git a/tests/include/mir/test/doubles/null_platform.h b/tests/include/mir/test/doubles/null_platform.h index b8239d8b185..0d805043288 100644 --- a/tests/include/mir/test/doubles/null_platform.h +++ b/tests/include/mir/test/doubles/null_platform.h @@ -32,8 +32,7 @@ class NullDisplayPlatform : public graphics::DisplayPlatform public: auto create_display( std::shared_ptr const&, - std::shared_ptr const&, - std::shared_ptr const&) -> mir::UniqueModulePtr override + std::shared_ptr const&) -> mir::UniqueModulePtr override { return mir::make_module_ptr(); } diff --git a/tests/mir_test_framework/platform_graphics_throw.cpp b/tests/mir_test_framework/platform_graphics_throw.cpp index 95f08d3cc7e..7d84d014761 100644 --- a/tests/mir_test_framework/platform_graphics_throw.cpp +++ b/tests/mir_test_framework/platform_graphics_throw.cpp @@ -85,13 +85,12 @@ class ExceptionThrowingPlatform : public mg::DisplayPlatform, public mg::Renderi mir::UniqueModulePtr create_display( std::shared_ptr const& ptr, - std::shared_ptr const& gl_config, - std::shared_ptr const& options) override + std::shared_ptr const& shared_ptr) override { if (should_throw.at(ExceptionLocation::at_create_display)) BOOST_THROW_EXCEPTION(std::runtime_error("Exception during create_display")); - return stub_display_platform->create_display(ptr, gl_config, options); + return stub_display_platform->create_display(ptr, shared_ptr); } private: diff --git a/tests/mir_test_framework/stubbed_graphics_platform.cpp b/tests/mir_test_framework/stubbed_graphics_platform.cpp index 5dd28b7b474..8bfaf7e7d57 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.cpp +++ b/tests/mir_test_framework/stubbed_graphics_platform.cpp @@ -73,8 +73,7 @@ std::unique_ptr display_preset; mir::UniqueModulePtr mtf::StubGraphicPlatform::create_display( std::shared_ptr const&, - std::shared_ptr const&, - std::shared_ptr const&) + std::shared_ptr const&) { if (display_preset) { diff --git a/tests/mir_test_framework/stubbed_graphics_platform.h b/tests/mir_test_framework/stubbed_graphics_platform.h index cf3a936600f..70b93f3e0a2 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.h +++ b/tests/mir_test_framework/stubbed_graphics_platform.h @@ -33,8 +33,7 @@ class StubGraphicPlatform : mir::UniqueModulePtr create_display( std::shared_ptr const&, - std::shared_ptr const&, - std::shared_ptr const&) override; + std::shared_ptr const&) override; protected: auto maybe_create_interface( diff --git a/tests/platform_test_harness/graphics_platform_test_harness.cpp b/tests/platform_test_harness/graphics_platform_test_harness.cpp index fe21c35825a..4e17ffc0f6b 100644 --- a/tests/platform_test_harness/graphics_platform_test_harness.cpp +++ b/tests/platform_test_harness/graphics_platform_test_harness.cpp @@ -18,7 +18,6 @@ #include "mir/graphics/display.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/graphic_buffer_allocator.h" -#include "mir/options/option.h" #include "mir/options/program_option.h" #include "mir/shared_library.h" #include "mir/renderer/gl/context_source.h" @@ -104,11 +103,6 @@ class MinimalServerEnvironment : private mir::DefaultServerConfiguration return the_gl_config(); } - auto options() -> std::shared_ptr - { - return the_options(); - } - private: std::thread main_loop_thread; static char const* argv[]; @@ -209,8 +203,7 @@ auto test_display_construction(mir::graphics::DisplayPlatform& platform, Minimal { auto display = platform.create_display( env.initial_display_configuration(), - env.gl_config(), - env.options()); + env.gl_config()); std::cout << "Successfully created display" << std::endl; diff --git a/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h b/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h index b62b87dc77a..8db9b888672 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h +++ b/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h @@ -39,7 +39,7 @@ struct MockKMSOutput : public graphics::gbm::KMSOutput return set_crtc_thunk(&fb); } - MOCK_METHOD0(get_rectangle, mir::geometry::Rectangle()); + MOCK_METHOD0(get_rectangle, geometry::Rectangle()); MOCK_METHOD1(set_crtc_thunk, bool(graphics::gbm::FBHandle const*)); MOCK_METHOD0(clear_crtc, void()); diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp index acd111e934c..358e4a9fc7d 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp @@ -182,8 +182,7 @@ TEST_F(MesaDisplayBufferTest, unrotated_view_area_is_untouched) null_display_report(), {mock_kms_output}, display_area, - identity, - false); + identity); EXPECT_EQ(display_area, db.view_area()); } @@ -205,8 +204,7 @@ TEST_F(MesaDisplayBufferTest, frames_requiring_gl_are_not_throttled) null_display_report(), {mock_kms_output}, display_area, - identity, - false); + identity); for (int frame = 0; frame < 5; ++frame) { @@ -227,8 +225,7 @@ TEST_F(MesaDisplayBufferTest, untransformed_with_bypassable_list_can_bypass) null_display_report(), {mock_kms_output}, display_area, - identity, - false); + identity); EXPECT_TRUE(db.overlay(bypassable_list)); } @@ -253,8 +250,7 @@ TEST_F(MesaDisplayBufferTest, transformation_not_implemented_internally) null_display_report(), {mock_kms_output}, display_area, - rotate_left, - false); + rotate_left); EXPECT_EQ(rotate_left, db.transformation()); } diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp index 78f6d600b55..52d24f43047 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_configuration.cpp @@ -35,7 +35,6 @@ #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/stub_console_services.h" #include "mir/test/doubles/stub_gl_config.h" -#include "mir/test/doubles/mock_option.h" #include "mir_test_framework/udev_environment.h" @@ -143,8 +142,7 @@ class MesaDisplayConfigurationTest : public ::testing::Test { return platform->create_display( std::make_shared(), - std::make_shared(), - std::make_shared()); + std::make_shared()); } void setup_sample_modes() diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp index b2d5307db74..d611b7aac78 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_generic.cpp @@ -21,7 +21,6 @@ #include "mir/test/doubles/mock_egl.h" #include "mir/test/doubles/mock_gl.h" #include "mir/test/doubles/stub_gl_config.h" -#include "mir/test/doubles/mock_option.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "mir/test/doubles/stub_console_services.h" #include "src/server/report/null_report_factory.h" @@ -35,7 +34,6 @@ #include #include #include -#include namespace mg = mir::graphics; namespace mgg = mg::gbm; @@ -98,8 +96,7 @@ class DisplayTestGeneric : public ::testing::Test mgg::BypassOption::allowed); return platform->create_display( std::make_shared(), - std::make_shared(), - std::make_shared()); + std::make_shared()); } ::testing::NiceMock mock_egl; diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp index 6dc831e1fa7..8f34ccd195e 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp @@ -31,7 +31,6 @@ #include "mir/test/doubles/mock_gl.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/test/doubles/stub_gl_config.h" -#include "mir/test/doubles/mock_option.h" #include "mir_test_framework/udev_environment.h" @@ -168,8 +167,7 @@ class MesaDisplayMultiMonitorTest : public ::testing::Test { return platform->create_display( std::make_shared(), - std::make_shared(), - std::make_shared()); + std::make_shared()); } std::shared_ptr create_display_side_by_side( @@ -177,8 +175,7 @@ class MesaDisplayMultiMonitorTest : public ::testing::Test { return platform->create_display( std::make_shared(), - std::make_shared(), - std::make_shared());; + std::make_shared());; } void setup_outputs(int connected, int disconnected) From 6898e530e02bf92cb455da5e292a3a9cbac7b89e Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Thu, 7 Sep 2023 11:29:33 -0400 Subject: [PATCH 049/108] Resetting some files that are now deprecated --- debian/libmiral6.symbols | 5 ----- src/miral/symbols.map | 5 ----- src/platforms/gbm-kms/server/kms/platform.cpp | 5 +---- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/debian/libmiral6.symbols b/debian/libmiral6.symbols index db61512fa3e..aa6b245b2bd 100644 --- a/debian/libmiral6.symbols +++ b/debian/libmiral6.symbols @@ -422,10 +422,5 @@ libmiral.so.6 libmiral6 #MINVER# (c++)"vtable for miral::MinimalWindowManager@MIRAL_4.0" 4.0.0 (c++)"vtable for miral::WindowManagementPolicy@MIRAL_4.0" 4.0.0 MIRAL_4.1@MIRAL_4.1 4.1.0 - (c++)"miral::SmoothBootSupport::SmoothBootSupport()@MIRAL_4.1" 4.1.0 - (c++)"miral::SmoothBootSupport::SmoothBootSupport(miral::SmoothBootSupport const&)@MIRAL_4.1" 4.1.0 - (c++)"miral::SmoothBootSupport::operator()(mir::Server&) const@MIRAL_4.1" 4.1.0 - (c++)"miral::SmoothBootSupport::operator=(miral::SmoothBootSupport const&)@MIRAL_4.1" 4.1.0 - (c++)"miral::SmoothBootSupport::~SmoothBootSupport()@MIRAL_4.1" 4.1.0 (c++)"miral::WaylandExtensions::zwp_input_method_v1@MIRAL_4.1" 4.1.0 (c++)"miral::WaylandExtensions::zwp_input_panel_v1@MIRAL_4.1" 4.1.0 diff --git a/src/miral/symbols.map b/src/miral/symbols.map index d1d0d69bc9e..a58dfddb609 100644 --- a/src/miral/symbols.map +++ b/src/miral/symbols.map @@ -478,11 +478,6 @@ global: extern "C++" { miral::WaylandExtensions::zwp_input_method_v1*; miral::WaylandExtensions::zwp_input_panel_v1*; - miral::SmoothBootSupport::?SmoothBootSupport*; - miral::SmoothBootSupport::SmoothBootSupport*; - miral::SmoothBootSupport::operator*; - typeinfo?for?miral::SmoothBootSupport; - vtable?for?miral::SmoothBootSupport; }; } MIRAL_4.0; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 8ed1efb108f..f0cb67e0ad2 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -26,8 +26,6 @@ #include "mir/graphics/egl_error.h" #include "mir/graphics/egl_extensions.h" #include "one_shot_device_observer.h" -#include "mir/options/configuration.h" -#include "mir/options/option.h" #include #define MIR_LOG_COMPONENT "platform-graphics-gbm-kms" @@ -298,8 +296,7 @@ class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfacePr }; mir::UniqueModulePtr mgg::Platform::create_display( - std::shared_ptr const& initial_conf_policy, - std::shared_ptr const&) + std::shared_ptr const& initial_conf_policy, std::shared_ptr const&) { return make_module_ptr( provider, From 804453ba953bf7ef6b536e498229e085112eb424 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Fri, 8 Sep 2023 14:15:51 -0400 Subject: [PATCH 050/108] Adding a check for a crtc mismatch instead of just rectangle comparing --- .../gbm-kms/server/kms/display_buffer.cpp | 29 +++++-------- src/platforms/gbm-kms/server/kms/kms_output.h | 7 +++- .../gbm-kms/server/kms/real_kms_output.cpp | 41 +++++++++---------- .../gbm-kms/server/kms/real_kms_output.h | 2 +- .../platforms/gbm-kms/kms/mock_kms_output.h | 2 +- 5 files changed, 38 insertions(+), 43 deletions(-) diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index 1d723dc197d..9563923c1e6 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -63,34 +63,25 @@ mgg::DisplayBuffer::DisplayBuffer( { listener->report_successful_setup_of_native_resources(); - bool needs_crtc_set = false; + // If any of the outputs have a CRTC mismatch, we will want to set all of them + // so that they're all showing the same buffer. + bool has_crtc_mismatch = false; for (auto& output : outputs) { - try - { - geometry::Rectangle rectangle = output->get_rectangle(); - if (rectangle != area) - { - needs_crtc_set = true; - break; - } - } - catch (std::invalid_argument&) - { - needs_crtc_set = true; + has_crtc_mismatch = output->has_crtc_mismatch(); + if (has_crtc_mismatch) break; - } } - if (needs_crtc_set) + if (has_crtc_mismatch) { mir::log_info("Clearing screen due to differing encountered and target modes"); // TODO: Pull a supported format out of KMS rather than assuming XRGB8888 auto initial_fb = std::make_shared( - std::move(drm_fd), - false, - DRMFormat{DRM_FORMAT_XRGB8888}, - area.size); + std::move(drm_fd), + false, + DRMFormat{DRM_FORMAT_XRGB8888}, + area.size); auto mapping = initial_fb->map_writeable(); ::memset(mapping->data(), 24, mapping->len()); diff --git a/src/platforms/gbm-kms/server/kms/kms_output.h b/src/platforms/gbm-kms/server/kms/kms_output.h index abe6037f1a6..f68a59c6b6a 100644 --- a/src/platforms/gbm-kms/server/kms/kms_output.h +++ b/src/platforms/gbm-kms/server/kms/kms_output.h @@ -63,7 +63,12 @@ class KMSOutput virtual int max_refresh_rate() const = 0; virtual bool set_crtc(FBHandle const& fb) = 0; - virtual auto get_rectangle() -> mir::geometry::Rectangle = 0; + + /** + * Check if the pending call to set_crtc is compatible with the current state of the CRTC. + * @returns true if a set_crtc is required, otherwise false + */ + virtual bool has_crtc_mismatch() = 0; virtual void clear_crtc() = 0; virtual bool schedule_page_flip(FBHandle const& fb) = 0; virtual void wait_for_page_flip() = 0; diff --git a/src/platforms/gbm-kms/server/kms/real_kms_output.cpp b/src/platforms/gbm-kms/server/kms/real_kms_output.cpp index 7e700dacbb6..795258ff4e1 100644 --- a/src/platforms/gbm-kms/server/kms/real_kms_output.cpp +++ b/src/platforms/gbm-kms/server/kms/real_kms_output.cpp @@ -34,6 +34,22 @@ namespace mgk = mg::kms; namespace geom = mir::geometry; +namespace +{ +bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2) +{ + return (info1.clock == info2.clock && + info1.hdisplay == info2.hdisplay && + info1.hsync_start == info2.hsync_start && + info1.hsync_end == info2.hsync_end && + info1.htotal == info2.htotal && + info1.hskew == info2.hskew && + info1.vdisplay == info2.vdisplay && + info1.vsync_start == info2.vsync_start && + info1.vsync_end == info2.vsync_end && + info1.vtotal == info2.vtotal); +} +} mgg::RealKMSOutput::RealKMSOutput( int drm_fd, @@ -140,7 +156,7 @@ bool mgg::RealKMSOutput::set_crtc(FBHandle const& fb) return false; } - auto ret = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id, + auto ret = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id, fb, fb_offset.dx.as_int(), fb_offset.dy.as_int(), &connector->connector_id, 1, &connector->modes[mode_index]); @@ -155,17 +171,15 @@ bool mgg::RealKMSOutput::set_crtc(FBHandle const& fb) return true; } -auto mgg::RealKMSOutput::get_rectangle() -> mir::geometry::Rectangle +bool mgg::RealKMSOutput::has_crtc_mismatch() { if (!ensure_crtc()) { mir::log_error("Output %s has no associated CRTC to get ", mgk::connector_name(connector).c_str()); - BOOST_THROW_EXCEPTION(std::invalid_argument("get_crtc: Output has no associated CRTC to get")); + return true; } - return mir::geometry::Rectangle( - mir::geometry::Point(current_crtc->x, current_crtc->y), - mir::geometry::Size(current_crtc->width, current_crtc->height)); + return !kms_modes_are_equal(current_crtc->mode, connector->modes[mode_index]); } void mgg::RealKMSOutput::clear_crtc() @@ -394,21 +408,6 @@ void mgg::RealKMSOutput::refresh_hardware_state() namespace { - -bool kms_modes_are_equal(drmModeModeInfo const& info1, drmModeModeInfo const& info2) -{ - return (info1.clock == info2.clock && - info1.hdisplay == info2.hdisplay && - info1.hsync_start == info2.hsync_start && - info1.hsync_end == info2.hsync_end && - info1.htotal == info2.htotal && - info1.hskew == info2.hskew && - info1.vdisplay == info2.vdisplay && - info1.vsync_start == info2.vsync_start && - info1.vsync_end == info2.vsync_end && - info1.vtotal == info2.vtotal); -} - double calculate_vrefresh_hz(drmModeModeInfo const& mode) { if (mode.htotal == 0 || mode.vtotal == 0) diff --git a/src/platforms/gbm-kms/server/kms/real_kms_output.h b/src/platforms/gbm-kms/server/kms/real_kms_output.h index de9a24cae75..ee9286d39ab 100644 --- a/src/platforms/gbm-kms/server/kms/real_kms_output.h +++ b/src/platforms/gbm-kms/server/kms/real_kms_output.h @@ -49,7 +49,7 @@ class RealKMSOutput : public KMSOutput int max_refresh_rate() const override; bool set_crtc(FBHandle const& fb) override; - auto get_rectangle() -> mir::geometry::Rectangle override; + bool has_crtc_mismatch() override; void clear_crtc() override; bool schedule_page_flip(FBHandle const& fb) override; void wait_for_page_flip() override; diff --git a/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h b/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h index 8db9b888672..d457d465b36 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h +++ b/tests/unit-tests/platforms/gbm-kms/kms/mock_kms_output.h @@ -39,7 +39,7 @@ struct MockKMSOutput : public graphics::gbm::KMSOutput return set_crtc_thunk(&fb); } - MOCK_METHOD0(get_rectangle, geometry::Rectangle()); + MOCK_METHOD0(has_crtc_mismatch, bool()); MOCK_METHOD1(set_crtc_thunk, bool(graphics::gbm::FBHandle const*)); MOCK_METHOD0(clear_crtc, void()); From 0d4d85b1176ec4e24f0ed81cba03a83971fa2afb Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Fri, 8 Sep 2023 14:26:33 -0400 Subject: [PATCH 051/108] Getting rid of accidental spacing --- src/platforms/gbm-kms/server/kms/real_kms_output.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/gbm-kms/server/kms/real_kms_output.cpp b/src/platforms/gbm-kms/server/kms/real_kms_output.cpp index 795258ff4e1..214a0da8a7a 100644 --- a/src/platforms/gbm-kms/server/kms/real_kms_output.cpp +++ b/src/platforms/gbm-kms/server/kms/real_kms_output.cpp @@ -156,7 +156,7 @@ bool mgg::RealKMSOutput::set_crtc(FBHandle const& fb) return false; } - auto ret = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id, + auto ret = drmModeSetCrtc(drm_fd_, current_crtc->crtc_id, fb, fb_offset.dx.as_int(), fb_offset.dy.as_int(), &connector->connector_id, 1, &connector->modes[mode_index]); From 4c562817f99cd57574eae394f0420969d95557fc Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 29 Aug 2023 14:14:51 +1000 Subject: [PATCH 052/108] platform: Drop `NativeBuffer` detritus Turns out there's *still* a bunch of only-used-by-mirclient detritus in the platform code. Remove this bit. --- include/common/mir/graphics/native_buffer.h | 35 --------------- include/platform/mir/graphics/buffer.h | 5 +-- src/platforms/gbm-kms/include/native_buffer.h | 43 ------------------- .../gbm-kms/server/kms/display_buffer.cpp | 1 - tests/include/mir/test/doubles/mock_buffer.h | 12 ++---- tests/include/mir/test/doubles/stub_buffer.h | 26 +++-------- .../mir/test/doubles/stub_buffer_stream.h | 4 +- .../stub_platform_native_buffer.h | 37 ---------------- .../stub_buffer_allocator.cpp | 6 +-- tests/mir_test_framework/stub_session.cpp | 2 - .../stubbed_graphics_platform.cpp | 4 +- .../graphics/test_software_cursor.cpp | 1 - .../input/test_touchspot_controller.cpp | 1 - 13 files changed, 16 insertions(+), 161 deletions(-) delete mode 100644 include/common/mir/graphics/native_buffer.h delete mode 100644 src/platforms/gbm-kms/include/native_buffer.h delete mode 100644 tests/include/mir_test_framework/stub_platform_native_buffer.h diff --git a/include/common/mir/graphics/native_buffer.h b/include/common/mir/graphics/native_buffer.h deleted file mode 100644 index 91807c2ea38..00000000000 --- a/include/common/mir/graphics/native_buffer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 . - */ - -#ifndef MIR_GRAPHICS_NATIVE_BUFFER_H_ -#define MIR_GRAPHICS_NATIVE_BUFFER_H_ - -namespace mir -{ -namespace graphics -{ -class NativeBuffer -{ -protected: - NativeBuffer() = default; - virtual ~NativeBuffer() = default; - NativeBuffer(NativeBuffer const&) = delete; - NativeBuffer operator=(NativeBuffer const&) = delete; -}; -} -} - -#endif /* MIR_GRAPHICS_NATIVE_BUFFER_H_ */ diff --git a/include/platform/mir/graphics/buffer.h b/include/platform/mir/graphics/buffer.h index 0d6b0f79b1b..0aed4e83074 100644 --- a/include/platform/mir/graphics/buffer.h +++ b/include/platform/mir/graphics/buffer.h @@ -17,7 +17,6 @@ #ifndef MIR_GRAPHICS_BUFFER_H_ #define MIR_GRAPHICS_BUFFER_H_ -#include "mir/graphics/native_buffer.h" #include "mir/graphics/buffer_id.h" #include "mir/geometry/size.h" #include "mir_toolkit/common.h" @@ -35,8 +34,8 @@ class NativeBufferBase protected: NativeBufferBase() = default; virtual ~NativeBufferBase() = default; - NativeBufferBase(NativeBuffer const&) = delete; - NativeBufferBase operator=(NativeBuffer const&) = delete; + NativeBufferBase(NativeBufferBase const&) = delete; + NativeBufferBase operator=(NativeBufferBase const&) = delete; }; class Buffer diff --git a/src/platforms/gbm-kms/include/native_buffer.h b/src/platforms/gbm-kms/include/native_buffer.h deleted file mode 100644 index 00a3b3abcfc..00000000000 --- a/src/platforms/gbm-kms/include/native_buffer.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 . - */ - -#ifndef MIR_GRAPHICS_GBM_NATIVE_BUFFER_H_ -#define MIR_GRAPHICS_GBM_NATIVE_BUFFER_H_ - -#include -#include "mir/graphics/native_buffer.h" - -#include -#include - -namespace mir -{ -namespace graphics -{ -namespace gbm -{ -struct NativeBuffer : graphics::NativeBuffer, MirBufferPackage -{ - struct gbm_bo *bo; - bool is_gbm_buffer; - uint32_t native_format; - uint32_t native_flags; -}; -} -} -} - -#endif /* MIR_GRAPHICS_GBM_NATIVE_BUFFER_H_ */ diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index 9563923c1e6..130ae5f523f 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -23,7 +23,6 @@ #include "bypass.h" #include "mir/fatal.h" #include "mir/log.h" -#include "native_buffer.h" #include "display_helpers.h" #include "egl_helper.h" #include "mir/graphics/egl_error.h" diff --git a/tests/include/mir/test/doubles/mock_buffer.h b/tests/include/mir/test/doubles/mock_buffer.h index df23d016d4c..3115be20f70 100644 --- a/tests/include/mir/test/doubles/mock_buffer.h +++ b/tests/include/mir/test/doubles/mock_buffer.h @@ -55,18 +55,14 @@ struct MockBuffer : public graphics::Buffer, public graphics::NativeBufferBase ON_CALL(*this, id()) .WillByDefault(Return(graphics::BufferID{4})); - ON_CALL(*this, native_buffer_handle()) - .WillByDefault(Return(std::shared_ptr())); } - MOCK_CONST_METHOD0(size, geometry::Size()); - MOCK_CONST_METHOD0(pixel_format, MirPixelFormat()); - MOCK_CONST_METHOD0(native_buffer_handle, std::shared_ptr()); + MOCK_METHOD(geometry::Size, size, (), (const override)); + MOCK_METHOD(MirPixelFormat, pixel_format, (), (const override)); - MOCK_CONST_METHOD0(id, graphics::BufferID()); + MOCK_METHOD(graphics::BufferID, id, (), (const override)); - MOCK_METHOD0(native_buffer_base, graphics::NativeBufferBase*()); - MOCK_METHOD0(used_as_texture, void()); + MOCK_METHOD(graphics::NativeBufferBase*, native_buffer_base, (), (override)); }; } diff --git a/tests/include/mir/test/doubles/stub_buffer.h b/tests/include/mir/test/doubles/stub_buffer.h index c5511c4abd8..e45325547b9 100644 --- a/tests/include/mir/test/doubles/stub_buffer.h +++ b/tests/include/mir/test/doubles/stub_buffer.h @@ -25,10 +25,6 @@ #include #include -#include -#include -#include - namespace mir { namespace test @@ -44,7 +40,6 @@ class StubBuffer : public: StubBuffer() : StubBuffer{ - nullptr, graphics::BufferProperties{ geometry::Size{}, mir_pixel_format_abgr_8888, @@ -56,7 +51,6 @@ class StubBuffer : StubBuffer(geometry::Size const& size) : StubBuffer{ - nullptr, graphics::BufferProperties{ size, mir_pixel_format_abgr_8888, @@ -66,9 +60,8 @@ class StubBuffer : { } - StubBuffer(std::shared_ptr const& native_buffer, geometry::Size const& size, MirPixelFormat const pixel_format) + StubBuffer(geometry::Size const& size, MirPixelFormat const pixel_format) : StubBuffer{ - native_buffer, graphics::BufferProperties{ size, pixel_format, @@ -78,13 +71,8 @@ class StubBuffer : { } - StubBuffer(std::shared_ptr const& native_buffer) - : StubBuffer{native_buffer, {}, mir_pixel_format_abgr_8888} - { - } - StubBuffer(graphics::BufferProperties const& properties) - : StubBuffer{nullptr, properties, geometry::Stride{properties.size.width.as_int() * MIR_BYTES_PER_PIXEL(properties.format)}} + : StubBuffer{properties, geometry::Stride{properties.size.width.as_int() * MIR_BYTES_PER_PIXEL(properties.format)}} { written_pixels.resize(buf_size.height.as_uint32_t() * buf_stride.as_uint32_t()); if (written_pixels.size()) @@ -95,19 +83,16 @@ class StubBuffer : } StubBuffer(graphics::BufferID id) - : native_buffer(nullptr), - buf_size{}, + : buf_size{}, buf_pixel_format{mir_pixel_format_abgr_8888}, buf_stride{}, buf_id{id} { } - StubBuffer(std::shared_ptr const& native_buffer, - graphics::BufferProperties const& properties, + StubBuffer(graphics::BufferProperties const& properties, geometry::Stride stride) - : native_buffer(native_buffer), - buf_size{properties.size}, + : buf_size{properties.size}, buf_pixel_format{properties.format}, buf_stride{stride}, buf_id{graphics::BufferBasic::id()} @@ -190,7 +175,6 @@ class StubBuffer : return this; } - std::shared_ptr const native_buffer; geometry::Size const buf_size; MirPixelFormat const buf_pixel_format; geometry::Stride const buf_stride; diff --git a/tests/include/mir/test/doubles/stub_buffer_stream.h b/tests/include/mir/test/doubles/stub_buffer_stream.h index 5a79d27b76f..2f90b265e9a 100644 --- a/tests/include/mir/test/doubles/stub_buffer_stream.h +++ b/tests/include/mir/test/doubles/stub_buffer_stream.h @@ -19,7 +19,6 @@ #include #include -#include "mir_test_framework/stub_platform_native_buffer.h" namespace mir { @@ -33,8 +32,7 @@ class StubBufferStream : public compositor::BufferStream public: StubBufferStream() { - stub_compositor_buffer = std::make_shared( - std::make_shared(graphics::BufferProperties{})); + stub_compositor_buffer = std::make_shared(); } diff --git a/tests/include/mir_test_framework/stub_platform_native_buffer.h b/tests/include/mir_test_framework/stub_platform_native_buffer.h deleted file mode 100644 index 2f2c338faf4..00000000000 --- a/tests/include/mir_test_framework/stub_platform_native_buffer.h +++ /dev/null @@ -1,37 +0,0 @@ - -#ifndef MIR_TEST_FRAMEWORK_STUB_PLATFORM_NATIVE_BUFFER_H_ -#define MIR_TEST_FRAMEWORK_STUB_PLATFORM_NATIVE_BUFFER_H_ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace mir_test_framework -{ -//just a simple FD and int, helps to check for leaks/memory issues. -struct NativeBuffer : mir::graphics::NativeBuffer -{ - NativeBuffer(mir::graphics::BufferProperties const& properties) : - properties(properties) - { - if (fd < 0) - { - BOOST_THROW_EXCEPTION( - boost::enable_error_info( - std::system_error(errno, std::system_category(), "Failed to open dummy fd"))); - } - } - int const data {0x328}; - mir::Fd const fd{open("/dev/zero", O_RDONLY)}; - mir::graphics::BufferProperties const properties; -}; -} -#endif /* MIR_TEST_FRAMEWORK_STUB_PLATFORM_NATIVE_BUFFER_H_ */ diff --git a/tests/mir_test_doubles/stub_buffer_allocator.cpp b/tests/mir_test_doubles/stub_buffer_allocator.cpp index 5a4c0177816..fd3a30d2e2e 100644 --- a/tests/mir_test_doubles/stub_buffer_allocator.cpp +++ b/tests/mir_test_doubles/stub_buffer_allocator.cpp @@ -17,8 +17,6 @@ #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/stub_buffer.h" -#include "mir_test_framework/stub_platform_native_buffer.h" -#include "mir_toolkit/client_types.h" #include "src/platforms/common/server/shm_buffer.h" #include "mir/graphics/egl_context_executor.h" #include "mir/test/doubles/null_gl_context.h" @@ -28,6 +26,8 @@ #include #include +#include + namespace mtd = mir::test::doubles; namespace mg = mir::graphics; @@ -64,7 +64,7 @@ inline void memcpy_from_mapping(mir::renderer::software::ReadMappableBuffer& buf auto mtd::StubBufferAllocator::alloc_software_buffer(geometry::Size sz, MirPixelFormat pf) -> std::shared_ptr { graphics::BufferProperties properties{sz, pf, graphics::BufferUsage::software}; - return std::make_shared(std::make_shared(properties), properties, geometry::Stride{sz.width.as_uint32_t() * MIR_BYTES_PER_PIXEL(pf)}); + return std::make_shared(properties, geometry::Stride{sz.width.as_uint32_t() * MIR_BYTES_PER_PIXEL(pf)}); } auto mtd::StubBufferAllocator::supported_pixel_formats() -> std::vector diff --git a/tests/mir_test_framework/stub_session.cpp b/tests/mir_test_framework/stub_session.cpp index 81bc8756c1b..9b45e5ad415 100644 --- a/tests/mir_test_framework/stub_session.cpp +++ b/tests/mir_test_framework/stub_session.cpp @@ -15,8 +15,6 @@ */ #include "mir/test/doubles/stub_session.h" -#include "mir/test/doubles/stub_buffer.h" -#include "mir_test_framework/stub_platform_native_buffer.h" namespace mtd = mir::test::doubles; namespace ms = mir::scene; diff --git a/tests/mir_test_framework/stubbed_graphics_platform.cpp b/tests/mir_test_framework/stubbed_graphics_platform.cpp index 8bfaf7e7d57..b3c734cc7fa 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.cpp +++ b/tests/mir_test_framework/stubbed_graphics_platform.cpp @@ -17,19 +17,17 @@ #include "stubbed_graphics_platform.h" #include "mir/graphics/platform.h" -#include "mir_test_framework/stub_platform_native_buffer.h" #include "mir_toolkit/common.h" #include "mir/test/doubles/stub_buffer_allocator.h" #include "mir/test/doubles/stub_gl_rendering_provider.h" #include "mir/test/doubles/fake_display.h" -#include "mir/fd.h" #include "mir/assert_module_entry_point.h" #include #include -#include +#include namespace geom = mir::geometry; namespace mg = mir::graphics; diff --git a/tests/unit-tests/graphics/test_software_cursor.cpp b/tests/unit-tests/graphics/test_software_cursor.cpp index ec56d03d932..b2fa263bfef 100644 --- a/tests/unit-tests/graphics/test_software_cursor.cpp +++ b/tests/unit-tests/graphics/test_software_cursor.cpp @@ -498,7 +498,6 @@ TEST_F(SoftwareCursor, handles_argb_8888_buffer_with_stride) sz.width.as_uint32_t() * MIR_BYTES_PER_PIXEL(pf) + 41 }; auto buffer = std::make_shared( - nullptr, mg::BufferProperties{ sz, pf, diff --git a/tests/unit-tests/input/test_touchspot_controller.cpp b/tests/unit-tests/input/test_touchspot_controller.cpp index 194a59942d2..23539cc1659 100644 --- a/tests/unit-tests/input/test_touchspot_controller.cpp +++ b/tests/unit-tests/input/test_touchspot_controller.cpp @@ -129,7 +129,6 @@ TEST_F(TestTouchspotController, handles_stride_mismatch_in_buffer) { mg::BufferProperties properties{size, pf, mg::BufferUsage::software}; return std::make_shared( - nullptr, properties, geom::Stride{size.width.as_uint32_t() * MIR_BYTES_PER_PIXEL(pf) + 29}); // Return a stride != width })); From 3cbce7a117986b70346681c0f09323a155b6859f Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 29 Aug 2023 16:10:39 +1000 Subject: [PATCH 053/108] platform/linux-dma-buf: Use DRMFormat more. There's a long-standing TODO in the code to "have something like libweston/pixel-formats". We now *do* have something like that, `mg::DRMFormat`. Use it, rather than hardcoding a partial lookup table. --- src/platform/graphics/linux_dmabuf.cpp | 30 ++------------------------ 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index f5a750f711a..b4827e79a66 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -291,7 +291,7 @@ class WlDmaBufBuffer : public mir::wayland::Buffer } } - auto format() -> uint32_t + auto format() -> mg::DRMFormat { return format_; } @@ -723,32 +723,6 @@ GLuint get_tex_id() return tex; } -bool drm_format_has_alpha(uint32_t format) -{ - /* TODO: We should really have something like libweston/pixel-formats.h - * We've said this multiple times before, so 🤷 - */ - - switch (format) - { - /* This is only an optimisation, so pick a bunch of formats and hope that - * covers it… - */ - case DRM_FORMAT_XBGR2101010: - case DRM_FORMAT_BGRX1010102: - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_BGRX8888: - case DRM_FORMAT_XRGB2101010: - case DRM_FORMAT_RGBX1010102: - case DRM_FORMAT_RGBX8888: - case DRM_FORMAT_XRGB8888: - return false; - default: - // TODO: I *think* true is a safe default, as it'll just mean we're blending something without alpha? - return true; - } -} - class WaylandDmabufTexBuffer : public mg::BufferBasic, public mg::gl::Texture, @@ -769,7 +743,7 @@ class WaylandDmabufTexBuffer : on_release{std::move(on_release)}, size_{source.size()}, layout_{source.layout()}, - has_alpha{drm_format_has_alpha(source.format())}, + has_alpha{source.format().has_alpha()}, planes_{source.planes()}, modifier_{source.modifier()}, fourcc{source.format()}, From 08203abbd227d363435ee99dca7de270f01d932e Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 29 Aug 2023 16:52:52 +1000 Subject: [PATCH 054/108] platform/dma-buf: More DRMFormat usage --- src/platform/graphics/linux_dmabuf.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index b4827e79a66..9206faf03cd 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -746,7 +746,7 @@ class WaylandDmabufTexBuffer : has_alpha{source.format().has_alpha()}, planes_{source.planes()}, modifier_{source.modifier()}, - fourcc{source.format()}, + format{source.format()}, egl_delegate{std::move(egl_delegate)} { eglBindAPI(EGL_OPENGL_ES_API); @@ -782,6 +782,10 @@ class WaylandDmabufTexBuffer : MirPixelFormat pixel_format() const override { + if (auto mir_format = format.as_mir_format()) + { + return mir_format.value(); + } // There's no way to implement this corectly… if (has_alpha) { @@ -830,7 +834,7 @@ class WaylandDmabufTexBuffer : auto drm_fourcc() const -> uint32_t override { - return fourcc; + return format; } auto modifier() const -> std::optional override @@ -857,7 +861,7 @@ class WaylandDmabufTexBuffer : std::vector const planes_; std::optional const modifier_; - uint32_t const fourcc; + mg::DRMFormat const format; std::shared_ptr const egl_delegate; }; From 103e34704027f58898b4b471e6a1b2c9f21d28d3 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 12 Sep 2023 14:44:02 +1000 Subject: [PATCH 055/108] platform/DMA-buf: Handle cross-GPU buffers When we have multiple output cards with renderers on each then the `BufferAllocator` that allocated a `Buffer` is not necessarily the one that will be providing the `GLRenderingProvider`. In that case we need to re-import the dma-buf on the GPU that is actually trying to render from it. Set this up with a `DMABufEGLProvider` class that provides an `as_texture()` method and delegate to that from the relevant `GLRenderingProvider`s. --- include/platform/mir/graphics/dmabuf_buffer.h | 8 +- include/platform/mir/graphics/linux_dmabuf.h | 57 +- src/platform/graphics/linux_dmabuf.cpp | 668 +++++++++++------- src/platform/symbols.map | 9 + .../gbm-kms/server/buffer_allocator.cpp | 96 ++- .../gbm-kms/server/buffer_allocator.h | 3 + src/platforms/gbm-kms/server/kms/platform.cpp | 5 +- .../renderer-generic-egl/buffer_allocator.cpp | 83 ++- .../renderer-generic-egl/buffer_allocator.h | 1 + .../gbm-kms/kms/test_display_buffer.cpp | 11 +- 10 files changed, 595 insertions(+), 346 deletions(-) diff --git a/include/platform/mir/graphics/dmabuf_buffer.h b/include/platform/mir/graphics/dmabuf_buffer.h index 8ef979f3d3d..347f64aa859 100644 --- a/include/platform/mir/graphics/dmabuf_buffer.h +++ b/include/platform/mir/graphics/dmabuf_buffer.h @@ -21,12 +21,14 @@ #include #include "mir/graphics/buffer.h" +#include "mir/graphics/texture.h" #include "mir/fd.h" namespace mir { namespace graphics { +class DRMFormat; /** * A logical buffer backed by one-or-more dmabuf buffers */ @@ -42,9 +44,9 @@ class DMABufBuffer : public NativeBufferBase virtual ~DMABufBuffer() = default; /** - * The format of this logical buffer, as in + * The format of this logical buffer */ - virtual auto drm_fourcc() const -> uint32_t = 0; + virtual auto format() const -> DRMFormat = 0; /** * The DRM modifier of this logical buffer, if specified. @@ -57,6 +59,8 @@ class DMABufBuffer : public NativeBufferBase virtual auto planes() const -> std::vector const& = 0; + virtual auto layout() const -> gl::Texture::Layout = 0; + virtual auto size() const -> geometry::Size = 0; }; } diff --git a/include/platform/mir/graphics/linux_dmabuf.h b/include/platform/mir/graphics/linux_dmabuf.h index d3a99000d81..5c1645af017 100644 --- a/include/platform/mir/graphics/linux_dmabuf.h +++ b/include/platform/mir/graphics/linux_dmabuf.h @@ -38,35 +38,74 @@ class Context; namespace graphics { +namespace gl +{ +class Texture; +} + namespace common { class EGLContextExecutor; } class DmaBufFormatDescriptors; +class DMABufBuffer; + +class DMABufEGLProvider +{ +public: + DMABufEGLProvider( + EGLDisplay dpy, + std::shared_ptr egl_extensions, + EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext, + std::shared_ptr egl_delegate); + + ~DMABufEGLProvider(); + + auto import_dma_buf( + DMABufBuffer const& dma_buf, + std::function&& on_consumed, + std::function&& on_release) + -> std::shared_ptr; + + /** + * Validate that this provider *can* import this DMA-BUF + * + * @param dma_buf + * \throws EGL exception on failure + */ + void validate_import(DMABufBuffer const& dma_buf); + + auto as_texture( + std::shared_ptr buffer) + -> std::shared_ptr; + + auto supported_formats() const -> DmaBufFormatDescriptors const&; +private: + EGLDisplay const dpy; + std::shared_ptr const egl_extensions; + std::unique_ptr const formats; + std::shared_ptr const egl_delegate; +}; class LinuxDmaBufUnstable : public mir::wayland::LinuxDmabufV1::Global { public: LinuxDmaBufUnstable( wl_display* display, - EGLDisplay dpy, - std::shared_ptr egl_extensions, - EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext); + std::shared_ptr provider); - std::shared_ptr buffer_from_resource( + auto buffer_from_resource( wl_resource* buffer, std::function&& on_consumed, std::function&& on_release, - std::shared_ptr egl_delegate); - + std::shared_ptr egl_delegate) + -> std::shared_ptr; private: class Instance; void bind(wl_resource* new_resource) override; - EGLDisplay const dpy; - std::shared_ptr const egl_extensions; - std::shared_ptr const formats; + std::shared_ptr const provider; }; } diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index 9206faf03cd..9d512cb2323 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -23,13 +23,13 @@ #include "mir/wayland/client.h" #include "mir/graphics/egl_extensions.h" #include "mir/graphics/egl_error.h" -#include "mir/renderer/gl/context.h" #include "mir/graphics/texture.h" #include "mir/graphics/program_factory.h" #include "mir/graphics/buffer.h" #include "mir/graphics/buffer_basic.h" #include "mir/graphics/dmabuf_buffer.h" #include "mir/graphics/egl_context_executor.h" +#include #define MIR_LOG_COMPONENT "linux-dmabuf-import" #include "mir/log.h" @@ -227,18 +227,174 @@ BufferGLDescription const ExternalOES = { "}\n" }; +namespace +{ +struct EGLPlaneAttribs +{ + EGLint fd; + EGLint offset; + EGLint pitch; + EGLint modifier_lo; + EGLint modifier_hi; +}; + +static constexpr std::array egl_attribs = { + EGLPlaneAttribs { + EGL_DMA_BUF_PLANE0_FD_EXT, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, + EGL_DMA_BUF_PLANE0_PITCH_EXT, + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT + }, + EGLPlaneAttribs { + EGL_DMA_BUF_PLANE1_FD_EXT, + EGL_DMA_BUF_PLANE1_OFFSET_EXT, + EGL_DMA_BUF_PLANE1_PITCH_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT + }, + EGLPlaneAttribs { + EGL_DMA_BUF_PLANE2_FD_EXT, + EGL_DMA_BUF_PLANE2_OFFSET_EXT, + EGL_DMA_BUF_PLANE2_PITCH_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT + }, + EGLPlaneAttribs { + EGL_DMA_BUF_PLANE3_FD_EXT, + EGL_DMA_BUF_PLANE3_OFFSET_EXT, + EGL_DMA_BUF_PLANE3_PITCH_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT + } +}; + + +/** + * Reimport dmabufs into EGL + * + * This is necessary to call each time the buffer is re-submitted by the client, + * to ensure any state is properly synchronised. + * + * \return An EGLImageKHR handle to the imported + * \throws A std::system_error containing the EGL error on failure. + */ +auto import_egl_image( + int32_t width, + int32_t height, + mg::DRMFormat format, + std::optional modifier, + std::vector const& planes, + EGLDisplay dpy, + mg::EGLExtensions const& egl_extensions + ) -> EGLImageKHR +{ + std::vector attributes; + + attributes.push_back(EGL_WIDTH); + attributes.push_back(width); + attributes.push_back(EGL_HEIGHT); + attributes.push_back(height); + attributes.push_back(EGL_LINUX_DRM_FOURCC_EXT); + attributes.push_back(format); + + for(auto i = 0u; i < planes.size(); ++i) + { + auto const& attrib_names = egl_attribs[i]; + auto const& plane = planes[i]; + + attributes.push_back(attrib_names.fd); + attributes.push_back(static_cast(plane.dma_buf)); + attributes.push_back(attrib_names.offset); + attributes.push_back(plane.offset); + attributes.push_back(attrib_names.pitch); + attributes.push_back(plane.stride); + if (auto modifier_present = modifier) + { + attributes.push_back(attrib_names.modifier_lo); + attributes.push_back(modifier_present.value() & 0xFFFFFFFF); + attributes.push_back(attrib_names.modifier_hi); + attributes.push_back(modifier_present.value() >> 32); + } + } + attributes.push_back(EGL_NONE); + EGLImage image = egl_extensions.base(dpy).eglCreateImageKHR( + dpy, + EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + nullptr, + attributes.data()); + + if (image == EGL_NO_IMAGE_KHR) + { + auto const msg = planes.size() > 1 ? + "Failed to import supplied dmabufs" : + "Failed to import supplied dmabuf"; + BOOST_THROW_EXCEPTION((mg::egl_error(msg))); + } + + return image; +} + +/** + * Get a description of how to use a specified format/modifier pair in GL + * + * \return A pointer to a statically-allocated descriptor, or nullptr if + * this format/modifier pair is invalid. + */ +BufferGLDescription const* descriptor_for_format_and_modifiers( + mg::DRMFormat format, + uint64_t modifier, + mg::DMABufEGLProvider const& provider) +{ + auto const& formats = provider.supported_formats(); + for (auto i = 0u; i < formats.num_formats(); ++i) + { + auto const& [supported_format, modifiers, external_only] = formats[i]; + + for (auto j = 0u ; j < modifiers.size(); ++j) + { + auto const supported_modifier = modifiers[j]; + + if (static_cast(supported_format) == format && + modifier == supported_modifier) + { + if (external_only[j]) + { + return &ExternalOES; + } + return &Tex2D; + } + } + } + + /* The specification of zwp_linux_buffer_params_v1.add says: + * + * Warning: It should be an error if the format/modifier pair was not + * advertised with the modifier event. This is not enforced yet because + * some implementations always accept `DRM_FORMAT_MOD_INVALID`. Also + * version 2 of this protocol does not have the modifier event. + * + * So don't enforce the error for this case + */ + if (modifier == DRM_FORMAT_MOD_INVALID) + { + return &Tex2D; + } + return nullptr; +} + +} + /** * Holds on to all imported dmabuf buffers, and allows looking up by wl_buffer * * \note This is not threadsafe, and should only be accessed on the Wayland thread */ -class WlDmaBufBuffer : public mir::wayland::Buffer +class WlDmaBufBuffer : public mir::wayland::Buffer, public mir::graphics::DMABufBuffer { public: WlDmaBufBuffer( - EGLDisplay dpy, - std::shared_ptr egl_extensions, - BufferGLDescription const& desc, wl_resource* wl_buffer, int32_t width, int32_t height, @@ -247,39 +403,28 @@ class WlDmaBufBuffer : public mir::wayland::Buffer uint64_t modifier, std::vector plane_params) : Buffer(wl_buffer, Version<1>{}), - dpy{dpy}, - egl_extensions{std::move(egl_extensions)}, - desc{desc}, width{width}, height{height}, format_{format}, flags{flags}, modifier_{modifier}, - planes_{std::move(plane_params)}, - image{EGL_NO_IMAGE_KHR} + planes_{std::move(plane_params)} { - reimport_egl_image(); } - ~WlDmaBufBuffer() - { - if (image != EGL_NO_IMAGE_KHR) - { - egl_extensions->base(dpy).eglDestroyImageKHR(dpy, image); - } - } + ~WlDmaBufBuffer() = default; static auto maybe_dmabuf_from_wl_buffer(wl_resource* buffer) -> WlDmaBufBuffer* { return dynamic_cast(Buffer::from(buffer)); } - auto size() -> geom::Size + auto size() const -> geom::Size override { return {width, height}; } - auto layout() -> mg::gl::Texture::Layout + auto layout() const -> mg::gl::Texture::Layout override { if (flags & mw::LinuxBufferParamsV1::Flags::y_invert) { @@ -291,135 +436,26 @@ class WlDmaBufBuffer : public mir::wayland::Buffer } } - auto format() -> mg::DRMFormat + auto format() const -> mg::DRMFormat override { return format_; } - auto descriptor() const -> BufferGLDescription const& - { - return desc; - } - /** - * Reimport dmabufs into EGL - * - * This is necessary to call each time the buffer is re-submitted by the client, - * to ensure any state is properly synchronised. - * - * \return An EGLImageKHR handle to the imported - * \throws A std::system_error containing the EGL error on failure. - */ - auto reimport_egl_image() -> EGLImageKHR - { - std::vector attributes; - - attributes.push_back(EGL_WIDTH); - attributes.push_back(width); - attributes.push_back(EGL_HEIGHT); - attributes.push_back(height); - attributes.push_back(EGL_LINUX_DRM_FOURCC_EXT); - attributes.push_back(format()); - - for(auto i = 0u; i < planes_.size(); ++i) - { - auto const& attrib_names = egl_attribs[i]; - auto const& plane = planes()[i]; - - attributes.push_back(attrib_names.fd); - attributes.push_back(static_cast(plane.dma_buf)); - attributes.push_back(attrib_names.offset); - attributes.push_back(plane.offset); - attributes.push_back(attrib_names.pitch); - attributes.push_back(plane.stride); - if (modifier() != DRM_FORMAT_MOD_INVALID) - { - attributes.push_back(attrib_names.modifier_lo); - attributes.push_back(modifier() & 0xFFFFFFFF); - attributes.push_back(attrib_names.modifier_hi); - attributes.push_back(modifier() >> 32); - } - } - attributes.push_back(EGL_NONE); - if (image != EGL_NO_IMAGE_KHR) - { - egl_extensions->base(dpy).eglDestroyImageKHR(dpy, image); - } - image = egl_extensions->base(dpy).eglCreateImageKHR( - dpy, - EGL_NO_CONTEXT, - EGL_LINUX_DMA_BUF_EXT, - nullptr, - attributes.data()); - - if (image == EGL_NO_IMAGE_KHR) - { - auto const msg = planes_.size() > 1 ? - "Failed to import supplied dmabufs" : - "Failed to import supplied dmabuf"; - BOOST_THROW_EXCEPTION((mg::egl_error(msg))); - } - - return image; - } - - auto modifier() -> uint64_t + auto modifier() const -> std::optional override { return modifier_; } - auto planes() -> std::vector const& + auto planes() const -> std::vector const& override { return planes_; } private: - EGLDisplay const dpy; - std::shared_ptr const egl_extensions; - BufferGLDescription const& desc; int32_t const width, height; mg::DRMFormat const format_; uint32_t const flags; - uint64_t const modifier_; + std::optional const modifier_; std::vector const planes_; - EGLImageKHR image; - - struct EGLPlaneAttribs - { - EGLint fd; - EGLint offset; - EGLint pitch; - EGLint modifier_lo; - EGLint modifier_hi; - }; - static constexpr std::array egl_attribs = { - EGLPlaneAttribs { - EGL_DMA_BUF_PLANE0_FD_EXT, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, - EGL_DMA_BUF_PLANE0_PITCH_EXT, - EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, - EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT - }, - EGLPlaneAttribs { - EGL_DMA_BUF_PLANE1_FD_EXT, - EGL_DMA_BUF_PLANE1_OFFSET_EXT, - EGL_DMA_BUF_PLANE1_PITCH_EXT, - EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, - EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT - }, - EGLPlaneAttribs { - EGL_DMA_BUF_PLANE2_FD_EXT, - EGL_DMA_BUF_PLANE2_OFFSET_EXT, - EGL_DMA_BUF_PLANE2_PITCH_EXT, - EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, - EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT - }, - EGLPlaneAttribs { - EGL_DMA_BUF_PLANE3_FD_EXT, - EGL_DMA_BUF_PLANE3_OFFSET_EXT, - EGL_DMA_BUF_PLANE3_PITCH_EXT, - EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, - EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT - } - }; }; class LinuxDmaBufParams : public mir::wayland::LinuxBufferParamsV1 @@ -427,14 +463,10 @@ class LinuxDmaBufParams : public mir::wayland::LinuxBufferParamsV1 public: LinuxDmaBufParams( wl_resource* new_resource, - EGLDisplay dpy, - std::shared_ptr egl_extensions, - std::shared_ptr formats) + std::shared_ptr provider) : mir::wayland::LinuxBufferParamsV1(new_resource, Version<3>{}), consumed{false}, - dpy{dpy}, - egl_extensions{std::move(egl_extensions)}, - formats{std::move(formats)} + provider{std::move(provider)} { } @@ -445,9 +477,12 @@ class LinuxDmaBufParams : public mir::wayland::LinuxBufferParamsV1 std::array planes; std::optional modifier; bool consumed; - EGLDisplay dpy; - std::shared_ptr egl_extensions; - std::shared_ptr const formats; + /* We don't *really* need to create an mg::Buffer from here, but we *do* need to validate + * that the dma-buf(s) can be succesfully imported to an EGLImage. + * + * The only way to ensure that is to actually import them. + */ + std::shared_ptr const provider; void add( mir::Fd fd, @@ -555,8 +590,9 @@ class LinuxDmaBufParams : public mir::wayland::LinuxBufferParamsV1 return planes.cbegin() + plane_count; } - void validate_params(int32_t width, int32_t height, uint32_t /*format*/, uint32_t /*flags*/) + void validate_params(int32_t width, int32_t height, uint32_t format, uint32_t /*flags*/) { + // TODO: Validate flags if (width < 1 || height < 1) { BOOST_THROW_EXCEPTION(( @@ -565,61 +601,20 @@ class LinuxDmaBufParams : public mir::wayland::LinuxBufferParamsV1 Error::invalid_dimensions, "Width %i or height %i invalid; both must be >= 1!", width, height})); - - // TODO: Validate format & flags } - } - - BufferGLDescription const& descriptor_for_format_and_modifiers(mg::DRMFormat format) - { - /* The optional modifier is guaranteed to be engaged here, - * as the add() call fills it if it is unset, and validate_and_count_planes() - * has already checked that add() has been called at least once. - */ - auto const requested_modifier = modifier.value(); - for (auto i = 0u; i < formats->num_formats(); ++i) - { - auto const& [supported_format, modifiers, external_only] = (*formats)[i]; - - for (auto j = 0u ; j < modifiers.size(); ++j) - { - auto const supported_modifier = modifiers[j]; - - if (static_cast(supported_format) == format && - requested_modifier == supported_modifier) - { - if (external_only[j]) - { - return ExternalOES; - } - return Tex2D; - } - } - } - - // The specification of zwp_linux_buffer_params_v1.add says: - // - // Warning: It should be an error if the format/modifier pair was not - // advertised with the modifier event. This is not enforced yet because - // some implementations always accept `DRM_FORMAT_MOD_INVALID`. Also - // version 2 of this protocol does not have the modifier event. - // - // So don't enforce the error for this case - if (requested_modifier == DRM_FORMAT_MOD_INVALID) + if (!descriptor_for_format_and_modifiers(mg::DRMFormat{format}, modifier.value(), *provider)) { - return Tex2D; + BOOST_THROW_EXCEPTION(( + mw::ProtocolError{ + resource, + Error::invalid_format, + "Client requested unsupported format/modifier combination %s/%s (%u/%u,%u)", + mg::DRMFormat{format}.name(), + mg::drm_modifier_to_string(*modifier).c_str(), + static_cast(format), + static_cast(*modifier >> 32), + static_cast(*modifier & 0xFFFFFFFF)})); } - - BOOST_THROW_EXCEPTION(( - mw::ProtocolError{ - resource, - Error::invalid_format, - "Client requested unsupported format/modifier combination %s/%s (%u/%u,%u)", - format.name(), - mg::drm_modifier_to_string(requested_modifier).c_str(), - static_cast(format), - static_cast(requested_modifier >> 32), - static_cast(requested_modifier & 0xFFFFFFFF)})); } void create(int32_t width, int32_t height, uint32_t format, uint32_t flags) override @@ -638,10 +633,7 @@ class LinuxDmaBufParams : public mir::wayland::LinuxBufferParamsV1 auto const last_valid_plane = validate_and_count_planes(); mg::DRMFormat const drm_format{format}; - new WlDmaBufBuffer{ - dpy, - egl_extensions, - descriptor_for_format_and_modifiers(drm_format), + auto dma_buf = new WlDmaBufBuffer{ buffer_resource, width, height, @@ -649,6 +641,10 @@ class LinuxDmaBufParams : public mir::wayland::LinuxBufferParamsV1 flags, modifier.value(), {planes.cbegin(), last_valid_plane}}; + + // We don't need to keep it around, but we do need to ensure that we *can* create a Buffer + // from this dma-buf + provider->validate_import(*dma_buf); send_created_event(buffer_resource); } catch (std::system_error const& err) @@ -681,10 +677,7 @@ class LinuxDmaBufParams : public mir::wayland::LinuxBufferParamsV1 auto const last_valid_plane = validate_and_count_planes(); mg::DRMFormat const drm_format{format}; - new WlDmaBufBuffer{ - dpy, - egl_extensions, - descriptor_for_format_and_modifiers(drm_format), + auto dma_buf = new WlDmaBufBuffer{ buffer_id, width, height, @@ -692,6 +685,9 @@ class LinuxDmaBufParams : public mir::wayland::LinuxBufferParamsV1 flags, modifier.value(), {planes.cbegin(), last_valid_plane}}; + // We don't need to keep it around, but we do need to ensure that we *can* create a Buffer + // from this dma-buf + provider->validate_import(*dma_buf); } catch (std::system_error const& err) { @@ -723,40 +719,39 @@ GLuint get_tex_id() return tex; } -class WaylandDmabufTexBuffer : - public mg::BufferBasic, - public mg::gl::Texture, - public mg::DMABufBuffer +class DMABufTex : public mg::gl::Texture { public: - // Note: Must be called with a current EGL context - WaylandDmabufTexBuffer( - WlDmaBufBuffer& source, - mg::EGLExtensions const& extensions, + DMABufTex( EGLDisplay dpy, - std::shared_ptr egl_delegate, - std::function&& on_consumed, - std::function&& on_release) + mg::EGLExtensions const& extensions, + mg::DMABufBuffer const& dma_buf, + BufferGLDescription const& descriptor, + std::shared_ptr egl_delegate) : tex{get_tex_id()}, - desc{source.descriptor()}, - on_consumed{std::move(on_consumed)}, - on_release{std::move(on_release)}, - size_{source.size()}, - layout_{source.layout()}, - has_alpha{source.format().has_alpha()}, - planes_{source.planes()}, - modifier_{source.modifier()}, - format{source.format()}, + desc{descriptor}, + layout_{dma_buf.layout()}, egl_delegate{std::move(egl_delegate)} { eglBindAPI(EGL_OPENGL_ES_API); - auto const target = source.descriptor().target; + auto const target = descriptor.target; + + EGLImage image = import_egl_image( + dma_buf.size().width.as_int(), + dma_buf.size().height.as_int(), + dma_buf.format(), + dma_buf.modifier(), + dma_buf.planes(), + dpy, + extensions); glBindTexture(target, tex); - extensions.base(dpy).glEGLImageTargetTexture2DOES(target, source.reimport_egl_image()); + extensions.base(dpy).glEGLImageTargetTexture2DOES(target, image); + // tex is now an EGLImage sibling, so we can free the EGLImage without // freeing the backing data. + extensions.base(dpy).eglDestroyImageKHR(dpy, image); glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -764,17 +759,84 @@ class WaylandDmabufTexBuffer : glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - ~WaylandDmabufTexBuffer() override + ~DMABufTex() override { egl_delegate->spawn( [tex = tex]() { - glDeleteTextures(1, &tex); + glDeleteTextures(1, &tex); }); + } + + mir::graphics::gl::Program const& shader(mir::graphics::gl::ProgramFactory& cache) const override + { + /* We rely on the fact that `desc` is a reference to a statically-allocated namespaced + * variable, and so taking the address will give us the address of the static instance, + * making the cache compile only once for each `desc`. + */ + return cache.compile_fragment_shader( + &desc, + desc.extension_fragment, + desc.fragment_fragment); + } + + Layout layout() const override + { + return layout_; + } + + void bind() override + { + glBindTexture(desc.target, tex); + } + + void add_syncpoint() override + { + } +private: + GLuint const tex; + BufferGLDescription const& desc; + Layout const layout_; + std::shared_ptr const egl_delegate; +}; + +class DmabufTexBuffer : + public mg::BufferBasic, + public mg::gl::Texture, + public mg::DMABufBuffer +{ +public: + // Note: Must be called with a current EGL context + DmabufTexBuffer( + EGLDisplay dpy, + mg::EGLExtensions const& extensions, + mg::DMABufBuffer const& dma_buf, + BufferGLDescription const& descriptor, + std::shared_ptr egl_delegate, + std::function&& on_consumed, + std::function&& on_release) + : dpy{dpy}, + tex{dpy, extensions, dma_buf, descriptor, std::move(egl_delegate)}, + on_consumed{std::move(on_consumed)}, + on_release{std::move(on_release)}, + size_{dma_buf.size()}, + has_alpha{dma_buf.format().has_alpha()}, + planes_{dma_buf.planes()}, + modifier_{dma_buf.modifier()}, + format_{dma_buf.format()} + { + } + ~DmabufTexBuffer() override + { on_release(); } + auto on_same_egl_display(EGLDisplay dpy) -> bool + { + return this->dpy == dpy; + } + mir::geometry::Size size() const override { return size_; @@ -782,7 +844,7 @@ class WaylandDmabufTexBuffer : MirPixelFormat pixel_format() const override { - if (auto mir_format = format.as_mir_format()) + if (auto mir_format = format_.as_mir_format()) { return mir_format.value(); } @@ -802,26 +864,20 @@ class WaylandDmabufTexBuffer : return this; } - mir::graphics::gl::Program const& shader(mir::graphics::gl::ProgramFactory& cache) const override + auto shader(mir::graphics::gl::ProgramFactory& cache) const + -> mg::gl::Program const& override { - /* We rely on the fact that `desc` is a reference to a statically-allocated namespaced - * variable, and so taking the address will give us the address of the static instance, - * making the cache compile only once for each `desc`. - */ - return cache.compile_fragment_shader( - &desc, - desc.extension_fragment, - desc.fragment_fragment); + return tex.shader(cache); } Layout layout() const override { - return layout_; + return tex.layout(); } void bind() override { - glBindTexture(desc.target, tex); + tex.bind(); std::lock_guard lock(consumed_mutex); on_consumed(); @@ -830,11 +886,12 @@ class WaylandDmabufTexBuffer : void add_syncpoint() override { + tex.add_syncpoint(); } - auto drm_fourcc() const -> uint32_t override + auto format() const ->mg::DRMFormat override { - return format; + return format_; } auto modifier() const -> std::optional override @@ -848,22 +905,19 @@ class WaylandDmabufTexBuffer : } private: - GLuint const tex; - BufferGLDescription const& desc; + EGLDisplay const dpy; + DMABufTex tex; std::mutex consumed_mutex; std::function on_consumed; std::function const on_release; geom::Size const size_; - Layout const layout_; bool const has_alpha; std::vector const planes_; std::optional const modifier_; - mg::DRMFormat const format; - - std::shared_ptr const egl_delegate; + mg::DRMFormat const format_; }; @@ -874,17 +928,14 @@ class mg::LinuxDmaBufUnstable::Instance : public mir::wayland::LinuxDmabufV1 public: Instance( wl_resource* new_resource, - EGLDisplay dpy, - std::shared_ptr egl_extensions, - std::shared_ptr formats) + std::shared_ptr provider) : mir::wayland::LinuxDmabufV1(new_resource, Version<3>{}), - dpy{dpy}, - egl_extensions{std::move(egl_extensions)}, - formats{std::move(formats)} + provider{std::move(provider)} { - for (auto i = 0u; i < this->formats->num_formats(); ++i) + auto const& formats = this->provider->supported_formats(); + for (auto i = 0u; i < formats.num_formats(); ++i) { - auto [format, modifiers, external_only] = (*(this->formats))[i]; + auto [format, modifiers, external_only] = formats[i]; send_format_event(format); for (auto j = 0u; j < modifiers.size(); ++j) @@ -900,23 +951,17 @@ class mg::LinuxDmaBufUnstable::Instance : public mir::wayland::LinuxDmabufV1 private: void create_params(struct wl_resource* params_id) override { - new LinuxDmaBufParams{params_id, dpy, egl_extensions, formats}; + new LinuxDmaBufParams{params_id, provider}; } - EGLDisplay const dpy; - std::shared_ptr const egl_extensions; - std::shared_ptr const formats; + std::shared_ptr const provider; }; mg::LinuxDmaBufUnstable::LinuxDmaBufUnstable( wl_display* display, - EGLDisplay dpy, - std::shared_ptr egl_extensions, - EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext) + std::shared_ptr provider) : mir::wayland::LinuxDmabufV1::Global(display, Version<3>{}), - dpy{dpy}, - egl_extensions{std::move(egl_extensions)}, - formats{std::make_shared(dpy, dmabuf_ext)} + provider{std::move(provider)} { } @@ -924,16 +969,13 @@ auto mg::LinuxDmaBufUnstable::buffer_from_resource( wl_resource* buffer, std::function&& on_consumed, std::function&& on_release, - std::shared_ptr egl_delegate) + std::shared_ptr /*egl_delegate*/) -> std::shared_ptr { if (auto dmabuf = WlDmaBufBuffer::maybe_dmabuf_from_wl_buffer(buffer)) { - return std::make_shared( + return provider->import_dma_buf( *dmabuf, - *egl_extensions, - dpy, - std::move(egl_delegate), std::move(on_consumed), std::move(on_release)); } @@ -942,5 +984,89 @@ auto mg::LinuxDmaBufUnstable::buffer_from_resource( void mg::LinuxDmaBufUnstable::bind(wl_resource* new_resource) { - new LinuxDmaBufUnstable::Instance{new_resource, dpy, egl_extensions, formats}; + new LinuxDmaBufUnstable::Instance{new_resource, provider}; +} + +mg::DMABufEGLProvider::DMABufEGLProvider( + EGLDisplay dpy, + std::shared_ptr egl_extensions, + mg::EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext, + std::shared_ptr egl_delegate) + : dpy{dpy}, + egl_extensions{std::move(egl_extensions)}, + formats{std::make_unique(dpy, dmabuf_ext)}, + egl_delegate{std::move(egl_delegate)} +{ +} + +mg::DMABufEGLProvider::~DMABufEGLProvider() = default; + +auto mg::DMABufEGLProvider::supported_formats() const -> mg::DmaBufFormatDescriptors const& +{ + return *formats; +} + +auto mg::DMABufEGLProvider::import_dma_buf( + mg::DMABufBuffer const& dma_buf, + std::function&& on_consumed, + std::function&& on_release) -> std::shared_ptr +{ + // This will have been pre-validated at point the Wayland client created the buffer + auto descriptor = descriptor_for_format_and_modifiers( + dma_buf.format(), + dma_buf.modifier().value_or(DRM_FORMAT_MOD_INVALID), + *this); + return std::make_shared( + dpy, + *egl_extensions, + dma_buf, + *descriptor, + egl_delegate, + std::move(on_consumed), + std::move(on_release)); +} + +void mg::DMABufEGLProvider::validate_import(DMABufBuffer const& dma_buf) +{ + auto image = import_egl_image( + dma_buf.size().width.as_int(), dma_buf.size().height.as_int(), + dma_buf.format(), + dma_buf.modifier(), + dma_buf.planes(), + dpy, + *egl_extensions); + if (image != EGL_NO_IMAGE_KHR) + { + // We can throw this image away immediately + egl_extensions->base(dpy).eglDestroyImageKHR(dpy, image); + } +} + +auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) + -> std::shared_ptr +{ + if (auto dmabuf_tex = std::dynamic_pointer_cast(buffer)) + { + if (dmabuf_tex->on_same_egl_display(dpy)) + { + return dmabuf_tex; + } + else if (auto descriptor = descriptor_for_format_and_modifiers( + dmabuf_tex->format(), + dmabuf_tex->modifier().value_or(DRM_FORMAT_MOD_INVALID), + *this)) + { + return std::make_shared( + dpy, + *egl_extensions, + *dmabuf_tex, + *descriptor, + egl_delegate); + } + else + { + mir::log_warning("Failed to import cross-GPU dma-buf for rendering"); + } + } + return nullptr; } diff --git a/src/platform/symbols.map b/src/platform/symbols.map index 4961ca78429..591ac9e2f1d 100644 --- a/src/platform/symbols.map +++ b/src/platform/symbols.map @@ -227,3 +227,12 @@ MIR_PLATFORM_2.13 { mir::graphics::DRMFormat::from_mir_format*; }; } MIR_PLATFORM_2.11; + +MIR_PLATFORM_2.16 { + global: + extern "C++" { + mir::graphics::DMABufEGLProvider::DMABufEGLProvider*; + mir::graphics::DMABufEGLProvider::?DMABufEGLProvider*; + mir::graphics::DMABufEGLProvider::as_texture*; + }; +} MIR_PLATFORM_2.13; diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index e81992aab8b..c4480ac13c2 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -17,6 +17,7 @@ #include "buffer_allocator.h" #include "mir/graphics/gl_config.h" #include "mir/graphics/linux_dmabuf.h" +#include "mir/graphics/dmabuf_buffer.h" #include "mir/anonymous_shm_file.h" #include "mir/renderer/sw/pixel_source.h" #include "mir/graphics/platform.h" @@ -147,13 +148,38 @@ class SurfacelessEGLContext : public mir::renderer::gl::Context EGLDisplay const dpy; EGLContext const ctx; }; + +auto maybe_make_dmabuf_provider( + EGLDisplay dpy, + std::shared_ptr egl_extensions, + std::shared_ptr egl_delegate) + -> std::shared_ptr +{ + try + { + mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; + return std::make_shared(dpy, std::move(egl_extensions), modifier_ext, std::move(egl_delegate)); + } + catch (std::runtime_error const& error) + { + mir::log_info( + "Cannot enable linux-dmabuf import support: %s", error.what()); + mir::log( + mir::logging::Severity::debug, + MIR_LOG_COMPONENT, + std::current_exception(), + "Detailed error: "); + } + return nullptr; +} } mgg::BufferAllocator::BufferAllocator(EGLDisplay dpy, EGLContext share_with) : ctx{std::make_unique(dpy, share_with)}, egl_delegate{ std::make_shared(ctx->make_share_context())}, - egl_extensions(std::make_shared()) + egl_extensions(std::make_shared()), + dmabuf_provider{maybe_make_dmabuf_provider(dpy, egl_extensions, egl_delegate)} { } @@ -219,34 +245,35 @@ void mgg::BufferAllocator::bind_display(wl_display* display, std::shared_ptr>( - new LinuxDmaBufUnstable{ - display, - dpy, - egl_extensions, - modifier_ext, - }, - [wayland_executor](LinuxDmaBufUnstable* global) - { - // The global must be destroyed on the Wayland thread - wayland_executor->spawn( - [global]() - { - /* This is safe against double-frees, as the WaylandExecutor - * guarantees that work scheduled will only run while the Wayland - * event loop is running, and the main loop is stopped before - * wl_display_destroy() frees any globals - * - * This will, however, leak the global if the main loop is destroyed - * before the buffer allocator. Fixing that requires work in the - * wrapper generator. - */ - delete global; - }); - }); - mir::log_info("Enabled linux-dmabuf import support"); + if (dmabuf_provider) + { + mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; + dmabuf_extension = + std::unique_ptr>( + new LinuxDmaBufUnstable{ + display, + dmabuf_provider, + }, + [wayland_executor](LinuxDmaBufUnstable* global) + { + // The global must be destroyed on the Wayland thread + wayland_executor->spawn( + [global]() + { + /* This is safe against double-frees, as the WaylandExecutor + * guarantees that work scheduled will only run while the Wayland + * event loop is running, and the main loop is stopped before + * wl_display_destroy() frees any globals + * + * This will, however, leak the global if the main loop is destroyed + * before the buffer allocator. Fixing that requires work in the + * wrapper generator. + */ + delete global; + }); + }); + mir::log_info("Enabled linux-dmabuf import support"); + } } catch (std::runtime_error const& error) { @@ -319,6 +346,10 @@ auto mgg::BufferAllocator::shared_egl_context() -> EGLContext auto mgg::GLRenderingProvider::as_texture(std::shared_ptr buffer) -> std::shared_ptr { + if (auto dmabuf_texture = dmabuf_provider->as_texture(buffer)) + { + return dmabuf_texture; + } return std::dynamic_pointer_cast(buffer); } @@ -618,6 +649,11 @@ mgg::GLRenderingProvider::GLRenderingProvider( EGLContext ctx) : bound_display{std::move(associated_display)}, dpy{dpy}, - ctx{ctx} + ctx{ctx}, + egl_delegate{ + std::make_shared( + std::make_unique( + dpy, + make_share_only_context(dpy, ctx)))} { } diff --git a/src/platforms/gbm-kms/server/buffer_allocator.h b/src/platforms/gbm-kms/server/buffer_allocator.h index a88d0089912..a3972061e2c 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.h +++ b/src/platforms/gbm-kms/server/buffer_allocator.h @@ -77,6 +77,7 @@ class BufferAllocator: std::shared_ptr wayland_executor; std::unique_ptr> dmabuf_extension; std::shared_ptr const egl_extensions; + std::shared_ptr const dmabuf_provider; bool egl_display_bound{false}; }; @@ -104,6 +105,8 @@ class GLRenderingProvider : public graphics::GLRenderingProvider std::shared_ptr const bound_display; ///< Associated Display provider (if any - null is valid) EGLDisplay const dpy; EGLContext const ctx; + std::shared_ptr const dmabuf_provider; + std::shared_ptr const egl_delegate; }; } } diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index f0cb67e0ad2..10c416b6d69 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -246,7 +246,10 @@ auto mgg::RenderingPlatform::maybe_create_interface( { if (dynamic_cast(&type_tag)) { - return std::make_shared(bound_display, dpy, share_ctx); + return std::make_shared( + bound_display, + dpy, + share_ctx); } return nullptr; } diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index a6d4e78a90c..03ebe202664 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -147,13 +147,38 @@ class SurfacelessEGLContext : public mir::renderer::gl::Context EGLDisplay const dpy; EGLContext const ctx; }; + +auto maybe_make_dmabuf_provider( + EGLDisplay dpy, + std::shared_ptr egl_extensions, + std::shared_ptr egl_delegate) + -> std::shared_ptr +{ + try + { + mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; + return std::make_shared(dpy, std::move(egl_extensions), modifier_ext, std::move(egl_delegate)); + } + catch (std::runtime_error const& error) + { + mir::log_info( + "Cannot enable linux-dmabuf import support: %s", error.what()); + mir::log( + mir::logging::Severity::debug, + MIR_LOG_COMPONENT, + std::current_exception(), + "Detailed error: "); + } + return nullptr; +} } mge::BufferAllocator::BufferAllocator(EGLDisplay dpy, EGLContext share_with) : ctx{std::make_unique(dpy, share_with)}, egl_delegate{ std::make_shared(ctx->make_share_context())}, - egl_extensions(std::make_shared()) + egl_extensions(std::make_shared()), + dmabuf_provider{maybe_make_dmabuf_provider(dpy, egl_extensions, egl_delegate)} { } @@ -221,34 +246,34 @@ void mge::BufferAllocator::bind_display(wl_display* display, std::shared_ptr>( - new LinuxDmaBufUnstable{ - display, - dpy, - egl_extensions, - modifier_ext, - }, - [wayland_executor](LinuxDmaBufUnstable* global) - { - // The global must be destroyed on the Wayland thread - wayland_executor->spawn( - [global]() - { - /* This is safe against double-frees, as the WaylandExecutor - * guarantees that work scheduled will only run while the Wayland - * event loop is running, and the main loop is stopped before - * wl_display_destroy() frees any globals - * - * This will, however, leak the global if the main loop is destroyed - * before the buffer allocator. Fixing that requires work in the - * wrapper generator. - */ - delete global; - }); - }); - mir::log_info("Enabled linux-dmabuf import support"); + if (dmabuf_provider) + { + dmabuf_extension = + std::unique_ptr>( + new LinuxDmaBufUnstable{ + display, + dmabuf_provider + }, + [wayland_executor](LinuxDmaBufUnstable* global) + { + // The global must be destroyed on the Wayland thread + wayland_executor->spawn( + [global]() + { + /* This is safe against double-frees, as the WaylandExecutor + * guarantees that work scheduled will only run while the Wayland + * event loop is running, and the main loop is stopped before + * wl_display_destroy() frees any globals + * + * This will, however, leak the global if the main loop is destroyed + * before the buffer allocator. Fixing that requires work in the + * wrapper generator. + */ + delete global; + }); + }); + mir::log_info("Enabled linux-dmabuf import support"); + } } catch (std::runtime_error const& error) { diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.h b/src/platforms/renderer-generic-egl/buffer_allocator.h index 5f278bfdcd4..8acf8e40a03 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.h +++ b/src/platforms/renderer-generic-egl/buffer_allocator.h @@ -78,6 +78,7 @@ class BufferAllocator: std::shared_ptr wayland_executor; std::unique_ptr> dmabuf_extension; std::shared_ptr const egl_extensions; + std::shared_ptr const dmabuf_provider; bool egl_display_bound{false}; }; diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp index 358e4a9fc7d..7b89dd281da 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_buffer.cpp @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include "mir/graphics/texture.h" #include "mir/test/doubles/null_emergency_cleanup.h" #include "src/server/report/null_report_factory.h" #include "src/platforms/gbm-kms/server/kms/platform.h" @@ -49,10 +50,12 @@ namespace class MockDMABufBuffer : public DMABufBuffer { public: - MOCK_CONST_METHOD0(drm_fourcc, uint32_t()); - MOCK_CONST_METHOD0(modifier, std::optional()); - MOCK_CONST_METHOD0(planes, std::vector const&()); - MOCK_CONST_METHOD0(size, geometry::Size()); + MOCK_METHOD(uint32_t, drm_fourcc, (), (const override)); + MOCK_METHOD(std::optional, modifier, (), (const override)); + MOCK_METHOD(std::vector const&, planes, (), (const override)); + MOCK_METHOD(geometry::Size, size, (), (const override)); + MOCK_METHOD(gl::Texture::Layout, layout, (), (const override)); + MOCK_METHOD(DRMFormat, format, (), (const override)); }; class MockKMSFramebuffer : public FBHandle From 12163f85211c7b7a56e98ea8bc1d69a652a07ee4 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 12 Sep 2023 18:31:29 +1000 Subject: [PATCH 056/108] platform/DMA-buf: Ensure on_consumed is triggered For cross-GPU buffers we will not call `bind()` on the original buffer, so we can't use that as the trigger for `on_consumed()`. Instead, re-jig things so the call to `as_texture()` is the trigger for `on_consumed()`. --- src/platform/graphics/linux_dmabuf.cpp | 39 ++++++++++++-------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index 9d512cb2323..a49a424ad4c 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -802,7 +802,6 @@ class DMABufTex : public mg::gl::Texture class DmabufTexBuffer : public mg::BufferBasic, - public mg::gl::Texture, public mg::DMABufBuffer { public: @@ -864,29 +863,17 @@ class DmabufTexBuffer : return this; } - auto shader(mir::graphics::gl::ProgramFactory& cache) const - -> mg::gl::Program const& override + auto as_texture() -> DMABufTex* { - return tex.shader(cache); - } - - Layout layout() const override - { - return tex.layout(); - } - - void bind() override - { - tex.bind(); - - std::lock_guard lock(consumed_mutex); + /* We only get asked for a texture when the Renderer is about to + * texture from this buffer; it's a good indication that the buffer + * has been consumed. + */ + std::lock_guard lock{consumed_mutex}; on_consumed(); on_consumed = [](){}; - } - void add_syncpoint() override - { - tex.add_syncpoint(); + return &tex; } auto format() const ->mg::DRMFormat override @@ -904,6 +891,11 @@ class DmabufTexBuffer : return planes_; } + auto layout() const -> mg::gl::Texture::Layout override + { + return tex.layout(); + } + private: EGLDisplay const dpy; DMABufTex tex; @@ -1049,13 +1041,18 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) { if (dmabuf_tex->on_same_egl_display(dpy)) { - return dmabuf_tex; + auto tex = dmabuf_tex->as_texture(); + return std::shared_ptr(std::move(dmabuf_tex), tex); } else if (auto descriptor = descriptor_for_format_and_modifiers( dmabuf_tex->format(), dmabuf_tex->modifier().value_or(DRM_FORMAT_MOD_INVALID), *this)) { + /* We're being naughty here and using the fact that `as_texture()` has a side-effect + * of invoking the buffer's `on_consumed()` callback. + */ + dmabuf_tex->as_texture(); return std::make_shared( dpy, *egl_extensions, From 54ce2d22f611bfdd4911a9331ca42dd98ea3519d Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 12 Sep 2023 18:31:56 +1000 Subject: [PATCH 057/108] platform/DMA-buf: Tiny style --- src/platform/graphics/linux_dmabuf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index a49a424ad4c..94f90f17627 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -768,7 +768,7 @@ class DMABufTex : public mg::gl::Texture }); } - mir::graphics::gl::Program const& shader(mir::graphics::gl::ProgramFactory& cache) const override + mg::gl::Program const& shader(mg::gl::ProgramFactory& cache) const override { /* We rely on the fact that `desc` is a reference to a statically-allocated namespaced * variable, and so taking the address will give us the address of the static instance, From 3a226de9f590fb791a4ef1edce765dd8fa415c77 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 14 Sep 2023 18:08:56 +1000 Subject: [PATCH 058/108] platforms/gbm-kms: Oops. Actually hook up DMABufEGLProvider. It turns out that just having a `shared_ptr` isn't enough, you need to actually initialise it! Who knew! --- src/platforms/gbm-kms/server/CMakeLists.txt | 2 + .../gbm-kms/server/buffer_allocator.cpp | 136 +++--------------- .../gbm-kms/server/buffer_allocator.h | 10 +- src/platforms/gbm-kms/server/kms/platform.cpp | 76 +++++----- src/platforms/gbm-kms/server/kms/platform.h | 15 +- .../server/surfaceless_egl_context.cpp | 106 ++++++++++++++ .../gbm-kms/server/surfaceless_egl_context.h | 43 ++++++ 7 files changed, 229 insertions(+), 159 deletions(-) create mode 100644 src/platforms/gbm-kms/server/surfaceless_egl_context.cpp create mode 100644 src/platforms/gbm-kms/server/surfaceless_egl_context.h diff --git a/src/platforms/gbm-kms/server/CMakeLists.txt b/src/platforms/gbm-kms/server/CMakeLists.txt index fbef43749ed..3122c903661 100644 --- a/src/platforms/gbm-kms/server/CMakeLists.txt +++ b/src/platforms/gbm-kms/server/CMakeLists.txt @@ -6,6 +6,8 @@ add_library( display_helpers.cpp buffer_allocator.cpp buffer_allocator.h + surfaceless_egl_context.h + surfaceless_egl_context.cpp ) target_include_directories( diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index c4480ac13c2..d7baeeb3b55 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -39,6 +39,7 @@ #include "display_helpers.h" #include "mir/graphics/egl_error.h" #include "cpu_copy_output_surface.h" +#include "surfaceless_egl_context.h" #include #include @@ -66,120 +67,14 @@ namespace mgg = mg::gbm; namespace mgc = mg::common; namespace geom = mir::geometry; -namespace -{ -auto make_share_only_context(EGLDisplay dpy, std::optional share_with) -> EGLContext -{ - eglBindAPI(EGL_OPENGL_ES_API); - - static const EGLint context_attr[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - EGLint const config_attr[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - EGLConfig cfg; - 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"))); - } - - auto ctx = eglCreateContext(dpy, cfg, share_with.value_or(EGL_NO_CONTEXT), context_attr); - if (ctx == EGL_NO_CONTEXT) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); - } - return ctx; -} - -class SurfacelessEGLContext : public mir::renderer::gl::Context -{ -public: - SurfacelessEGLContext(EGLDisplay dpy) - : dpy{dpy}, - ctx{make_share_only_context(dpy, {})} - { - } - - SurfacelessEGLContext(EGLDisplay dpy, EGLContext share_with) - : dpy{dpy}, - ctx{make_share_only_context(dpy, share_with)} - { - } - - ~SurfacelessEGLContext() override - { - 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 override - { - return std::unique_ptr{new SurfacelessEGLContext{dpy, ctx}}; - } - - explicit operator EGLContext() override - { - return ctx; - } -private: - EGLDisplay const dpy; - EGLContext const ctx; -}; - -auto maybe_make_dmabuf_provider( - EGLDisplay dpy, - std::shared_ptr egl_extensions, - std::shared_ptr egl_delegate) - -> std::shared_ptr -{ - try - { - mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; - return std::make_shared(dpy, std::move(egl_extensions), modifier_ext, std::move(egl_delegate)); - } - catch (std::runtime_error const& error) - { - mir::log_info( - "Cannot enable linux-dmabuf import support: %s", error.what()); - mir::log( - mir::logging::Severity::debug, - MIR_LOG_COMPONENT, - std::current_exception(), - "Detailed error: "); - } - return nullptr; -} -} - -mgg::BufferAllocator::BufferAllocator(EGLDisplay dpy, EGLContext share_with) - : ctx{std::make_unique(dpy, share_with)}, - egl_delegate{ - std::make_shared(ctx->make_share_context())}, +mgg::BufferAllocator::BufferAllocator( + std::unique_ptr context, + std::shared_ptr egl_delegate, + std::shared_ptr dmabuf_provider) + : ctx{std::move(context)}, + egl_delegate{std::move(egl_delegate)}, egl_extensions(std::make_shared()), - dmabuf_provider{maybe_make_dmabuf_provider(dpy, egl_extensions, egl_delegate)} + dmabuf_provider{std::move(dmabuf_provider)} { } @@ -350,7 +245,11 @@ auto mgg::GLRenderingProvider::as_texture(std::shared_ptr buffer) -> std { return dmabuf_texture; } - return std::dynamic_pointer_cast(buffer); + else if (auto tex = std::dynamic_pointer_cast(buffer)) + { + return tex; + } + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to import buffer as texture; rendering will be incomplete"})); } namespace @@ -645,15 +544,14 @@ auto mgg::GLRenderingProvider::make_framebuffer_provider(std::shared_ptr associated_display, + std::shared_ptr egl_delegate, + std::shared_ptr dmabuf_provider, EGLDisplay dpy, EGLContext ctx) : bound_display{std::move(associated_display)}, dpy{dpy}, ctx{ctx}, - egl_delegate{ - std::make_shared( - std::make_unique( - dpy, - make_share_only_context(dpy, ctx)))} + dmabuf_provider{std::move(dmabuf_provider)}, + egl_delegate{std::move(egl_delegate)} { } diff --git a/src/platforms/gbm-kms/server/buffer_allocator.h b/src/platforms/gbm-kms/server/buffer_allocator.h index a3972061e2c..2feef0e761d 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.h +++ b/src/platforms/gbm-kms/server/buffer_allocator.h @@ -49,13 +49,17 @@ namespace gbm { class GLRenderingProvider; +class SurfacelessEGLContext; class BufferAllocator: public graphics::GraphicBufferAllocator { public: - BufferAllocator(EGLDisplay dpy, EGLContext share_with); - + BufferAllocator( + std::unique_ptr ctx, + std::shared_ptr egl_delegate, + std::shared_ptr dmabuf_provider); + std::shared_ptr alloc_software_buffer(geometry::Size size, MirPixelFormat) override; std::vector supported_pixel_formats() override; @@ -86,6 +90,8 @@ class GLRenderingProvider : public graphics::GLRenderingProvider public: GLRenderingProvider( std::shared_ptr associated_display, + std::shared_ptr egl_delegate, + std::shared_ptr dmabuf_provider, EGLDisplay dpy, EGLContext ctx); diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 10c416b6d69..22a77a25d6a 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -19,6 +19,7 @@ #include "display.h" #include "mir/console_services.h" #include "mir/emergency_cleanup_registry.h" +#include "mir/graphics/egl_context_executor.h" #include "mir/graphics/platform.h" #include "mir/renderer/gl/context.h" #include "mir/udev/wrapper.h" @@ -26,6 +27,9 @@ #include "mir/graphics/egl_error.h" #include "mir/graphics/egl_extensions.h" #include "one_shot_device_observer.h" +#include "mir/graphics/linux_dmabuf.h" +#include "mir/graphics/egl_context_executor.h" +#include "surfaceless_egl_context.h" #include #define MIR_LOG_COMPONENT "platform-graphics-gbm-kms" @@ -174,37 +178,6 @@ auto dpy_for_gbm_device(gbm_device* device) -> EGLDisplay return egl_display; } -auto make_share_only_context(EGLDisplay dpy) -> EGLContext -{ - eglBindAPI(EGL_OPENGL_ES_API); - - static const EGLint context_attr[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - EGLint const config_attr[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - EGLConfig cfg; - 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"))); - } - - auto ctx = eglCreateContext(dpy, cfg, EGL_NO_CONTEXT, context_attr); - if (ctx == EGL_NO_CONTEXT) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); - } - return ctx; -} - struct display_provider_or_nothing { auto operator()(std::shared_ptr provider) { return provider; } @@ -216,6 +189,30 @@ struct gbm_device_from_hw auto operator()(std::shared_ptr const& provider) { return provider->gbm_device(); } auto operator()(std::shared_ptr device) { return device; } }; + +auto maybe_make_dmabuf_provider( + EGLDisplay dpy, + std::shared_ptr egl_extensions, + std::shared_ptr egl_delegate) + -> std::shared_ptr +{ + try + { + mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; + return std::make_shared(dpy, std::move(egl_extensions), modifier_ext, std::move(egl_delegate)); + } + catch (std::runtime_error const& error) + { + mir::log_info( + "Cannot enable linux-dmabuf import support: %s", error.what()); + mir::log( + mir::logging::Severity::debug, + MIR_LOG_COMPONENT, + std::current_exception(), + "Detailed error: "); + } + return nullptr; +} } mgg::RenderingPlatform::RenderingPlatform( @@ -229,16 +226,21 @@ mgg::RenderingPlatform::RenderingPlatform( std::variant, std::shared_ptr> hw) : device{std::visit(gbm_device_from_hw{}, hw)}, bound_display{std::visit(display_provider_or_nothing{}, hw)}, - dpy{initialise_egl(dpy_for_gbm_device(device.get()), 1, 4)}, - share_ctx{make_share_only_context(dpy)} + share_ctx{std::make_unique(initialise_egl(dpy_for_gbm_device(device.get()), 1, 4))}, + egl_delegate{std::make_shared(share_ctx->make_share_context())}, + dmabuf_provider{maybe_make_dmabuf_provider(share_ctx->egl_display(), std::make_shared(), egl_delegate)} { } +mgg::RenderingPlatform::~RenderingPlatform() = default; mir::UniqueModulePtr mgg::RenderingPlatform::create_buffer_allocator( mg::Display const&) { - return make_module_ptr(dpy, share_ctx); + return make_module_ptr( + std::make_unique(share_ctx->egl_display(), static_cast(*share_ctx)), + egl_delegate, + dmabuf_provider); } auto mgg::RenderingPlatform::maybe_create_interface( @@ -248,8 +250,10 @@ auto mgg::RenderingPlatform::maybe_create_interface( { return std::make_shared( bound_display, - dpy, - share_ctx); + egl_delegate, + dmabuf_provider, + share_ctx->egl_display(), + static_cast(*share_ctx)); } return nullptr; } diff --git a/src/platforms/gbm-kms/server/kms/platform.h b/src/platforms/gbm-kms/server/kms/platform.h index 70c845d6a5d..97982318074 100644 --- a/src/platforms/gbm-kms/server/kms/platform.h +++ b/src/platforms/gbm-kms/server/kms/platform.h @@ -31,10 +31,18 @@ class ConsoleServices; namespace graphics { +class DMABufEGLProvider; + +namespace common +{ +class EGLContextExecutor; +} + namespace gbm { class Quirks; +class SurfacelessEGLContext; class Platform : public graphics::DisplayPlatform { @@ -83,6 +91,8 @@ class RenderingPlatform : public graphics::RenderingPlatform udev::Device const& device, std::vector> const& displays); + ~RenderingPlatform() override; + auto create_buffer_allocator( graphics::Display const&) -> UniqueModulePtr override; @@ -96,8 +106,9 @@ class RenderingPlatform : public graphics::RenderingPlatform std::shared_ptr const device; ///< gbm_device this platform is created on, always valid. std::shared_ptr const bound_display; ///< Associated Display, if any (nullptr is valid) - EGLDisplay const dpy; - EGLContext const share_ctx; + std::unique_ptr const share_ctx; + std::shared_ptr const egl_delegate; + std::shared_ptr const dmabuf_provider; }; } } diff --git a/src/platforms/gbm-kms/server/surfaceless_egl_context.cpp b/src/platforms/gbm-kms/server/surfaceless_egl_context.cpp new file mode 100644 index 00000000000..869df4beb62 --- /dev/null +++ b/src/platforms/gbm-kms/server/surfaceless_egl_context.cpp @@ -0,0 +1,106 @@ +/* + * 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 . + */ + +#include "surfaceless_egl_context.h" +#include "mir/graphics/egl_error.h" + +#include +#include +#include + +namespace mg = mir::graphics; + +namespace +{ +auto make_share_only_context(EGLDisplay dpy, std::optional share_with) -> EGLContext +{ + eglBindAPI(EGL_OPENGL_ES_API); + + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint const config_attr[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLConfig cfg; + 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"))); + } + + auto ctx = eglCreateContext(dpy, cfg, share_with.value_or(EGL_NO_CONTEXT), context_attr); + if (ctx == EGL_NO_CONTEXT) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); + } + return ctx; +} +} + +mg::gbm::SurfacelessEGLContext::SurfacelessEGLContext(EGLDisplay dpy) + : dpy{dpy}, + ctx{make_share_only_context(dpy, {})} + { + } + +mg::gbm::SurfacelessEGLContext::SurfacelessEGLContext(EGLDisplay dpy, EGLContext share_with) + : dpy{dpy}, + ctx{make_share_only_context(dpy, share_with)} +{ +} + +mg::gbm::SurfacelessEGLContext::~SurfacelessEGLContext() +{ + eglDestroyContext(dpy, ctx); +} + +void mg::gbm::SurfacelessEGLContext::make_current() const +{ + if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make context current")); + } +} + +void mg::gbm::SurfacelessEGLContext::release_current() const +{ + 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 mg::gbm::SurfacelessEGLContext::make_share_context() const -> std::unique_ptr +{ + return std::unique_ptr{new SurfacelessEGLContext{dpy, ctx}}; +} + +mg::gbm::SurfacelessEGLContext::operator EGLContext() +{ + return ctx; +} + +auto mg::gbm::SurfacelessEGLContext::egl_display() const -> EGLDisplay +{ + return dpy; +} diff --git a/src/platforms/gbm-kms/server/surfaceless_egl_context.h b/src/platforms/gbm-kms/server/surfaceless_egl_context.h new file mode 100644 index 00000000000..1b21e252913 --- /dev/null +++ b/src/platforms/gbm-kms/server/surfaceless_egl_context.h @@ -0,0 +1,43 @@ +/* + * 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 . + */ + +#include "mir/renderer/gl/context.h" + +#include + +namespace mir::graphics::gbm +{ +class SurfacelessEGLContext : public mir::renderer::gl::Context +{ +public: + SurfacelessEGLContext(EGLDisplay dpy); + SurfacelessEGLContext(EGLDisplay dpy, EGLContext share_with); + + ~SurfacelessEGLContext() override; + + void make_current() const override; + + void release_current() const override; + + auto make_share_context() const -> std::unique_ptr override; + explicit operator EGLContext() override; + + auto egl_display() const -> EGLDisplay; +private: + EGLDisplay const dpy; + EGLContext const ctx; +}; +} \ No newline at end of file From 620dd8e6dafdfd7213fef21f01cd873751bbb385 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 25 Sep 2023 16:56:09 +1000 Subject: [PATCH 059/108] platform/linux-dma-buf: More complete cross-device buffer handling. When there are multiple GPU devices it's possible that a client will allocate a dmabuf with a format/modifier pair that one GPU can import but another cannot. Handle this case by having the importing GPU blit into a buffer with a format/modifier supported by both. --- include/platform/mir/graphics/linux_dmabuf.h | 14 +- src/platform/CMakeLists.txt | 2 + src/platform/graphics/CMakeLists.txt | 2 + src/platform/graphics/egl_buffer_copy.cpp | 376 ++++++++++++++++++ src/platform/graphics/egl_buffer_copy.h | 49 +++ src/platform/graphics/linux_dmabuf.cpp | 232 ++++++++++- src/platforms/gbm-kms/server/kms/platform.cpp | 112 +++++- .../renderer-generic-egl/buffer_allocator.cpp | 10 +- 8 files changed, 781 insertions(+), 16 deletions(-) create mode 100644 src/platform/graphics/egl_buffer_copy.cpp create mode 100644 src/platform/graphics/egl_buffer_copy.h diff --git a/include/platform/mir/graphics/linux_dmabuf.h b/include/platform/mir/graphics/linux_dmabuf.h index 5c1645af017..ef08103b3c0 100644 --- a/include/platform/mir/graphics/linux_dmabuf.h +++ b/include/platform/mir/graphics/linux_dmabuf.h @@ -21,11 +21,13 @@ #include "linux-dmabuf-unstable-v1_wrapper.h" #include +#include +#include #include "mir/graphics/buffer.h" +#include "mir/graphics/drm_formats.h" #include "mir/graphics/egl_extensions.h" - namespace mir { namespace renderer @@ -50,15 +52,19 @@ class EGLContextExecutor; class DmaBufFormatDescriptors; class DMABufBuffer; +class EGLBufferCopier; -class DMABufEGLProvider +class DMABufEGLProvider : public std::enable_shared_from_this { public: + using EGLImageAllocator = + std::function(DRMFormat, std::span, geometry::Size)>; DMABufEGLProvider( EGLDisplay dpy, std::shared_ptr egl_extensions, EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext, - std::shared_ptr egl_delegate); + std::shared_ptr egl_delegate, + EGLImageAllocator allocate_importable_image); ~DMABufEGLProvider(); @@ -86,6 +92,8 @@ class DMABufEGLProvider std::shared_ptr const egl_extensions; std::unique_ptr const formats; std::shared_ptr const egl_delegate; + EGLImageAllocator allocate_importable_image; + std::unique_ptr const blitter; }; class LinuxDmaBufUnstable : public mir::wayland::LinuxDmabufV1::Global diff --git a/src/platform/CMakeLists.txt b/src/platform/CMakeLists.txt index de41bed4e98..53188ca559a 100644 --- a/src/platform/CMakeLists.txt +++ b/src/platform/CMakeLists.txt @@ -71,6 +71,8 @@ target_link_libraries(mirplatform PRIVATE mircommon ${MIR_PLATFORM_REFERENCES} + PUBLIC + PkgConfig::EPOXY ) set_target_properties( diff --git a/src/platform/graphics/CMakeLists.txt b/src/platform/graphics/CMakeLists.txt index 11c333a46ba..ea9bd7887d8 100644 --- a/src/platform/graphics/CMakeLists.txt +++ b/src/platform/graphics/CMakeLists.txt @@ -32,6 +32,8 @@ add_library(mirplatformgraphicscommon OBJECT ${PROJECT_SOURCE_DIR}/include/platform/mir/graphics/drm_formats.h ${PROJECT_SOURCE_DIR}/include/platform/mir/graphics/egl_context_executor.h egl_context_executor.cpp + egl_buffer_copy.h + egl_buffer_copy.cpp ) mir_generate_protocol_wrapper(mirplatformgraphicscommon "zwp_" protocol/linux-dmabuf-unstable-v1.xml) diff --git a/src/platform/graphics/egl_buffer_copy.cpp b/src/platform/graphics/egl_buffer_copy.cpp new file mode 100644 index 00000000000..b851e780486 --- /dev/null +++ b/src/platform/graphics/egl_buffer_copy.cpp @@ -0,0 +1,376 @@ +/* + * 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 . + */ + +#include +#include + +#include "egl_buffer_copy.h" +#include "mir/graphics/egl_context_executor.h" +#include "mir/graphics/egl_error.h" +#include "mir/graphics/egl_extensions.h" + +#include +#include +#include +#include +#include + +#define MIR_LOG_COMPONENT "egl-buffer-copy" +#include "mir/log.h" + +namespace mg = mir::graphics; + +namespace +{ +const GLchar* const vshader = + { + "attribute vec4 position;\n" + "attribute vec2 texcoord;\n" + "varying vec2 v_texcoord;\n" + "void main() {\n" + " gl_Position = position;\n" + " v_texcoord = texcoord;\n" + "}\n" + }; + +const GLchar* const fshader = + { + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "uniform sampler2D tex;" + "varying vec2 v_texcoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(tex, v_texcoord);\n" + "}\n" + }; + +template +class GLBulkHandle +{ +public: + GLBulkHandle() + { + (**allocator)(1, &id); + } + + ~GLBulkHandle() + { + if (id) + (**deleter)(1, &id); + } + + GLBulkHandle(GLBulkHandle const&) = delete; + GLBulkHandle& operator=(GLBulkHandle const&) = delete; + GLBulkHandle& operator=(GLBulkHandle&& rhs) + { + std::swap(id, rhs.id); + return *this; + } + + GLBulkHandle(GLBulkHandle&& from) + : id{std::exchange(from.id, 0)} + { + } + + operator GLuint() const + { + return id; + } + +private: + GLuint id; +}; + +template +class GLTypedHandle +{ +public: + GLTypedHandle(GLenum type) + : id{(**allocator)(type)} + { + } + + ~GLTypedHandle() + { + if (id) + (**deleter)(id); + } + + GLTypedHandle(GLTypedHandle const&) = delete; + GLTypedHandle& operator=(GLTypedHandle const&) = delete; + GLTypedHandle& operator=(GLTypedHandle&& rhs) + { + std::swap(id, rhs.id); + return *this; + } + + GLTypedHandle(GLTypedHandle&& from) + : id{std::exchange(from.id, 0)} + { + } + + operator GLuint() const + { + return id; + } + +private: + GLuint id; +}; + +template +class GLHandle +{ +public: + GLHandle() + : id{(**allocator)()} + { + } + + ~GLHandle() + { + if (id) + (**deleter)(id); + } + + GLHandle(GLHandle const&) = delete; + GLHandle& operator=(GLHandle const&) = delete; + GLHandle& operator=(GLHandle&& rhs) + { + std::swap(id, rhs.id); + return *this; + } + + GLHandle(GLHandle&& from) + : id{std::exchange(from.id, 0)} + { + } + + operator GLuint() const + { + return id; + } + +private: + GLuint id; +}; + +using TextureHandle = GLBulkHandle<&glGenTextures, &glDeleteTextures>; +using GLBufferHandle = GLBulkHandle<&glGenBuffers, &glDeleteBuffers>; + +using RenderbufferHandle = GLBulkHandle<&glGenRenderbuffers, &glDeleteRenderbuffers>; +using FramebufferHandle = GLBulkHandle<&glGenFramebuffers, &glDeleteFramebuffers>; + +using ProgramHandle = GLHandle<&glCreateProgram, &glDeleteProgram>; +using ShaderHandle = GLTypedHandle<&glCreateShader, &glDeleteShader>; + +ShaderHandle compile_shader(GLenum type, GLchar const* src) +{ + auto id = ShaderHandle(type); + if (!id) + { + BOOST_THROW_EXCEPTION(mg::gl_error("Failed to create shader")); + } + + glShaderSource(id, 1, &src, NULL); + glCompileShader(id); + GLint ok; + glGetShaderiv(id, GL_COMPILE_STATUS, &ok); + if (!ok) + { + GLchar log[1024] = "(No log info)"; + glGetShaderInfoLog(id, sizeof log, NULL, log); + glDeleteShader(id); + BOOST_THROW_EXCEPTION( + std::runtime_error( + std::string("Compile failed: ") + log + " for:\n" + src)); + } + return id; +} + +ProgramHandle link_shader( + ShaderHandle const& vertex_shader, + ShaderHandle const& fragment_shader) +{ + ProgramHandle program; + glAttachShader(program, fragment_shader); + glAttachShader(program, vertex_shader); + glLinkProgram(program); + GLint ok; + glGetProgramiv(program, GL_LINK_STATUS, &ok); + if (!ok) + { + GLchar log[1024]; + glGetProgramInfoLog(program, sizeof log - 1, NULL, log); + log[sizeof log - 1] = '\0'; + BOOST_THROW_EXCEPTION( + std::runtime_error( + std::string("Linking GL shader failed: ") + log)); + } + + return program; +} +} + +class mg::EGLBufferCopier::Impl +{ +public: + Impl(std::shared_ptr egl_delegate) + : egl_delegate{std::move(egl_delegate)} + { + auto exception_catcher = std::make_shared>(); + auto initialised = exception_catcher->get_future(); + this->egl_delegate->spawn( + [this, exception_catcher = std::move(exception_catcher)]() + { + try + { + initialise(); + exception_catcher->set_value(); + } + catch(std::exception const& err) + { + exception_catcher->set_exception(std::make_exception_ptr(err)); + } + }); + initialised.get(); + } + + ~Impl() + { + // Ensure EGL resources are cleaned up in the right context + egl_delegate->spawn([this]() { state = nullptr; }); + } + + auto blit(EGLImage from, EGLImage to, geometry::Size size) -> std::optional + { + /* TODO: It *must* be possible to create the fence FD first and *then* + * insert it into the command stream! That would let us immediately return + * the fd without waiting for the EGL thread. + */ + auto sync_promise = std::make_shared>>(); + auto sync = sync_promise->get_future(); + egl_delegate->spawn( + [sync = std::move(sync_promise), from, to, state = state, size]() + { + glUseProgram(state->prog); + + TextureHandle tex; + RenderbufferHandle renderbuffer; + FramebufferHandle fbo; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, from); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, to); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); + + glBindBuffer(GL_ARRAY_BUFFER, state->vert_data); + glVertexAttribPointer (state->attrpos, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, state->tex_data); + glVertexAttribPointer (state->attrtex, 2, GL_FLOAT, GL_FALSE, 0, 0); + + glEnableVertexAttribArray(state->attrpos); + glEnableVertexAttribArray(state->attrtex); + + glViewport(0, 0, size.width.as_int(), size.height.as_int()); + GLubyte const idx[] = { 0, 1, 3, 2 }; + glDrawElements (GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx); + + // TODO: Actually use the sync + glFinish(); + sync->set_value({}); + + // Unbind all our resources + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + }); + return sync.get(); + } +private: + std::shared_ptr const egl_delegate; + // State accessed only on the EGL thread + std::shared_ptr const egl_extensions; + struct State + { + ProgramHandle prog; + GLint attrtex; + GLint attrpos; + GLBufferHandle vert_data; + GLBufferHandle tex_data; + }; + /* This has to be initialised on the EGL thread, but it contains non-default-initialisable state. + * + * We use a shared_ptr to avoid worrying about making sure we outlive any invocations on the EGL thread. + * We don't need any synchronisation because state is only ever accessed on EGLContextExecutor, which + * is single-threaded. + */ + std::shared_ptr state; + + // Called once, from the EGL thread + void initialise() + { + state = std::make_shared(); + state->prog = link_shader(compile_shader(GL_VERTEX_SHADER, vshader), compile_shader(GL_FRAGMENT_SHADER, fshader)); + + glUseProgram(state->prog); + + state->attrpos = glGetAttribLocation(state->prog, "position"); + state->attrtex = glGetAttribLocation(state->prog, "texcoord"); + + auto unitex = glGetUniformLocation(state->prog, "tex"); + + glUniform1i(unitex, 0); + + static GLfloat const dest_vert[4][2] = + { { -1.f, 1.f }, { 1.f, 1.f }, { 1.f, -1.f }, { -1.f, -1.f } }; + glBindBuffer(GL_ARRAY_BUFFER, state->vert_data); + glBufferData(GL_ARRAY_BUFFER, sizeof(dest_vert), dest_vert, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + static GLfloat const tex_vert[4][2] = + { + { 0.f, 0.f }, { 1.f, 0.f }, { 1.f, 1.f }, { 0.f, 1.f }, + }; + glBindBuffer(GL_ARRAY_BUFFER, state->tex_data); + glBufferData(GL_ARRAY_BUFFER, sizeof(tex_vert), tex_vert, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } +}; + +mg::EGLBufferCopier::EGLBufferCopier(std::shared_ptr egl_delegate) + : impl{std::make_unique(std::move(egl_delegate))} +{ +} + +mg::EGLBufferCopier::~EGLBufferCopier() = default; + +auto mg::EGLBufferCopier::blit(EGLImage src, EGLImage dest, geometry::Size size) -> std::optional +{ + return impl->blit(src, dest, size); +} diff --git a/src/platform/graphics/egl_buffer_copy.h b/src/platform/graphics/egl_buffer_copy.h new file mode 100644 index 00000000000..ce65c01d02f --- /dev/null +++ b/src/platform/graphics/egl_buffer_copy.h @@ -0,0 +1,49 @@ +/* + * 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 . + */ + +#include "mir/graphics/egl_context_executor.h" +#include "mir/fd.h" +#include "mir/geometry/size.h" + +#include +#include + +#include + +namespace mir::graphics +{ +class EGLBufferCopier +{ +public: + EGLBufferCopier(std::shared_ptr egl_delegate); + + ~EGLBufferCopier(); + /** + * Copy whole image from src to dest + * + * Both src and dest must be EGLImages sharing an EGLDisplay with + * the EGLContext in egl_delegate + * + * \returns An optional native fence object, as per EGL_ANDROID_native_fence_sync + * If the optional is empty, the copy has been completed before blit returns. + * Otherwise, the fence must be used for synchronisation. + */ + auto blit(EGLImage src, EGLImage dest, geometry::Size size) -> std::optional; +private: + class Impl; + std::unique_ptr const impl; +}; +} diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index 94f90f17627..da69a2ba86e 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -14,9 +14,13 @@ * along with this program. If not, see . */ +#include +#include #include "mir/graphics/linux_dmabuf.h" +#include "mir/fd.h" #include "mir/graphics/drm_formats.h" +#include "egl_buffer_copy.h" #include "wayland_wrapper.h" #include "mir/wayland/protocol_error.h" @@ -29,18 +33,16 @@ #include "mir/graphics/buffer_basic.h" #include "mir/graphics/dmabuf_buffer.h" #include "mir/graphics/egl_context_executor.h" +#include #include #define MIR_LOG_COMPONENT "linux-dmabuf-import" #include "mir/log.h" - -#include -#include - #include #include #include +#include #include #include @@ -269,6 +271,124 @@ static constexpr std::array egl_attribs = { } }; +class DMABuf : public mg::DMABufBuffer +{ +public: + DMABuf( + mg::DRMFormat format, + std::optional modifier, + std::vector planes, + mg::gl::Texture::Layout layout, + geom::Size size) + : format_{format}, + modifier_{std::move(modifier)}, + planes_{std::move(planes)}, + layout_{layout}, + size_{size} + { + } + + auto format() const -> mg::DRMFormat override + { + return format_; + } + auto modifier() const -> std::optional override + { + return modifier_; + } + auto planes() const -> std::vector const& override + { + return planes_; + } + auto layout() const -> mg::gl::Texture::Layout override + { + return layout_; + } + auto size() const -> geom::Size override + { + return size_; + } +private: + mg::DRMFormat const format_; + std::optional const modifier_; + std::vector const planes_; + mg::gl::Texture::Layout const layout_; + geom::Size const size_; +}; + +auto export_egl_image(EGLDisplay dpy, EGLImage image, geom::Size size) -> std::unique_ptr +{ + constexpr int const max_planes = 4; + + int fourcc; + int num_planes; + std::array modifiers; + if (eglExportDMABUFImageQueryMESA(dpy, image, &fourcc, &num_planes, modifiers.data()) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to query EGLImage for dma-buf export"))); + } + + /* There's only a single modifier for a logical buffer. For some reason the EGL interface + * decided to return one modifier per plane, but they are always the same. + * + * We handle DRM_FORMAT_MOD_INVALID as an empty modifier; fix that up here if we get it. + */ + auto modifier = + [](uint64_t egl_modifier) -> std::optional + { + if (egl_modifier == DRM_FORMAT_MOD_INVALID) + { + return std::nullopt; + } + return egl_modifier; + }(modifiers[0]); + + std::array fds; + std::array strides; + std::array offsets; + + if (eglExportDMABUFImageMESA(dpy, image, fds.data(), strides.data(), offsets.data()) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to export EGLImage to dma-buf(s)"))); + } + + std::vector planes; + planes.reserve(num_planes); + for (int i = 0; i < num_planes; ++i) + { + mir::Fd fd; + // If multiple planes use the same buffer, the fds array will be filled with -1 for subsequent + // planes. + if (fds[i] == -1) + { + // Paranoia + if (i == 0) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Driver has a broken EGL_MESA_image_dma_buf_export extension"})); + } + fds[i] = fds[i - 1]; + fd = mir::Fd{mir::IntOwnedFd{fds[i]}}; + } + else + { + // We own these FDs now. + fd = mir::Fd{fds[i]}; + } + planes.push_back( + PlaneInfo { + .dma_buf = std::move(fd), + .stride = static_cast(strides[i]), + .offset = static_cast(offsets[i]) + }); + } + + return std::make_unique( + mg::DRMFormat{static_cast(fourcc)}, + modifier, + std::move(planes), + mg::gl::Texture::Layout::TopRowFirst, + size); +} /** * Reimport dmabufs into EGL @@ -286,8 +406,7 @@ auto import_egl_image( std::optional modifier, std::vector const& planes, EGLDisplay dpy, - mg::EGLExtensions const& egl_extensions - ) -> EGLImageKHR + mg::EGLExtensions const& egl_extensions) -> EGLImageKHR { std::vector attributes; @@ -811,11 +930,13 @@ class DmabufTexBuffer : mg::EGLExtensions const& extensions, mg::DMABufBuffer const& dma_buf, BufferGLDescription const& descriptor, + std::shared_ptr provider, std::shared_ptr egl_delegate, std::function&& on_consumed, std::function&& on_release) : dpy{dpy}, tex{dpy, extensions, dma_buf, descriptor, std::move(egl_delegate)}, + provider_{std::move(provider)}, on_consumed{std::move(on_consumed)}, on_release{std::move(on_release)}, size_{dma_buf.size()}, @@ -876,7 +997,7 @@ class DmabufTexBuffer : return &tex; } - auto format() const ->mg::DRMFormat override + auto format() const -> mg::DRMFormat override { return format_; } @@ -896,10 +1017,16 @@ class DmabufTexBuffer : return tex.layout(); } + auto provider() const -> std::shared_ptr + { + return provider_; + } private: EGLDisplay const dpy; DMABufTex tex; + std::shared_ptr const provider_; + std::mutex consumed_mutex; std::function on_consumed; std::function const on_release; @@ -983,11 +1110,14 @@ mg::DMABufEGLProvider::DMABufEGLProvider( EGLDisplay dpy, std::shared_ptr egl_extensions, mg::EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext, - std::shared_ptr egl_delegate) + std::shared_ptr egl_delegate, + EGLImageAllocator allocate_importable_image) : dpy{dpy}, egl_extensions{std::move(egl_extensions)}, formats{std::make_unique(dpy, dmabuf_ext)}, - egl_delegate{std::move(egl_delegate)} + egl_delegate{std::move(egl_delegate)}, + allocate_importable_image{std::move(allocate_importable_image)}, + blitter{std::make_unique(this->egl_delegate)} { } @@ -1013,6 +1143,7 @@ auto mg::DMABufEGLProvider::import_dma_buf( *egl_extensions, dma_buf, *descriptor, + shared_from_this(), egl_delegate, std::move(on_consumed), std::move(on_release)); @@ -1062,7 +1193,88 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) } else { - mir::log_warning("Failed to import cross-GPU dma-buf for rendering"); + /* Oh, no. We've got a dma-buf in a format that our rendering GPU can't handle. + * + * In this case we'll need to get the *importing* GPU to blit to a format + * we *can* handle. + */ + auto importing_provider = dmabuf_tex->provider(); + + /* TODO: Be smarter about finding a shared pixel format; everything *should* do + * ARGB8888, but if the buffer is in a higher bitdepth this will lose colour information + */ + auto const& supported_formats = *formats; + auto const& modifiers = + [&supported_formats]() -> std::vector const& + { + for (size_t i = 0; i < supported_formats.num_formats(); ++i) + { + if (supported_formats[i].format == DRM_FORMAT_ARGB8888) + { + return supported_formats[i].modifiers; + } + } + BOOST_THROW_EXCEPTION((std::runtime_error{"Platform doesn't support ARGB8888?!"})); + }(); + + auto importable_buf = importing_provider->allocate_importable_image( + mg::DRMFormat{DRM_FORMAT_ARGB8888}, + std::span{modifiers.data(), modifiers.size()}, + dmabuf_tex->size()); + + if (!importable_buf) + { + mir::log_warning("Failed to allocate common-format buffer for cross-GPU buffer import"); + return nullptr; + } + + auto src_image = import_egl_image( + dmabuf_tex->size().width.as_int(), dmabuf_tex->size().height.as_int(), + dmabuf_tex->format(), + dmabuf_tex->modifier(), + dmabuf_tex->planes(), + importing_provider->dpy, + *importing_provider->egl_extensions); + auto importable_image = import_egl_image( + importable_buf->size().width.as_int(), importable_buf->size().height.as_int(), + importable_buf->format(), + importable_buf->modifier(), + importable_buf->planes(), + importing_provider->dpy, + *importing_provider->egl_extensions); + auto sync = importing_provider->blitter->blit(src_image, importable_image, dmabuf_tex->size()); + if (sync) + { + BOOST_THROW_EXCEPTION((std::logic_error{"EGL_ANDROID_native_fence_sync support not implemented yet"})); + } + auto importable_dmabuf = export_egl_image(importing_provider->dpy, importable_image, dmabuf_tex->size()); + + eglDestroyImage(importing_provider->dpy, src_image); + eglDestroyImage(importing_provider->dpy, importable_image); + + if (auto descriptor = descriptor_for_format_and_modifiers( + importable_dmabuf->format(), + importable_dmabuf->modifier().value_or(DRM_FORMAT_MOD_INVALID), + *this)) + { + /* We're being naughty here and using the fact that `as_texture()` has a side-effect + * of invoking the buffer's `on_consumed()` callback. + */ + dmabuf_tex->as_texture(); + return std::make_shared( + dpy, + *egl_extensions, + *importable_dmabuf, + *descriptor, + egl_delegate); + } + + /* To get here we have to have failed to find the format/modifier descriptor for a + * buffer that we've explicitly allocated to be importable by us. + * + * This is a logic bug, so go noisily. + */ + BOOST_THROW_EXCEPTION((std::logic_error{"Failed to find import parameterns for buffer we explicitly allocated for import"})); } } return nullptr; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 22a77a25d6a..30015b19a8a 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -19,8 +19,11 @@ #include "display.h" #include "mir/console_services.h" #include "mir/emergency_cleanup_registry.h" +#include "mir/graphics/dmabuf_buffer.h" +#include "mir/graphics/drm_formats.h" #include "mir/graphics/egl_context_executor.h" #include "mir/graphics/platform.h" +#include "mir/graphics/texture.h" #include "mir/renderer/gl/context.h" #include "mir/udev/wrapper.h" #include "kms_framebuffer.h" @@ -30,6 +33,8 @@ #include "mir/graphics/linux_dmabuf.h" #include "mir/graphics/egl_context_executor.h" #include "surfaceless_egl_context.h" +#include +#include #include #define MIR_LOG_COMPONENT "platform-graphics-gbm-kms" @@ -190,7 +195,101 @@ struct gbm_device_from_hw auto operator()(std::shared_ptr device) { return device; } }; +class DMABufBuffer : public mg::DMABufBuffer +{ +public: + DMABufBuffer( + mg::DRMFormat format, + std::optional modifier, + std::vector planes, + mg::gl::Texture::Layout layout, + mir::geometry::Size size) + : format_{format}, + modifier_{modifier}, + planes_{std::move(planes)}, + layout_{layout}, + size_{size} + { + } + + auto format() const -> mg::DRMFormat override + { + return format_; + } + auto modifier() const -> std::optional override + { + return modifier_; + } + auto planes() const -> std::vector const& override + { + return planes_; + } + auto layout() const -> mg::gl::Texture::Layout override + { + return layout_; + } + auto size() const -> mir::geometry::Size override + { + return size_; + } +private: + mg::DRMFormat format_; + std::optional modifier_; + std::vector planes_; + mg::gl::Texture::Layout layout_; + mir::geometry::Size size_; +}; + +auto alloc_dma_buf( + gbm_device* gbm, + mg::DRMFormat format, + std::span modifiers, + mir::geometry::Size size) -> std::shared_ptr +{ + auto gbm_bo = std::unique_ptr{ + gbm_bo_create_with_modifiers2( + gbm, + size.width.as_uint32_t(), size.height.as_uint32_t(), + format, + modifiers.data(), modifiers.size(), + GBM_BO_USE_RENDERING), + &gbm_bo_destroy}; + + auto plane_count = gbm_bo_get_plane_count(gbm_bo.get()); + std::vector planes; + planes.reserve(plane_count); + + for (int i = 0; i < plane_count; ++i) + { + planes.push_back( + mg::DMABufBuffer::PlaneDescriptor { + .dma_buf = mir::Fd{gbm_bo_get_fd_for_plane(gbm_bo.get(), i)}, + .stride = gbm_bo_get_stride_for_plane(gbm_bo.get(), i), + .offset = gbm_bo_get_offset(gbm_bo.get(), i) + }); + } + + std::optional modifier; + auto raw_modifier = gbm_bo_get_modifier(gbm_bo.get()); + if (raw_modifier == DRM_FORMAT_MOD_INVALID) + { + modifier = std::nullopt; + } + else + { + modifier = raw_modifier; + } + + return std::make_shared( + format, + modifier, + std::move(planes), + mg::gl::Texture::Layout::GL, + size); +} + auto maybe_make_dmabuf_provider( + std::shared_ptr gbm, EGLDisplay dpy, std::shared_ptr egl_extensions, std::shared_ptr egl_delegate) @@ -199,7 +298,16 @@ auto maybe_make_dmabuf_provider( try { mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; - return std::make_shared(dpy, std::move(egl_extensions), modifier_ext, std::move(egl_delegate)); + return std::make_shared( + dpy, + std::move(egl_extensions), + modifier_ext, + std::move(egl_delegate), + [gbm](mg::DRMFormat format, std::span modifiers, mir::geometry::Size size) + -> std::shared_ptr + { + return alloc_dma_buf(gbm.get(), format, modifiers, size); + }); } catch (std::runtime_error const& error) { @@ -228,7 +336,7 @@ mgg::RenderingPlatform::RenderingPlatform( bound_display{std::visit(display_provider_or_nothing{}, hw)}, share_ctx{std::make_unique(initialise_egl(dpy_for_gbm_device(device.get()), 1, 4))}, egl_delegate{std::make_shared(share_ctx->make_share_context())}, - dmabuf_provider{maybe_make_dmabuf_provider(share_ctx->egl_display(), std::make_shared(), egl_delegate)} + dmabuf_provider{maybe_make_dmabuf_provider(device, share_ctx->egl_display(), std::make_shared(), egl_delegate)} { } diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index 03ebe202664..cc8c6799f60 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -157,7 +157,15 @@ auto maybe_make_dmabuf_provider( try { mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; - return std::make_shared(dpy, std::move(egl_extensions), modifier_ext, std::move(egl_delegate)); + return std::make_shared( + dpy, + std::move(egl_extensions), + modifier_ext, + std::move(egl_delegate), + [](mg::DRMFormat, std::span, geom::Size) -> std::shared_ptr + { + return nullptr; + }); } catch (std::runtime_error const& error) { From 1676ee5d306e18f20aa9846533c23a259cf0fefe Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Fri, 15 Sep 2023 10:28:01 +0100 Subject: [PATCH 060/108] Drop the default quirk disabling evdi (#2997) --- src/platforms/gbm-kms/server/kms/quirks.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platforms/gbm-kms/server/kms/quirks.cpp b/src/platforms/gbm-kms/server/kms/quirks.cpp index 48f7d659e07..44fdbf5a380 100644 --- a/src/platforms/gbm-kms/server/kms/quirks.cpp +++ b/src/platforms/gbm-kms/server/kms/quirks.cpp @@ -154,8 +154,7 @@ class mgg::Quirks::Impl private: // Mir's gbm-kms support isn't (yet) working with nvidia - // Mir's gbm-kms support isn't (yet) working with evdi - std::unordered_set drivers_to_skip = { "nvidia", "evdi", "ast" }; + std::unordered_set drivers_to_skip = { "nvidia", "ast" }; std::unordered_set devnodes_to_skip; // We know this is currently useful for virtio_gpu, vc4-drm and v3d std::unordered_set skip_modesetting_support = { "virtio_gpu", "vc4-drm", "v3d" }; From 3eb1e777bf2a3c73fe1b246d46e1802e39b4deb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sawicz=20=28Saviq=29?= Date: Mon, 25 Sep 2023 10:22:10 +0200 Subject: [PATCH 061/108] sphinx: add libepoxy dependency --- doc/sphinx/.readthedocs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/sphinx/.readthedocs.yaml b/doc/sphinx/.readthedocs.yaml index cb0bd3d2f8f..f6798b5ca54 100644 --- a/doc/sphinx/.readthedocs.yaml +++ b/doc/sphinx/.readthedocs.yaml @@ -20,6 +20,7 @@ build: - libboost-system-dev - libdrm-dev - libegl-dev + - libepoxy-dev - libfreetype-dev - libglib2.0-dev - libgles2-mesa-dev From 171c42ac3929f946a70505ee42be0ce8220f245a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sawicz=20=28Saviq=29?= Date: Mon, 25 Sep 2023 10:36:33 +0200 Subject: [PATCH 062/108] cmake: always require epoxy --- CMakeLists.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 995c653154e..30825e5ffd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -262,6 +262,7 @@ if(NOT GLM_FOUND) endif() pkg_check_modules(DRM REQUIRED IMPORTED_TARGET libdrm) pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl) +pkg_check_modules(EPOXY REQUIRED IMPORTED_TARGET epoxy) pkg_check_modules(GFlags REQUIRED IMPORTED_TARGET gflags) pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0 gio-unix-2.0) pkg_check_modules(GLESv2 REQUIRED IMPORTED_TARGET glesv2) @@ -300,14 +301,6 @@ if (MIR_BUILD_PLATFORM_GBM_KMS) pkg_check_modules(GBM REQUIRED IMPORTED_TARGET gbm>=11.0.0) endif() -if (MIR_BUILD_PLATFORM_EGLSTREAM_KMS) - pkg_check_modules(EPOXY REQUIRED IMPORTED_TARGET epoxy) -endif() - -if (MIR_BUILD_PLATFORM_WAYLAND) - pkg_check_modules(EPOXY REQUIRED IMPORTED_TARGET epoxy) -endif() - set(MIR_TRACEPOINT_LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/mir/tools) mir_make_pkgconfig_variable(PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}") From 4a56d2c188ff7564cf84333260b678ee6f908cba Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Tue, 26 Sep 2023 15:59:27 +1000 Subject: [PATCH 063/108] platforms/x11: Fix linux-dmabuf integration. `Buffer`s allocated by the linux-dmabuf infrastructure no longer directly implement `gl::Texture`, so we need to call into `DMABufEGLProvider::as_texture()` rather than `dynamic_cast`ing. --- .../renderer-generic-egl/buffer_allocator.cpp | 53 +++------ .../renderer-generic-egl/buffer_allocator.h | 6 +- .../rendering_platform.cpp | 106 +++++++++++++++++- .../renderer-generic-egl/rendering_platform.h | 10 +- 4 files changed, 131 insertions(+), 44 deletions(-) diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index cc8c6799f60..29d1d375c79 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -147,46 +147,17 @@ class SurfacelessEGLContext : public mir::renderer::gl::Context EGLDisplay const dpy; EGLContext const ctx; }; - -auto maybe_make_dmabuf_provider( - EGLDisplay dpy, - std::shared_ptr egl_extensions, - std::shared_ptr egl_delegate) - -> std::shared_ptr -{ - try - { - mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; - return std::make_shared( - dpy, - std::move(egl_extensions), - modifier_ext, - std::move(egl_delegate), - [](mg::DRMFormat, std::span, geom::Size) -> std::shared_ptr - { - return nullptr; - }); - } - catch (std::runtime_error const& error) - { - mir::log_info( - "Cannot enable linux-dmabuf import support: %s", error.what()); - mir::log( - mir::logging::Severity::debug, - MIR_LOG_COMPONENT, - std::current_exception(), - "Detailed error: "); - } - return nullptr; -} } -mge::BufferAllocator::BufferAllocator(EGLDisplay dpy, EGLContext share_with) +mge::BufferAllocator::BufferAllocator( + EGLDisplay dpy, + EGLContext share_with, + std::shared_ptr dmabuf_provider) : ctx{std::make_unique(dpy, share_with)}, egl_delegate{ std::make_shared(ctx->make_share_context())}, egl_extensions(std::make_shared()), - dmabuf_provider{maybe_make_dmabuf_provider(dpy, egl_extensions, egl_delegate)} + dmabuf_provider{std::move(dmabuf_provider)} { } @@ -354,6 +325,14 @@ auto mge::BufferAllocator::shared_egl_context() -> EGLContext auto mge::GLRenderingProvider::as_texture(std::shared_ptr buffer) -> std::shared_ptr { + if (dmabuf_provider) + { + if (auto tex = dmabuf_provider->as_texture(buffer)) + { + return tex; + } + } + // TODO: Should this be abstracted, like dmabuf_provider above? return std::dynamic_pointer_cast(buffer); } @@ -465,8 +444,10 @@ auto mge::GLRenderingProvider::make_framebuffer_provider(std::shared_ptr dmabuf_provider) : dpy{dpy}, - ctx{ctx} + ctx{ctx}, + dmabuf_provider{std::move(dmabuf_provider)} { } diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.h b/src/platforms/renderer-generic-egl/buffer_allocator.h index 8acf8e40a03..53b53c77c66 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.h +++ b/src/platforms/renderer-generic-egl/buffer_allocator.h @@ -54,7 +54,7 @@ class BufferAllocator: public graphics::GraphicBufferAllocator { public: - BufferAllocator(EGLDisplay dpy, EGLContext share_with); + BufferAllocator(EGLDisplay dpy, EGLContext share_with, std::shared_ptr dmabuf_provider); ~BufferAllocator() override; std::shared_ptr alloc_software_buffer(geometry::Size size, MirPixelFormat) override; @@ -87,7 +87,8 @@ class GLRenderingProvider : public graphics::GLRenderingProvider public: GLRenderingProvider( EGLDisplay dpy, - EGLContext ctx); + EGLContext ctx, + std::shared_ptr dmabuf_provider); auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr override; @@ -104,6 +105,7 @@ class GLRenderingProvider : public graphics::GLRenderingProvider private: EGLDisplay const dpy; EGLContext const ctx; + std::shared_ptr const dmabuf_provider; }; } } diff --git a/src/platforms/renderer-generic-egl/rendering_platform.cpp b/src/platforms/renderer-generic-egl/rendering_platform.cpp index 9a06b3fc2d3..db499a42ebb 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.cpp +++ b/src/platforms/renderer-generic-egl/rendering_platform.cpp @@ -16,14 +16,21 @@ #include "rendering_platform.h" #include "buffer_allocator.h" +#include "mir/graphics/egl_extensions.h" #include "mir/graphics/platform.h" #include "mir/graphics/egl_error.h" +#include "mir/renderer/gl/context.h" + +#define MIR_LOG_COMPONENT "platform-generic-egl" +#include "mir/log.h" #include #include namespace mg = mir::graphics; +namespace mgc = mir::graphics::common; namespace mge = mg::egl::generic; +namespace geom = mir::geometry; namespace { @@ -46,7 +53,7 @@ auto egl_display_from_platforms(std::vector EGLContext +auto make_share_only_context(EGLDisplay dpy, std::optional share_context) -> EGLContext { eglBindAPI(EGL_OPENGL_ES_API); @@ -69,25 +76,114 @@ auto make_share_only_context(EGLDisplay dpy) -> EGLContext BOOST_THROW_EXCEPTION((mg::egl_error("Failed to find any matching EGL config"))); } - auto ctx = eglCreateContext(dpy, cfg, EGL_NO_CONTEXT, context_attr); + auto ctx = eglCreateContext(dpy, cfg, share_context.value_or(EGL_NO_CONTEXT), context_attr); if (ctx == EGL_NO_CONTEXT) { BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); } return ctx; } + +class SurfacelessEGLContext : public mir::renderer::gl::Context +{ +public: + explicit SurfacelessEGLContext(EGLDisplay dpy) + : dpy{dpy}, + ctx{make_share_only_context(dpy, {})} + { + } + + SurfacelessEGLContext(EGLDisplay dpy, EGLContext share_with) + : dpy{dpy}, + ctx{make_share_only_context(dpy, share_with)} + { + } + + ~SurfacelessEGLContext() override + { + 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 override + { + return std::unique_ptr{new SurfacelessEGLContext{dpy, ctx}}; + } + + explicit operator EGLContext() override + { + return ctx; + } +private: + EGLDisplay const dpy; + EGLContext const ctx; +}; + +auto maybe_make_dmabuf_provider( + EGLDisplay dpy, + std::shared_ptr egl_extensions, + std::shared_ptr egl_delegate) + -> std::shared_ptr +{ + try + { + mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; + return std::make_shared( + dpy, + std::move(egl_extensions), + modifier_ext, + std::move(egl_delegate), + [](mg::DRMFormat, std::span, geom::Size) -> std::shared_ptr + { + return nullptr; // We can't (portably) allocate dmabufs, but we also shouldn't need to + }); + } + catch (std::runtime_error const& error) + { + mir::log_info( + "Cannot enable linux-dmabuf import support: %s", error.what()); + mir::log( + mir::logging::Severity::debug, + MIR_LOG_COMPONENT, + std::current_exception(), + "Detailed error: "); + } + return nullptr; +} } mge::RenderingPlatform::RenderingPlatform(std::vector> const& displays) : dpy{egl_display_from_platforms(displays)}, - ctx{make_share_only_context(dpy)} + ctx{std::make_unique(dpy)}, + dmabuf_provider{ + maybe_make_dmabuf_provider( + dpy, + std::make_shared(), + std::make_shared(ctx->make_share_context()))} { } +mge::RenderingPlatform::~RenderingPlatform() = default; + auto mge::RenderingPlatform::create_buffer_allocator( mg::Display const& /*output*/) -> mir::UniqueModulePtr { - return make_module_ptr(dpy, ctx); + return make_module_ptr(dpy, static_cast(*ctx), dmabuf_provider); } auto mge::RenderingPlatform::maybe_create_interface(RendererInterfaceBase::Tag const& tag) @@ -95,7 +191,7 @@ auto mge::RenderingPlatform::maybe_create_interface(RendererInterfaceBase::Tag c { if (dynamic_cast(&tag)) { - return std::make_shared(dpy, ctx); + return std::make_shared(dpy, static_cast(*ctx), dmabuf_provider); } return nullptr; } diff --git a/src/platforms/renderer-generic-egl/rendering_platform.h b/src/platforms/renderer-generic-egl/rendering_platform.h index 171df8273e4..c198f434a96 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.h +++ b/src/platforms/renderer-generic-egl/rendering_platform.h @@ -17,12 +17,17 @@ #ifndef MIR_GRAPHICS_RENDERING_EGL_GENERIC_H_ #define MIR_GRAPHICS_RENDERING_EGL_GENERIC_H_ +#include "mir/graphics/linux_dmabuf.h" #include "mir/graphics/platform.h" #include namespace mir { +namespace renderer::gl +{ +class Context; +} namespace graphics::egl::generic { @@ -32,6 +37,8 @@ class RenderingPlatform : public graphics::RenderingPlatform public: explicit RenderingPlatform(std::vector> const& displays); + ~RenderingPlatform(); + auto create_buffer_allocator( graphics::Display const& output) -> UniqueModulePtr override; @@ -41,7 +48,8 @@ class RenderingPlatform : public graphics::RenderingPlatform private: EGLDisplay const dpy; - EGLContext const ctx; + std::unique_ptr const ctx; + std::shared_ptr const dmabuf_provider; }; } From b4efda401d19374d040a861533a714045e8d67b6 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 2 Oct 2023 18:02:35 +1100 Subject: [PATCH 064/108] platform/EGLBufferCopier: Fix release of EGL resources --- src/platform/graphics/egl_buffer_copy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/egl_buffer_copy.cpp b/src/platform/graphics/egl_buffer_copy.cpp index b851e780486..c54bd36bdca 100644 --- a/src/platform/graphics/egl_buffer_copy.cpp +++ b/src/platform/graphics/egl_buffer_copy.cpp @@ -253,7 +253,7 @@ class mg::EGLBufferCopier::Impl ~Impl() { // Ensure EGL resources are cleaned up in the right context - egl_delegate->spawn([this]() { state = nullptr; }); + egl_delegate->spawn([state = state]() mutable { state = nullptr; }); } auto blit(EGLImage from, EGLImage to, geometry::Size size) -> std::optional From b04856c165adae651cd09c210a0e543dd9cafc0c Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 2 Oct 2023 18:05:30 +1100 Subject: [PATCH 065/108] platforms/wayland: Update to new Platform API. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently I hadn't quite got around to un-stubbing it 🤦‍♀️ --- src/platforms/wayland/CMakeLists.txt | 1 + src/platforms/wayland/display.cpp | 5 +- src/platforms/wayland/display.h | 2 + src/platforms/wayland/displayclient.cpp | 138 ++++++++---------------- src/platforms/wayland/displayclient.h | 11 +- src/platforms/wayland/platform.cpp | 54 +++++++++- src/platforms/wayland/platform.h | 11 +- 7 files changed, 112 insertions(+), 110 deletions(-) diff --git a/src/platforms/wayland/CMakeLists.txt b/src/platforms/wayland/CMakeLists.txt index 16b8ce31ba7..5dd1ce55852 100644 --- a/src/platforms/wayland/CMakeLists.txt +++ b/src/platforms/wayland/CMakeLists.txt @@ -10,6 +10,7 @@ add_library(mirplatformwayland-graphics STATIC displayclient.cpp displayclient.h wayland_display.cpp wayland_display.h cursor.cpp cursor.h + wl_egl_display_provider.cpp wl_egl_display_provider.h ) target_include_directories(mirplatformwayland-graphics diff --git a/src/platforms/wayland/display.cpp b/src/platforms/wayland/display.cpp index 67b0ea5fe8c..f653eedd10c 100644 --- a/src/platforms/wayland/display.cpp +++ b/src/platforms/wayland/display.cpp @@ -93,9 +93,10 @@ mgw::Display* the_display = nullptr; mgw::Display::Display( wl_display* const wl_display, - std::shared_ptr const& gl_config, + std::shared_ptr provider, + std::shared_ptr const&, std::shared_ptr const& report) : - DisplayClient{wl_display, gl_config}, + DisplayClient{wl_display, std::move(provider)}, report{report}, shutdown_signal{::eventfd(0, EFD_CLOEXEC)}, flush_signal{::eventfd(0, EFD_SEMAPHORE)}, diff --git a/src/platforms/wayland/display.h b/src/platforms/wayland/display.h index 16c894db7a3..b0c2eb58d6e 100644 --- a/src/platforms/wayland/display.h +++ b/src/platforms/wayland/display.h @@ -18,6 +18,7 @@ #define MIR_PLATFORMS_WAYLAND_DISPLAY_H_ #include "displayclient.h" +#include "platform.h" #include #include @@ -58,6 +59,7 @@ class Display : public mir::graphics::Display, public: Display( wl_display* const wl_display, + std::shared_ptr provider, std::shared_ptr const& gl_config, std::shared_ptr const& report); diff --git a/src/platforms/wayland/displayclient.cpp b/src/platforms/wayland/displayclient.cpp index b396a4f4971..4240fcdec0d 100644 --- a/src/platforms/wayland/displayclient.cpp +++ b/src/platforms/wayland/displayclient.cpp @@ -16,8 +16,8 @@ */ #include "displayclient.h" +#include "wl_egl_display_provider.h" #include "mir/graphics/platform.h" -#include "mir/graphics/egl_error.h" #include #include @@ -33,7 +33,7 @@ #include #include #include -#include +#include namespace mgw = mir::graphics::wayland; namespace geom = mir::geometry; @@ -45,8 +45,7 @@ class mgw::DisplayClient::Output : public: Output( wl_output* output, - DisplayClient* owner, - std::function on_change); + DisplayClient* owner); ~Output(); @@ -71,7 +70,6 @@ class mgw::DisplayClient::Output : std::optional pending_toplevel_size; bool has_initialized{false}; - std::function on_change; // wl_output events void geometry( @@ -104,26 +102,16 @@ class mgw::DisplayClient::Output : void set_next_image(std::unique_ptr content) override; private: - std::unique_ptr next_frame; + std::unique_ptr next_frame; + std::shared_ptr provider; }; -namespace -{ -static EGLint const ctxattribs[] = - { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; -} - mgw::DisplayClient::Output::Output( wl_output* output, - DisplayClient* owner, - std::function on_change) : + DisplayClient* owner) : output{output}, owner_{owner}, - surface{wl_compositor_create_surface(owner->compositor)}, - on_change{std::move(on_change)} + surface{wl_compositor_create_surface(owner->compositor)} { // If building against newer Wayland protocol definitions we may miss trailing fields #pragma GCC diagnostic push @@ -170,20 +158,10 @@ mgw::DisplayClient::Output::~Output() wl_surface_destroy(surface); - if (eglsurface != EGL_NO_SURFACE) - { - eglDestroySurface(owner_->egldisplay, eglsurface); - } - if (egl_window != nullptr) { wl_egl_window_destroy(egl_window); } - - if (eglctx != EGL_NO_CONTEXT) - { - eglDestroyContext(owner_->egldisplay, eglctx); - } } void mgw::DisplayClient::Output::geometry( @@ -304,7 +282,9 @@ void mgw::DisplayClient::Output::done() } else { - on_change(); + /* TODO: We should handle this by raising a hardware-changed notification and reconfiguring in + * the subsequent `configure()` call. + */ } } @@ -326,17 +306,15 @@ void mgw::DisplayClient::Output::surface_configure(uint32_t serial) output_size = dcout.extents().size * dcout.scale; if (!has_initialized) { - egl_window = wl_egl_window_create(surface, output_size.width.as_int(), output_size.height.as_int()); - eglsurface = eglCreatePlatformWindowSurface(owner_->egldisplay, owner_->eglconfig, egl_window, nullptr); has_initialized = true; + provider = std::make_shared(*owner_->provider, surface, output_size); } else if (size_is_changed) { - if (egl_window) - { - wl_egl_window_resize(egl_window, output_size.width.as_int(), output_size.height.as_int(), 0, 0); - } - on_change(); + /* TODO: We should, again, handle this by storing the pending size, raising a hardware-changed + * notification, and then letting the `configure()` system tear down everything and bring it back + * up at the new size. + */ } } @@ -401,10 +379,10 @@ void mgw::DisplayClient::Output::post() // Avoid throttling compositing by blocking in eglSwapBuffers(). // Instead we use the frame "done" notification. - eglSwapInterval(owner_->egldisplay, 0); + // TODO: We probably don't need to do this every frame! + eglSwapInterval(provider->get_egl_display(), 0); - if (eglSwapBuffers(owner_->egldisplay, eglsurface) != EGL_TRUE) - BOOST_THROW_EXCEPTION(egl_error("Failed to perform buffer swap")); + next_frame->swap_buffers(); frame_sync->wait_for_done(); } @@ -431,18 +409,42 @@ auto mgw::DisplayClient::Output::transformation() const -> glm::mat2 auto mgw::DisplayClient::Output::display_provider() const -> std::shared_ptr { - return {}; + return provider; +} + +namespace +{ +template +auto unique_ptr_cast(std::unique_ptr ptr) -> std::unique_ptr +{ + From* unowned_src = ptr.release(); + if (auto to_src = dynamic_cast(unowned_src)) + { + return std::unique_ptr{to_src}; + } + delete unowned_src; + BOOST_THROW_EXCEPTION(( + std::bad_cast())); +} } void mgw::DisplayClient::Output::set_next_image(std::unique_ptr content) { - next_frame = std::move(content); + if (auto wl_content = unique_ptr_cast(std::move(content))) + { + next_frame = std::move(wl_content); + } + else + { + BOOST_THROW_EXCEPTION((std::logic_error{"set_next_image called with a Framebuffer from a different platform"})); + } } mgw::DisplayClient::DisplayClient( wl_display* display, - std::shared_ptr const& gl_config) : + std::shared_ptr provider) : display{display}, + provider{std::move(provider)}, keyboard_context_{xkb_context_new(XKB_CONTEXT_NO_FLAGS)}, registry{nullptr, [](auto){}} { @@ -460,46 +462,6 @@ mgw::DisplayClient::DisplayClient( wl_registry_add_listener(registry.get(), ®istry_listener, this); - auto format = shm_pixel_format; - - EGLint const cfgattribs[] = - { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, red_channel_depth(format), - EGL_GREEN_SIZE, green_channel_depth(format), - EGL_BLUE_SIZE, blue_channel_depth(format), - EGL_ALPHA_SIZE, alpha_channel_depth(format), - EGL_DEPTH_SIZE, gl_config->depth_buffer_bits(), - EGL_STENCIL_SIZE, gl_config->stencil_buffer_bits(), - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - eglBindAPI(EGL_OPENGL_ES_API); - - egldisplay = eglGetDisplay((EGLNativeDisplayType)(display)); - if (egldisplay == EGL_NO_DISPLAY) - BOOST_THROW_EXCEPTION(egl_error("Can't eglGetDisplay")); - - EGLint major; - EGLint minor; - if (!eglInitialize(egldisplay, &major, &minor)) - BOOST_THROW_EXCEPTION(egl_error("Can't eglInitialize")); - - if (std::tuple{major, minor} < std::tuple{1, 4}) - BOOST_THROW_EXCEPTION(egl_error("EGL version is not at least 1.4")); - - EGLint neglconfigs; - if (!eglChooseConfig(egldisplay, cfgattribs, &eglconfig, 1, &neglconfigs)) - BOOST_THROW_EXCEPTION(egl_error("Could not eglChooseConfig")); - - if (neglconfigs == 0) - BOOST_THROW_EXCEPTION(egl_error("No EGL config available")); - - eglctx = eglCreateContext(egldisplay, eglconfig, EGL_NO_CONTEXT, ctxattribs); - if (eglctx == EGL_NO_CONTEXT) - BOOST_THROW_EXCEPTION(egl_error("eglCreateContext failed")); - auto const has_uninitialized_output = [&]() { for (auto const& pair : bound_outputs) @@ -533,22 +495,17 @@ void mgw::DisplayClient::on_display_config_changed() void mgw::DisplayClient::delete_outputs_to_be_deleted() { - std::lock_guard{outputs_mutex}; + std::lock_guard lock{outputs_mutex}; outputs_to_be_deleted.clear(); } mgw::DisplayClient::~DisplayClient() { - eglMakeCurrent(egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - { std::lock_guard lock{outputs_mutex}; bound_outputs.clear(); } registry.reset(); - - eglDestroyContext(egldisplay, eglctx); - eglTerminate(egldisplay); } void mgw::DisplayClient::new_global( @@ -590,8 +547,7 @@ void mgw::DisplayClient::new_global( id, std::make_unique( output, - self, - [self]() { self->on_display_config_changed(); }))); + self))); } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { diff --git a/src/platforms/wayland/displayclient.h b/src/platforms/wayland/displayclient.h index f95c955d2f0..ffb068bbca6 100644 --- a/src/platforms/wayland/displayclient.h +++ b/src/platforms/wayland/displayclient.h @@ -46,19 +46,22 @@ namespace graphics { namespace wayland { +class WlDisplayProvider; class DisplayClient : public Executor { public: - DisplayClient(wl_display* display, - std::shared_ptr const& gl_config); + DisplayClient( + wl_display* display, + std::shared_ptr provider); virtual ~DisplayClient(); protected: wl_display* const display; + std::shared_ptr const provider; auto display_configuration() const -> std::unique_ptr; void for_each_display_sync_group(const std::function& f); @@ -170,10 +173,6 @@ class DisplayClient std::unordered_map> bound_outputs; std::mutex mutable config_change_handlers_mutex; std::vector config_change_handlers; - - EGLDisplay egldisplay; - EGLConfig eglconfig; - EGLContext eglctx; }; } } diff --git a/src/platforms/wayland/platform.cpp b/src/platforms/wayland/platform.cpp index d3f7041d3e8..6b428841e3f 100644 --- a/src/platforms/wayland/platform.cpp +++ b/src/platforms/wayland/platform.cpp @@ -14,17 +14,64 @@ * along with this program. If not, see . */ +#include + +#include "wl_egl_display_provider.h" #include "platform.h" #include "display.h" +#include "mir/graphics/egl_error.h" #include "mir/graphics/platform.h" namespace mg = mir::graphics; namespace mgw = mir::graphics::wayland; using namespace std::literals; +namespace +{ +auto make_initialised_egl_display(struct wl_display* wl_display) -> EGLDisplay +{ + EGLDisplay dpy; + // TODO: When we require EGL 1.5 support this check can go away; the EXT_platform_base is core in EGL 1.5 + if (epoxy_has_egl_extension(EGL_NO_DISPLAY, "EGL_EXT_platform_base")) + { + if (!epoxy_has_egl_extension(EGL_NO_DISPLAY, "EGL_EXT_platform_wayland")) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"EGL implementation does not have Wayland support"})); + } + dpy = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_KHR, wl_display, nullptr); + } + else + { + dpy = eglGetDisplay(wl_display); + } + if (dpy == EGL_NO_DISPLAY) + { + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to get EGLDisplay"))); + } + + std::tuple version; + if (eglInitialize(dpy, &std::get<0>(version), &std::get<1>(version)) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to initialise EGLDisplay"))); + } + if (version < std::make_tuple(1, 4)) + { + BOOST_THROW_EXCEPTION(( + std::runtime_error{ + "EGL version unsupported. Require at least 1.4, got " + + std::to_string(std::get<0>(version)) + + "." + + std::to_string(std::get<1>(version)) + })); + } + return dpy; +} +} + mgw::Platform::Platform(struct wl_display* const wl_display, std::shared_ptr const& report) : wl_display{wl_display}, - report{report} + report{report}, + provider{std::make_shared(make_initialised_egl_display(wl_display))} { } @@ -32,11 +79,10 @@ mir::UniqueModulePtr mgw::Platform::create_display( std::shared_ptr const&, std::shared_ptr const& gl_config) { - return mir::make_module_ptr(wl_display, gl_config, report); + return mir::make_module_ptr(wl_display, provider, gl_config, report); } auto mgw::Platform::interface_for() -> std::shared_ptr { - return nullptr; + return provider; } - diff --git a/src/platforms/wayland/platform.h b/src/platforms/wayland/platform.h index 1ac260d8a53..3bbc3a3347d 100644 --- a/src/platforms/wayland/platform.h +++ b/src/platforms/wayland/platform.h @@ -18,14 +18,8 @@ #define MIR_GRAPHICS_WAYLAND_PLATFORM_H_ #include "mir/graphics/platform.h" -#include "mir/options/option.h" #include "mir/graphics/display.h" -#include "mir/fd.h" -#include "displayclient.h" - -#include -#include #include #include @@ -35,13 +29,14 @@ namespace graphics { namespace wayland { +class WlDisplayProvider; + class Platform : public graphics::DisplayPlatform { public: Platform(struct wl_display* const wl_display, std::shared_ptr const& report); ~Platform() = default; - UniqueModulePtr create_display( std::shared_ptr const& initial_conf_policy, std::shared_ptr const& gl_config) override; @@ -52,6 +47,8 @@ class Platform : public graphics::DisplayPlatform struct wl_display* const wl_display; std::shared_ptr const report; + + std::shared_ptr const provider; }; } } From c08fba98f41c6c11a40e2d662b872c1c6055b6ff Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 2 Oct 2023 21:02:01 +1100 Subject: [PATCH 066/108] =?UTF-8?q?platforms/wayland:=20Actually=20add=20n?= =?UTF-8?q?ew=20files=20to=20git=20=F0=9F=A4=A6=E2=80=8D=E2=99=80=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wayland/wl_egl_display_provider.cpp | 217 ++++++++++++++++++ .../wayland/wl_egl_display_provider.h | 55 +++++ 2 files changed, 272 insertions(+) create mode 100644 src/platforms/wayland/wl_egl_display_provider.cpp create mode 100644 src/platforms/wayland/wl_egl_display_provider.h diff --git a/src/platforms/wayland/wl_egl_display_provider.cpp b/src/platforms/wayland/wl_egl_display_provider.cpp new file mode 100644 index 00000000000..8fbb66bb88e --- /dev/null +++ b/src/platforms/wayland/wl_egl_display_provider.cpp @@ -0,0 +1,217 @@ +#include "wl_egl_display_provider.h" + +#include "mir/graphics/egl_error.h" +#include "mir/graphics/gl_config.h" +#include "mir/graphics/platform.h" +#include +#include + +namespace mg = mir::graphics; +namespace mgw = mir::graphics::wayland; +namespace geom = mir::geometry; + +class mgw::WlDisplayProvider::Framebuffer::EGLState +{ +public: + EGLState(EGLDisplay dpy, EGLContext ctx, EGLSurface surf) + : dpy{dpy}, + ctx{ctx}, + surf{surf} + { + } + + ~EGLState() + { + eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(dpy, ctx); + eglDestroySurface(dpy, surf); + } + + EGLDisplay const dpy; + EGLContext const ctx; + EGLSurface const surf; +}; + +mgw::WlDisplayProvider::Framebuffer::Framebuffer(EGLDisplay dpy, EGLContext ctx, EGLSurface surf, geom::Size size) + : Framebuffer(std::make_shared(dpy, ctx, surf), size) +{ +} + +mgw::WlDisplayProvider::Framebuffer::Framebuffer(std::shared_ptr state, geom::Size size) + : state{std::move(state)}, + size_{size} +{ +} + +auto mgw::WlDisplayProvider::Framebuffer::size() const -> geom::Size +{ + return size_; +} + +void mgw::WlDisplayProvider::Framebuffer::make_current() +{ + if (eglMakeCurrent(state->dpy, state->surf, state->surf, state->ctx) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION((mg::egl_error("eglMakeCurrent failed"))); + } +} + +void mgw::WlDisplayProvider::Framebuffer::release_current() +{ + if (eglMakeCurrent(state->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to release EGL context")); + } +} + +void mgw::WlDisplayProvider::Framebuffer::swap_buffers() +{ + if (eglSwapBuffers(state->dpy, state->surf) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION((mg::egl_error("eglSwapBuffers failed"))); + } +} + +auto mgw::WlDisplayProvider::Framebuffer::clone_handle() -> std::unique_ptr +{ + return std::unique_ptr{new Framebuffer(state, size_)}; +} + +class mgw::WlDisplayProvider::EGLDisplayProvider : public mg::GenericEGLDisplayProvider +{ +public: + EGLDisplayProvider(EGLDisplay dpy); + + EGLDisplayProvider( + EGLDisplayProvider const& from, + struct wl_surface* surface, + geometry::Size size); + + ~EGLDisplayProvider(); + + auto get_egl_display() -> EGLDisplay override; + + auto alloc_framebuffer(GLConfig const& config, EGLContext share_context) -> std::unique_ptr override; + +private: + EGLDisplay const dpy; + + struct OutputContext + { + struct wl_egl_window* wl_window; + geometry::Size size; + }; + std::optional const output; +}; + +mgw::WlDisplayProvider::EGLDisplayProvider::EGLDisplayProvider(EGLDisplay dpy) + : dpy{dpy} +{ +} + +mgw::WlDisplayProvider::EGLDisplayProvider::EGLDisplayProvider( + EGLDisplayProvider const& from, + struct wl_surface* surface, + geometry::Size size) + : dpy{from.dpy}, + output{ + std::make_optional( + wl_egl_window_create(surface, size.width.as_int(), size.height.as_int()), + size)} +{ +} + +mgw::WlDisplayProvider::EGLDisplayProvider::~EGLDisplayProvider() +{ + if (output) + { + wl_egl_window_destroy(output->wl_window); + } +} + +auto mgw::WlDisplayProvider::EGLDisplayProvider::alloc_framebuffer( + GLConfig const& config, + EGLContext share_context) -> std::unique_ptr +{ + if (!output) + { + BOOST_THROW_EXCEPTION((std::logic_error{"Ooops"})); + } + auto const [wl_window, size] = *output; + + EGLint const config_attr[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, config.depth_buffer_bits(), + EGL_STENCIL_SIZE, config.stencil_buffer_bits(), + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + eglBindAPI(EGL_OPENGL_ES_API); + + static const EGLint context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint num_egl_configs; + EGLConfig egl_config; + if (eglChooseConfig(dpy, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE || + num_egl_configs != 1) + { + BOOST_THROW_EXCEPTION(egl_error("Failed to choose ARGB EGL config")); + } + + auto egl_context = eglCreateContext(dpy, egl_config, share_context, context_attr); + if (egl_context == EGL_NO_CONTEXT) + BOOST_THROW_EXCEPTION(egl_error("Failed to create EGL context")); + + auto surf = eglCreatePlatformWindowSurface(dpy, egl_config, wl_window, nullptr); + if (surf == EGL_NO_SURFACE) + { + BOOST_THROW_EXCEPTION(egl_error("Failed to create EGL surface")); + } + + return std::make_unique( + dpy, + egl_context, + surf, + size); +} + +auto mgw::WlDisplayProvider::EGLDisplayProvider::get_egl_display() -> EGLDisplay +{ + return dpy; +} + +mgw::WlDisplayProvider::WlDisplayProvider(EGLDisplay dpy) + : egl_provider{std::make_shared(dpy)} +{ +} + +mgw::WlDisplayProvider::WlDisplayProvider( + WlDisplayProvider const& from, + struct wl_surface* surface, + geometry::Size size) + : egl_provider{std::make_shared(*from.egl_provider, surface, size)} +{ +} + +auto mgw::WlDisplayProvider::get_egl_display() const -> EGLDisplay +{ + return egl_provider->get_egl_display(); +} + +auto mgw::WlDisplayProvider::maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr +{ + if (dynamic_cast(&type_tag)) + { + return egl_provider; + } + return nullptr; +} diff --git a/src/platforms/wayland/wl_egl_display_provider.h b/src/platforms/wayland/wl_egl_display_provider.h new file mode 100644 index 00000000000..b4caf90242f --- /dev/null +++ b/src/platforms/wayland/wl_egl_display_provider.h @@ -0,0 +1,55 @@ + +#include "mir/graphics/platform.h" + +#include + +namespace mir::graphics::wayland +{ + +class WlDisplayProvider : public DisplayInterfaceProvider +{ +public: + WlDisplayProvider(EGLDisplay dpy); + + WlDisplayProvider( + WlDisplayProvider const& from, + struct wl_surface* surface, + geometry::Size size); + + auto get_egl_display() const -> EGLDisplay; + + auto maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) + -> std::shared_ptr override; + + class Framebuffer : public GenericEGLDisplayProvider::EGLFramebuffer + { + public: + /** + * Handle for an EGL output surface + * + * \note This takes ownership of \param ctx and \param surf; when the + * final handle generated from this Framebuffer is released, + * the EGL resources \param ctx and \param surff will be freed. + */ + Framebuffer(EGLDisplay dpy, EGLContext ctx, EGLSurface surf, geometry::Size size); + + auto size() const -> geometry::Size override; + + void make_current() override; + void release_current() override; + auto clone_handle() -> std::unique_ptr override; + + void swap_buffers(); + private: + class EGLState; + Framebuffer(std::shared_ptr surf, geometry::Size size); + + std::shared_ptr const state; + geometry::Size const size_; + }; +private: + class EGLDisplayProvider; + + std::shared_ptr const egl_provider; +}; +} From feab24b84957ed8dd52d664ec2ee623fc1e3b151 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Wed, 4 Oct 2023 13:08:46 +1100 Subject: [PATCH 067/108] =?UTF-8?q?performance-tests:=20Explicitly=20selec?= =?UTF-8?q?t=20hosted=20driver=20for=20nested=20tests.=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=EE=82=B6=20=E2=99=A5=2013:05?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we can load multiple platforms at once things get confused in the nested performance tests. Explicitly select the Wayland platform that will test the Wayland implementation provided by the host Mir. It might additionally be worth making Mir handle having multiple hosted platforms loaded at once, but this is sensible for now --- tests/performance-tests/test_glmark2-es2.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/performance-tests/test_glmark2-es2.cpp b/tests/performance-tests/test_glmark2-es2.cpp index 74bfea04d0d..9004bd25277 100644 --- a/tests/performance-tests/test_glmark2-es2.cpp +++ b/tests/performance-tests/test_glmark2-es2.cpp @@ -205,6 +205,7 @@ struct HostedGLMark2Wayland : GLMark2Wayland EXPECT_TRUE(spin_wait_for_condition_or_timeout(wait_for_host, 10s)); add_to_environment("MIR_SERVER_WAYLAND_HOST", host_socket); + add_to_environment("MIR_SERVER_PLATFORM_DISPLAY_LIBS", "mir:wayland"); auto args = get_nested_args(); server.set_command_line(args.size(), args.data()); From dfb228b9dfbb8838ee534392a514ab4ed573c014 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Wed, 4 Oct 2023 17:33:26 +1100 Subject: [PATCH 068/108] platform/linux-dma-buf: Drop use of mir::EGLExtensions Due to `#define` trickery, `#include`ing `mir::EGLExtensions` in a file which also `#include`s libepoxy will result in ODR violations. Since libepoxy exposes a more pleasant interface than `mir::EGLExtensions` anyway, fully convert linux-dma-buf to libepoxy and drop `EGLExtensions` usage. --- include/platform/mir/graphics/linux_dmabuf.h | 4 -- src/platform/graphics/egl_buffer_copy.cpp | 2 - src/platform/graphics/linux_dmabuf.cpp | 48 +++++++------------ src/platforms/gbm-kms/server/kms/platform.cpp | 6 +-- .../rendering_platform.cpp | 6 --- 5 files changed, 18 insertions(+), 48 deletions(-) diff --git a/include/platform/mir/graphics/linux_dmabuf.h b/include/platform/mir/graphics/linux_dmabuf.h index ef08103b3c0..e3f09576d57 100644 --- a/include/platform/mir/graphics/linux_dmabuf.h +++ b/include/platform/mir/graphics/linux_dmabuf.h @@ -26,7 +26,6 @@ #include "mir/graphics/buffer.h" #include "mir/graphics/drm_formats.h" -#include "mir/graphics/egl_extensions.h" namespace mir { @@ -61,8 +60,6 @@ class DMABufEGLProvider : public std::enable_shared_from_this std::function(DRMFormat, std::span, geometry::Size)>; DMABufEGLProvider( EGLDisplay dpy, - std::shared_ptr egl_extensions, - EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext, std::shared_ptr egl_delegate, EGLImageAllocator allocate_importable_image); @@ -89,7 +86,6 @@ class DMABufEGLProvider : public std::enable_shared_from_this auto supported_formats() const -> DmaBufFormatDescriptors const&; private: EGLDisplay const dpy; - std::shared_ptr const egl_extensions; std::unique_ptr const formats; std::shared_ptr const egl_delegate; EGLImageAllocator allocate_importable_image; diff --git a/src/platform/graphics/egl_buffer_copy.cpp b/src/platform/graphics/egl_buffer_copy.cpp index c54bd36bdca..b16fecc49ac 100644 --- a/src/platform/graphics/egl_buffer_copy.cpp +++ b/src/platform/graphics/egl_buffer_copy.cpp @@ -20,7 +20,6 @@ #include "egl_buffer_copy.h" #include "mir/graphics/egl_context_executor.h" #include "mir/graphics/egl_error.h" -#include "mir/graphics/egl_extensions.h" #include #include @@ -315,7 +314,6 @@ class mg::EGLBufferCopier::Impl private: std::shared_ptr const egl_delegate; // State accessed only on the EGL thread - std::shared_ptr const egl_extensions; struct State { ProgramHandle prog; diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index da69a2ba86e..06f94a5f259 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -25,7 +25,6 @@ #include "wayland_wrapper.h" #include "mir/wayland/protocol_error.h" #include "mir/wayland/client.h" -#include "mir/graphics/egl_extensions.h" #include "mir/graphics/egl_error.h" #include "mir/graphics/texture.h" #include "mir/graphics/program_factory.h" @@ -35,6 +34,7 @@ #include "mir/graphics/egl_context_executor.h" #include #include +#include #define MIR_LOG_COMPONENT "linux-dmabuf-import" #include "mir/log.h" @@ -56,11 +56,10 @@ class mg::DmaBufFormatDescriptors { public: DmaBufFormatDescriptors( - EGLDisplay dpy, - mg::EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext) + EGLDisplay dpy) { EGLint num_formats; - if (dmabuf_ext.eglQueryDmaBufFormatsExt(dpy, 0, nullptr, &num_formats) != EGL_TRUE) + if (eglQueryDmaBufFormatsEXT(dpy, 0, nullptr, &num_formats) != EGL_TRUE) { BOOST_THROW_EXCEPTION((mg::egl_error("Failed to query number of dma-buf formats"))); } @@ -71,7 +70,7 @@ class mg::DmaBufFormatDescriptors resize(num_formats); EGLint returned_formats; - if (dmabuf_ext.eglQueryDmaBufFormatsExt( + if (eglQueryDmaBufFormatsEXT( dpy, formats.size(), formats.data(), &returned_formats) != EGL_TRUE) { BOOST_THROW_EXCEPTION((mg::egl_error("Failed to list dma-buf formats"))); @@ -92,7 +91,7 @@ class mg::DmaBufFormatDescriptors EGLint num_modifiers; if ( - dmabuf_ext.eglQueryDmaBufModifiersExt( + eglQueryDmaBufModifiersEXT( dpy, format, 0, @@ -107,7 +106,7 @@ class mg::DmaBufFormatDescriptors EGLint returned_modifiers; if ( - dmabuf_ext.eglQueryDmaBufModifiersExt( + eglQueryDmaBufModifiersEXT( dpy, format, modifiers.size(), @@ -405,8 +404,7 @@ auto import_egl_image( mg::DRMFormat format, std::optional modifier, std::vector const& planes, - EGLDisplay dpy, - mg::EGLExtensions const& egl_extensions) -> EGLImageKHR + EGLDisplay dpy) -> EGLImageKHR { std::vector attributes; @@ -437,7 +435,7 @@ auto import_egl_image( } } attributes.push_back(EGL_NONE); - EGLImage image = egl_extensions.base(dpy).eglCreateImageKHR( + EGLImage image = eglCreateImageKHR( dpy, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, @@ -843,7 +841,6 @@ class DMABufTex : public mg::gl::Texture public: DMABufTex( EGLDisplay dpy, - mg::EGLExtensions const& extensions, mg::DMABufBuffer const& dma_buf, BufferGLDescription const& descriptor, std::shared_ptr egl_delegate) @@ -862,15 +859,14 @@ class DMABufTex : public mg::gl::Texture dma_buf.format(), dma_buf.modifier(), dma_buf.planes(), - dpy, - extensions); + dpy); glBindTexture(target, tex); - extensions.base(dpy).glEGLImageTargetTexture2DOES(target, image); + glEGLImageTargetTexture2DOES(target, image); // tex is now an EGLImage sibling, so we can free the EGLImage without // freeing the backing data. - extensions.base(dpy).eglDestroyImageKHR(dpy, image); + eglDestroyImageKHR(dpy, image); glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -927,7 +923,6 @@ class DmabufTexBuffer : // Note: Must be called with a current EGL context DmabufTexBuffer( EGLDisplay dpy, - mg::EGLExtensions const& extensions, mg::DMABufBuffer const& dma_buf, BufferGLDescription const& descriptor, std::shared_ptr provider, @@ -935,7 +930,7 @@ class DmabufTexBuffer : std::function&& on_consumed, std::function&& on_release) : dpy{dpy}, - tex{dpy, extensions, dma_buf, descriptor, std::move(egl_delegate)}, + tex{dpy, dma_buf, descriptor, std::move(egl_delegate)}, provider_{std::move(provider)}, on_consumed{std::move(on_consumed)}, on_release{std::move(on_release)}, @@ -1108,13 +1103,10 @@ void mg::LinuxDmaBufUnstable::bind(wl_resource* new_resource) mg::DMABufEGLProvider::DMABufEGLProvider( EGLDisplay dpy, - std::shared_ptr egl_extensions, - mg::EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext, std::shared_ptr egl_delegate, EGLImageAllocator allocate_importable_image) : dpy{dpy}, - egl_extensions{std::move(egl_extensions)}, - formats{std::make_unique(dpy, dmabuf_ext)}, + formats{std::make_unique(dpy)}, egl_delegate{std::move(egl_delegate)}, allocate_importable_image{std::move(allocate_importable_image)}, blitter{std::make_unique(this->egl_delegate)} @@ -1140,7 +1132,6 @@ auto mg::DMABufEGLProvider::import_dma_buf( *this); return std::make_shared( dpy, - *egl_extensions, dma_buf, *descriptor, shared_from_this(), @@ -1156,12 +1147,11 @@ void mg::DMABufEGLProvider::validate_import(DMABufBuffer const& dma_buf) dma_buf.format(), dma_buf.modifier(), dma_buf.planes(), - dpy, - *egl_extensions); + dpy); if (image != EGL_NO_IMAGE_KHR) { // We can throw this image away immediately - egl_extensions->base(dpy).eglDestroyImageKHR(dpy, image); + eglDestroyImageKHR(dpy, image); } } @@ -1186,7 +1176,6 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) dmabuf_tex->as_texture(); return std::make_shared( dpy, - *egl_extensions, *dmabuf_tex, *descriptor, egl_delegate); @@ -1233,15 +1222,13 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) dmabuf_tex->format(), dmabuf_tex->modifier(), dmabuf_tex->planes(), - importing_provider->dpy, - *importing_provider->egl_extensions); + importing_provider->dpy); auto importable_image = import_egl_image( importable_buf->size().width.as_int(), importable_buf->size().height.as_int(), importable_buf->format(), importable_buf->modifier(), importable_buf->planes(), - importing_provider->dpy, - *importing_provider->egl_extensions); + importing_provider->dpy); auto sync = importing_provider->blitter->blit(src_image, importable_image, dmabuf_tex->size()); if (sync) { @@ -1263,7 +1250,6 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) dmabuf_tex->as_texture(); return std::make_shared( dpy, - *egl_extensions, *importable_dmabuf, *descriptor, egl_delegate); diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 30015b19a8a..ccf28ce0ae3 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -291,17 +291,13 @@ auto alloc_dma_buf( auto maybe_make_dmabuf_provider( std::shared_ptr gbm, EGLDisplay dpy, - std::shared_ptr egl_extensions, std::shared_ptr egl_delegate) -> std::shared_ptr { try { - mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; return std::make_shared( dpy, - std::move(egl_extensions), - modifier_ext, std::move(egl_delegate), [gbm](mg::DRMFormat format, std::span modifiers, mir::geometry::Size size) -> std::shared_ptr @@ -336,7 +332,7 @@ mgg::RenderingPlatform::RenderingPlatform( bound_display{std::visit(display_provider_or_nothing{}, hw)}, share_ctx{std::make_unique(initialise_egl(dpy_for_gbm_device(device.get()), 1, 4))}, egl_delegate{std::make_shared(share_ctx->make_share_context())}, - dmabuf_provider{maybe_make_dmabuf_provider(device, share_ctx->egl_display(), std::make_shared(), egl_delegate)} + dmabuf_provider{maybe_make_dmabuf_provider(device, share_ctx->egl_display(), egl_delegate)} { } diff --git a/src/platforms/renderer-generic-egl/rendering_platform.cpp b/src/platforms/renderer-generic-egl/rendering_platform.cpp index db499a42ebb..23df957fa73 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.cpp +++ b/src/platforms/renderer-generic-egl/rendering_platform.cpp @@ -16,7 +16,6 @@ #include "rendering_platform.h" #include "buffer_allocator.h" -#include "mir/graphics/egl_extensions.h" #include "mir/graphics/platform.h" #include "mir/graphics/egl_error.h" #include "mir/renderer/gl/context.h" @@ -136,17 +135,13 @@ class SurfacelessEGLContext : public mir::renderer::gl::Context auto maybe_make_dmabuf_provider( EGLDisplay dpy, - std::shared_ptr egl_extensions, std::shared_ptr egl_delegate) -> std::shared_ptr { try { - mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; return std::make_shared( dpy, - std::move(egl_extensions), - modifier_ext, std::move(egl_delegate), [](mg::DRMFormat, std::span, geom::Size) -> std::shared_ptr { @@ -173,7 +168,6 @@ mge::RenderingPlatform::RenderingPlatform(std::vector(), std::make_shared(ctx->make_share_context()))} { } From 80262c69f823daa6025ac48fa9dcf5704d2d4ef7 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Wed, 4 Oct 2023 17:33:58 +1100 Subject: [PATCH 069/108] Trivial: fix trailing whitespace --- src/platforms/renderer-generic-egl/rendering_platform.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platforms/renderer-generic-egl/rendering_platform.cpp b/src/platforms/renderer-generic-egl/rendering_platform.cpp index 23df957fa73..caf20b18dc8 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.cpp +++ b/src/platforms/renderer-generic-egl/rendering_platform.cpp @@ -50,7 +50,7 @@ auto egl_display_from_platforms(std::vector share_context) -> EGLContext { @@ -69,12 +69,12 @@ auto make_share_only_context(EGLDisplay dpy, std::optional share_con EGLConfig cfg; 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"))); } - + auto ctx = eglCreateContext(dpy, cfg, share_context.value_or(EGL_NO_CONTEXT), context_attr); if (ctx == EGL_NO_CONTEXT) { From 54668847756613022c1e2cd2b632c777a987f5fb Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Wed, 4 Oct 2023 17:34:21 +1100 Subject: [PATCH 070/108] platforms/gbm-kms: Drop some unused headers --- src/platforms/gbm-kms/server/kms/platform.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index ccf28ce0ae3..a7f260fa6a8 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -24,9 +24,7 @@ #include "mir/graphics/egl_context_executor.h" #include "mir/graphics/platform.h" #include "mir/graphics/texture.h" -#include "mir/renderer/gl/context.h" #include "mir/udev/wrapper.h" -#include "kms_framebuffer.h" #include "mir/graphics/egl_error.h" #include "mir/graphics/egl_extensions.h" #include "one_shot_device_observer.h" From 4de2f9329967c517455a8b98f6bb8396359ae077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sawicz=20=28Saviq=29?= Date: Wed, 4 Oct 2023 19:49:39 +0200 Subject: [PATCH 071/108] wayland: add missing include --- src/platforms/wayland/wl_egl_display_provider.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platforms/wayland/wl_egl_display_provider.cpp b/src/platforms/wayland/wl_egl_display_provider.cpp index 8fbb66bb88e..0e3babd6b4a 100644 --- a/src/platforms/wayland/wl_egl_display_provider.cpp +++ b/src/platforms/wayland/wl_egl_display_provider.cpp @@ -6,6 +6,8 @@ #include #include +#include + namespace mg = mir::graphics; namespace mgw = mir::graphics::wayland; namespace geom = mir::geometry; From cec1335490e7e5a1bdd6a93b9cee09ae8e8991de Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 5 Oct 2023 12:31:34 +1100 Subject: [PATCH 072/108] Revert "platform/linux-dma-buf: Drop use of mir::EGLExtensions" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit libepoxy is much more convenient than `mir::EGLExtensions` in all but one, very important respect: you can't call *any* EGL entrypoint using libepoxy without a current EGL context. Since these EGL APIs don't actually *require* a current EGL context, we frequently call them without a current context. 🤦‍♀️ --- include/platform/mir/graphics/linux_dmabuf.h | 4 ++ src/platform/graphics/egl_buffer_copy.cpp | 2 + src/platform/graphics/linux_dmabuf.cpp | 48 ++++++++++++------- src/platforms/gbm-kms/server/kms/platform.cpp | 6 ++- .../rendering_platform.cpp | 6 +++ 5 files changed, 48 insertions(+), 18 deletions(-) diff --git a/include/platform/mir/graphics/linux_dmabuf.h b/include/platform/mir/graphics/linux_dmabuf.h index e3f09576d57..ef08103b3c0 100644 --- a/include/platform/mir/graphics/linux_dmabuf.h +++ b/include/platform/mir/graphics/linux_dmabuf.h @@ -26,6 +26,7 @@ #include "mir/graphics/buffer.h" #include "mir/graphics/drm_formats.h" +#include "mir/graphics/egl_extensions.h" namespace mir { @@ -60,6 +61,8 @@ class DMABufEGLProvider : public std::enable_shared_from_this std::function(DRMFormat, std::span, geometry::Size)>; DMABufEGLProvider( EGLDisplay dpy, + std::shared_ptr egl_extensions, + EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext, std::shared_ptr egl_delegate, EGLImageAllocator allocate_importable_image); @@ -86,6 +89,7 @@ class DMABufEGLProvider : public std::enable_shared_from_this auto supported_formats() const -> DmaBufFormatDescriptors const&; private: EGLDisplay const dpy; + std::shared_ptr const egl_extensions; std::unique_ptr const formats; std::shared_ptr const egl_delegate; EGLImageAllocator allocate_importable_image; diff --git a/src/platform/graphics/egl_buffer_copy.cpp b/src/platform/graphics/egl_buffer_copy.cpp index b16fecc49ac..c54bd36bdca 100644 --- a/src/platform/graphics/egl_buffer_copy.cpp +++ b/src/platform/graphics/egl_buffer_copy.cpp @@ -20,6 +20,7 @@ #include "egl_buffer_copy.h" #include "mir/graphics/egl_context_executor.h" #include "mir/graphics/egl_error.h" +#include "mir/graphics/egl_extensions.h" #include #include @@ -314,6 +315,7 @@ class mg::EGLBufferCopier::Impl private: std::shared_ptr const egl_delegate; // State accessed only on the EGL thread + std::shared_ptr const egl_extensions; struct State { ProgramHandle prog; diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index 06f94a5f259..da69a2ba86e 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -25,6 +25,7 @@ #include "wayland_wrapper.h" #include "mir/wayland/protocol_error.h" #include "mir/wayland/client.h" +#include "mir/graphics/egl_extensions.h" #include "mir/graphics/egl_error.h" #include "mir/graphics/texture.h" #include "mir/graphics/program_factory.h" @@ -34,7 +35,6 @@ #include "mir/graphics/egl_context_executor.h" #include #include -#include #define MIR_LOG_COMPONENT "linux-dmabuf-import" #include "mir/log.h" @@ -56,10 +56,11 @@ class mg::DmaBufFormatDescriptors { public: DmaBufFormatDescriptors( - EGLDisplay dpy) + EGLDisplay dpy, + mg::EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext) { EGLint num_formats; - if (eglQueryDmaBufFormatsEXT(dpy, 0, nullptr, &num_formats) != EGL_TRUE) + if (dmabuf_ext.eglQueryDmaBufFormatsExt(dpy, 0, nullptr, &num_formats) != EGL_TRUE) { BOOST_THROW_EXCEPTION((mg::egl_error("Failed to query number of dma-buf formats"))); } @@ -70,7 +71,7 @@ class mg::DmaBufFormatDescriptors resize(num_formats); EGLint returned_formats; - if (eglQueryDmaBufFormatsEXT( + if (dmabuf_ext.eglQueryDmaBufFormatsExt( dpy, formats.size(), formats.data(), &returned_formats) != EGL_TRUE) { BOOST_THROW_EXCEPTION((mg::egl_error("Failed to list dma-buf formats"))); @@ -91,7 +92,7 @@ class mg::DmaBufFormatDescriptors EGLint num_modifiers; if ( - eglQueryDmaBufModifiersEXT( + dmabuf_ext.eglQueryDmaBufModifiersExt( dpy, format, 0, @@ -106,7 +107,7 @@ class mg::DmaBufFormatDescriptors EGLint returned_modifiers; if ( - eglQueryDmaBufModifiersEXT( + dmabuf_ext.eglQueryDmaBufModifiersExt( dpy, format, modifiers.size(), @@ -404,7 +405,8 @@ auto import_egl_image( mg::DRMFormat format, std::optional modifier, std::vector const& planes, - EGLDisplay dpy) -> EGLImageKHR + EGLDisplay dpy, + mg::EGLExtensions const& egl_extensions) -> EGLImageKHR { std::vector attributes; @@ -435,7 +437,7 @@ auto import_egl_image( } } attributes.push_back(EGL_NONE); - EGLImage image = eglCreateImageKHR( + EGLImage image = egl_extensions.base(dpy).eglCreateImageKHR( dpy, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, @@ -841,6 +843,7 @@ class DMABufTex : public mg::gl::Texture public: DMABufTex( EGLDisplay dpy, + mg::EGLExtensions const& extensions, mg::DMABufBuffer const& dma_buf, BufferGLDescription const& descriptor, std::shared_ptr egl_delegate) @@ -859,14 +862,15 @@ class DMABufTex : public mg::gl::Texture dma_buf.format(), dma_buf.modifier(), dma_buf.planes(), - dpy); + dpy, + extensions); glBindTexture(target, tex); - glEGLImageTargetTexture2DOES(target, image); + extensions.base(dpy).glEGLImageTargetTexture2DOES(target, image); // tex is now an EGLImage sibling, so we can free the EGLImage without // freeing the backing data. - eglDestroyImageKHR(dpy, image); + extensions.base(dpy).eglDestroyImageKHR(dpy, image); glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -923,6 +927,7 @@ class DmabufTexBuffer : // Note: Must be called with a current EGL context DmabufTexBuffer( EGLDisplay dpy, + mg::EGLExtensions const& extensions, mg::DMABufBuffer const& dma_buf, BufferGLDescription const& descriptor, std::shared_ptr provider, @@ -930,7 +935,7 @@ class DmabufTexBuffer : std::function&& on_consumed, std::function&& on_release) : dpy{dpy}, - tex{dpy, dma_buf, descriptor, std::move(egl_delegate)}, + tex{dpy, extensions, dma_buf, descriptor, std::move(egl_delegate)}, provider_{std::move(provider)}, on_consumed{std::move(on_consumed)}, on_release{std::move(on_release)}, @@ -1103,10 +1108,13 @@ void mg::LinuxDmaBufUnstable::bind(wl_resource* new_resource) mg::DMABufEGLProvider::DMABufEGLProvider( EGLDisplay dpy, + std::shared_ptr egl_extensions, + mg::EGLExtensions::EXTImageDmaBufImportModifiers const& dmabuf_ext, std::shared_ptr egl_delegate, EGLImageAllocator allocate_importable_image) : dpy{dpy}, - formats{std::make_unique(dpy)}, + egl_extensions{std::move(egl_extensions)}, + formats{std::make_unique(dpy, dmabuf_ext)}, egl_delegate{std::move(egl_delegate)}, allocate_importable_image{std::move(allocate_importable_image)}, blitter{std::make_unique(this->egl_delegate)} @@ -1132,6 +1140,7 @@ auto mg::DMABufEGLProvider::import_dma_buf( *this); return std::make_shared( dpy, + *egl_extensions, dma_buf, *descriptor, shared_from_this(), @@ -1147,11 +1156,12 @@ void mg::DMABufEGLProvider::validate_import(DMABufBuffer const& dma_buf) dma_buf.format(), dma_buf.modifier(), dma_buf.planes(), - dpy); + dpy, + *egl_extensions); if (image != EGL_NO_IMAGE_KHR) { // We can throw this image away immediately - eglDestroyImageKHR(dpy, image); + egl_extensions->base(dpy).eglDestroyImageKHR(dpy, image); } } @@ -1176,6 +1186,7 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) dmabuf_tex->as_texture(); return std::make_shared( dpy, + *egl_extensions, *dmabuf_tex, *descriptor, egl_delegate); @@ -1222,13 +1233,15 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) dmabuf_tex->format(), dmabuf_tex->modifier(), dmabuf_tex->planes(), - importing_provider->dpy); + importing_provider->dpy, + *importing_provider->egl_extensions); auto importable_image = import_egl_image( importable_buf->size().width.as_int(), importable_buf->size().height.as_int(), importable_buf->format(), importable_buf->modifier(), importable_buf->planes(), - importing_provider->dpy); + importing_provider->dpy, + *importing_provider->egl_extensions); auto sync = importing_provider->blitter->blit(src_image, importable_image, dmabuf_tex->size()); if (sync) { @@ -1250,6 +1263,7 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) dmabuf_tex->as_texture(); return std::make_shared( dpy, + *egl_extensions, *importable_dmabuf, *descriptor, egl_delegate); diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index a7f260fa6a8..b91ac34976c 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -289,13 +289,17 @@ auto alloc_dma_buf( auto maybe_make_dmabuf_provider( std::shared_ptr gbm, EGLDisplay dpy, + std::shared_ptr egl_extensions, std::shared_ptr egl_delegate) -> std::shared_ptr { try { + mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; return std::make_shared( dpy, + std::move(egl_extensions), + modifier_ext, std::move(egl_delegate), [gbm](mg::DRMFormat format, std::span modifiers, mir::geometry::Size size) -> std::shared_ptr @@ -330,7 +334,7 @@ mgg::RenderingPlatform::RenderingPlatform( bound_display{std::visit(display_provider_or_nothing{}, hw)}, share_ctx{std::make_unique(initialise_egl(dpy_for_gbm_device(device.get()), 1, 4))}, egl_delegate{std::make_shared(share_ctx->make_share_context())}, - dmabuf_provider{maybe_make_dmabuf_provider(device, share_ctx->egl_display(), egl_delegate)} + dmabuf_provider{maybe_make_dmabuf_provider(device, share_ctx->egl_display(), std::make_shared(), egl_delegate)} { } diff --git a/src/platforms/renderer-generic-egl/rendering_platform.cpp b/src/platforms/renderer-generic-egl/rendering_platform.cpp index caf20b18dc8..82c18a2ff1b 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.cpp +++ b/src/platforms/renderer-generic-egl/rendering_platform.cpp @@ -16,6 +16,7 @@ #include "rendering_platform.h" #include "buffer_allocator.h" +#include "mir/graphics/egl_extensions.h" #include "mir/graphics/platform.h" #include "mir/graphics/egl_error.h" #include "mir/renderer/gl/context.h" @@ -135,13 +136,17 @@ class SurfacelessEGLContext : public mir::renderer::gl::Context auto maybe_make_dmabuf_provider( EGLDisplay dpy, + std::shared_ptr egl_extensions, std::shared_ptr egl_delegate) -> std::shared_ptr { try { + mg::EGLExtensions::EXTImageDmaBufImportModifiers modifier_ext{dpy}; return std::make_shared( dpy, + std::move(egl_extensions), + modifier_ext, std::move(egl_delegate), [](mg::DRMFormat, std::span, geom::Size) -> std::shared_ptr { @@ -168,6 +173,7 @@ mge::RenderingPlatform::RenderingPlatform(std::vector(), std::make_shared(ctx->make_share_context()))} { } From 708cc58ac045ce5f987c9ebf06e68fbcc3a04a01 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 5 Oct 2023 17:27:37 +1100 Subject: [PATCH 073/108] platform/linux-dma-buf: Drop remnant libepoxy use. We were using this for `EGL_MESA_image_dma_buf_export` entrypoints. Add that instead to `mg::EGLExtensions`. Sigh. --- .../platform/mir/graphics/egl_extensions.h | 11 +++++++- include/platform/mir/graphics/linux_dmabuf.h | 1 + src/platform/graphics/egl_extensions.cpp | 27 +++++++++++++++++++ src/platform/graphics/linux_dmabuf.cpp | 25 ++++++++++++----- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/include/platform/mir/graphics/egl_extensions.h b/include/platform/mir/graphics/egl_extensions.h index 7f9f2b9b81c..8b4a72f4e06 100644 --- a/include/platform/mir/graphics/egl_extensions.h +++ b/include/platform/mir/graphics/egl_extensions.h @@ -313,8 +313,17 @@ struct EGLExtensions PFNEGLQUERYDMABUFFORMATSEXTPROC const eglQueryDmaBufFormatsExt; PFNEGLQUERYDMABUFMODIFIERSEXTPROC const eglQueryDmaBufModifiersExt; }; -}; + struct MESADmaBufExport + { + MESADmaBufExport(EGLDisplay dpy); + + static auto extension_if_supported(EGLDisplay dpy) -> std::optional; + + PFNEGLEXPORTDMABUFIMAGEMESAPROC const eglExportDMABUFImageMESA; + PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC const eglExportDMABUFImageQueryMESA; + }; +}; } } diff --git a/include/platform/mir/graphics/linux_dmabuf.h b/include/platform/mir/graphics/linux_dmabuf.h index ef08103b3c0..9f435e81853 100644 --- a/include/platform/mir/graphics/linux_dmabuf.h +++ b/include/platform/mir/graphics/linux_dmabuf.h @@ -90,6 +90,7 @@ class DMABufEGLProvider : public std::enable_shared_from_this private: EGLDisplay const dpy; std::shared_ptr const egl_extensions; + std::optional const dmabuf_export_ext; std::unique_ptr const formats; std::shared_ptr const egl_delegate; EGLImageAllocator allocate_importable_image; diff --git a/src/platform/graphics/egl_extensions.cpp b/src/platform/graphics/egl_extensions.cpp index 3b3f0289b4b..bfc077c4491 100644 --- a/src/platform/graphics/egl_extensions.cpp +++ b/src/platform/graphics/egl_extensions.cpp @@ -201,3 +201,30 @@ mg::EGLExtensions::EXTImageDmaBufImportModifiers::EXTImageDmaBufImportModifiers( std::runtime_error{"EGL_EXT_image_dma_buf_import_modifiers not supported"})); } } + +mg::EGLExtensions::MESADmaBufExport::MESADmaBufExport(EGLDisplay dpy) + : eglExportDMABUFImageMESA{ + reinterpret_cast( + eglGetProcAddress("eglExportDMABUFImageMESA"))}, + eglExportDMABUFImageQueryMESA{ + reinterpret_cast( + eglGetProcAddress("eglExportDMABUFImageQueryMESA"))} + { + if (!strstr(eglQueryString(dpy, EGL_EXTENSIONS), "EGL_MESA_image_dma_buf_export")) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Missing required EGL_MESA_image_dma_buf_export extension"})); + } + } + +auto mg::EGLExtensions::MESADmaBufExport::extension_if_supported(EGLDisplay dpy) -> std::optional +{ + try + { + return MESADmaBufExport{dpy}; + } + catch (std::runtime_error const&) + { + return std::nullopt; + } +} + diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index da69a2ba86e..af74e2ba6a7 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -14,9 +14,6 @@ * along with this program. If not, see . */ -#include -#include - #include "mir/graphics/linux_dmabuf.h" #include "mir/fd.h" #include "mir/graphics/drm_formats.h" @@ -33,7 +30,10 @@ #include "mir/graphics/buffer_basic.h" #include "mir/graphics/dmabuf_buffer.h" #include "mir/graphics/egl_context_executor.h" + +#include #include +#include #include #define MIR_LOG_COMPONENT "linux-dmabuf-import" @@ -316,14 +316,18 @@ class DMABuf : public mg::DMABufBuffer geom::Size const size_; }; -auto export_egl_image(EGLDisplay dpy, EGLImage image, geom::Size size) -> std::unique_ptr +auto export_egl_image( + mg::EGLExtensions::MESADmaBufExport const& ext, + EGLDisplay dpy, + EGLImage image, + geom::Size size) -> std::unique_ptr { constexpr int const max_planes = 4; int fourcc; int num_planes; std::array modifiers; - if (eglExportDMABUFImageQueryMESA(dpy, image, &fourcc, &num_planes, modifiers.data()) != EGL_TRUE) + if (ext.eglExportDMABUFImageQueryMESA(dpy, image, &fourcc, &num_planes, modifiers.data()) != EGL_TRUE) { BOOST_THROW_EXCEPTION((mg::egl_error("Failed to query EGLImage for dma-buf export"))); } @@ -347,7 +351,7 @@ auto export_egl_image(EGLDisplay dpy, EGLImage image, geom::Size size) -> std::u std::array strides; std::array offsets; - if (eglExportDMABUFImageMESA(dpy, image, fds.data(), strides.data(), offsets.data()) != EGL_TRUE) + if (ext.eglExportDMABUFImageMESA(dpy, image, fds.data(), strides.data(), offsets.data()) != EGL_TRUE) { BOOST_THROW_EXCEPTION((mg::egl_error("Failed to export EGLImage to dma-buf(s)"))); } @@ -1114,6 +1118,7 @@ mg::DMABufEGLProvider::DMABufEGLProvider( EGLImageAllocator allocate_importable_image) : dpy{dpy}, egl_extensions{std::move(egl_extensions)}, + dmabuf_export_ext{mg::EGLExtensions::MESADmaBufExport::extension_if_supported(dpy)}, formats{std::make_unique(dpy, dmabuf_ext)}, egl_delegate{std::move(egl_delegate)}, allocate_importable_image{std::move(allocate_importable_image)}, @@ -1200,6 +1205,12 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) */ auto importing_provider = dmabuf_tex->provider(); + if (!importing_provider->dmabuf_export_ext) + { + mir::log_warning("EGL implementation does not handle cross-GPU buffer export"); + return nullptr; + } + /* TODO: Be smarter about finding a shared pixel format; everything *should* do * ARGB8888, but if the buffer is in a higher bitdepth this will lose colour information */ @@ -1247,7 +1258,7 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) { BOOST_THROW_EXCEPTION((std::logic_error{"EGL_ANDROID_native_fence_sync support not implemented yet"})); } - auto importable_dmabuf = export_egl_image(importing_provider->dpy, importable_image, dmabuf_tex->size()); + auto importable_dmabuf = export_egl_image(*importing_provider->dmabuf_export_ext, importing_provider->dpy, importable_image, dmabuf_tex->size()); eglDestroyImage(importing_provider->dpy, src_image); eglDestroyImage(importing_provider->dpy, importable_image); From b2b0f9e901ace2fb35eba2229cfaf978a5f38106 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 5 Oct 2023 17:28:52 +1100 Subject: [PATCH 074/108] platform/EGLBufferCopier: Drop libepoxy usage --- src/platform/graphics/egl_buffer_copy.cpp | 29 +++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/platform/graphics/egl_buffer_copy.cpp b/src/platform/graphics/egl_buffer_copy.cpp index c54bd36bdca..f5e5e781e33 100644 --- a/src/platform/graphics/egl_buffer_copy.cpp +++ b/src/platform/graphics/egl_buffer_copy.cpp @@ -14,8 +14,9 @@ * along with this program. If not, see . */ -#include -#include +#include +#include +#include #include "egl_buffer_copy.h" #include "mir/graphics/egl_context_executor.h" @@ -23,6 +24,7 @@ #include "mir/graphics/egl_extensions.h" #include +#include #include #include #include @@ -58,7 +60,7 @@ const GLchar* const fshader = "}\n" }; -template +template class GLBulkHandle { public: @@ -95,7 +97,7 @@ class GLBulkHandle GLuint id; }; -template +template class GLTypedHandle { public: @@ -132,7 +134,7 @@ class GLTypedHandle GLuint id; }; -template +template class GLHandle { public: @@ -275,7 +277,7 @@ class mg::EGLBufferCopier::Impl glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, from); + state->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, from); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -283,7 +285,7 @@ class mg::EGLBufferCopier::Impl glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); - glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, to); + state->glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, to); glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); @@ -323,6 +325,8 @@ class mg::EGLBufferCopier::Impl GLint attrpos; GLBufferHandle vert_data; GLBufferHandle tex_data; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; + PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES; }; /* This has to be initialised on the EGL thread, but it contains non-default-initialisable state. * @@ -336,6 +340,17 @@ class mg::EGLBufferCopier::Impl void initialise() { state = std::make_shared(); + + // Check necessary EGL extensions + if (!strstr(reinterpret_cast(glGetString(GL_EXTENSIONS)), "GL_OES_EGL_image")) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Missing required GL_OES_EGL_image extension"})); + } + state->glEGLImageTargetRenderbufferStorageOES = + reinterpret_cast(eglGetProcAddress("glEGLImageTargetRenderbufferStorageOES")); + state->glEGLImageTargetTexture2DOES = + reinterpret_cast(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + state->prog = link_shader(compile_shader(GL_VERTEX_SHADER, vshader), compile_shader(GL_FRAGMENT_SHADER, fshader)); glUseProgram(state->prog); From 4772b34bcd30b1f9cff97bb2b835c9918f2eb22e Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Thu, 5 Oct 2023 17:31:14 +1100 Subject: [PATCH 075/108] Trivial: Trailing whitespace --- src/platform/graphics/linux_dmabuf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index af74e2ba6a7..775bebc4a62 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -1215,7 +1215,7 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) * ARGB8888, but if the buffer is in a higher bitdepth this will lose colour information */ auto const& supported_formats = *formats; - auto const& modifiers = + auto const& modifiers = [&supported_formats]() -> std::vector const& { for (size_t i = 0; i < supported_formats.num_formats(); ++i) From 724ff805d70576c8e6921249c5e69b43bd039ea7 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 6 Oct 2023 12:50:41 +1100 Subject: [PATCH 076/108] platforms/eglstream-kms: Re-add missing Display implementation. Somewhere along the line we lost the DisplayPlatform implementation for eglstream-kms somehow. Bring it back! --- .../eglstream-kms/server/CMakeLists.txt | 11 + .../eglstream-kms/server/display.cpp | 111 +++---- src/platforms/eglstream-kms/server/display.h | 6 +- .../server/eglstream_interface_provider.cpp | 73 +++++ .../server/eglstream_interface_provider.h | 44 +++ .../eglstream-kms/server/platform.cpp | 75 ++++- src/platforms/eglstream-kms/server/platform.h | 26 +- .../eglstream-kms/server/platform_symbols.cpp | 281 +++++++++++++++++- 8 files changed, 570 insertions(+), 57 deletions(-) create mode 100644 src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp create mode 100644 src/platforms/eglstream-kms/server/eglstream_interface_provider.h diff --git a/src/platforms/eglstream-kms/server/CMakeLists.txt b/src/platforms/eglstream-kms/server/CMakeLists.txt index cb903d80c83..346f11c9775 100644 --- a/src/platforms/eglstream-kms/server/CMakeLists.txt +++ b/src/platforms/eglstream-kms/server/CMakeLists.txt @@ -13,8 +13,19 @@ add_library(mirplatformgraphicseglstreamkmsobjects OBJECT platform.cpp buffer_allocator.cpp buffer_allocator.h + display.cpp + display.h + kms_display_configuration.h + kms_display_configuration.cpp + egl_output.h + egl_output.cpp utils.cpp utils.h + drm_event_handler.h + threaded_drm_event_handler.h + threaded_drm_event_handler.cpp + eglstream_interface_provider.h + eglstream_interface_provider.cpp ) target_link_libraries(mirplatformgraphicseglstreamkmsobjects diff --git a/src/platforms/eglstream-kms/server/display.cpp b/src/platforms/eglstream-kms/server/display.cpp index 01a76a8eea1..4a70e2f05a0 100644 --- a/src/platforms/eglstream-kms/server/display.cpp +++ b/src/platforms/eglstream-kms/server/display.cpp @@ -20,6 +20,7 @@ #include "egl_output.h" #include "kms-utils/drm_mode_resources.h" +#include "mir/graphics/platform.h" #include "threaded_drm_event_handler.h" #include "mir/graphics/display_configuration.h" @@ -29,8 +30,6 @@ #include "mir/graphics/egl_error.h" #include "mir/graphics/display_buffer.h" #include "mir/graphics/transformation.h" -#include "mir/renderer/gl/render_target.h" -#include "mir/renderer/gl/context.h" #include "mir/graphics/egl_extensions.h" #include "mir/graphics/display_report.h" #include "mir/graphics/event_handler_register.h" @@ -46,7 +45,7 @@ namespace mg = mir::graphics; namespace mge = mir::graphics::eglstream; -namespace mgk = mir::graphics::kms; +namespace geom = mir::geometry; #ifndef EGL_NV_output_drm_flip_event #define EGL_NV_output_drm_flip_event 1 @@ -125,7 +124,7 @@ class DisplayBuffer { public: DisplayBuffer( - std::shared_ptr owner, + std::shared_ptr owner, mir::Fd drm_node, EGLDisplay dpy, EGLContext ctx, @@ -193,46 +192,11 @@ class DisplayBuffer pending_flip = satisfied_promise.get_future(); } - /* gl::RenderTarget */ - auto size() const -> mir::geometry::Size override - { - return output_size; - } - - void make_current() override - { - if (eglMakeCurrent(dpy, surface, surface, ctx) != EGL_TRUE) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make context current")); - } - } - - void release_current() override - { - if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to release context")); - } - } - - void swap_buffers() override - { - if (eglSwapBuffers(dpy, surface) != EGL_TRUE) - { - BOOST_THROW_EXCEPTION(mg::egl_error("eglSwapBuffers failed")); - } - } - mir::geometry::Rectangle view_area() const override { return view_area_; } - bool overlay(std::vector const&) override - { - return false; - } - glm::mat2 transformation() const override { return transform; @@ -270,18 +234,60 @@ class DisplayBuffer } } - void bind() override + std::chrono::milliseconds recommended_sleep() const override { + return std::chrono::milliseconds{0}; } - std::chrono::milliseconds recommended_sleep() const override + auto display_provider() const -> std::shared_ptr override { - return std::chrono::milliseconds{0}; + return std::make_shared(*owner, output_stream); } + void set_next_image(std::unique_ptr content) override + { + std::vector const single_buffer = { + mg::DisplayElement { + view_area(), + mir::geometry::RectangleF{ + {0, 0}, + {view_area().size.width.as_value(), view_area().size.height.as_value()}}, + std::move(content) + } + }; + if (!overlay(single_buffer)) + { + // Oh, oh! We should be *guaranteed* to “overlay” a single Framebuffer; this is likely a programming error + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to post buffer to display"})); + } + } + + bool overlay(std::vector const& renderable_list) override + { + // TODO: implement more than the most basic case. + if (renderable_list.size() != 1) + { + return false; + } + + if (renderable_list[0].screen_positon != view_area()) + { + return false; + } + + if (renderable_list[0].source_position.top_left != geom::PointF {0,0} || + renderable_list[0].source_position.size.width.as_value() != view_area().size.width.as_int() || + renderable_list[0].source_position.size.height.as_value() != view_area().size.height.as_int()) + { + return false; + } + + // TODO: Validate that the submitted "framebuffer" is *actually* our EGLStream + return true; + } private: - std::shared_ptr const owner; + std::shared_ptr const owner; EGLDisplay dpy; EGLContext ctx; EGLOutputLayerEXT layer; @@ -313,12 +319,14 @@ mge::KMSDisplayConfiguration create_display_configuration( } mge::Display::Display( + std::shared_ptr provider, mir::Fd drm_node, EGLDisplay display, std::shared_ptr const& configuration_policy, GLConfig const& gl_conf, std::shared_ptr display_report) - : drm_node{drm_node}, + : provider{std::move(provider)}, + drm_node{drm_node}, display{display}, config{choose_config(display, gl_conf)}, context{create_context(display, config)}, @@ -368,13 +376,14 @@ void mge::Display::configure(DisplayConfiguration const& conf) const_cast(output).configure(output.current_mode_index); active_sync_groups.emplace_back( std::make_unique<::DisplayBuffer>( - drm_node, - display, - context, - config, - event_handler, - output, - display_report)); + provider, + drm_node, + display, + context, + config, + event_handler, + output, + display_report)); } }); } diff --git a/src/platforms/eglstream-kms/server/display.h b/src/platforms/eglstream-kms/server/display.h index 8604ef08dfb..b8ec32579d9 100644 --- a/src/platforms/eglstream-kms/server/display.h +++ b/src/platforms/eglstream-kms/server/display.h @@ -17,10 +17,12 @@ #ifndef MIR_PLATFORMS_EGLSTREAM_KMS_DISPLAY_H_ #define MIR_PLATFORMS_EGLSTREAM_KMS_DISPLAY_H_ +#include "eglstream_interface_provider.h" + #include "mir/graphics/display.h" #include "kms_display_configuration.h" #include "mir/fd.h" -#include "mir/renderer/gl/context_source.h" +#include "mir/graphics/platform.h" #include @@ -40,6 +42,7 @@ class Display : public mir::graphics::Display { public: Display( + std::shared_ptr provider, mir::Fd drm_node, EGLDisplay display, std::shared_ptr const& configuration_policy, @@ -64,6 +67,7 @@ class Display : public mir::graphics::Display std::shared_ptr create_hardware_cursor() override; private: + std::shared_ptr const provider; mir::Fd const drm_node; EGLDisplay display; EGLConfig config; diff --git a/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp b/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp new file mode 100644 index 00000000000..7e1aae81779 --- /dev/null +++ b/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp @@ -0,0 +1,73 @@ +/* + * 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 . + */ + +#include "eglstream_interface_provider.h" +#include "mir/graphics/platform.h" + +#include +#include + +#include + +namespace mg = mir::graphics; + +namespace +{ +class DisplayProvider : public mg::EGLStreamDisplayProvider +{ +public: + DisplayProvider(EGLDisplay dpy, std::optional stream); + + auto claim_stream() -> EGLStreamKHR override; +private: + std::optional stream; +}; + +DisplayProvider::DisplayProvider(EGLDisplay /*dpy*/, std::optional stream) + : stream{stream} +{ +} + +auto DisplayProvider::claim_stream() -> EGLStreamKHR +{ + if (stream) + { + return *std::exchange(stream, std::nullopt); + } + BOOST_THROW_EXCEPTION((std::logic_error{"No EGLStream to claim (either incorrect object, or stream already claimed)"})); +} +} + +mg::eglstream::InterfaceProvider::InterfaceProvider(EGLDisplay dpy) + : dpy{dpy} +{ +} + +mg::eglstream::InterfaceProvider::InterfaceProvider(InterfaceProvider const& from, EGLStreamKHR with_stream) + : dpy{from.dpy}, + stream{with_stream} +{ +} + +auto mg::eglstream::InterfaceProvider::maybe_create_interface(DisplayInterfaceBase::Tag const& tag) + -> std::shared_ptr +{ + if (dynamic_cast(&tag)) + { + return std::make_shared(dpy, std::exchange(stream, std::nullopt)); + } + return nullptr; +} diff --git a/src/platforms/eglstream-kms/server/eglstream_interface_provider.h b/src/platforms/eglstream-kms/server/eglstream_interface_provider.h new file mode 100644 index 00000000000..dd41dafc0b1 --- /dev/null +++ b/src/platforms/eglstream-kms/server/eglstream_interface_provider.h @@ -0,0 +1,44 @@ +/* + * 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 . + */ + +#ifndef MIR_GRAPHICS_EGLSTREAM_INTERFACE_PROVIDER_H_ +#define MIR_GRAPHICS_EGLSTREAM_INTERFACE_PROVIDER_H_ + +#include "mir/graphics/platform.h" + +#include + +namespace mir::graphics::eglstream +{ +class InterfaceProvider : public DisplayInterfaceProvider +{ +public: + InterfaceProvider(EGLDisplay dpy); + InterfaceProvider(InterfaceProvider const& from, EGLStreamKHR with_stream); + +protected: + auto maybe_create_interface(DisplayInterfaceBase::Tag const& tag) + -> std::shared_ptr override; + +private: + EGLDisplay dpy; + std::optional stream; +}; + +; +} + +#endif // MIR_GRAPHICS_EGLSTREAM_INTERFACE_PROVIDER_H_ diff --git a/src/platforms/eglstream-kms/server/platform.cpp b/src/platforms/eglstream-kms/server/platform.cpp index 36b2879be1d..d675569c647 100644 --- a/src/platforms/eglstream-kms/server/platform.cpp +++ b/src/platforms/eglstream-kms/server/platform.cpp @@ -19,15 +19,22 @@ #include "platform.h" #include "buffer_allocator.h" #include "display.h" +#include "mir/graphics/platform.h" #include "utils.h" +#include "eglstream_interface_provider.h" +#include "one_shot_device_observer.h" + +#include "mir/console_services.h" #include "mir/graphics/egl_error.h" #include "mir/renderer/gl/context.h" +#include #include #include namespace mg = mir::graphics; +namespace mgc = mir::graphics::common; namespace mge = mir::graphics::eglstream; namespace @@ -47,10 +54,10 @@ auto make_egl_context(EGLDisplay dpy) -> EGLContext EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; - + EGLConfig egl_config; EGLint num_egl_configs{0}; - + if (eglChooseConfig(dpy, config_attr, &egl_config, 1, &num_egl_configs) == EGL_FALSE || num_egl_configs != 1) { @@ -193,3 +200,67 @@ auto mge::RenderingPlatform::maybe_create_interface( } return nullptr; } + +mge::DisplayPlatform::DisplayPlatform( + ConsoleServices& console, + EGLDeviceEXT device, + std::shared_ptr display_report) + : display_report{std::move(display_report)} +{ + using namespace std::literals; + + auto const devnum = devnum_for_device(device); + drm_device = console.acquire_device( + major(devnum), minor(devnum), + std::make_unique(drm_node)) + .get(); + + if (drm_node == mir::Fd::invalid) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Failed to acquire DRM device node for device"})); + } + + int const drm_node_attrib[] = { + EGL_DRM_MASTER_FD_EXT, static_cast(drm_node), EGL_NONE + }; + display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, drm_node_attrib); + + if (display == EGL_NO_DISPLAY) + { + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to create EGLDisplay on EGLDeviceEXT"))); + } + + std::tuple egl_version = std::make_tuple(1, 4); + if (eglInitialize(display, &std::get<0>(egl_version), &std::get<1>(egl_version)) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to initialize EGL"))); + } + if (egl_version < std::make_tuple(1, 4)) + { + BOOST_THROW_EXCEPTION((std::runtime_error{ + "Incompatible EGL version"s + + "Wanted 1.4, got " + + std::to_string(std::get<0>(egl_version)) + "." + std::to_string(std::get<1>(egl_version))})); + } + + provider = std::make_shared(display); +} + +auto mge::DisplayPlatform::create_display( + std::shared_ptr const& configuration_policy, + std::shared_ptr const& gl_config) + -> UniqueModulePtr +{ + return mir::make_module_ptr( + provider, + drm_node, + display, + configuration_policy, + *gl_config, + display_report); +} + +auto mge::DisplayPlatform::interface_for() -> std::shared_ptr +{ + return provider; +} diff --git a/src/platforms/eglstream-kms/server/platform.h b/src/platforms/eglstream-kms/server/platform.h index bece633b51d..c1be78c2b86 100644 --- a/src/platforms/eglstream-kms/server/platform.h +++ b/src/platforms/eglstream-kms/server/platform.h @@ -18,7 +18,6 @@ #define MIR_PLATFORMS_EGLSTREAM_KMS_PLATFORM_H_ #include "mir/graphics/platform.h" -#include "mir/options/option.h" #include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/display.h" #include "mir/fd.h" @@ -39,6 +38,7 @@ namespace graphics { namespace eglstream { +class InterfaceProvider; class RenderingPlatform : public graphics::RenderingPlatform { @@ -56,8 +56,30 @@ class RenderingPlatform : public graphics::RenderingPlatform EGLDisplay const dpy; std::unique_ptr const ctx; }; + +class DisplayPlatform : public graphics::DisplayPlatform +{ +public: + DisplayPlatform( + ConsoleServices& console, + EGLDeviceEXT device, + std::shared_ptr display_report); + + auto create_display( + std::shared_ptr const& initial_conf_policy, + std::shared_ptr const& gl_config) + ->UniqueModulePtr override; + +private: + auto interface_for() -> std::shared_ptr override; + + std::unique_ptr drm_device; + EGLDisplay display; + std::shared_ptr provider; + mir::Fd drm_node; + std::shared_ptr const display_report; +}; } } } - #endif // MIR_PLATFORMS_EGLSTREAM_KMS_PLATFORM_H_ diff --git a/src/platforms/eglstream-kms/server/platform_symbols.cpp b/src/platforms/eglstream-kms/server/platform_symbols.cpp index 443393369b6..1a7d04a3d8f 100644 --- a/src/platforms/eglstream-kms/server/platform_symbols.cpp +++ b/src/platforms/eglstream-kms/server/platform_symbols.cpp @@ -43,6 +43,7 @@ namespace mg = mir::graphics; namespace mo = mir::options; +namespace mgc = mir::graphics::common; namespace mge = mir::graphics::eglstream; auto create_rendering_platform( @@ -100,7 +101,7 @@ auto probe_rendering_platform( } if (display_provider->acquire_interface()) { - /* We *can* support this output, but with slower buffer copies + /* We *can* support this output, but with slower buffer copies * If another platform supports this device better, let it. */ maximum_suitability = mg::PlatformPriority::supported; @@ -239,6 +240,284 @@ auto probe_rendering_platform( return supported_devices; } +mir::UniqueModulePtr create_display_platform( + mg::SupportedDevice const& device, + std::shared_ptr const& options, + std::shared_ptr const&, + std::shared_ptr const& console, + std::shared_ptr const& display_report) +{ + mir::assert_entry_point_signature(&create_display_platform); + + if (options->is_set(mo::debug_opt)) + { + mg::initialise_egl_logger(); + } + + return mir::make_module_ptr( + *console, + std::any_cast(device.platform_data), + display_report); +} + +auto probe_display_platform( + std::shared_ptr const& console, + std::shared_ptr const& udev, + mo::ProgramOption const&) -> std::vector +{ + mir::assert_entry_point_signature(&probe_display_platform); + + std::vector missing_extensions; + for (char const* extension : { + "EGL_EXT_platform_base", + "EGL_EXT_platform_device", + "EGL_EXT_device_base",}) + { + if (!epoxy_has_egl_extension(EGL_NO_DISPLAY, extension)) + { + missing_extensions.push_back(extension); + } + } + + if (!missing_extensions.empty()) + { + std::stringstream message; + message << "Missing required extension" << (missing_extensions.size() > 1 ? "s:" : ":"); + for (auto missing_extension : missing_extensions) + { + message << " " << missing_extension; + } + + mir::log_debug("EGLStream platform is unsupported: %s", + message.str().c_str()); + return {}; + } + + int device_count{0}; + if (eglQueryDevicesEXT(0, nullptr, &device_count) != EGL_TRUE) + { + mir::log_info("Platform claims to support EGL_EXT_device_base, but " + "eglQueryDevicesEXT falied: %s", + mg::egl_category().message(eglGetError()).c_str()); + return {}; + } + + auto devices = std::make_unique(device_count); + if (eglQueryDevicesEXT(device_count, devices.get(), &device_count) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to get device list with eglQueryDevicesEXT")); + } + + std::vector supported_devices; + for (auto i = 0; i != device_count; ++i) + { + auto const& device = devices[i]; + auto device_extensions = eglQueryDeviceStringEXT(device, EGL_EXTENSIONS); + if (device_extensions) + { + mir::log_debug("Found EGLDeviceEXT with device extensions: %s", + device_extensions); + // TODO: This test is not strictly correct (will incorrectly match + // EGL_EXT_device_drmish_but_not_drm) + if (strstr(device_extensions, "EGL_EXT_device_drm") != NULL) + { + // Check we can acquire the device... + mir::Fd drm_fd; + std::unique_ptr device_holder; + try + { + auto const devnum = mge::devnum_for_device(device); + + device_holder = console->acquire_device( + major(devnum), minor(devnum), + std::make_unique(drm_fd)).get(); + + supported_devices.emplace_back( + mg::SupportedDevice{ + udev->char_device_from_devnum(devnum), + mg::PlatformPriority::unsupported, + device + }); + } + catch (std::exception const& e) + { + mir::log_info("Failed to query DRM node for EGLDevice: %s", e.what()); + continue; + } + if (drm_fd == mir::Fd::invalid) + { + mir::log_debug( + "EGL_EXT_device_drm found, but can't acquire DRM node."); + continue; + } + + // Check that the drm device is usable by setting the interface version we use (1.4) + drmSetVersion sv; + sv.drm_di_major = 1; + sv.drm_di_minor = 4; + sv.drm_dd_major = -1; /* Don't care */ + sv.drm_dd_minor = -1; /* Don't care */ + + if (auto error = -drmSetInterfaceVersion(drm_fd, &sv)) + { + mir::log_warning( + "Failed to set DRM interface version on device: %i (%s)", + error, + strerror(error)); + continue; + } + + auto busid = std::unique_ptr{ + drmGetBusid(drm_fd), + &drmFreeBusid + }; + if (auto err = drmCheckModesettingSupported(busid.get())) + { + if (err == -ENOSYS) + { + mir::log_info("EGL_EXT_device_drm found, but no KMS support"); + mir::log_info("You may need to set the nvidia_drm.modeset kernel parameter"); + } + else + { + mir::log_warning( + "Failed to check DRM modesetting support for device %s: %s (%i)", + busid.get(), + strerror(-err), + -err); + } + continue; + } + + mg::kms::DRMModeResources kms_resources{drm_fd}; + if ( + kms_resources.num_connectors() == 0 || + kms_resources.num_encoders() == 0 || + kms_resources.num_crtcs() == 0) + { + mir::log_info("KMS support found, but device has no output hardware."); + mir::log_info("This is probably a render-only hybrid graphics device"); + continue; + } + + EGLDisplay display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, nullptr); + + if (display == EGL_NO_DISPLAY) + { + mir::log_debug("Failed to create EGLDisplay: %s", mg::egl_category().message(eglGetError()).c_str()); + continue; + } + + auto egl_init = mir::raii::paired_calls( + [&display]() + { + EGLint major_ver{1}, minor_ver{4}; + if (!eglInitialize(display, &major_ver, &minor_ver)) + { + mir::log_debug("Failed to initialise EGL: %s", mg::egl_category().message(eglGetError()).c_str()); + display = EGL_NO_DISPLAY; + } + }, + [&display]() + { + if (display != EGL_NO_DISPLAY) + { + eglTerminate(display); + } + }); + + if (display == EGL_NO_DISPLAY) + { + continue; + } + + eglBindAPI(EGL_OPENGL_ES_API); + EGLint const config_attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR, + EGL_NONE + }; + EGLConfig config; + EGLint num_configs; + if (eglChooseConfig(display, config_attribs, &config, 1, &num_configs) != EGL_TRUE) + { + mir::log_warning("Failed to create EGL context: no EGL_STREAM_BIT_KHR configs supported"); + continue; + } + EGLContext ctx{EGL_NO_CONTEXT}; + auto ctx_init = mir::raii::paired_calls( + [&ctx, display, config]() + { + EGLint const context_attr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + ctx = eglCreateContext(display, config, EGL_NO_CONTEXT, context_attr); + }, + [&ctx, display]() + { + if (ctx != EGL_NO_CONTEXT) + { + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(display, ctx); + } + }); + + if (ctx == EGL_NO_CONTEXT) + { + mir::log_warning("Failed to create EGL context: %s", mg::egl_category().message(eglGetError()).c_str()); + continue; + } + + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx); + + auto const gl_version = reinterpret_cast(glGetString(GL_VERSION)); + if (!gl_version) + { + mir::log_warning("glGetString(GL_VERSION) call failed. This probably indicates a problem with the GL drivers."); + } + else if (auto version = mge::parse_nvidia_version(gl_version)) + { + mir::log_debug("Detected NVIDIA driver version %i.%i", version->major, version->minor); + if (version->major < 396) + { + mir::log_warning( + "Detected NVIDIA driver version %i.%i is older than 396.xx", + version->major, + version->minor); + mir::log_warning( + "This driver is known to interact badly with Mir. See https://github.com/MirServer/mir/issues/650"); + mir::log_warning( + "Mir will not auto-load the eglstream-kms platform on this driver. To proceed anyway, manually specify the platform library."); + continue; + } + } + + if (epoxy_has_egl_extension(display, "EGL_EXT_output_base")) + { + supported_devices.back().support_level = mg::PlatformPriority::best; + } + else + { + mir::log_debug("EGL_EXT_device_drm found, but missing EGL_EXT_output_base extension."); + mir::log_debug("Available extensions are: %s", eglQueryString(display, EGL_EXTENSIONS)); + } + } + } + else + { + mir::log_debug("Found EGLDeviceEXT with no device extensions"); + } + } + if (supported_devices.empty()) + { + mir::log_debug( + "EGLDeviceEXTs found, but none are suitable for Mir"); + } + + return supported_devices; +} + namespace { mir::ModuleProperties const description = { From fd3f31c26cdefaba6a21a209632f819bb7b23c0f Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 6 Oct 2023 12:53:29 +1100 Subject: [PATCH 077/108] graphics/probe: Tweak manually-specified display-platform selection. It's acceptable for a display platform's `probe` to return `SupportedDevices` with `support_level == unsupported`. This is filtered out in our normal probe path. Fix the manually-specified path to also filter out any devices with `support_level == unsupported` if the platform claims to support *any* device. If not, continue with the same "add everything" behaviour. --- src/server/graphics/default_configuration.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index 57002edf494..7296d43151b 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -170,11 +170,12 @@ auto mir::DefaultServerConfiguration::the_display_platforms() -> std::vector= mg::PlatformPriority::supported) { found_supported_device = true; + platform_modules.emplace_back(std::move(device), platform); } - platform_modules.emplace_back(std::move(device), platform); } if (!found_supported_device) @@ -185,6 +186,13 @@ auto mir::DefaultServerConfiguration::the_display_platforms() -> std::vectorname); + + // We're here only if the platform doesn't claim to support *any* of the detected devices + // Add *all* the found devices into our platform list, and hope. + for (auto& device : supported_devices) + { + platform_modules.emplace_back(std::move(device), platform); + } } } } From 264eb0ea9a85fa2a3a4f8565be6b64ecd9cf227f Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 6 Oct 2023 14:56:19 +1100 Subject: [PATCH 078/108] platforms/eglstream-kms: Fixup. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to use the same EGLDisplay for the DisplayPlatform and the RenderingPlatform (so they can share `EGLStreamKHR` objects), so plumb that through the platform probing. Make sure there's a current EGLContext when constructing the EGLStreamOutputSurface (thanks, libepoxy 😠). Add some debugging for when DisplayBuffer::post() fails. Ensure we've got up-to-date KMSConnector information when we go searching for CRTCs. --- include/platform/mir/graphics/platform.h | 2 + .../eglstream-kms/server/buffer_allocator.cpp | 25 +++-- .../eglstream-kms/server/display.cpp | 45 ++++++--- .../eglstream-kms/server/egl_output.cpp | 2 + .../server/eglstream_interface_provider.cpp | 15 ++- .../eglstream-kms/server/platform_symbols.cpp | 99 ++++++++++++++----- 6 files changed, 138 insertions(+), 50 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index 4f325d66e0f..d5f65ba0332 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -358,6 +358,8 @@ class EGLStreamDisplayProvider : public DisplayInterfaceBase { }; + virtual auto get_egl_display() const -> EGLDisplay = 0; + virtual auto claim_stream() -> EGLStreamKHR = 0; }; diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index 7a803cb118e..46cb13c27df 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -602,6 +602,10 @@ auto make_stream_ctx(EGLDisplay dpy, EGLConfig cfg, EGLContext share_with) -> EG // on a different device to the display. BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create EGL context")); } + if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, context) != EGL_TRUE) + { + BOOST_THROW_EXCEPTION(mg::egl_error("Failed to make EGL context current")); + } return context; } @@ -636,6 +640,7 @@ class EGLStreamOutputSurface : public mg::gl::OutputSurface surface{make_output_surface(dpy, config, output_stream, size)}, size_{std::move(size)} { + eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } void bind() override @@ -745,15 +750,17 @@ auto mge::GLRenderingProvider::surface_for_output( err.what()); } } - auto cpu_provider = target->acquire_interface(); - - auto fb_context = ctx->make_share_context(); - fb_context->make_current(); - return std::make_unique( - dpy, - static_cast(*ctx), - cpu_provider, - size); + if (auto cpu_provider = target->acquire_interface()) + { + auto fb_context = ctx->make_share_context(); + fb_context->make_current(); + return std::make_unique( + dpy, + static_cast(*ctx), + cpu_provider, + size); + } + BOOST_THROW_EXCEPTION((std::runtime_error{"DisplayInterfaceProvider does not support any viable output interface"})); } auto mge::GLRenderingProvider::suitability_for_display( diff --git a/src/platforms/eglstream-kms/server/display.cpp b/src/platforms/eglstream-kms/server/display.cpp index 4a70e2f05a0..46cef442c0d 100644 --- a/src/platforms/eglstream-kms/server/display.cpp +++ b/src/platforms/eglstream-kms/server/display.cpp @@ -171,17 +171,6 @@ class DisplayBuffer BOOST_THROW_EXCEPTION(mg::egl_error("Failed to attach EGLStream to output")); }; - EGLint const surface_attribs[] = { - EGL_WIDTH, output_size.width.as_int(), - EGL_HEIGHT, output_size.height.as_int(), - EGL_NONE, - }; - surface = eglCreateStreamProducerSurfaceKHR(dpy, config, output_stream, surface_attribs); - if (surface == EGL_NO_SURFACE) - { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to create StreamProducerSurface")); - } - this->display_report->report_successful_setup_of_native_resources(); this->display_report->report_successful_display_construction(); this->display_report->report_egl_configuration(dpy, config); @@ -230,7 +219,38 @@ class DisplayBuffer }; if (nv_stream(dpy).eglStreamConsumerAcquireAttribNV(dpy, output_stream, acquire_attribs) != EGL_TRUE) { - BOOST_THROW_EXCEPTION(mg::egl_error("Failed to submit frame from EGLStream for display")); + auto error = eglGetError(); + EGLAttrib stream_state{0}; + eglQueryStreamAttribKHR(dpy, output_stream, EGL_STREAM_STATE_KHR, &stream_state); + std::string state; + switch (stream_state) + { + case EGL_STREAM_STATE_CREATED_KHR: + state = "EGL_STREAM_STATE_CREATED_KHR"; + break; + case EGL_STREAM_STATE_CONNECTING_KHR: + state = "EGL_STREAM_STATE_CONNECTING_KHR"; + break; + case EGL_STREAM_STATE_EMPTY_KHR: + state = "EGL_STREAM_STATE_EMPTY_KHR"; + break; + case EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR: + state = "EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR"; + break; + case EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR: + state = "EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR"; + break; + case EGL_STREAM_STATE_DISCONNECTED_KHR: + state = "EGL_STREAM_STATE_DISCONNECTED_KHR"; + break; + default: + state = ""; + } + BOOST_THROW_EXCEPTION(( + std::system_error{ + error, + mg::egl_category(), + std::string{"Failed to submit frame from EGLStream for display. (Stream in state: "} + state + ")"})); } } @@ -296,7 +316,6 @@ class DisplayBuffer mir::geometry::Size const output_size; glm::mat2 const transform; EGLStreamKHR output_stream; - EGLSurface surface; mir::Fd const drm_node; std::shared_ptr const event_handler; std::future pending_flip; diff --git a/src/platforms/eglstream-kms/server/egl_output.cpp b/src/platforms/eglstream-kms/server/egl_output.cpp index 56b0ecf7505..e9b9b3100ce 100644 --- a/src/platforms/eglstream-kms/server/egl_output.cpp +++ b/src/platforms/eglstream-kms/server/egl_output.cpp @@ -217,6 +217,8 @@ void mgek::EGLOutput::configure(size_t kms_mode_index) mgk::DRMModeCrtcUPtr crtc; mgk::DRMModePlaneUPtr plane; + refresh_connector(drm_fd, connector); + std::tie(crtc, plane) = mgk::find_crtc_with_primary_plane(drm_fd, connector); auto const crtc_id = crtc->crtc_id; diff --git a/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp b/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp index 7e1aae81779..52c29432c26 100644 --- a/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp +++ b/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp @@ -31,14 +31,23 @@ class DisplayProvider : public mg::EGLStreamDisplayProvider public: DisplayProvider(EGLDisplay dpy, std::optional stream); + auto get_egl_display() const -> EGLDisplay override; + auto claim_stream() -> EGLStreamKHR override; private: + EGLDisplay dpy; std::optional stream; }; -DisplayProvider::DisplayProvider(EGLDisplay /*dpy*/, std::optional stream) - : stream{stream} +DisplayProvider::DisplayProvider(EGLDisplay dpy, std::optional stream) + : dpy{dpy}, + stream{stream} +{ +} + +auto DisplayProvider::get_egl_display() const -> EGLDisplay { + return dpy; } auto DisplayProvider::claim_stream() -> EGLStreamKHR @@ -67,7 +76,7 @@ auto mg::eglstream::InterfaceProvider::maybe_create_interface(DisplayInterfaceBa { if (dynamic_cast(&tag)) { - return std::make_shared(dpy, std::exchange(stream, std::nullopt)); + return std::make_shared(dpy, stream); } return nullptr; } diff --git a/src/platforms/eglstream-kms/server/platform_symbols.cpp b/src/platforms/eglstream-kms/server/platform_symbols.cpp index 1a7d04a3d8f..a0d3659d806 100644 --- a/src/platforms/eglstream-kms/server/platform_symbols.cpp +++ b/src/platforms/eglstream-kms/server/platform_symbols.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -54,22 +55,31 @@ auto create_rendering_platform( { mir::assert_entry_point_signature(&create_rendering_platform); + auto platform_data = std::any_cast>(device.platform_data); EGLDisplay display{EGL_NO_DISPLAY}; - display = eglGetPlatformDisplayEXT( - EGL_PLATFORM_DEVICE_EXT, - std::any_cast(device.platform_data), - nullptr); - if (display == EGL_NO_DISPLAY) + if (auto dpy = std::get_if<0>(&platform_data)) { - BOOST_THROW_EXCEPTION((mg::egl_error("Failed to create EGL Display"))); + display = *dpy; } - - EGLint major_ver{1}, minor_ver{4}; - if (!eglInitialize(display, &major_ver, &minor_ver)) + else if (auto device = std::get_if<1>(&platform_data)) { - BOOST_THROW_EXCEPTION((mg::egl_error("Failed to initialise EGL"))); + display = eglGetPlatformDisplayEXT( + EGL_PLATFORM_DEVICE_EXT, + *device, + nullptr); + + if (display == EGL_NO_DISPLAY) + { + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to create EGL Display"))); + } + + EGLint major_ver{1}, minor_ver{4}; + if (!eglInitialize(display, &major_ver, &minor_ver)) + { + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to initialise EGL"))); + } } return mir::make_module_ptr(display); @@ -90,14 +100,15 @@ auto probe_rendering_platform( mg::PlatformPriority maximum_suitability = mg::PlatformPriority::unsupported; // First check if there are any displays we can possibly drive + std::vector> eglstream_providers; for (auto const& display_provider : displays) { - if (display_provider->acquire_interface()) + if (auto provider = display_provider->acquire_interface()) { // We can optimally drive an EGLStream display mir::log_debug("EGLStream-capable display found"); maximum_suitability = mg::PlatformPriority::best; - break; + eglstream_providers.push_back(provider); } if (display_provider->acquire_interface()) { @@ -119,7 +130,9 @@ auto probe_rendering_platform( for (char const* extension : { "EGL_EXT_platform_base", "EGL_EXT_platform_device", - "EGL_EXT_device_base",}) + "EGL_EXT_device_base", + "EGL_EXT_device_enumeration", + "EGL_EXT_device_query"}) { if (!epoxy_has_egl_extension(EGL_NO_DISPLAY, extension)) { @@ -165,7 +178,7 @@ auto probe_rendering_platform( supported_devices.emplace_back(mg::SupportedDevice{ udev->char_device_from_devnum(mge::devnum_for_device(device)), mg::PlatformPriority::unsupported, - device + nullptr }); } catch (std::exception const& e) @@ -174,27 +187,53 @@ auto probe_rendering_platform( continue; } - EGLDisplay display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, nullptr); + EGLDisplay display{EGL_NO_DISPLAY}; + bool using_display_platform_dpy = false; + for (auto const& display_provider : eglstream_providers) + { + auto display_dpy = display_provider->get_egl_display(); + EGLAttrib display_device; + if (eglQueryDisplayAttribEXT(display_dpy, EGL_DEVICE_EXT, &display_device) == EGL_TRUE) + { + if (reinterpret_cast(display_device) == device) + { + mir::log_debug("Rendering platform using EGLDeviceEXT matching Display platform"); + display = display_dpy; + using_display_platform_dpy = true; + } + } + else + { + mir::log_info("Failed to query EGLDeviceEXT from display platform"); + } + } if (display == EGL_NO_DISPLAY) { - mir::log_debug("Failed to create EGLDisplay: %s", mg::egl_category().message(eglGetError()).c_str()); - continue; - } + display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, nullptr); + if (display == EGL_NO_DISPLAY) + { + mir::log_debug("Failed to create EGLDisplay: %s", mg::egl_category().message(eglGetError()).c_str()); + continue; + } + } auto egl_init = mir::raii::paired_calls( - [&display]() + [&display, using_display_platform_dpy]() { - EGLint major_ver{1}, minor_ver{4}; - if (!eglInitialize(display, &major_ver, &minor_ver)) + if (!using_display_platform_dpy) { - mir::log_debug("Failed to initialise EGL: %s", mg::egl_category().message(eglGetError()).c_str()); - display = EGL_NO_DISPLAY; + EGLint major_ver{1}, minor_ver{4}; + if (!eglInitialize(display, &major_ver, &minor_ver)) + { + mir::log_debug("Failed to initialise EGL: %s", mg::egl_category().message(eglGetError()).c_str()); + display = EGL_NO_DISPLAY; + } } }, - [&display]() + [&display, using_display_platform_dpy]() { - if (display != EGL_NO_DISPLAY) + if (display != EGL_NO_DISPLAY && ! using_display_platform_dpy) { eglTerminate(display); } @@ -222,6 +261,16 @@ auto probe_rendering_platform( { // We've got EGL, and we've got the necessary EGL extensions. We're good. supported_devices.back().support_level = maximum_suitability; + if (using_display_platform_dpy) + { + supported_devices.back().platform_data = std::variant{ + std::in_place_index<0>, display}; + } + else + { + supported_devices.back().platform_data = std::variant{ + std::in_place_index<1>, device}; + } } } } From 35f4a395a068eb20b51590947ffd2cb018ce0fa6 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 6 Oct 2023 16:54:30 +1100 Subject: [PATCH 079/108] platforms/eglstream-kms: Just the worst bug. So, it turns out that if your `EGLStreamProducerSurfaceKHR` is constructed in an EGL context with an EGL config with non-zero alpha channel then everything will *appear* to work until, when actually trying to get something on the screen, `eglStreamConsumerAquireAttribNV` will fail with an undocumented `EGL_BAD_STATE_KHR` error. Request a 0-sized alpha channel in `EGLStreamOutputSurface`. --- src/platforms/eglstream-kms/server/buffer_allocator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index 46cb13c27df..2803eb59ae5 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -696,7 +696,7 @@ auto pick_stream_surface_config(EGLDisplay dpy, mg::GLConfig const& gl_config) - EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, + EGL_ALPHA_SIZE, 0, EGL_DEPTH_SIZE, gl_config.depth_buffer_bits(), EGL_STENCIL_SIZE, gl_config.stencil_buffer_bits(), EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, From 24530739cda10a480ff175f4582639a1ef60175b Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 6 Oct 2023 17:04:05 +1100 Subject: [PATCH 080/108] platforms/eglstream-kms: Disable eglstream acceleration for XWayland It's always been a *bit* flaky, but it seems moreso now. It doesn't enable 3D acceleration under XWayland anyway, so just use the reliable, fallback shm paths. --- src/platforms/eglstream-kms/server/platform.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/platforms/eglstream-kms/server/platform.cpp b/src/platforms/eglstream-kms/server/platform.cpp index d675569c647..0fb8ee43c16 100644 --- a/src/platforms/eglstream-kms/server/platform.cpp +++ b/src/platforms/eglstream-kms/server/platform.cpp @@ -177,7 +177,9 @@ mge::RenderingPlatform::RenderingPlatform(EGLDisplay dpy) : dpy{dpy}, ctx{std::make_unique(dpy)} { - setenv(mir_xwayland_option, "-eglstream", 1); + // XWayland eglstream has always been kinda flaky, now it's somehow worse. + // Disable it until we've had a chance to look at what's wrong. + // setenv(mir_xwayland_option, "-eglstream", 1); } mge::RenderingPlatform::~RenderingPlatform() From 3f195a9038451fb18eebd56d3ec9d7b472d56d8b Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 6 Oct 2023 18:05:43 +1100 Subject: [PATCH 081/108] platform/GLRenderingPlatform: Add `suitability_for_allocator()` It's sensible to prioritise rendering location based on where the client buffers are, and for now this also gives us a simple way of ensuring that the GLRenderingPlatform *can* import all the client buffers it may see. In future, with improvements to `GLRenderingPlatform::as_texture()` implementations, we can relax the tight binding here, but this gives us something that'll definitely work, *now*. --- include/platform/mir/graphics/platform.h | 6 ++++++ .../eglstream-kms/server/buffer_allocator.cpp | 12 ++++++++++++ .../eglstream-kms/server/buffer_allocator.h | 2 ++ src/platforms/gbm-kms/server/buffer_allocator.cpp | 13 +++++++++++++ src/platforms/gbm-kms/server/buffer_allocator.h | 2 ++ .../renderer-generic-egl/buffer_allocator.cpp | 12 ++++++++++++ .../renderer-generic-egl/buffer_allocator.h | 2 ++ src/server/compositor/default_configuration.cpp | 2 +- .../default_display_buffer_compositor_factory.cpp | 5 ++++- .../default_display_buffer_compositor_factory.h | 3 +++ .../mir/test/doubles/mock_gl_rendering_provider.h | 5 +++++ .../mir/test/doubles/stub_gl_rendering_provider.h | 6 ++++++ .../test_surface_stack_with_compositor.cpp | 2 ++ 13 files changed, 70 insertions(+), 2 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index d5f65ba0332..07ed3adeac8 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -127,6 +127,12 @@ class RendererInterfaceBase virtual auto suitability_for_display(std::shared_ptr const& target) -> probe::Result = 0; + /** + * Check how well this Renderer can support a particular BufferAllocator + */ + virtual auto suitability_for_allocator(std::shared_ptr const& target) + -> probe::Result = 0; + virtual auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr = 0; }; diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index 2803eb59ae5..884923265c5 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -763,6 +763,18 @@ auto mge::GLRenderingProvider::surface_for_output( BOOST_THROW_EXCEPTION((std::runtime_error{"DisplayInterfaceProvider does not support any viable output interface"})); } +auto mge::GLRenderingProvider::suitability_for_allocator(std::shared_ptr const& target) + -> probe::Result +{ + // TODO: We *can* import from other allocators, maybe (anything with dma-buf is probably possible) + // For now, the simplest thing is to bind hard to own own allocator. + if (dynamic_cast(target.get())) + { + return probe::best; + } + return probe::unsupported; +} + auto mge::GLRenderingProvider::suitability_for_display( std::shared_ptr const& target) -> probe::Result { diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.h b/src/platforms/eglstream-kms/server/buffer_allocator.h index d5f5cbf12cc..6d4a782b1e3 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.h +++ b/src/platforms/eglstream-kms/server/buffer_allocator.h @@ -99,6 +99,8 @@ class GLRenderingProvider : public graphics::GLRenderingProvider auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; + auto suitability_for_allocator(std::shared_ptr const& target) -> probe::Result override; + auto suitability_for_display(std::shared_ptr const& target) -> probe::Result override; auto make_framebuffer_provider(std::shared_ptr target) -> std::unique_ptr override; diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index d7baeeb3b55..c707977a463 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -16,6 +16,7 @@ #include "buffer_allocator.h" #include "mir/graphics/gl_config.h" +#include "mir/graphics/graphic_buffer_allocator.h" #include "mir/graphics/linux_dmabuf.h" #include "mir/graphics/dmabuf_buffer.h" #include "mir/anonymous_shm_file.h" @@ -468,6 +469,18 @@ class GBMOutputSurface : public mg::gl::OutputSurface }; } +auto mgg::GLRenderingProvider::suitability_for_allocator( + std::shared_ptr const& target) -> probe::Result +{ + // TODO: We *can* import from other allocators, maybe (anything with dma-buf is probably possible) + // For now, the simplest thing is to bind hard to own own allocator. + if (dynamic_cast(target.get())) + { + return probe::best; + } + return probe::unsupported; +} + auto mgg::GLRenderingProvider::suitability_for_display( std::shared_ptr const& target) -> probe::Result { diff --git a/src/platforms/gbm-kms/server/buffer_allocator.h b/src/platforms/gbm-kms/server/buffer_allocator.h index 2feef0e761d..9303e7b2c22 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.h +++ b/src/platforms/gbm-kms/server/buffer_allocator.h @@ -100,6 +100,8 @@ class GLRenderingProvider : public graphics::GLRenderingProvider auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; + auto suitability_for_allocator(std::shared_ptr const& target) -> probe::Result override; + auto suitability_for_display(std::shared_ptr const& target) -> probe::Result override; auto surface_for_output( diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.cpp b/src/platforms/renderer-generic-egl/buffer_allocator.cpp index 29d1d375c79..356e6dc5bcc 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.cpp +++ b/src/platforms/renderer-generic-egl/buffer_allocator.cpp @@ -382,6 +382,18 @@ class EGLOutputSurface : public mg::gl::OutputSurface }; } +auto mge::GLRenderingProvider::suitability_for_allocator(std::shared_ptr const& target) + -> probe::Result +{ + // TODO: We *can* import from other allocators, maybe (anything with dma-buf is probably possible) + // For now, the simplest thing is to bind hard to own own allocator. + if (dynamic_cast(target.get())) + { + return probe::best; + } + return probe::unsupported; +} + auto mge::GLRenderingProvider::suitability_for_display( std::shared_ptr const& target) -> probe::Result { diff --git a/src/platforms/renderer-generic-egl/buffer_allocator.h b/src/platforms/renderer-generic-egl/buffer_allocator.h index 53b53c77c66..f29754f12cc 100644 --- a/src/platforms/renderer-generic-egl/buffer_allocator.h +++ b/src/platforms/renderer-generic-egl/buffer_allocator.h @@ -95,6 +95,8 @@ class GLRenderingProvider : public graphics::GLRenderingProvider auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; + auto suitability_for_allocator(std::shared_ptr const& target) -> probe::Result override; + auto suitability_for_display(std::shared_ptr const& target) -> probe::Result override; auto surface_for_output( diff --git a/src/server/compositor/default_configuration.cpp b/src/server/compositor/default_configuration.cpp index 9e48b026d85..79e6709e1a0 100644 --- a/src/server/compositor/default_configuration.cpp +++ b/src/server/compositor/default_configuration.cpp @@ -64,7 +64,7 @@ mir::DefaultServerConfiguration::the_display_buffer_compositor_factory() } return wrap_display_buffer_compositor_factory( std::make_shared( - std::move(providers), the_gl_config(), the_renderer_factory(), the_compositor_report())); + std::move(providers), the_gl_config(), the_renderer_factory(), the_buffer_allocator(), the_compositor_report())); }); } diff --git a/src/server/compositor/default_display_buffer_compositor_factory.cpp b/src/server/compositor/default_display_buffer_compositor_factory.cpp index 737dc1ff6f0..85c39de0244 100644 --- a/src/server/compositor/default_display_buffer_compositor_factory.cpp +++ b/src/server/compositor/default_display_buffer_compositor_factory.cpp @@ -34,10 +34,12 @@ mc::DefaultDisplayBufferCompositorFactory::DefaultDisplayBufferCompositorFactory std::vector> render_platforms, std::shared_ptr gl_config, std::shared_ptr const& renderer_factory, + std::shared_ptr const& buffer_allocator, std::shared_ptr const& report) : platforms{std::move(render_platforms)}, gl_config{std::move(gl_config)}, renderer_factory{renderer_factory}, + buffer_allocator{buffer_allocator}, report{report} { } @@ -65,7 +67,8 @@ mc::DefaultDisplayBufferCompositorFactory::create_compositor_for( for (auto const& provider : platforms) { auto suitability = provider->suitability_for_display(display_provider); - if (suitability > best_provider.first) + // We also need to make sure that the GLRenderingProvider can access client buffers... + if (provider->suitability_for_allocator(buffer_allocator) > mg::probe::unsupported && suitability > best_provider.first) { best_provider = std::make_pair(suitability, provider); } diff --git a/src/server/compositor/default_display_buffer_compositor_factory.h b/src/server/compositor/default_display_buffer_compositor_factory.h index c1b00f8f05b..efee53b1a33 100644 --- a/src/server/compositor/default_display_buffer_compositor_factory.h +++ b/src/server/compositor/default_display_buffer_compositor_factory.h @@ -35,6 +35,7 @@ namespace graphics { class GLRenderingProvider; class GLConfig; +class GraphicBufferAllocator; } /// Compositing. Combining renderables into a display image. namespace compositor @@ -47,6 +48,7 @@ class DefaultDisplayBufferCompositorFactory : public DisplayBufferCompositorFact std::vector> render_platforms, std::shared_ptr gl_config, std::shared_ptr const& renderer_factory, + std::shared_ptr const& buffer_allocator, std::shared_ptr const& report); std::unique_ptr create_compositor_for(graphics::DisplayBuffer& display_buffer) override; @@ -55,6 +57,7 @@ class DefaultDisplayBufferCompositorFactory : public DisplayBufferCompositorFact std::vector> const platforms; std::shared_ptr const gl_config; std::shared_ptr const renderer_factory; + std::shared_ptr const buffer_allocator; std::shared_ptr const report; }; diff --git a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h index 3afe91b91f8..0bb0f9da7d2 100644 --- a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h @@ -35,6 +35,11 @@ class MockGlRenderingPlatform : public graphics::GLRenderingProvider surface_for_output, (std::shared_ptr, geometry::Size, graphics::GLConfig const&), (override)); + MOCK_METHOD( + graphics::probe::Result, + suitability_for_allocator, + (std::shared_ptr const&), + (override)); MOCK_METHOD( graphics::probe::Result, suitability_for_display, diff --git a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h index f1b7c61025d..83d23a852e4 100644 --- a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h @@ -58,6 +58,12 @@ class StubGlRenderingPlatform : public graphics::GLRenderingProvider return std::make_unique>(); } + auto suitability_for_allocator(std::shared_ptr const& /*target*/) + -> graphics::probe::Result override + { + return graphics::probe::supported; + } + auto suitability_for_display(std::shared_ptr const& /*target*/) -> graphics::probe::Result override { diff --git a/tests/integration-tests/test_surface_stack_with_compositor.cpp b/tests/integration-tests/test_surface_stack_with_compositor.cpp index e4aa7a321c2..95d1bd4a250 100644 --- a/tests/integration-tests/test_surface_stack_with_compositor.cpp +++ b/tests/integration-tests/test_surface_stack_with_compositor.cpp @@ -36,6 +36,7 @@ #include "mir/test/doubles/mock_output_surface.h" #include "mir/test/doubles/stub_gl_rendering_provider.h" #include "mir/test/doubles/null_gl_config.h" +#include "mir/test/doubles/stub_buffer_allocator.h" #include #include @@ -187,6 +188,7 @@ struct SurfaceStackCompositor : public Test std::vector>{std::make_shared()}, std::make_shared(), mt::fake_shared(renderer_factory), + std::make_shared(), null_comp_report}; }; From 6b3b3bb202cbfdcf886b1f28ab8b82e3a8568355 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Fri, 6 Oct 2023 18:07:51 +1100 Subject: [PATCH 082/108] platforms/wayland: Hand-hold clang And hopefully fix the build? --- src/platforms/wayland/wl_egl_display_provider.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/platforms/wayland/wl_egl_display_provider.cpp b/src/platforms/wayland/wl_egl_display_provider.cpp index 0e3babd6b4a..64206f7918b 100644 --- a/src/platforms/wayland/wl_egl_display_provider.cpp +++ b/src/platforms/wayland/wl_egl_display_provider.cpp @@ -117,9 +117,10 @@ mgw::WlDisplayProvider::EGLDisplayProvider::EGLDisplayProvider( geometry::Size size) : dpy{from.dpy}, output{ - std::make_optional( + OutputContext { wl_egl_window_create(surface, size.width.as_int(), size.height.as_int()), - size)} + size + }} { } From d160b64ea9dcb870b51451d81063ced61cf2b899 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Tue, 10 Oct 2023 17:48:14 +0100 Subject: [PATCH 083/108] Some error logging --- src/platforms/common/server/CMakeLists.txt | 2 ++ .../common/server/cpu_copy_output_surface.cpp | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/platforms/common/server/CMakeLists.txt b/src/platforms/common/server/CMakeLists.txt index 3c1053f9070..ba2fd013204 100644 --- a/src/platforms/common/server/CMakeLists.txt +++ b/src/platforms/common/server/CMakeLists.txt @@ -6,6 +6,8 @@ include_directories( ${server_common_include_dirs} ) +add_compile_definitions(MIR_LOG_COMPONENT_FALLBACK="server_platform_common") + add_library(server_platform_common STATIC shm_buffer.cpp one_shot_device_observer.h diff --git a/src/platforms/common/server/cpu_copy_output_surface.cpp b/src/platforms/common/server/cpu_copy_output_surface.cpp index 468cb4feb22..0b6d87caac7 100644 --- a/src/platforms/common/server/cpu_copy_output_surface.cpp +++ b/src/platforms/common/server/cpu_copy_output_surface.cpp @@ -26,6 +26,7 @@ #include "mir/graphics/egl_error.h" #include "mir/graphics/platform.h" +#include "mir/log.h" #include "cpu_copy_output_surface.h" @@ -249,12 +250,18 @@ void mgc::CPUCopyOutputSurface::Impl::bind() void mgc::CPUCopyOutputSurface::Impl::make_current() { - eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx); + if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx) != EGL_TRUE) + { + mir::log_debug("Failed to make EGL context current"); + } } void mgc::CPUCopyOutputSurface::Impl::release_current() { - eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE) + { + mir::log_debug("Failed to release current EGL context"); + } } auto mgc::CPUCopyOutputSurface::Impl::commit() -> std::unique_ptr From bda9447f254ab839f3af1ca59ac83e825533b746 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Tue, 10 Oct 2023 17:48:22 +0100 Subject: [PATCH 084/108] Fix spelling --- src/platforms/common/server/cpu_copy_output_surface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/common/server/cpu_copy_output_surface.cpp b/src/platforms/common/server/cpu_copy_output_surface.cpp index 0b6d87caac7..b0fff4a6709 100644 --- a/src/platforms/common/server/cpu_copy_output_surface.cpp +++ b/src/platforms/common/server/cpu_copy_output_surface.cpp @@ -175,7 +175,7 @@ void mgc::CPUCopyOutputSurface::make_current() void mgc::CPUCopyOutputSurface::release_current() { - impl->make_current(); + impl->release_current(); } auto mgc::CPUCopyOutputSurface::commit() -> std::unique_ptr From 12ea68ab6a2a2636b412f0ebec0dcc20f279fbfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sawicz?= Date: Tue, 10 Oct 2023 18:55:10 +0200 Subject: [PATCH 085/108] linux_dmabuf: use eglDestroyImageKHR extension (#3069) Rather than naked eglDestroyImage. https://registry.khronos.org/EGL/sdk/docs/man/html/eglDestroyImage.xhtml --- src/platform/graphics/linux_dmabuf.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/platform/graphics/linux_dmabuf.cpp b/src/platform/graphics/linux_dmabuf.cpp index 775bebc4a62..bd27f9f3f1d 100644 --- a/src/platform/graphics/linux_dmabuf.cpp +++ b/src/platform/graphics/linux_dmabuf.cpp @@ -1260,8 +1260,9 @@ auto mg::DMABufEGLProvider::as_texture(std::shared_ptr buffer) } auto importable_dmabuf = export_egl_image(*importing_provider->dmabuf_export_ext, importing_provider->dpy, importable_image, dmabuf_tex->size()); - eglDestroyImage(importing_provider->dpy, src_image); - eglDestroyImage(importing_provider->dpy, importable_image); + auto base_extension = importing_provider->egl_extensions->base(importing_provider->dpy); + base_extension.eglDestroyImageKHR(importing_provider->dpy, src_image); + base_extension.eglDestroyImageKHR(importing_provider->dpy, importable_image); if (auto descriptor = descriptor_for_format_and_modifiers( importable_dmabuf->format(), From 330fee34f1ef97329c2427561ef5da279f8c04f1 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Wed, 11 Oct 2023 11:06:33 +0100 Subject: [PATCH 086/108] Unused headers --- include/platform/mir/graphics/buffer.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/platform/mir/graphics/buffer.h b/include/platform/mir/graphics/buffer.h index 0aed4e83074..cfb3f7a9127 100644 --- a/include/platform/mir/graphics/buffer.h +++ b/include/platform/mir/graphics/buffer.h @@ -21,9 +21,6 @@ #include "mir/geometry/size.h" #include "mir_toolkit/common.h" -#include -#include - namespace mir { namespace graphics From fb01be0313056d7db240dfb093659071df142a04 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Wed, 11 Oct 2023 11:10:04 +0100 Subject: [PATCH 087/108] Bump platform ABI --- debian/control | 4 +- debian/libmirplatform26.install | 1 - debian/libmirplatform27.install | 1 + src/CMakeLists.txt | 2 +- src/platform/symbols.map | 76 ++++++++++----------------------- 5 files changed, 26 insertions(+), 58 deletions(-) delete mode 100644 debian/libmirplatform26.install create mode 100644 debian/libmirplatform27.install diff --git a/debian/control b/debian/control index dfbf65cc953..33e6cd85679 100644 --- a/debian/control +++ b/debian/control @@ -76,7 +76,7 @@ Description: Display server for Ubuntu - server library . Contains the shared library needed by server applications for Mir. -Package: libmirplatform26 +Package: libmirplatform27 Section: libs Architecture: linux-any Multi-Arch: same @@ -124,7 +124,7 @@ Section: libdevel Architecture: linux-any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends} -Depends: libmirplatform26 (= ${binary:Version}), +Depends: libmirplatform27 (= ${binary:Version}), libmircommon-dev (= ${binary:Version}), libboost-program-options-dev, ${misc:Depends}, diff --git a/debian/libmirplatform26.install b/debian/libmirplatform26.install deleted file mode 100644 index b5b49cd0c74..00000000000 --- a/debian/libmirplatform26.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/*/libmirplatform.so.26 diff --git a/debian/libmirplatform27.install b/debian/libmirplatform27.install new file mode 100644 index 00000000000..21e6f7cea33 --- /dev/null +++ b/debian/libmirplatform27.install @@ -0,0 +1 @@ +usr/lib/*/libmirplatform.so.27 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd32449ba72..4425ea9101d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ # We need MIRPLATFORM_ABI in both libmirplatform and the platform implementations. -set(MIRPLATFORM_ABI 26) +set(MIRPLATFORM_ABI 27) set(MIRAL_VERSION_MAJOR 4) set(MIRAL_VERSION_MINOR 1) diff --git a/src/platform/symbols.map b/src/platform/symbols.map index 591ac9e2f1d..d6a0d145878 100644 --- a/src/platform/symbols.map +++ b/src/platform/symbols.map @@ -1,4 +1,4 @@ -MIRPLATFORM_2.5 { +MIR_PLATFORM_2.16 { global: extern "C++" { mir::graphics::AtomicFrame::increment*; @@ -6,6 +6,18 @@ MIRPLATFORM_2.5 { mir::graphics::AtomicFrame::store*; mir::graphics::Buffer::Buffer*; mir::graphics::BufferBasic::BufferBasic*; + mir::graphics::DMABufEGLProvider::?DMABufEGLProvider*; + mir::graphics::DMABufEGLProvider::DMABufEGLProvider*; + mir::graphics::DMABufEGLProvider::as_texture*; + mir::graphics::DRMFormat::DRMFormat*; + mir::graphics::DRMFormat::alpha_equivalent*; + mir::graphics::DRMFormat::as_mir_format*; + mir::graphics::DRMFormat::components*; + mir::graphics::DRMFormat::from_mir_format*; + mir::graphics::DRMFormat::has_alpha*; + mir::graphics::DRMFormat::name*; + mir::graphics::DRMFormat::opaque_equivalent*; + mir::graphics::DRMFormat::operator?unsigned?int*; /* Is actually operator uint32_t(), but 🤷 */ mir::graphics::DisplayConfiguration::operator*; mir::graphics::DisplayConfiguration::valid*; mir::graphics::DisplayConfigurationOutput::extents*; @@ -51,7 +63,11 @@ MIRPLATFORM_2.5 { mir::graphics::UserDisplayConfigurationOutput::extents*; mir::graphics::alpha_channel_depth*; mir::graphics::blue_channel_depth*; + mir::graphics::common::EGLContextExecutor::?EGLContextExecutor*; + mir::graphics::common::EGLContextExecutor::EGLContextExecutor*; + mir::graphics::common::EGLContextExecutor::spawn*; mir::graphics::contains_alpha*; + mir::graphics::drm_modifier_to_string*; mir::graphics::egl_category*; mir::graphics::gl::Program::?Program*; mir::graphics::gl::ProgramFactory::?ProgramFactory*; @@ -108,6 +124,7 @@ MIRPLATFORM_2.5 { mir::options::glog_log_dir*; mir::options::glog_minloglevel*; mir::options::glog_stderrthreshold*; + mir::options::idle_timeout_opt; mir::options::input_report_opt*; mir::options::log_opt_value*; mir::options::logind_console; @@ -135,8 +152,10 @@ MIRPLATFORM_2.5 { mir::renderer::software::as_read_mappable_buffer*; mir::udev::Context::?Context*; mir::udev::Context::Context*; + mir::udev::Context::char_device_from_devnum*; mir::udev::Context::ctx*; mir::udev::Context::device_from_syspath*; + mir::udev::Device::clone*; mir::udev::Device::initialised*; mir::udev::Device::sysname*; mir::udev::Device::syspath*; @@ -161,6 +180,7 @@ MIRPLATFORM_2.5 { typeinfo?for?mir::graphics::Buffer; typeinfo?for?mir::graphics::BufferBasic; typeinfo?for?mir::graphics::DisplayConfiguration; + typeinfo?for?mir::graphics::common::EGLContextExecutor; typeinfo?for?mir::graphics::gl::Program; typeinfo?for?mir::graphics::gl::ProgramFactory; typeinfo?for?mir::graphics::gl::Texture; @@ -172,6 +192,7 @@ MIRPLATFORM_2.5 { vtable?for?mir::graphics::Buffer; vtable?for?mir::graphics::BufferBasic; vtable?for?mir::graphics::DisplayConfiguration; + vtable?for?mir::graphics::common::EGLContextExecutor; vtable?for?mir::graphics::gl::Program; vtable?for?mir::graphics::gl::ProgramFactory; vtable?for?mir::graphics::gl::Texture; @@ -183,56 +204,3 @@ MIRPLATFORM_2.5 { local: *; }; -MIRPLATFORM_2.7 { - global: - extern "C++" { - mir::options::idle_timeout_opt; - }; -} MIRPLATFORM_2.5; - -MIR_PLATFORM_2.8 { - global: - extern "C++" { - mir::graphics::DRMFormat::DRMFormat*; - mir::graphics::DRMFormat::name*; - mir::graphics::DRMFormat::alpha_equivalent*; - mir::graphics::DRMFormat::opaque_equivalent*; - mir::graphics::DRMFormat::has_alpha*; - mir::graphics::DRMFormat::components*; - mir::graphics::DRMFormat::operator?unsigned?int*; /* Is actually operator uint32_t(), but 🤷 */ - - mir::graphics::drm_modifier_to_string*; - - mir::udev::Device::clone*; - mir::udev::Context::char_device_from_devnum*; - - mir::graphics::common::EGLContextExecutor::EGLContextExecutor*; - mir::graphics::common::EGLContextExecutor::?EGLContextExecutor*; - mir::graphics::common::EGLContextExecutor::spawn*; - typeinfo?for?mir::graphics::common::EGLContextExecutor; - vtable?for?mir::graphics::common::EGLContextExecutor; - }; -} MIRPLATFORM_2.7; - -MIR_PLATFORM_2.11 { - global: - extern "C++" { - mir::graphics::DRMFormat::as_mir_format*; - }; -} MIR_PLATFORM_2.8; - -MIR_PLATFORM_2.13 { - global: - extern "C++" { - mir::graphics::DRMFormat::from_mir_format*; - }; -} MIR_PLATFORM_2.11; - -MIR_PLATFORM_2.16 { - global: - extern "C++" { - mir::graphics::DMABufEGLProvider::DMABufEGLProvider*; - mir::graphics::DMABufEGLProvider::?DMABufEGLProvider*; - mir::graphics::DMABufEGLProvider::as_texture*; - }; -} MIR_PLATFORM_2.13; From ccc6e476d5df16e59a995e9ea839da7936471b32 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Wed, 11 Oct 2023 11:13:29 +0100 Subject: [PATCH 088/108] Update file headers --- include/platform/mir/renderer/gl/gl_surface.h | 4 +--- src/platforms/common/server/cpu_copy_output_surface.cpp | 2 -- src/platforms/common/server/cpu_copy_output_surface.h | 2 -- src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp | 4 +--- src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h | 4 +--- src/platforms/gbm-kms/server/kms/kms_framebuffer.h | 4 +--- tests/include/mir/test/doubles/mock_gl_rendering_provider.h | 4 +--- tests/include/mir/test/doubles/stub_gl_rendering_provider.h | 6 +++--- 8 files changed, 8 insertions(+), 22 deletions(-) diff --git a/include/platform/mir/renderer/gl/gl_surface.h b/include/platform/mir/renderer/gl/gl_surface.h index d2dbabe3b20..852c3639611 100644 --- a/include/platform/mir/renderer/gl/gl_surface.h +++ b/include/platform/mir/renderer/gl/gl_surface.h @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Canonical Ltd. + * 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, @@ -12,8 +12,6 @@ * * 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 */ #ifndef MIR_RENDERER_GL_SURFACE_H_ diff --git a/src/platforms/common/server/cpu_copy_output_surface.cpp b/src/platforms/common/server/cpu_copy_output_surface.cpp index b0fff4a6709..5001f282304 100644 --- a/src/platforms/common/server/cpu_copy_output_surface.cpp +++ b/src/platforms/common/server/cpu_copy_output_surface.cpp @@ -12,8 +12,6 @@ * * 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 diff --git a/src/platforms/common/server/cpu_copy_output_surface.h b/src/platforms/common/server/cpu_copy_output_surface.h index a1d239efa8e..fc38036b8af 100644 --- a/src/platforms/common/server/cpu_copy_output_surface.h +++ b/src/platforms/common/server/cpu_copy_output_surface.h @@ -12,8 +12,6 @@ * * 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 diff --git a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp index fa6e0d1e150..dacf1932a60 100644 --- a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp +++ b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Canonical Ltd. + * 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, @@ -12,8 +12,6 @@ * * 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 "cpu_addressable_fb.h" diff --git a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h index 949a035269a..9d2b3494168 100644 --- a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h +++ b/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Canonical Ltd. + * 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, @@ -12,8 +12,6 @@ * * 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 */ #ifndef MIR_GRAPHICS_GBM_FB_H_ diff --git a/src/platforms/gbm-kms/server/kms/kms_framebuffer.h b/src/platforms/gbm-kms/server/kms/kms_framebuffer.h index 1e753d5326a..32b16c2ebf6 100644 --- a/src/platforms/gbm-kms/server/kms/kms_framebuffer.h +++ b/src/platforms/gbm-kms/server/kms/kms_framebuffer.h @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Canonical Ltd. + * 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, @@ -12,8 +12,6 @@ * * 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 */ #ifndef MIR_GRAPHICS_GBM_KMS_FRAMEBUFFER_H_ diff --git a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h index 0bb0f9da7d2..c2d8622e2d9 100644 --- a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Canonical Ltd. + * Copyright © Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 or 3, @@ -12,8 +12,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_DOUBLES_MOCK_GL_RENDERING_PROVIDER_H_ diff --git a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h index 83d23a852e4..53180fbe750 100644 --- a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h @@ -1,5 +1,7 @@ /* - * Copyright © 2021 Canonical Ltd. + * Copyright © Canonical Ltd. + * + * Copyright © Canonical Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 or 3, @@ -12,8 +14,6 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * - * Authored by: Christopher James Halse Rogers */ #ifndef MIR_TEST_DOUBLES_STUB_GL_RENDERING_PROVIDER_H_ From 18a303c464d507dba2a86521dcec074accd83871 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Wed, 11 Oct 2023 11:25:31 +0100 Subject: [PATCH 089/108] Bunp Miroil ABI (Miroil uses `graphics::Buffer` which has changed) --- debian/control | 4 ++-- debian/libmiroil3.install | 1 - debian/libmiroil4.install | 1 + src/miroil/CMakeLists.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) delete mode 100644 debian/libmiroil3.install create mode 100644 debian/libmiroil4.install diff --git a/debian/control b/debian/control index 33e6cd85679..f0117044346 100644 --- a/debian/control +++ b/debian/control @@ -520,7 +520,7 @@ Description: Developer files for the Mir ABI-stable abstraction layer Contains header files required for development using the MirAL abstraction layer. -Package: libmiroil3 +Package: libmiroil4 Section: libs Architecture: linux-any Multi-Arch: same @@ -537,7 +537,7 @@ Section: libdevel Architecture: linux-any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends} -Depends: libmiroil3 (= ${binary:Version}), +Depends: libmiroil4 (= ${binary:Version}), ${misc:Depends}, Description: Developer files for the Mir Lomiri compatibility library MirOil provides the Lomiri compatibility API. diff --git a/debian/libmiroil3.install b/debian/libmiroil3.install deleted file mode 100644 index f8f1a08d2d4..00000000000 --- a/debian/libmiroil3.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/*/libmiroil.so.3 diff --git a/debian/libmiroil4.install b/debian/libmiroil4.install new file mode 100644 index 00000000000..00634524443 --- /dev/null +++ b/debian/libmiroil4.install @@ -0,0 +1 @@ +usr/lib/*/libmiroil.so.4 diff --git a/src/miroil/CMakeLists.txt b/src/miroil/CMakeLists.txt index 6142a8e8b88..26baa539b82 100644 --- a/src/miroil/CMakeLists.txt +++ b/src/miroil/CMakeLists.txt @@ -1,4 +1,4 @@ -set(MIROIL_ABI 3) +set(MIROIL_ABI 4) set(MIROIL_VERSION_MAJOR ${MIROIL_ABI}) set(MIROIL_VERSION_MINOR 0) set(MIROIL_VERSION_PATCH 0) From 839a0aab9d23e86bbe085ee27517ab08892650bf Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Wed, 11 Oct 2023 12:10:41 +0100 Subject: [PATCH 090/108] Drop documentation stub --- src/platform/graphics/documentation.rst | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 src/platform/graphics/documentation.rst diff --git a/src/platform/graphics/documentation.rst b/src/platform/graphics/documentation.rst deleted file mode 100644 index ad7db6c49db..00000000000 --- a/src/platform/graphics/documentation.rst +++ /dev/null @@ -1,18 +0,0 @@ -Platform interfaces -=================== - -Top level design ----------------- - -Constraints -___________ - -* Platforms differ in their capabilities -* A single system may require multiple different platforms to support all the hardware - -Relevant objects: ------------------ - -`RenderingPlatform` - Provides graphics buffer allocation services, both to clients and internally to Mir. - In conjunction with the `DisplayPlatform` provides a From 49bd5a5e2a3e2326afdf2f3a5a7d5bac6205fbbd Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Wed, 11 Oct 2023 17:58:01 +0100 Subject: [PATCH 091/108] Add a feature flag to hide hybrid capability --- src/server/graphics/default_configuration.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index 7296d43151b..61a4bb32b60 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -134,6 +134,19 @@ auto select_platforms_from_list(std::string const& selection, std::vector>>& platform_modules) +{ + static bool const experimental_hybrid_graphics = getenv("MIR_EXPERIMENTAL_HYBRID_GRAPHICS"); + + if (!experimental_hybrid_graphics) + { + std::stable_sort(std::begin(platform_modules), std::end(platform_modules), + [](auto const& l, auto const& r) { return l.first.support_level > r.first.support_level; }); + + platform_modules.resize(std::min(1ul, platform_modules.size())); + } +} } auto mir::DefaultServerConfiguration::the_display_platforms() -> std::vector> const& @@ -199,6 +212,8 @@ auto mir::DefaultServerConfiguration::the_display_platforms() -> std::vector(*the_options()), the_console_services()); + + hybrid_check(platform_modules); } for (auto const& [device, platform]: platform_modules) @@ -326,6 +341,8 @@ auto mir::DefaultServerConfiguration::the_rendering_platforms() -> else { platform_modules = mir::graphics::rendering_modules_for_device(platforms, display_interfaces, dynamic_cast(*the_options()), the_console_services()); + + hybrid_check(platform_modules); } for (auto const& [device, platform]: platform_modules) From 55c9c91fefe846c81559c3f3459bc96f18081ac5 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Thu, 12 Oct 2023 09:50:58 +0100 Subject: [PATCH 092/108] C++23 FTW! --- CMakeLists.txt | 2 +- src/server/graphics/default_configuration.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 30825e5ffd2..64df5031da0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "${build_types}" FORCE) # Enable cmake-gui to display a drop down list for CMAKE_BUILD_TYPE set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${build_types}") -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index 61a4bb32b60..45e0ec89d15 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -144,7 +144,7 @@ void hybrid_check(std::vector r.first.support_level; }); - platform_modules.resize(std::min(1ul, platform_modules.size())); + platform_modules.resize(std::min(1uz, platform_modules.size())); } } } From c7c2d2bcdac52639107bb652ddd4bd478fe0cbbd Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Thu, 12 Oct 2023 16:39:18 +0100 Subject: [PATCH 093/108] Restore some dropped tests --- tests/acceptance-tests/platforms/eglstream-kms_platform.cpp | 1 + tests/unit-tests/platforms/eglstream-kms/server/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/acceptance-tests/platforms/eglstream-kms_platform.cpp b/tests/acceptance-tests/platforms/eglstream-kms_platform.cpp index 0ff79f9a12f..3878b6f4cc4 100644 --- a/tests/acceptance-tests/platforms/eglstream-kms_platform.cpp +++ b/tests/acceptance-tests/platforms/eglstream-kms_platform.cpp @@ -44,3 +44,4 @@ EGLStreamKMSPlatformEnv platform_harness; } INSTANTIATE_TEST_SUITE_P(EGLStreamKMS, RenderingPlatformTest, testing::Values(&platform_harness)); +INSTANTIATE_TEST_SUITE_P(EGLStreamKMS, DisplayPlatformTest, testing::Values(&platform_harness)); diff --git a/tests/unit-tests/platforms/eglstream-kms/server/CMakeLists.txt b/tests/unit-tests/platforms/eglstream-kms/server/CMakeLists.txt index 8d2e2f5bc0a..f3501451141 100644 --- a/tests/unit-tests/platforms/eglstream-kms/server/CMakeLists.txt +++ b/tests/unit-tests/platforms/eglstream-kms/server/CMakeLists.txt @@ -1,5 +1,6 @@ list(APPEND EGLSTREAM_KMS_UNIT_TEST_SOURCES $ + ${CMAKE_CURRENT_SOURCE_DIR}/test_threaded_drm_event_handler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_utils.cpp ) set(EGLSTREAM_KMS_UNIT_TEST_SOURCES ${EGLSTREAM_KMS_UNIT_TEST_SOURCES} PARENT_SCOPE) From d60bff95d74c77a4cc4fd57028c9e01ae6c9f560 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Thu, 12 Oct 2023 16:39:32 +0100 Subject: [PATCH 094/108] Make test slightly stricter --- tests/unit-tests/platforms/x11/test_platform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit-tests/platforms/x11/test_platform.cpp b/tests/unit-tests/platforms/x11/test_platform.cpp index 2198a45a78b..1da1f562087 100644 --- a/tests/unit-tests/platforms/x11/test_platform.cpp +++ b/tests/unit-tests/platforms/x11/test_platform.cpp @@ -81,7 +81,7 @@ TEST_F(X11GraphicsPlatformTest, failure_to_open_x11_display_results_in_an_error) { using namespace ::testing; - EXPECT_CALL(mock_x11, XOpenDisplay(_)) + EXPECT_CALL(mock_x11, XOpenDisplay(_)).Times(AtLeast(1)) .WillRepeatedly(Return(nullptr)); mir::SharedLibrary platform_lib{mtf::server_platform("server-x11")}; From a77df473b60ad3e95aa9c53da27a0410358def4c Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Thu, 12 Oct 2023 16:43:22 +0100 Subject: [PATCH 095/108] Use MOCK_METHOD constently --- tests/include/mir/test/doubles/mock_renderer.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/include/mir/test/doubles/mock_renderer.h b/tests/include/mir/test/doubles/mock_renderer.h index 13ec511419c..8e34db92529 100644 --- a/tests/include/mir/test/doubles/mock_renderer.h +++ b/tests/include/mir/test/doubles/mock_renderer.h @@ -30,10 +30,10 @@ namespace doubles struct MockRenderer : public renderer::Renderer { - MOCK_METHOD1(set_viewport, void(geometry::Rectangle const&)); - MOCK_METHOD1(set_output_transform, void(glm::mat2 const&)); + MOCK_METHOD(void, set_viewport, (geometry::Rectangle const&)); + MOCK_METHOD(void, set_output_transform, (glm::mat2 const&)); MOCK_METHOD(std::unique_ptr, render, (graphics::RenderableList const&), (const override)); - MOCK_METHOD0(suspend, void()); + MOCK_METHOD(void, suspend, ()); ~MockRenderer() noexcept {} }; From 69de0d4647a0c3f9852d843e6796eced8c3afc93 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Fri, 13 Oct 2023 11:39:32 +0100 Subject: [PATCH 096/108] Better naming in the RenderingProvider class hierarchy --- include/platform/mir/graphics/platform.h | 14 +++++++------- src/platforms/eglstream-kms/server/platform.cpp | 2 +- src/platforms/eglstream-kms/server/platform.h | 2 +- src/platforms/gbm-kms/server/kms/platform.cpp | 2 +- src/platforms/gbm-kms/server/kms/platform.h | 2 +- .../renderer-generic-egl/rendering_platform.cpp | 4 ++-- .../renderer-generic-egl/rendering_platform.h | 2 +- .../compositor/default_display_buffer_compositor.h | 2 +- .../mir/test/doubles/mock_gl_rendering_provider.h | 2 +- tests/include/mir/test/doubles/null_platform.h | 2 +- .../mir/test/doubles/stub_gl_rendering_provider.h | 2 +- .../test_surface_stack_with_compositor.cpp | 2 +- .../mir_test_framework/platform_graphics_throw.cpp | 2 +- .../stubbed_graphics_platform.cpp | 6 +++--- .../mir_test_framework/stubbed_graphics_platform.h | 4 ++-- .../compositor/test_basic_screen_shooter.cpp | 4 ++-- .../test_default_display_buffer_compositor.cpp | 2 +- tests/unit-tests/renderers/gl/test_gl_renderer.cpp | 2 +- 18 files changed, 29 insertions(+), 29 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index 07ed3adeac8..e920fbfe863 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -82,7 +82,7 @@ namespace probe */ } -class RendererInterfaceBase +class RenderingProvider { public: class Tag @@ -92,7 +92,7 @@ class RendererInterfaceBase virtual ~Tag() = default; }; - virtual ~RendererInterfaceBase() = default; + virtual ~RenderingProvider() = default; class FramebufferProvider { @@ -143,10 +143,10 @@ class Texture; class OutputSurface; } -class GLRenderingProvider : public RendererInterfaceBase +class GLRenderingProvider : public RenderingProvider { public: - class Tag : public RendererInterfaceBase::Tag + class Tag : public RenderingProvider::Tag { }; @@ -198,8 +198,8 @@ class RenderingPlatform static auto acquire_interface(std::shared_ptr platform) -> std::shared_ptr { static_assert( - std::is_convertible_v, - "Can only acquire a Renderer interface; Interface must implement RendererInterfaceBase"); + std::is_convertible_v, + "Can only acquire a Renderer interface; Interface must implement RenderingProvider"); if (auto const base_interface = platform->maybe_create_interface(typename Interface::Tag{})) { @@ -232,7 +232,7 @@ class RenderingPlatform * interface that corresponds to the most-derived type of tag_type. */ virtual auto maybe_create_interface( - RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr = 0; + RenderingProvider::Tag const& type_tag) -> std::shared_ptr = 0; }; class DisplayInterfaceBase diff --git a/src/platforms/eglstream-kms/server/platform.cpp b/src/platforms/eglstream-kms/server/platform.cpp index 0fb8ee43c16..624d1a11b89 100644 --- a/src/platforms/eglstream-kms/server/platform.cpp +++ b/src/platforms/eglstream-kms/server/platform.cpp @@ -194,7 +194,7 @@ mir::UniqueModulePtr mge::RenderingPlatform::create_ } auto mge::RenderingPlatform::maybe_create_interface( - RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr + RenderingProvider::Tag const& type_tag) -> std::shared_ptr { if (dynamic_cast(&type_tag)) { diff --git a/src/platforms/eglstream-kms/server/platform.h b/src/platforms/eglstream-kms/server/platform.h index c1be78c2b86..03688709e19 100644 --- a/src/platforms/eglstream-kms/server/platform.h +++ b/src/platforms/eglstream-kms/server/platform.h @@ -51,7 +51,7 @@ class RenderingPlatform : public graphics::RenderingPlatform protected: auto maybe_create_interface( - RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr override; + RenderingProvider::Tag const& type_tag) -> std::shared_ptr override; private: EGLDisplay const dpy; std::unique_ptr const ctx; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index b91ac34976c..e8694abb125 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -350,7 +350,7 @@ mir::UniqueModulePtr mgg::RenderingPlatform::create_ } auto mgg::RenderingPlatform::maybe_create_interface( - RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr + RenderingProvider::Tag const& type_tag) -> std::shared_ptr { if (dynamic_cast(&type_tag)) { diff --git a/src/platforms/gbm-kms/server/kms/platform.h b/src/platforms/gbm-kms/server/kms/platform.h index 97982318074..4ffab60ecb6 100644 --- a/src/platforms/gbm-kms/server/kms/platform.h +++ b/src/platforms/gbm-kms/server/kms/platform.h @@ -98,7 +98,7 @@ class RenderingPlatform : public graphics::RenderingPlatform protected: auto maybe_create_interface( - RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr override; + RenderingProvider::Tag const& type_tag) -> std::shared_ptr override; private: RenderingPlatform( diff --git a/src/platforms/renderer-generic-egl/rendering_platform.cpp b/src/platforms/renderer-generic-egl/rendering_platform.cpp index 82c18a2ff1b..db2abfd32c3 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.cpp +++ b/src/platforms/renderer-generic-egl/rendering_platform.cpp @@ -186,8 +186,8 @@ auto mge::RenderingPlatform::create_buffer_allocator( return make_module_ptr(dpy, static_cast(*ctx), dmabuf_provider); } -auto mge::RenderingPlatform::maybe_create_interface(RendererInterfaceBase::Tag const& tag) - -> std::shared_ptr +auto mge::RenderingPlatform::maybe_create_interface(RenderingProvider::Tag const& tag) + -> std::shared_ptr { if (dynamic_cast(&tag)) { diff --git a/src/platforms/renderer-generic-egl/rendering_platform.h b/src/platforms/renderer-generic-egl/rendering_platform.h index c198f434a96..e6bc6f58b4b 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.h +++ b/src/platforms/renderer-generic-egl/rendering_platform.h @@ -44,7 +44,7 @@ class RenderingPlatform : public graphics::RenderingPlatform protected: auto maybe_create_interface( - RendererInterfaceBase::Tag const& type_tag) -> std::shared_ptr override; + RenderingProvider::Tag const& type_tag) -> std::shared_ptr override; private: EGLDisplay const dpy; diff --git a/src/server/compositor/default_display_buffer_compositor.h b/src/server/compositor/default_display_buffer_compositor.h index d83c531a3ca..4bf13a3ca3b 100644 --- a/src/server/compositor/default_display_buffer_compositor.h +++ b/src/server/compositor/default_display_buffer_compositor.h @@ -54,7 +54,7 @@ class DefaultDisplayBufferCompositor : public DisplayBufferCompositor private: graphics::DisplayBuffer& display_buffer; std::shared_ptr const renderer; - std::unique_ptr const fb_adaptor; + std::unique_ptr const fb_adaptor; std::shared_ptr const report; }; diff --git a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h index c2d8622e2d9..31a859d030b 100644 --- a/tests/include/mir/test/doubles/mock_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/mock_gl_rendering_provider.h @@ -24,7 +24,7 @@ namespace mir::test::doubles { -class MockGlRenderingPlatform : public graphics::GLRenderingProvider +class MockGlRenderingProvider : public graphics::GLRenderingProvider { public: MOCK_METHOD(std::shared_ptr, as_texture, (std::shared_ptr), (override)); diff --git a/tests/include/mir/test/doubles/null_platform.h b/tests/include/mir/test/doubles/null_platform.h index 0d805043288..b5805b1fc41 100644 --- a/tests/include/mir/test/doubles/null_platform.h +++ b/tests/include/mir/test/doubles/null_platform.h @@ -56,7 +56,7 @@ class NullRenderingPlatform : public graphics::RenderingPlatform protected: auto maybe_create_interface( - graphics::RendererInterfaceBase::Tag const&) -> std::shared_ptr override + graphics::RenderingProvider::Tag const&) -> std::shared_ptr override { return nullptr; } diff --git a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h index 53180fbe750..9950ab3803c 100644 --- a/tests/include/mir/test/doubles/stub_gl_rendering_provider.h +++ b/tests/include/mir/test/doubles/stub_gl_rendering_provider.h @@ -25,7 +25,7 @@ namespace mir::test::doubles { -class StubGlRenderingPlatform : public graphics::GLRenderingProvider +class StubGlRenderingProvider : public graphics::GLRenderingProvider { public: auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override diff --git a/tests/integration-tests/test_surface_stack_with_compositor.cpp b/tests/integration-tests/test_surface_stack_with_compositor.cpp index 95d1bd4a250..9818bc3dbac 100644 --- a/tests/integration-tests/test_surface_stack_with_compositor.cpp +++ b/tests/integration-tests/test_surface_stack_with_compositor.cpp @@ -185,7 +185,7 @@ struct SurfaceStackCompositor : public Test StubDisplayListener stub_display_listener; mc::DefaultDisplayBufferCompositorFactory dbc_factory{ - std::vector>{std::make_shared()}, + std::vector>{std::make_shared()}, std::make_shared(), mt::fake_shared(renderer_factory), std::make_shared(), diff --git a/tests/mir_test_framework/platform_graphics_throw.cpp b/tests/mir_test_framework/platform_graphics_throw.cpp index 7d84d014761..50d12406222 100644 --- a/tests/mir_test_framework/platform_graphics_throw.cpp +++ b/tests/mir_test_framework/platform_graphics_throw.cpp @@ -62,7 +62,7 @@ class ExceptionThrowingPlatform : public mg::DisplayPlatform, public mg::Renderi protected: auto maybe_create_interface( - mg::RendererInterfaceBase::Tag const&) -> std::shared_ptr override + mg::RenderingProvider::Tag const&) -> std::shared_ptr override { return nullptr; } diff --git a/tests/mir_test_framework/stubbed_graphics_platform.cpp b/tests/mir_test_framework/stubbed_graphics_platform.cpp index b3c734cc7fa..75a1de09404 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.cpp +++ b/tests/mir_test_framework/stubbed_graphics_platform.cpp @@ -90,12 +90,12 @@ mir::UniqueModulePtr mtf::StubGraphicPlatform::create_display( } auto mtf::StubGraphicPlatform::maybe_create_interface( - mir::graphics::RendererInterfaceBase::Tag const& tag) - -> std::shared_ptr + mir::graphics::RenderingProvider::Tag const& tag) + -> std::shared_ptr { if (dynamic_cast(&tag)) { - return std::make_shared(); + return std::make_shared(); } return nullptr; } diff --git a/tests/mir_test_framework/stubbed_graphics_platform.h b/tests/mir_test_framework/stubbed_graphics_platform.h index 70b93f3e0a2..e97ea5101a4 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.h +++ b/tests/mir_test_framework/stubbed_graphics_platform.h @@ -37,8 +37,8 @@ class StubGraphicPlatform : protected: auto maybe_create_interface( - mir::graphics::RendererInterfaceBase::Tag const& tag) - -> std::shared_ptr override; + mir::graphics::RenderingProvider::Tag const& tag) + -> std::shared_ptr override; auto interface_for() -> std::shared_ptr override; diff --git a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp index 2db361dce1d..0a521bb313a 100644 --- a/tests/unit-tests/compositor/test_basic_screen_shooter.cpp +++ b/tests/unit-tests/compositor/test_basic_screen_shooter.cpp @@ -128,8 +128,8 @@ struct BasicScreenShooter : Test } return elements; }()}; - mtd::StubGlRenderingPlatform default_gl_behaviour_provider; - std::shared_ptr gl_provider{std::make_shared>()}; + mtd::StubGlRenderingProvider default_gl_behaviour_provider; + std::shared_ptr gl_provider{std::make_shared>()}; std::vector> gl_providers{gl_provider}; std::shared_ptr renderer_factory{std::make_shared>()}; std::shared_ptr clock{std::make_shared()}; diff --git a/tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp b/tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp index 3d72935bfd0..5e937aa3517 100644 --- a/tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp +++ b/tests/unit-tests/compositor/test_default_display_buffer_compositor.cpp @@ -96,7 +96,7 @@ struct DefaultDisplayBufferCompositor : public testing::Test testing::NiceMock mock_renderer; geom::Rectangle screen{{0, 0}, {1366, 768}}; testing::NiceMock display_buffer; - mtd::StubGlRenderingPlatform gl_provider; + mtd::StubGlRenderingProvider gl_provider; std::shared_ptr small; std::shared_ptr big; std::shared_ptr fullscreen; diff --git a/tests/unit-tests/renderers/gl/test_gl_renderer.cpp b/tests/unit-tests/renderers/gl/test_gl_renderer.cpp index 8ab477b6bdf..015ecd2f312 100644 --- a/tests/unit-tests/renderers/gl/test_gl_renderer.cpp +++ b/tests/unit-tests/renderers/gl/test_gl_renderer.cpp @@ -157,7 +157,7 @@ class GLRenderer : std::shared_ptr> renderable; mg::RenderableList renderable_list; glm::mat4 trans; - std::shared_ptr const gl_platform{std::make_shared()}; + std::shared_ptr const gl_platform{std::make_shared()}; class StubProgram : public mg::gl::Program { From cf7376c2d1adbdaa101530c5acb31984440d13e3 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Fri, 13 Oct 2023 11:55:36 +0100 Subject: [PATCH 097/108] Better naming in RenderingPlatform --- include/platform/mir/graphics/platform.h | 27 ++++++++++--------- .../eglstream-kms/server/platform.cpp | 2 +- src/platforms/eglstream-kms/server/platform.h | 2 +- src/platforms/gbm-kms/server/kms/platform.cpp | 2 +- src/platforms/gbm-kms/server/kms/platform.h | 2 +- .../rendering_platform.cpp | 2 +- .../renderer-generic-egl/rendering_platform.h | 2 +- .../compositor/default_configuration.cpp | 4 +-- .../platforms/test_rendering_platform.cpp | 2 +- .../include/mir/test/doubles/null_platform.h | 2 +- tests/mir_test_framework/headless_test.cpp | 2 +- .../platform_graphics_throw.cpp | 4 +-- .../stubbed_graphics_platform.cpp | 2 +- .../stubbed_graphics_platform.h | 2 +- .../test_display_server.cpp | 2 +- .../graphics_platform_test_harness.cpp | 2 +- 16 files changed, 31 insertions(+), 30 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index e920fbfe863..db48a91ca3d 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -190,20 +190,20 @@ class RenderingPlatform * Since this may result in a runtime probe the call may be costly, and the * result should be saved rather than re-acquiring an interface each time. * - * \tparam Interface - * \return On success: an occupied std::shared_ptr - * On failure: std::shared_ptr{nullptr} + * \tparam Provider + * \return On success: an occupied std::shared_ptr + * On failure: std::shared_ptr{nullptr} */ - template - static auto acquire_interface(std::shared_ptr platform) -> std::shared_ptr + template + static auto acquire_provider(std::shared_ptr platform) -> std::shared_ptr { static_assert( - std::is_convertible_v, - "Can only acquire a Renderer interface; Interface must implement RenderingProvider"); + std::is_convertible_v, + "Can only acquire a Renderer interface; Provider must implement RenderingProvider"); - if (auto const base_interface = platform->maybe_create_interface(typename Interface::Tag{})) + if (auto const base_interface = platform->maybe_create_provider(typename Provider::Tag{})) { - if (auto const requested_interface = std::dynamic_pointer_cast(base_interface)) + if (auto const requested_interface = std::dynamic_pointer_cast(base_interface)) { return requested_interface; } @@ -216,7 +216,7 @@ class RenderingPlatform protected: /** - * Acquire a specific hardware interface + * Acquire a specific rendering interface * * This should perform any runtime checks necessary to verify the requested interface is * expected to work and return a pointer to an implementation of that interface. @@ -228,10 +228,11 @@ class RenderingPlatform * \param type_tag [in] An instance of the Tag type for the requested interface. * Implementations are expected to dynamic_cast<> this to * discover the specific interface being requested. - * \return A pointer to an implementation of the RenderInterfaceBase-derived - * interface that corresponds to the most-derived type of tag_type. + * \return On success: A pointer to an implementation of the RenderingProvider-derived + * interface that corresponds to the most-derived type of tag_type + * On failure: std::shared_ptr{nullptr} */ - virtual auto maybe_create_interface( + virtual auto maybe_create_provider( RenderingProvider::Tag const& type_tag) -> std::shared_ptr = 0; }; diff --git a/src/platforms/eglstream-kms/server/platform.cpp b/src/platforms/eglstream-kms/server/platform.cpp index 624d1a11b89..dae3f89f80d 100644 --- a/src/platforms/eglstream-kms/server/platform.cpp +++ b/src/platforms/eglstream-kms/server/platform.cpp @@ -193,7 +193,7 @@ mir::UniqueModulePtr mge::RenderingPlatform::create_ return mir::make_module_ptr(ctx->make_share_context()); } -auto mge::RenderingPlatform::maybe_create_interface( +auto mge::RenderingPlatform::maybe_create_provider( RenderingProvider::Tag const& type_tag) -> std::shared_ptr { if (dynamic_cast(&type_tag)) diff --git a/src/platforms/eglstream-kms/server/platform.h b/src/platforms/eglstream-kms/server/platform.h index 03688709e19..53371c2ff64 100644 --- a/src/platforms/eglstream-kms/server/platform.h +++ b/src/platforms/eglstream-kms/server/platform.h @@ -50,7 +50,7 @@ class RenderingPlatform : public graphics::RenderingPlatform create_buffer_allocator(Display const& output) override; protected: - auto maybe_create_interface( + auto maybe_create_provider( RenderingProvider::Tag const& type_tag) -> std::shared_ptr override; private: EGLDisplay const dpy; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index e8694abb125..03cd222fcb7 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -349,7 +349,7 @@ mir::UniqueModulePtr mgg::RenderingPlatform::create_ dmabuf_provider); } -auto mgg::RenderingPlatform::maybe_create_interface( +auto mgg::RenderingPlatform::maybe_create_provider( RenderingProvider::Tag const& type_tag) -> std::shared_ptr { if (dynamic_cast(&type_tag)) diff --git a/src/platforms/gbm-kms/server/kms/platform.h b/src/platforms/gbm-kms/server/kms/platform.h index 4ffab60ecb6..abc7f777282 100644 --- a/src/platforms/gbm-kms/server/kms/platform.h +++ b/src/platforms/gbm-kms/server/kms/platform.h @@ -97,7 +97,7 @@ class RenderingPlatform : public graphics::RenderingPlatform graphics::Display const&) -> UniqueModulePtr override; protected: - auto maybe_create_interface( + auto maybe_create_provider( RenderingProvider::Tag const& type_tag) -> std::shared_ptr override; private: diff --git a/src/platforms/renderer-generic-egl/rendering_platform.cpp b/src/platforms/renderer-generic-egl/rendering_platform.cpp index db2abfd32c3..2115cc86e98 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.cpp +++ b/src/platforms/renderer-generic-egl/rendering_platform.cpp @@ -186,7 +186,7 @@ auto mge::RenderingPlatform::create_buffer_allocator( return make_module_ptr(dpy, static_cast(*ctx), dmabuf_provider); } -auto mge::RenderingPlatform::maybe_create_interface(RenderingProvider::Tag const& tag) +auto mge::RenderingPlatform::maybe_create_provider(RenderingProvider::Tag const& tag) -> std::shared_ptr { if (dynamic_cast(&tag)) diff --git a/src/platforms/renderer-generic-egl/rendering_platform.h b/src/platforms/renderer-generic-egl/rendering_platform.h index e6bc6f58b4b..dd6ebccbfd5 100644 --- a/src/platforms/renderer-generic-egl/rendering_platform.h +++ b/src/platforms/renderer-generic-egl/rendering_platform.h @@ -43,7 +43,7 @@ class RenderingPlatform : public graphics::RenderingPlatform graphics::Display const& output) -> UniqueModulePtr override; protected: - auto maybe_create_interface( + auto maybe_create_provider( RenderingProvider::Tag const& type_tag) -> std::shared_ptr override; private: diff --git a/src/server/compositor/default_configuration.cpp b/src/server/compositor/default_configuration.cpp index 79e6709e1a0..00a8a0f9a6d 100644 --- a/src/server/compositor/default_configuration.cpp +++ b/src/server/compositor/default_configuration.cpp @@ -53,7 +53,7 @@ mir::DefaultServerConfiguration::the_display_buffer_compositor_factory() providers.reserve(the_rendering_platforms().size()); for (auto const& platform : the_rendering_platforms()) { - if (auto gl_provider = mg::RenderingPlatform::acquire_interface(platform)) + if (auto gl_provider = mg::RenderingPlatform::acquire_provider(platform)) { providers.push_back(gl_provider); } @@ -115,7 +115,7 @@ auto mir::DefaultServerConfiguration::the_screen_shooter() -> std::shared_ptr(platform)) + if (auto gl_provider = mg::RenderingPlatform::acquire_provider(platform)) { providers.push_back(gl_provider); } diff --git a/tests/acceptance-tests/platforms/test_rendering_platform.cpp b/tests/acceptance-tests/platforms/test_rendering_platform.cpp index f3fa8142d36..3459fb8c7b3 100644 --- a/tests/acceptance-tests/platforms/test_rendering_platform.cpp +++ b/tests/acceptance-tests/platforms/test_rendering_platform.cpp @@ -134,7 +134,7 @@ TEST_P(RenderingPlatformTest, DISABLED_supports_gl_rendering) empty_options, emergency_cleanup); - auto const gl_interface = platform->acquire_interface(nullptr); + auto const gl_interface = platform->acquire_provider(nullptr); EXPECT_THAT(gl_interface, testing::NotNull()); } } diff --git a/tests/include/mir/test/doubles/null_platform.h b/tests/include/mir/test/doubles/null_platform.h index b5805b1fc41..919358ed1f2 100644 --- a/tests/include/mir/test/doubles/null_platform.h +++ b/tests/include/mir/test/doubles/null_platform.h @@ -55,7 +55,7 @@ class NullRenderingPlatform : public graphics::RenderingPlatform } protected: - auto maybe_create_interface( + auto maybe_create_provider( graphics::RenderingProvider::Tag const&) -> std::shared_ptr override { return nullptr; diff --git a/tests/mir_test_framework/headless_test.cpp b/tests/mir_test_framework/headless_test.cpp index a01645f244a..70555c14b8c 100644 --- a/tests/mir_test_framework/headless_test.cpp +++ b/tests/mir_test_framework/headless_test.cpp @@ -48,7 +48,7 @@ mtf::HeadlessTest::HeadlessTest() [this]() -> std::shared_ptr { auto first_platform = server.the_rendering_platforms().front(); - auto gl_platform = mg::RenderingPlatform::acquire_interface(std::move(first_platform)); + auto gl_platform = mg::RenderingPlatform::acquire_provider(std::move(first_platform)); if (gl_platform) { return std::make_shared(std::move(gl_platform), server.the_gl_config()); diff --git a/tests/mir_test_framework/platform_graphics_throw.cpp b/tests/mir_test_framework/platform_graphics_throw.cpp index 50d12406222..20973b2cb0c 100644 --- a/tests/mir_test_framework/platform_graphics_throw.cpp +++ b/tests/mir_test_framework/platform_graphics_throw.cpp @@ -61,8 +61,8 @@ class ExceptionThrowingPlatform : public mg::DisplayPlatform, public mg::Renderi } protected: - auto maybe_create_interface( - mg::RenderingProvider::Tag const&) -> std::shared_ptr override + auto maybe_create_provider( + mir::graphics::RenderingProvider::Tag const&) -> std::shared_ptr override { return nullptr; } diff --git a/tests/mir_test_framework/stubbed_graphics_platform.cpp b/tests/mir_test_framework/stubbed_graphics_platform.cpp index 75a1de09404..88cc45b0df2 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.cpp +++ b/tests/mir_test_framework/stubbed_graphics_platform.cpp @@ -89,7 +89,7 @@ mir::UniqueModulePtr mtf::StubGraphicPlatform::create_display( return mir::make_module_ptr(display_rects); } -auto mtf::StubGraphicPlatform::maybe_create_interface( +auto mtf::StubGraphicPlatform::maybe_create_provider( mir::graphics::RenderingProvider::Tag const& tag) -> std::shared_ptr { diff --git a/tests/mir_test_framework/stubbed_graphics_platform.h b/tests/mir_test_framework/stubbed_graphics_platform.h index e97ea5101a4..f045c176b33 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.h +++ b/tests/mir_test_framework/stubbed_graphics_platform.h @@ -36,7 +36,7 @@ class StubGraphicPlatform : std::shared_ptr const&) override; protected: - auto maybe_create_interface( + auto maybe_create_provider( mir::graphics::RenderingProvider::Tag const& tag) -> std::shared_ptr override; diff --git a/tests/mir_test_framework/test_display_server.cpp b/tests/mir_test_framework/test_display_server.cpp index 4fd2352abd0..567d4544bcc 100644 --- a/tests/mir_test_framework/test_display_server.cpp +++ b/tests/mir_test_framework/test_display_server.cpp @@ -110,7 +110,7 @@ void miral::TestDisplayServer::start_server() { auto first_rendering_platform = server.the_rendering_platforms().front(); auto gl_platform = - mg::RenderingPlatform::acquire_interface( + mg::RenderingPlatform::acquire_provider( std::move(first_rendering_platform)); if (gl_platform) { diff --git a/tests/platform_test_harness/graphics_platform_test_harness.cpp b/tests/platform_test_harness/graphics_platform_test_harness.cpp index 4e17ffc0f6b..64e455cbf80 100644 --- a/tests/platform_test_harness/graphics_platform_test_harness.cpp +++ b/tests/platform_test_harness/graphics_platform_test_harness.cpp @@ -387,7 +387,7 @@ void basic_software_buffer_drawing( display, [platform, /*&renderers, &factory,*/ &min_height, &min_width](mg::DisplayBuffer& db) { - if (auto gl_interface = mg::RenderingPlatform::acquire_interface(platform)) + if (auto gl_interface = mg::RenderingPlatform::acquire_provider(platform)) { // auto output_surface = gl_interface->surface_for_output(db, ); // renderers.push_back(factory.create_renderer_for(std::move(output_surface), gl_interface)); From 79622a99cb1d2f1ed4885fcd8bad555ce493cb8d Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Fri, 13 Oct 2023 12:06:19 +0100 Subject: [PATCH 098/108] Better naming in and around `RenderingPlatform::acquire_provider<>` --- include/platform/mir/graphics/platform.h | 6 +++--- .../acceptance-tests/platforms/test_rendering_platform.cpp | 4 ++-- tests/mir_test_framework/headless_test.cpp | 5 ++--- tests/mir_test_framework/test_display_server.cpp | 6 +++--- .../graphics_platform_test_harness.cpp | 6 +++--- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index db48a91ca3d..a8043c5d051 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -201,11 +201,11 @@ class RenderingPlatform std::is_convertible_v, "Can only acquire a Renderer interface; Provider must implement RenderingProvider"); - if (auto const base_interface = platform->maybe_create_provider(typename Provider::Tag{})) + if (auto const provider = platform->maybe_create_provider(typename Provider::Tag{})) { - if (auto const requested_interface = std::dynamic_pointer_cast(base_interface)) + if (auto const requested = std::dynamic_pointer_cast(provider)) { - return requested_interface; + return requested; } BOOST_THROW_EXCEPTION(( std::logic_error{ diff --git a/tests/acceptance-tests/platforms/test_rendering_platform.cpp b/tests/acceptance-tests/platforms/test_rendering_platform.cpp index 3459fb8c7b3..93fcdc9268e 100644 --- a/tests/acceptance-tests/platforms/test_rendering_platform.cpp +++ b/tests/acceptance-tests/platforms/test_rendering_platform.cpp @@ -134,7 +134,7 @@ TEST_P(RenderingPlatformTest, DISABLED_supports_gl_rendering) empty_options, emergency_cleanup); - auto const gl_interface = platform->acquire_provider(nullptr); - EXPECT_THAT(gl_interface, testing::NotNull()); + auto const provider = platform->acquire_provider(nullptr); + EXPECT_THAT(provider, testing::NotNull()); } } diff --git a/tests/mir_test_framework/headless_test.cpp b/tests/mir_test_framework/headless_test.cpp index 70555c14b8c..5b80751c043 100644 --- a/tests/mir_test_framework/headless_test.cpp +++ b/tests/mir_test_framework/headless_test.cpp @@ -48,10 +48,9 @@ mtf::HeadlessTest::HeadlessTest() [this]() -> std::shared_ptr { auto first_platform = server.the_rendering_platforms().front(); - auto gl_platform = mg::RenderingPlatform::acquire_provider(std::move(first_platform)); - if (gl_platform) + if (auto gl_provider = mg::RenderingPlatform::acquire_provider(std::move(first_platform))) { - return std::make_shared(std::move(gl_platform), server.the_gl_config()); + return std::make_shared(std::move(gl_provider), server.the_gl_config()); } BOOST_THROW_EXCEPTION((std::runtime_error{"Test RenderingPlatform does not support GL interface"})); }); diff --git a/tests/mir_test_framework/test_display_server.cpp b/tests/mir_test_framework/test_display_server.cpp index 567d4544bcc..55f61d58234 100644 --- a/tests/mir_test_framework/test_display_server.cpp +++ b/tests/mir_test_framework/test_display_server.cpp @@ -109,13 +109,13 @@ void miral::TestDisplayServer::start_server() [&server]() -> std::shared_ptr { auto first_rendering_platform = server.the_rendering_platforms().front(); - auto gl_platform = + auto gl_provider = mg::RenderingPlatform::acquire_provider( std::move(first_rendering_platform)); - if (gl_platform) + if (gl_provider) { return std::make_shared( - std::move(gl_platform), + std::move(gl_provider), server.the_gl_config()); } BOOST_THROW_EXCEPTION((std::runtime_error{"Platform does not support GL interface"})); diff --git a/tests/platform_test_harness/graphics_platform_test_harness.cpp b/tests/platform_test_harness/graphics_platform_test_harness.cpp index 64e455cbf80..88d730b8738 100644 --- a/tests/platform_test_harness/graphics_platform_test_harness.cpp +++ b/tests/platform_test_harness/graphics_platform_test_harness.cpp @@ -387,10 +387,10 @@ void basic_software_buffer_drawing( display, [platform, /*&renderers, &factory,*/ &min_height, &min_width](mg::DisplayBuffer& db) { - if (auto gl_interface = mg::RenderingPlatform::acquire_provider(platform)) + if (auto gl_provider = mg::RenderingPlatform::acquire_provider(platform)) { -// auto output_surface = gl_interface->surface_for_output(db, ); -// renderers.push_back(factory.create_renderer_for(std::move(output_surface), gl_interface)); +// auto output_surface = gl_provider->surface_for_output(db, ); +// renderers.push_back(factory.create_renderer_for(std::move(output_surface), gl_provider)); min_height = std::min(min_height, db.view_area().bottom().as_int()); min_width = std::min(min_width, db.view_area().right().as_int()); } From 2df8871e6db757bb377ed7463b39a370de727e94 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 16 Oct 2023 13:35:35 +1100 Subject: [PATCH 099/108] Fix the build(?!) Move some destructor instantiations from the header, where there's a `unique_ptr` member, to the implementation where `IncompleteType` is no longer incomplete. I'm not sure why this built in CI, nor why it only just *now* fails for me? --- src/include/server/mir/input/vt_filter.h | 1 + src/platforms/eglstream-kms/server/buffer_allocator.cpp | 2 ++ src/platforms/eglstream-kms/server/buffer_allocator.h | 1 + src/server/input/vt_filter.cpp | 2 ++ 4 files changed, 6 insertions(+) diff --git a/src/include/server/mir/input/vt_filter.h b/src/include/server/mir/input/vt_filter.h index 2970de0396f..f42a3b6a4ef 100644 --- a/src/include/server/mir/input/vt_filter.h +++ b/src/include/server/mir/input/vt_filter.h @@ -32,6 +32,7 @@ class VTFilter : public EventFilter { public: VTFilter(std::unique_ptr switcher); + ~VTFilter(); bool handle(MirEvent const& event) override; diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.cpp b/src/platforms/eglstream-kms/server/buffer_allocator.cpp index 884923265c5..f0f158356fa 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.cpp +++ b/src/platforms/eglstream-kms/server/buffer_allocator.cpp @@ -721,6 +721,8 @@ mge::GLRenderingProvider::GLRenderingProvider(EGLDisplay dpy, std::unique_ptr buffer) -> std::shared_ptr { diff --git a/src/platforms/eglstream-kms/server/buffer_allocator.h b/src/platforms/eglstream-kms/server/buffer_allocator.h index 6d4a782b1e3..9eb80b2a311 100644 --- a/src/platforms/eglstream-kms/server/buffer_allocator.h +++ b/src/platforms/eglstream-kms/server/buffer_allocator.h @@ -96,6 +96,7 @@ class GLRenderingProvider : public graphics::GLRenderingProvider { public: GLRenderingProvider(EGLDisplay dpy, std::unique_ptr ctx); + ~GLRenderingProvider(); auto as_texture(std::shared_ptr buffer) -> std::shared_ptr override; diff --git a/src/server/input/vt_filter.cpp b/src/server/input/vt_filter.cpp index b5112b112cc..ad969536d9d 100644 --- a/src/server/input/vt_filter.cpp +++ b/src/server/input/vt_filter.cpp @@ -26,6 +26,8 @@ mir::input::VTFilter::VTFilter(std::unique_ptr switcher) { } +mir::input::VTFilter::~VTFilter() = default; + bool mir::input::VTFilter::handle(MirEvent const& event) { if (mir_event_get_type(&event) != mir_event_type_input) From 36ccfe89d1be2cf0f4e03426d14c5105480e4227 Mon Sep 17 00:00:00 2001 From: Christopher James Halse Rogers Date: Mon, 16 Oct 2023 18:06:01 +1100 Subject: [PATCH 100/108] platforms/gbm-kms: Clean up EGL resources when destroying a GBMOutputSurface --- src/platforms/gbm-kms/server/buffer_allocator.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/platforms/gbm-kms/server/buffer_allocator.cpp b/src/platforms/gbm-kms/server/buffer_allocator.cpp index c707977a463..ee6b2b59e25 100644 --- a/src/platforms/gbm-kms/server/buffer_allocator.cpp +++ b/src/platforms/gbm-kms/server/buffer_allocator.cpp @@ -272,6 +272,12 @@ class GBMOutputSurface : public mg::gl::OutputSurface { } + ~GBMOutputSurface() + { + eglDestroySurface(dpy, egl_surf); + eglDestroyContext(dpy, ctx); + } + void bind() override { if (!gbm_surface_has_free_buffers(*surface)) From d7ebab7bb174723e36ad6623be388583adb171ad Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Mon, 16 Oct 2023 12:04:46 +0100 Subject: [PATCH 101/108] Drop dead files - these test are tests of implementation, not features; and they no longer compile. While it would be good to have specific tests fail if the features these should test for don't work, we won't miss bugs from not running these --- .../platforms/gbm-kms/kms/CMakeLists.txt | 2 - .../platforms/gbm-kms/kms/test_display.cpp | 1078 ----------------- .../gbm-kms/kms/test_graphics_platform.cpp | 105 -- 3 files changed, 1185 deletions(-) delete mode 100644 tests/unit-tests/platforms/gbm-kms/kms/test_display.cpp delete mode 100644 tests/unit-tests/platforms/gbm-kms/kms/test_graphics_platform.cpp diff --git a/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt b/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt index 90ddd36fb74..b1bef7f8ab7 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt +++ b/tests/unit-tests/platforms/gbm-kms/kms/CMakeLists.txt @@ -1,7 +1,5 @@ mir_add_wrapped_executable(mir_unit_tests_gbm-kms NOINSTALL ${CMAKE_CURRENT_SOURCE_DIR}/test_platform.cpp -# ${CMAKE_CURRENT_SOURCE_DIR}/test_graphics_platform.cpp -# ${CMAKE_CURRENT_SOURCE_DIR}/test_display.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_generic.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_buffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_display_multi_monitor.cpp diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display.cpp deleted file mode 100644 index 6ceb813b7ca..00000000000 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display.cpp +++ /dev/null @@ -1,1078 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include "mir/test/doubles/null_gl_config.h" -#include "src/platforms/gbm-kms/server/kms/platform.h" -#include "src/platforms/gbm-kms/server/kms/display.h" -#include "src/platforms/gbm-kms/server/kms/quirks.h" -#include "mir/console_services.h" -#include "src/server/report/logging/display_report.h" -#include "mir/logging/logger.h" -#include "mir/graphics/display_buffer.h" -#include "mir/graphics/default_display_configuration_policy.h" -#include "mir/time/steady_clock.h" -#include "mir/glib_main_loop.h" -#include "mir/fatal.h" -#include "mir/options/program_option.h" -#include "src/platforms/common/server/kms-utils/drm_mode_resources.h" - -#include "mir/test/doubles/mock_egl.h" -#include "mir/test/doubles/mock_gl.h" -#include "src/server/report/null_report_factory.h" -#include "mir/test/doubles/mock_display_report.h" -#include "mir/test/doubles/stub_console_services.h" -#include "mir/test/doubles/stub_gl_config.h" -#include "mir/test/doubles/mock_gl_config.h" -#include "mir/test/doubles/null_emergency_cleanup.h" -#include "mir/test/doubles/mock_event_handler_register.h" - -#include "mir/test/doubles/mock_drm.h" -#include "mir/test/doubles/mock_gbm.h" - -#include "mir_test_framework/udev_environment.h" -#include "mir/test/fake_shared.h" -#include "mir/test/auto_unblock_thread.h" -#include "mir/test/signal.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mg=mir::graphics; -namespace mgg=mir::graphics::gbm; -namespace ml=mir::logging; -namespace mrl=mir::report::logging; -namespace mtd=mir::test::doubles; -namespace mtf=mir_test_framework; -namespace mr=mir::report; -namespace mt=mir::test; - -namespace -{ -struct MockLogger : public ml::Logger -{ - MOCK_METHOD3(log, - void(ml::Severity, const std::string&, const std::string&)); - - ~MockLogger() noexcept(true) {} -}; - -class MesaDisplayTest : public ::testing::Test -{ -public: - MesaDisplayTest() : - mock_report{std::make_shared>()}, - null_report{mr::null_display_report()}, - drm_fd{open(drm_device, 0, 0)} - { - using namespace testing; - ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) - .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), - SetArgPointee<4>(1), - Return(EGL_TRUE))); - - ON_CALL(mock_egl, eglGetConfigAttrib(_, mock_egl.fake_configs[0], EGL_NATIVE_VISUAL_ID, _)) - .WillByDefault( - DoAll( - SetArgPointee<3>(GBM_FORMAT_XRGB8888), - Return(EGL_TRUE))); - - mock_egl.provide_egl_extensions(); - mock_gl.provide_gles_extensions(); - /* - * Silence uninteresting calls called when cleaning up resources in - * the MockGBM destructor, and which are not handled by NiceMock<>. - */ - EXPECT_CALL(mock_gbm, gbm_bo_get_device(_)) - .Times(AtLeast(0)); - EXPECT_CALL(mock_gbm, gbm_device_get_fd(_)) - .Times(AtLeast(0)) - .WillRepeatedly(Return(drm_fd)); - - fake_devices.add_standard_device("standard-drm-devices"); - - // Our standard mock devices have 2 DRM devices; kill all the outputs on - // the second one, so we don't try to test hybrid (for now) - mock_drm.reset("/dev/dri/card1"); - } - - std::shared_ptr create_platform() - { - mir::udev::Context ctx; - // Caution: non-local state! - // This works because standard-drm-devices contains a udev device with 226:0 and devnode /dev/dri/card0 - auto device = ctx.char_device_from_devnum(makedev(226, 0)); - - return std::make_shared( -<<<<<<< HEAD - mir::report::null_display_report(), - *std::make_shared(), - *std::make_shared(), - mgg::BypassOption::allowed, - std::make_unique(mir::options::ProgramOption{})); -||||||| da02aa60d3 - mir::report::null_display_report(), - std::make_shared(), - *std::make_shared(), - mgg::BypassOption::allowed, - std::make_unique(mir::options::ProgramOption{})); -======= - *device, - mir::report::null_display_report(), - std::make_shared(), - *std::make_shared(), - mgg::BypassOption::allowed); ->>>>>>> new-platform-API - } - - std::shared_ptr create_display( - std::shared_ptr const& platform) - { -<<<<<<< HEAD - return std::make_shared( - platform->drm, - platform->gbm, - platform->bypass_option(), -||||||| da02aa60d3 - return std::make_shared( - platform->drm, - platform->gbm, - platform->vt, - platform->bypass_option(), -======= - std::shared_ptr display = platform->create_display( ->>>>>>> new-platform-API - std::make_shared(), - std::make_shared() - ); - - return std::dynamic_pointer_cast(display); - } - - void setup_post_update_expectations() - { - using namespace testing; - - EXPECT_CALL(mock_egl, eglSwapBuffers(mock_egl.fake_egl_display, - mock_egl.fake_egl_surface)) - .Times(Exactly(2)); - - EXPECT_CALL(mock_gbm, gbm_surface_lock_front_buffer(mock_gbm.fake_gbm.surface)) - .Times(Exactly(2)) - .WillOnce(Return(fake.bo1)) - .WillOnce(Return(fake.bo2)); - - EXPECT_CALL(mock_gbm, gbm_bo_get_handle(fake.bo1)) - .Times(Exactly(1)) - .WillOnce(Return(fake.bo_handle1)); - - EXPECT_CALL(mock_gbm, gbm_bo_get_handle(fake.bo2)) - .Times(Exactly(1)) - .WillOnce(Return(fake.bo_handle2)); - - EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd, - _, _, _, - Pointee(fake.bo_handle1.u32), - _, _, _, _)) - .Times(Exactly(1)) - .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0))); - - EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd, - _, _, _, - Pointee(fake.bo_handle2.u32), - _, _, _, _)) - .Times(Exactly(1)) - .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id2), Return(0))); - } - - uint32_t get_connected_connector_id() - { - mg::kms::DRMModeResources resources{drm_fd}; - - int connected_id = 0; - resources.for_each_connector( - [&connected_id](auto const& connector) - { - if (connector->connection == DRM_MODE_CONNECTED) - connected_id = connector->connector_id; - }); - - return connected_id; - } - - uint32_t get_connected_crtc_id() - { - auto connector_id = get_connected_connector_id(); - auto connector = mg::kms::get_connector(drm_fd, connector_id); - - if (connector) - { - auto encoder = mg::kms::get_encoder(drm_fd, connector->encoder_id); - if (encoder) - return encoder->crtc_id; - } - - return 0; - } - - struct FakeData { - FakeData() - : bo1{reinterpret_cast(0xabcd)}, - bo2{reinterpret_cast(0xabce)}, - fb_id1{66}, fb_id2{67}, crtc() - { - bo_handle1.u32 = 0x1234; - bo_handle2.u32 = 0x1235; - crtc.buffer_id = 88; - crtc.crtc_id = 565; - } - - gbm_bo* bo1; - gbm_bo* bo2; - uint32_t fb_id1; - uint32_t fb_id2; - gbm_bo_handle bo_handle1; - gbm_bo_handle bo_handle2; - drmModeCrtc crtc; - } fake; - - ::testing::NiceMock mock_egl; - ::testing::NiceMock mock_gl; - ::testing::NiceMock mock_drm; - ::testing::NiceMock mock_gbm; - std::shared_ptr> const mock_report; - std::shared_ptr const null_report; - mtf::UdevEnvironment fake_devices; - - char const* const drm_device = "/dev/dri/card0"; - int const drm_fd; -}; - -} - -TEST_F(MesaDisplayTest, create_display) -{ - using namespace testing; - - auto const connector_id = get_connected_connector_id(); - auto const crtc_id = get_connected_crtc_id(); - - /* To display a gbm-kms surface, the MesaDisplay should... */ - - /* Create a gbm-kms surface to use as the frame buffer */ - EXPECT_CALL(mock_gbm, gbm_surface_create(mock_gbm.fake_gbm.device,_,_,_,_)) - .Times(Exactly(1)); - - /* Create an EGL window surface backed by the gbm-kms surface */ - EXPECT_CALL(mock_egl, eglCreatePlatformWindowSurfaceEXT( - mock_egl.fake_egl_display, - mock_egl.fake_configs[0], - mock_gbm.fake_gbm.surface, _)) - .Times(Exactly(1)); - - /* Swap the EGL window surface to bring the back buffer to the front */ - EXPECT_CALL(mock_egl, eglSwapBuffers(mock_egl.fake_egl_display, - mock_egl.fake_egl_surface)) - .Times(Exactly(1)); - - /* Get the gbm_bo object corresponding to the front buffer */ - EXPECT_CALL(mock_gbm, gbm_surface_lock_front_buffer(mock_gbm.fake_gbm.surface)) - .Times(Exactly(1)) - .WillOnce(Return(fake.bo1)); - - /* Get the DRM buffer handle associated with the gbm_bo */ - EXPECT_CALL(mock_gbm, gbm_bo_get_handle(fake.bo1)) - .Times(Exactly(1)) - .WillOnce(Return(fake.bo_handle1)); - - /* Create a a DRM FB with the DRM buffer attached */ - EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd, - _, _, _, - Pointee(fake.bo_handle1.u32), - _, _, _, _)) - .Times(Exactly(1)) - .WillOnce(DoAll(SetArgPointee<7>(fake.fb_id1), Return(0))); - - /* Display the DRM FB (first expectation is for cleanup) */ - EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, - crtc_id, Ne(fake.fb_id1), - _, _, - Pointee(connector_id), - _, _)) - .Times(AtLeast(0)); - - EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, - crtc_id, fake.fb_id1, - _, _, - Pointee(connector_id), - _, _)) - .Times(Exactly(1)) - .WillOnce(Return(0)); - - auto display = create_display(create_platform()); -} - -TEST_F(MesaDisplayTest, reset_crtc_on_destruction) -{ - using namespace testing; - - auto const connector_id = get_connected_connector_id(); - auto const crtc_id = get_connected_crtc_id(); - uint32_t const fb_id{66}; - - /* Create DRM FBs */ - EXPECT_CALL(mock_drm, drmModeAddFB2(drm_fd, - _, _, _, _, _, _, _, _)) - .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0))); - - - { - InSequence s; - - /* crtc is set */ - EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, - crtc_id, fb_id, - _, _, - Pointee(connector_id), - _, _)) - .Times(AtLeast(1)); - - /* crtc is reset */ - EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, - crtc_id, Ne(fb_id), - _, _, - Pointee(connector_id), - _, _)) - .Times(1); - } - - auto display = create_display(create_platform()); -} - -TEST_F(MesaDisplayTest, create_display_drm_failure) -{ - using namespace testing; - - EXPECT_CALL(mock_drm, open(_,_)) - .Times(AtLeast(1)) - .WillRepeatedly( - DoAll( - InvokeWithoutArgs([]() { errno = ENODEV; }), - Return(-1))); - - EXPECT_THROW( - { - auto display = create_display(create_platform()); - }, std::runtime_error); -} - -TEST_F(MesaDisplayTest, create_display_kms_failure) -{ - using namespace testing; - - auto platform = create_platform(); - - Mock::VerifyAndClearExpectations(&mock_drm); - - EXPECT_CALL(mock_drm, drmModeGetResources(_)) - .Times(AtLeast(1)) - .WillRepeatedly(Return(nullptr)); - - EXPECT_CALL(mock_drm, drmModeFreeResources(_)) - .Times(Exactly(0)); - - EXPECT_THROW({ - auto display = create_display(platform); - }, std::runtime_error) << "Expected that c'tor of mgg::Display throws"; -} - -TEST_F(MesaDisplayTest, create_display_gbm_failure) -{ - using namespace testing; - - EXPECT_CALL(mock_gbm, gbm_create_device(_)) - .Times(Exactly(1)) - .WillOnce(Return(reinterpret_cast(0))); - - EXPECT_CALL(mock_gbm, gbm_device_destroy(_)) - .Times(Exactly(0)); - - EXPECT_THROW({ - auto platform = create_platform(); - }, std::runtime_error) << "Expected c'tor of Platform to throw an exception"; -} - -TEST_F(MesaDisplayTest, platform_fails_if_no_modesetting_drm_nodes) -{ - using namespace testing; - - ON_CALL(mock_drm, drmCheckModesettingSupported(_)).WillByDefault(Return(-ENOSYS)); - - EXPECT_THROW({ - auto platform = create_platform(); - }, std::system_error) << "Expected c'tor of Platform to throw an exception"; -} - -TEST_F(MesaDisplayTest, ignores_non_modesetting_nodes) -{ - using namespace testing; - - // The platform should open all DRM nodes. In particular, it should open the second one… - EXPECT_CALL(mock_drm, open(StrEq("/dev/dri/card0"), _)).Times(AtLeast(1)); - EXPECT_CALL(mock_drm, open(StrEq("/dev/dri/card1"), _)).Times(AtLeast(1)); - - // …mark the second DRM node as not supporting modesetting… - char const busid[] = "pci:00:01:02:03"; - ON_CALL(mock_drm, drmGetBusid(mtd::IsFdOfDevice("/dev/dri/card1"))) - .WillByDefault(Return(const_cast(busid))); - ON_CALL(mock_drm, drmFreeBusid(busid)) - .WillByDefault(Invoke([](auto){})); - ON_CALL(mock_drm, drmCheckModesettingSupported(busid)) - .WillByDefault(Return(-ENOSYS)); - - // …and ensure that if we query the modesetting API, we'll fail. - ON_CALL(mock_drm, drmModeGetResources(mtd::IsFdOfDevice("/dev/dri/card1"))) - .WillByDefault(SetErrnoAndReturn(EINVAL, nullptr)); - - EXPECT_NO_THROW({ - auto platform = create_platform(); - auto display = create_display(platform); - }); -} - -TEST_F(MesaDisplayTest, handles_first_card_not_supporting_modeset) -{ - using namespace testing; - - // The platform should open all DRM nodes. - EXPECT_CALL(mock_drm, open(StrEq("/dev/dri/card0"), _)).Times(AtLeast(1)); - EXPECT_CALL(mock_drm, open(StrEq("/dev/dri/card1"), _)).Times(AtLeast(1)); - - // First device is rendering only… - mock_drm.reset("/dev/dri/card0"); - EXPECT_CALL(mock_drm, drmModeGetResources(mtd::IsFdOfDevice("/dev/dri/card0"))) - .Times(AtLeast(1)) - .WillRepeatedly(SetErrnoAndReturn(EOPNOTSUPP, nullptr)); - EXPECT_CALL(mock_drm, drmModeGetResources(mtd::IsFdOfDevice("/dev/dri/card1"))) - .Times(AtLeast(1)); - - // …and set up some connectors on the second card. - std::vector modes; - modes.push_back( - mtd::FakeDRMResources::create_mode( - 1920, 1080, - 138500, 2080, 1111, - mtd::FakeDRMResources::PreferredMode)); - uint32_t const encoder_id{33}; - uint32_t const crtc_id{22}; - uint32_t const connector_id{1}; - std::vector possible_encoder_ids{encoder_id}; - - mock_drm.add_crtc("/dev/dri/card1", crtc_id, modes[0]); - mock_drm.add_encoder("/dev/dri/card1", encoder_id, crtc_id, 0x1); - mock_drm.add_connector( - "/dev/dri/card1", - connector_id, - DRM_MODE_CONNECTOR_HDMIA, - DRM_MODE_CONNECTED, - encoder_id, - modes, - possible_encoder_ids, - mir::geometry::Size{150, 100}); - mock_drm.prepare("/dev/dri/card1"); - - EXPECT_NO_THROW({ - auto platform = create_platform(); - auto display = create_display(platform); - }); -} - -namespace -{ - -ACTION_P(QueuePageFlipEvent, mock_drm) -{ - static_cast(mock_drm).generate_event_on("/dev/dri/card0"); -} - -ACTION_P(InvokePageFlipHandler, param) -{ - int const dont_care{0}; - char dummy; - - arg1->page_flip_handler(dont_care, dont_care, dont_care, dont_care, *param); - ASSERT_EQ(1, read(arg0, &dummy, 1)); -} - -} - -TEST_F(MesaDisplayTest, post_update) -{ - using namespace testing; - - auto const crtc_id = get_connected_crtc_id(); - void* user_data{nullptr}; - - setup_post_update_expectations(); - - { - InSequence s; - - /* Flip the new FB */ - EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, - crtc_id, - fake.fb_id2, - _, _)) - .Times(Exactly(1)) - .WillOnce(DoAll(QueuePageFlipEvent(std::ref(mock_drm)), - SaveArg<4>(&user_data), - Return(0))); - - /* Handle the flip event */ - EXPECT_CALL(mock_drm, drmHandleEvent(drm_fd, _)) - .Times(1) - .WillOnce(DoAll(InvokePageFlipHandler(&user_data), Return(0))); - - // The initially-visible buffer will be released when the pageflip completes, - // replacing it. - EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo1)) - .Times(Exactly(1)); - - /* Release scheduled_composite_frame (at destruction time) */ - EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo2)) - .Times(Exactly(1)); - } - - - auto display = create_display(create_platform()); - - display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) { - group.for_each_display_buffer([](mg::DisplayBuffer&) { - // Do thing that submits framebuffer here - }); - group.post(); - }); -} - -TEST_F(MesaDisplayTest, post_update_flip_failure) -{ - mir::FatalErrorStrategy on_error{mir::fatal_error_except}; - using namespace testing; - - auto const crtc_id = get_connected_crtc_id(); - - setup_post_update_expectations(); - - // clear_crtc happens at some stage. Not interesting. - EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, - crtc_id, 0, - _, _, _, _, _)) - .WillOnce(Return(0)); - - { - InSequence s; - - // DisplayBuffer construction paints an empty screen. - // That's probably less than ideal but we've always had it that way. - EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, - crtc_id, fake.fb_id1, - _, _, _, _, _)) - .WillOnce(Return(0)); - - // New FB flip failure - EXPECT_CALL(mock_drm, drmModePageFlip(drm_fd, - crtc_id, - fake.fb_id2, - _, _)) - .Times(Exactly(1)) - .WillOnce(Return(-1)); - - // Expect fallback to blitting - EXPECT_CALL(mock_drm, drmModeSetCrtc(drm_fd, - crtc_id, fake.fb_id2, - _, _, _, _, _)) - .WillOnce(Return(0)); - - // Release all buffer objects - EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo1)) - .Times(Exactly(1)); - - EXPECT_CALL(mock_gbm, gbm_surface_release_buffer(mock_gbm.fake_gbm.surface, fake.bo2)) - .Times(Exactly(1)); - } - - // drmModePageFlip is allowed to fail (e.g. on VirtualBox) - EXPECT_NO_THROW( - { - auto display = create_display(create_platform()); - display->for_each_display_sync_group([](mg::DisplaySyncGroup& group) { - group.for_each_display_buffer([](mg::DisplayBuffer&) { - // Do whatever Framebuffer stuff is necessary… - }); - group.post(); - }); - }); -} - -TEST_F(MesaDisplayTest, successful_creation_of_display_reports_successful_setup_of_native_resources) -{ - using namespace ::testing; - - EXPECT_CALL( - *mock_report, - report_successful_setup_of_native_resources()).Times(Exactly(1)); - EXPECT_CALL( - *mock_report, - report_successful_egl_make_current_on_construction()).Times(Exactly(1)); - - EXPECT_CALL( - *mock_report, - report_successful_egl_buffer_swap_on_construction()).Times(Exactly(1)); - - EXPECT_CALL( - *mock_report, - report_successful_drm_mode_set_crtc_on_construction()).Times(Exactly(1)); - - EXPECT_CALL( - *mock_report, - report_successful_display_construction()).Times(Exactly(1)); - - EXPECT_CALL( - *mock_report, - report_egl_configuration(mock_egl.fake_egl_display,mock_egl.fake_configs[0])).Times(Exactly(1)); - - auto platform = create_platform(); - auto display = std::make_shared( -<<<<<<< HEAD - platform->drm, - platform->gbm, - platform->bypass_option(), - std::make_shared(), - std::make_shared(), - mock_report); -||||||| da02aa60d3 - platform->drm, - platform->gbm, - platform->vt, - platform->bypass_option(), - std::make_shared(), - std::make_shared(), - mock_report); -======= - nullptr, - platform->drm, - platform->vt, - platform->bypass_option(), - std::make_shared(), - mock_report); ->>>>>>> new-platform-API -} - -TEST_F(MesaDisplayTest, outputs_correct_string_for_successful_setup_of_native_resources) -{ - using namespace ::testing; - - auto logger = std::make_shared(); - auto reporter = std::make_shared(logger); - - EXPECT_CALL( - *logger, - log(Eq(ml::Severity::informational), - StrEq("Successfully setup native resources."), - StrEq("graphics"))).Times(Exactly(1)); - - reporter->report_successful_setup_of_native_resources(); -} - -TEST_F(MesaDisplayTest, outputs_correct_string_for_successful_egl_make_current_on_construction) -{ - using namespace ::testing; - - auto logger = std::make_shared(); - auto reporter = std::make_shared(logger); - - EXPECT_CALL( - *logger, - log(Eq(ml::Severity::informational), - StrEq("Successfully made egl context current on construction."), - StrEq("graphics"))).Times(Exactly(1)); - - reporter->report_successful_egl_make_current_on_construction(); -} - -TEST_F(MesaDisplayTest, outputs_correct_string_for_successful_egl_buffer_swap_on_construction) -{ - using namespace ::testing; - - auto logger = std::make_shared(); - auto reporter = std::make_shared(logger); - - EXPECT_CALL( - *logger, - log(Eq(ml::Severity::informational), - StrEq("Successfully performed egl buffer swap on construction."), - StrEq("graphics"))).Times(Exactly(1)); - - reporter->report_successful_egl_buffer_swap_on_construction(); -} - -TEST_F(MesaDisplayTest, outputs_correct_string_for_successful_drm_mode_set_crtc_on_construction) -{ - using namespace ::testing; - - auto logger = std::make_shared(); - auto reporter = std::make_shared(logger); - - EXPECT_CALL( - *logger, - log(Eq(ml::Severity::informational), - StrEq("Successfully performed drm mode setup on construction."), - StrEq("graphics"))).Times(Exactly(1)); - - reporter->report_successful_drm_mode_set_crtc_on_construction(); -} - -TEST_F(MesaDisplayTest, constructor_throws_if_egl_khr_image_pixmap_not_supported) -{ - using namespace ::testing; - - const char* egl_exts = "EGL_KHR_image EGL_KHR_image_base"; - - EXPECT_CALL(mock_egl, eglQueryString(_,EGL_EXTENSIONS)) - .WillOnce(Return(egl_exts)); - - EXPECT_THROW( - { - auto display = create_display(create_platform()); - }, std::runtime_error); -} - -TEST_F(MesaDisplayTest, for_each_display_buffer_calls_callback) -{ - using namespace ::testing; - - auto display = create_display(create_platform()); - - int callback_count{0}; - - display->for_each_display_sync_group([&](mg::DisplaySyncGroup& group) { - group.for_each_display_buffer([&](mg::DisplayBuffer&) { - callback_count++; - }); - }); - - EXPECT_NE(0, callback_count); -} - -TEST_F(MesaDisplayTest, configuration_change_registers_video_devices_handler) -{ - using namespace testing; - - auto display = create_display(create_platform()); - mtd::MockEventHandlerRegister mock_register; - - EXPECT_CALL(mock_register, register_fd_handler_module_ptr(_,_,_)); - - display->register_configuration_change_handler(mock_register, []{}); -} - -TEST_F(MesaDisplayTest, drm_device_change_event_triggers_handler) -{ - using namespace testing; - using namespace std::chrono_literals; - - auto display = create_display(create_platform()); - - mir::GLibMainLoop ml{std::make_shared()}; - mir::test::Signal done; - - int const device_add_count{1}; - int const device_change_count{10}; - int const expected_call_count{device_add_count + device_change_count}; - std::atomic call_count{0}; - - display->register_configuration_change_handler( - ml, - [&call_count, &done]() - { - if (++call_count == expected_call_count) - { - done.raise(); - } - }); - - - int const owner{0}; - mt::Signal mainloop_started; - ml.enqueue(&owner, [&] { mainloop_started.raise(); }); - - mt::AutoUnblockThread mainLoopThread([&ml]{ml.stop();}, [&ml]{ml.run();}); - ASSERT_TRUE(mainloop_started.wait_for(10s)); - - auto const syspath = - fake_devices.add_device( - "drm", - "card2", - NULL, - {}, - { - "DEVTYPE", "drm_minor", - "MAJOR", "226", - "MINOR", "42" - }); - - for (int i = 0; i != device_change_count; ++i) - { - // sleeping between calls to fake_devices hides race conditions - std::this_thread::sleep_for(std::chrono::microseconds{500}); - fake_devices.emit_device_changed(syspath); - } - - done.wait_for(20s); - EXPECT_EQ(expected_call_count, call_count); -} - -<<<<<<< HEAD -TEST_F(MesaDisplayTest, respects_gl_config) -{ - using namespace testing; - - mtd::MockGLConfig mock_gl_config; - EGLint const depth_bits{24}; - EGLint const stencil_bits{8}; - - EXPECT_CALL(mock_gl_config, depth_buffer_bits()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(depth_bits)); - EXPECT_CALL(mock_gl_config, stencil_buffer_bits()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(stencil_bits)); - - // We create at least one rendering context, with the requested attributes… - EXPECT_CALL(mock_egl, - eglChooseConfig( - _, - AllOf(mtd::EGLConfigContainsAttrib(EGL_DEPTH_SIZE, depth_bits), - mtd::EGLConfigContainsAttrib(EGL_STENCIL_SIZE, stencil_bits)), - NotNull(),_,_)) - .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), - SetArgPointee<4>(1), - Return(EGL_TRUE))); - //…we *also* create zero-or-more non-rendering contexts; we don't care what they ask for - EXPECT_CALL(mock_egl, - eglChooseConfig( - _, - Pointee(EGL_NONE), - NotNull(),_,_)) - .Times(AnyNumber()) - .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), - SetArgPointee<4>(1), - Return(EGL_TRUE))); - /* We actually want the default behaviour here, but because we've made an - * EXPECT_CALL for eglChooseConfig GMock will ignore the ON_CALL behaviour - */ - EXPECT_CALL(mock_egl, eglChooseConfig(_,_,nullptr,_,_)) - .Times(AnyNumber()) - .WillRepeatedly( - DoAll( - SetArgPointee<4>(1), - Return(EGL_TRUE))); - - auto platform = create_platform(); - mgg::Display display{ - platform->drm, - platform->gbm, - platform->bypass_option(), - std::make_shared(), - mir::test::fake_shared(mock_gl_config), - null_report}; -} - -||||||| da02aa60d3 -TEST_F(MesaDisplayTest, respects_gl_config) -{ - using namespace testing; - - mtd::MockGLConfig mock_gl_config; - EGLint const depth_bits{24}; - EGLint const stencil_bits{8}; - - EXPECT_CALL(mock_gl_config, depth_buffer_bits()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(depth_bits)); - EXPECT_CALL(mock_gl_config, stencil_buffer_bits()) - .Times(AtLeast(1)) - .WillRepeatedly(Return(stencil_bits)); - - // We create at least one rendering context, with the requested attributes… - EXPECT_CALL(mock_egl, - eglChooseConfig( - _, - AllOf(mtd::EGLConfigContainsAttrib(EGL_DEPTH_SIZE, depth_bits), - mtd::EGLConfigContainsAttrib(EGL_STENCIL_SIZE, stencil_bits)), - NotNull(),_,_)) - .Times(AtLeast(1)) - .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), - SetArgPointee<4>(1), - Return(EGL_TRUE))); - //…we *also* create zero-or-more non-rendering contexts; we don't care what they ask for - EXPECT_CALL(mock_egl, - eglChooseConfig( - _, - Pointee(EGL_NONE), - NotNull(),_,_)) - .Times(AnyNumber()) - .WillRepeatedly(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), - SetArgPointee<4>(1), - Return(EGL_TRUE))); - /* We actually want the default behaviour here, but because we've made an - * EXPECT_CALL for eglChooseConfig GMock will ignore the ON_CALL behaviour - */ - EXPECT_CALL(mock_egl, eglChooseConfig(_,_,nullptr,_,_)) - .Times(AnyNumber()) - .WillRepeatedly( - DoAll( - SetArgPointee<4>(1), - Return(EGL_TRUE))); - - auto platform = create_platform(); - mgg::Display display{ - platform->drm, - platform->gbm, - platform->vt, - platform->bypass_option(), - std::make_shared(), - mir::test::fake_shared(mock_gl_config), - null_report}; -} - -======= ->>>>>>> new-platform-API -TEST_F(MesaDisplayTest, uses_xrgb8888_framebuffer_when_argb8888_is_not_supported_by_EGL) -{ - using namespace testing; - - EXPECT_CALL(mock_egl, eglGetConfigAttrib(_, mock_egl.fake_configs[0], EGL_NATIVE_VISUAL_ID, _)) - .WillRepeatedly( - DoAll( - SetArgPointee<3>(GBM_FORMAT_XRGB8888), - Return(EGL_TRUE))); - - InSequence s; - // Maybe we first try ARGB8888, then fallback… - EXPECT_CALL(mock_gbm, gbm_surface_create(_, _, _, GBM_FORMAT_ARGB8888, _)) - .Times(AtMost(1)); - // …but we end up creating an XRGB8888 surface. - EXPECT_CALL(mock_gbm, gbm_surface_create(_, _, _, GBM_FORMAT_XRGB8888, _)); - - auto platform = create_platform(); - mgg::Display display{ - {}, // Hopefully DisplayPlatform{nullptr} is enough for now? - platform->drm, -<<<<<<< HEAD - platform->gbm, -||||||| da02aa60d3 - platform->gbm, - platform->vt, -======= - platform->vt, ->>>>>>> new-platform-API - platform->bypass_option(), - std::make_shared(), - null_report}; -} - -TEST_F(MesaDisplayTest, uses_argb8888_framebuffer_when_xrgb8888_is_not_supported_by_EGL) -{ - using namespace testing; - - EXPECT_CALL(mock_egl, eglGetConfigAttrib(_, _, EGL_NATIVE_VISUAL_ID, _)) - .WillRepeatedly( - DoAll( - SetArgPointee<3>(GBM_FORMAT_ARGB8888), - Return(EGL_TRUE))); - - InSequence s; - // Maybe we first try XRGB8888, then fallback… - EXPECT_CALL(mock_gbm, gbm_surface_create(_, _, _, GBM_FORMAT_XRGB8888, _)) - .Times(AtMost(1)); - // …but we end up creating an ARGB8888 surface. - EXPECT_CALL(mock_gbm, gbm_surface_create(_, _, _, GBM_FORMAT_ARGB8888, _)); - - auto platform = create_platform(); - mgg::Display display{ - {}, // Hopefully DisplayPlatform{nullptr} is enough for now? - platform->drm, -<<<<<<< HEAD - platform->gbm, -||||||| da02aa60d3 - platform->gbm, - platform->vt, -======= - platform->vt, ->>>>>>> new-platform-API - platform->bypass_option(), - std::make_shared(), - null_report}; -} - -TEST_F(MesaDisplayTest, can_change_configuration_metadata_without_invalidating_display_buffers) -{ - using namespace testing; - - auto display = create_display(create_platform()); - - auto config = display->configuration(); - - std::vector initial_display_buffer_references; - - display->for_each_display_sync_group( - [&initial_display_buffer_references](auto& group) - { - group.for_each_display_buffer( - [&initial_display_buffer_references](mg::DisplayBuffer& db) - { - initial_display_buffer_references.push_back(&db); - }); - }); - - bool has_active_display{false}; - config->for_each_output( - [&has_active_display](mg::UserDisplayConfigurationOutput& output) - { - has_active_display |= output.used; - - output.form_factor = mir_form_factor_projector; - output.scale = 3.1415f; - output.subpixel_arrangement = mir_subpixel_arrangement_vertical_bgr; - output.orientation = mir_orientation_inverted; - }); - - EXPECT_TRUE(display->apply_if_configuration_preserves_display_buffers(*config)); - - glm::mat2 const rotate_inverted(-1, 0, - 0,-1); - for (auto display_buffer : initial_display_buffer_references) - { - EXPECT_THAT(display_buffer->transformation(), Eq(rotate_inverted)); - } -} diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_graphics_platform.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_graphics_platform.cpp deleted file mode 100644 index 06c895e5eab..00000000000 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_graphics_platform.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright © Canonical Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "mir/graphics/platform.h" -#include "mir/graphics/graphic_buffer_allocator.h" -#include "mir/graphics/buffer_properties.h" -#include "mir/test/doubles/mock_egl.h" -#include "mir/test/doubles/mock_gl.h" -#include "mir/test/doubles/mock_option.h" -#include "mir/test/doubles/null_emergency_cleanup.h" -#include "src/server/report/null_report_factory.h" -#include "mir/test/doubles/stub_console_services.h" -#include "mir/options/program_option.h" -#include "mir/test/doubles/mock_drm.h" -#include "mir/test/doubles/mock_gbm.h" -#include "mir_test_framework/udev_environment.h" -#include "src/platforms/gbm-kms/server/kms/platform.h" -#include "src/platforms/gbm-kms/server/kms/quirks.h" - -#include "mir/logging/dumb_console_logger.h" - -#include - -namespace mg = mir::graphics; -namespace mgg = mg::gbm; -namespace ml = mir::logging; -namespace geom = mir::geometry; -namespace mtd = mir::test::doubles; -namespace mo = mir::options; -namespace mtf = mir_test_framework; - -class GraphicsPlatform : public ::testing::Test -{ -public: - GraphicsPlatform() : logger(std::make_shared()) - { - using namespace testing; - - ON_CALL(mock_gbm, gbm_bo_get_width(_)) - .WillByDefault(Return(320)); - - ON_CALL(mock_gbm, gbm_bo_get_height(_)) - .WillByDefault(Return(240)); - - // FIXME: This format needs to match Mesa's first supported pixel - // format or tests will fail. The coupling is presently loose. - ON_CALL(mock_gbm, gbm_bo_get_format(_)) - .WillByDefault(Return(GBM_FORMAT_ARGB8888)); - - ON_CALL(mock_egl, eglChooseConfig(_,_,_,1,_)) - .WillByDefault(DoAll(SetArgPointee<2>(mock_egl.fake_configs[0]), - SetArgPointee<4>(1), - Return(EGL_TRUE))); - - ON_CALL(mock_egl, eglGetConfigAttrib(_, mock_egl.fake_configs[0], EGL_NATIVE_VISUAL_ID, _)) - .WillByDefault( - DoAll( - SetArgPointee<3>(GBM_FORMAT_XRGB8888), - Return(EGL_TRUE))); - - mock_egl.provide_egl_extensions(); - mock_gl.provide_gles_extensions(); - - - fake_devices.add_standard_device("standard-drm-devices"); - } - - std::shared_ptr create_platform() - { - mir::udev::Context ctx; - // Caution: non-local state! - // This works because standard-drm-devices contains a udev device with 226:0 and devnode /dev/dri/card0 - auto device = ctx.char_device_from_devnum(makedev(226, 0)); - - return std::make_shared( - *device, - mir::report::null_display_report(), - std::make_shared(), - *std::make_shared(), - mgg::BypassOption::allowed); - } - - std::shared_ptr logger; - - ::testing::NiceMock mock_egl; - ::testing::NiceMock mock_gl; - ::testing::NiceMock mock_drm; - ::testing::NiceMock mock_gbm; - mtf::UdevEnvironment fake_devices; -}; - -#include "../../test_graphics_platform.h" From 7bea0014f0be16fd800e9722fc82b0da7775816d Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Mon, 16 Oct 2023 12:19:17 +0100 Subject: [PATCH 102/108] Replace graphics::PlatformPriority with graphics::probe::Result --- include/platform/mir/graphics/platform.h | 35 +++++-------------- .../eglstream-kms/server/platform_symbols.cpp | 16 ++++----- .../gbm-kms/server/kms/platform_symbols.cpp | 22 ++++++------ .../renderer-generic-egl/platform_symbols.cpp | 4 +-- src/platforms/wayland/platform_symbols.cpp | 2 +- src/platforms/x11/graphics/graphics.cpp | 2 +- src/server/graphics/default_configuration.cpp | 4 +-- src/server/graphics/platform_probe.cpp | 6 ++-- .../platform_graphics_dummy.cpp | 4 +-- .../platform_graphics_throw.cpp | 6 ++-- .../graphics_platform_test_harness.cpp | 22 ++++++------ .../graphics/test_platform_prober.cpp | 2 +- .../platforms/gbm-kms/kms/test_platform.cpp | 14 ++++---- 13 files changed, 61 insertions(+), 78 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index a8043c5d051..cddc8284d1a 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -66,6 +66,14 @@ class DisplayInterfaceProvider; namespace probe { + /** + * A measure of how well a platform supports a device + * + * \note This is compared as an integer; best + 1 is a valid Result that + * will be used in preference to a module that reports best. + * Platform modules distributed with Mir will never use a priority higher + * than best. + */ using Result = uint32_t; Result const unsupported = 0; /**< Unable to function at all on this device */ Result const dummy = 1; /**< Used only for dummy or stub platforms. @@ -477,31 +485,6 @@ class DisplayPlatform : public std::enable_shared_from_this virtual auto interface_for() -> std::shared_ptr = 0; }; -/** - * A measure of how well a platform supports a device - * - * \note This is compared as an integer; best + 1 is a valid PlatformPriority that - * will be used in preference to a module that reports best. - * Platform modules distributed with Mir will never use a priority higher - * than best. - */ -enum PlatformPriority : uint32_t -{ - unsupported = 0, /**< Unable to function at all on this device */ - dummy = 1, /**< Used only for dummy or stub platforms. - */ - supported = 128, /**< Capable of providing a functioning Platform on this device, - * possibly with degraded performance or features. - */ - hosted = 192, /**< Capable of providing a fully-featured Platform on this device, - * running nested under some other display server rather than with - * exclusive hardware access. - */ - best = 256 /**< Capable of providing a Platform with the best features and - * performance this device is capable of. - */ -}; - struct SupportedDevice { /** @@ -517,7 +500,7 @@ struct SupportedDevice * particular hardware. */ std::unique_ptr device; - PlatformPriority support_level; /**< How well the platform can support this device */ + probe::Result support_level; /**< How well the platform can support this device */ /** * Platform-private data from probing diff --git a/src/platforms/eglstream-kms/server/platform_symbols.cpp b/src/platforms/eglstream-kms/server/platform_symbols.cpp index a0d3659d806..c9ac198e100 100644 --- a/src/platforms/eglstream-kms/server/platform_symbols.cpp +++ b/src/platforms/eglstream-kms/server/platform_symbols.cpp @@ -98,7 +98,7 @@ auto probe_rendering_platform( { mir::assert_entry_point_signature(&probe_rendering_platform); - mg::PlatformPriority maximum_suitability = mg::PlatformPriority::unsupported; + mg::probe::Result maximum_suitability = mg::probe::unsupported; // First check if there are any displays we can possibly drive std::vector> eglstream_providers; for (auto const& display_provider : displays) @@ -107,7 +107,7 @@ auto probe_rendering_platform( { // We can optimally drive an EGLStream display mir::log_debug("EGLStream-capable display found"); - maximum_suitability = mg::PlatformPriority::best; + maximum_suitability = mg::probe::best; eglstream_providers.push_back(provider); } if (display_provider->acquire_interface()) @@ -115,11 +115,11 @@ auto probe_rendering_platform( /* We *can* support this output, but with slower buffer copies * If another platform supports this device better, let it. */ - maximum_suitability = mg::PlatformPriority::supported; + maximum_suitability = mg::probe::supported; } } - if (maximum_suitability == mg::PlatformPriority::unsupported) + if (maximum_suitability == mg::probe::unsupported) { mir::log_debug("No outputs capable of accepting EGLStream input detected"); mir::log_debug("Probing will be skipped"); @@ -177,7 +177,7 @@ auto probe_rendering_platform( { supported_devices.emplace_back(mg::SupportedDevice{ udev->char_device_from_devnum(mge::devnum_for_device(device)), - mg::PlatformPriority::unsupported, + mg::probe::unsupported, nullptr }); } @@ -279,7 +279,7 @@ auto probe_rendering_platform( supported_devices.end(), [](auto const& device) { - return device.support_level > mg::PlatformPriority::unsupported; + return device.support_level > mg::probe::unsupported; })) { mir::log_debug( @@ -384,7 +384,7 @@ auto probe_display_platform( supported_devices.emplace_back( mg::SupportedDevice{ udev->char_device_from_devnum(devnum), - mg::PlatformPriority::unsupported, + mg::probe::unsupported, device }); } @@ -544,7 +544,7 @@ auto probe_display_platform( if (epoxy_has_egl_extension(display, "EGL_EXT_output_base")) { - supported_devices.back().support_level = mg::PlatformPriority::best; + supported_devices.back().support_level = mg::probe::best; } else { diff --git a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp index 33d395895c9..ad5526c9328 100644 --- a/src/platforms/gbm-kms/server/kms/platform_symbols.cpp +++ b/src/platforms/gbm-kms/server/kms/platform_symbols.cpp @@ -188,7 +188,7 @@ auto probe_display_platform( supported_devices.emplace_back( mg::SupportedDevice{ device.clone(), - mg::PlatformPriority::unsupported, + mg::probe::unsupported, nullptr }); @@ -228,7 +228,7 @@ auto probe_display_platform( if ("llvmpipe"s == renderer_string) { mir::log_info("KMS device only has associated software renderer: %s, device unsuitable", renderer_string); - supported_devices.back().support_level = mg::PlatformPriority::unsupported; + supported_devices.back().support_level = mg::probe::unsupported; continue; } @@ -246,7 +246,7 @@ auto probe_display_platform( mir::log_warning( "Failed to query BusID for device %s; cannot check if KMS is available", device.devnode()); - supported_devices.back().support_level = mg::PlatformPriority::supported; + supported_devices.back().support_level = mg::probe::supported; } else { @@ -260,7 +260,7 @@ auto probe_display_platform( (kms_resources.num_encoders() > 0)) { // It supports KMS *and* can drive at least one physical output! Top hole! - supported_devices.back().support_level = mg::PlatformPriority::best; + supported_devices.back().support_level = mg::probe::best; } else { @@ -280,7 +280,7 @@ auto probe_display_platform( mir::log_warning( "Failed to detect whether device %s supports KMS, continuing with lower confidence", device.devnode()); - supported_devices.back().support_level = mg::PlatformPriority::supported; + supported_devices.back().support_level = mg::probe::supported; break; default: @@ -288,7 +288,7 @@ auto probe_display_platform( "but continuing anyway", strerror(err), err); mir::log_warning("Please file a bug at " "https://github.com/MirServer/mir/issues containing this message"); - supported_devices.back().support_level = mg::PlatformPriority::supported; + supported_devices.back().support_level = mg::probe::supported; } } } @@ -314,7 +314,7 @@ auto probe_rendering_platform( { mir::assert_entry_point_signature(&probe_rendering_platform); - mg::PlatformPriority maximum_suitability = mg::PlatformPriority::unsupported; + mg::probe::Result maximum_suitability = mg::probe::unsupported; // First check if there are any displays we can possibly drive for (auto const& display_provider : displays) { @@ -322,7 +322,7 @@ auto probe_rendering_platform( { // We can optimally drive a GBM-backed display mir::log_debug("GBM-capable display found"); - maximum_suitability = mg::PlatformPriority::best; + maximum_suitability = mg::probe::best; break; } if (display_provider->acquire_interface()) @@ -330,11 +330,11 @@ auto probe_rendering_platform( /* We *can* support this output, but with slower buffer copies * If another platform supports this device better, let it. */ - maximum_suitability = mg::PlatformPriority::supported; + maximum_suitability = mg::probe::supported; } } - if (maximum_suitability == mg::PlatformPriority::unsupported) + if (maximum_suitability == mg::probe::unsupported) { mir::log_debug("No outputs capable of accepting GBM input detected"); mir::log_debug("Probing will be skipped"); @@ -421,7 +421,7 @@ auto probe_rendering_platform( } // We know we've got a device that we *might* be able to use - supported_devices.emplace_back(mg::SupportedDevice{device.clone(), mg::PlatformPriority::unsupported, nullptr}); + supported_devices.emplace_back(mg::SupportedDevice{device.clone(), mg::probe::unsupported, nullptr}); if (tmp_fd != mir::Fd::invalid) { mgg::helpers::GBMHelper gbm_device{tmp_fd}; diff --git a/src/platforms/renderer-generic-egl/platform_symbols.cpp b/src/platforms/renderer-generic-egl/platform_symbols.cpp index 86d63bee2df..f8b4a6e2c33 100644 --- a/src/platforms/renderer-generic-egl/platform_symbols.cpp +++ b/src/platforms/renderer-generic-egl/platform_symbols.cpp @@ -57,13 +57,13 @@ auto probe_rendering_platform( { mir::assert_entry_point_signature(&probe_rendering_platform); - mg::PlatformPriority maximum_suitability = mg::PlatformPriority::unsupported; + mg::probe::Result maximum_suitability = mg::probe::unsupported; // First check if there are any displays we can possibly drive for (auto const& display_provider : displays) { if (display_provider->acquire_interface()) { - maximum_suitability = mg::PlatformPriority::hosted; + maximum_suitability = mg::probe::hosted; break; } /* TODO: We *can* drive a CPUAddressableDisplayProvider, too, but without diff --git a/src/platforms/wayland/platform_symbols.cpp b/src/platforms/wayland/platform_symbols.cpp index 312108ba103..cc72c10fe4b 100644 --- a/src/platforms/wayland/platform_symbols.cpp +++ b/src/platforms/wayland/platform_symbols.cpp @@ -64,7 +64,7 @@ auto probe_graphics_platform( { return mg::SupportedDevice { nullptr, - mg::PlatformPriority::hosted, + mg::probe::hosted, nullptr }; } diff --git a/src/platforms/x11/graphics/graphics.cpp b/src/platforms/x11/graphics/graphics.cpp index eaceb8e91ea..85f8760f5a7 100644 --- a/src/platforms/x11/graphics/graphics.cpp +++ b/src/platforms/x11/graphics/graphics.cpp @@ -89,7 +89,7 @@ auto probe_graphics_platform() -> std::optional { return mg::SupportedDevice { nullptr, - mg::PlatformPriority::hosted, + mg::probe::hosted, std::move(resources) }; } diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index 45e0ec89d15..dd172769f37 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -184,7 +184,7 @@ auto mir::DefaultServerConfiguration::the_display_platforms() -> std::vector= mg::PlatformPriority::supported) + if (device.support_level >= mg::probe::supported) { found_supported_device = true; platform_modules.emplace_back(std::move(device), platform); @@ -320,7 +320,7 @@ auto mir::DefaultServerConfiguration::the_rendering_platforms() -> bool found_supported_device{false}; for (auto& device : supported_devices) { - if (device.support_level >= mg::PlatformPriority::supported) + if (device.support_level >= mg::probe::supported) { found_supported_device = true; } diff --git a/src/server/graphics/platform_probe.cpp b/src/server/graphics/platform_probe.cpp index e53a3a7f051..8651f1994c8 100644 --- a/src/server/graphics/platform_probe.cpp +++ b/src/server/graphics/platform_probe.cpp @@ -161,13 +161,13 @@ auto modules_for_device( *existing_device = std::make_pair(std::move(device), module); } } - else if (device.support_level > mg::PlatformPriority::unsupported) + else if (device.support_level > mg::probe::unsupported) { // Not-seen-before device, which this platform supports in some fashion best_modules_so_far.emplace_back(std::move(device), module); } } - else if (device.support_level > mg::PlatformPriority::unsupported) + else if (device.support_level > mg::probe::unsupported) { // Devices with null associated udev device are not combined with any others best_modules_so_far.emplace_back(std::move(device), module); @@ -192,7 +192,7 @@ auto modules_for_device( best_modules_so_far.end(), [](auto const& module) { - return module.first.support_level > mg::PlatformPriority::dummy; + return module.first.support_level > mg::probe::dummy; }); // …then, if there are any platforms before the start of the dummy platforms… diff --git a/tests/mir_test_framework/platform_graphics_dummy.cpp b/tests/mir_test_framework/platform_graphics_dummy.cpp index 95c1d5d7064..e376d9aa0db 100644 --- a/tests/mir_test_framework/platform_graphics_dummy.cpp +++ b/tests/mir_test_framework/platform_graphics_dummy.cpp @@ -38,7 +38,7 @@ auto probe_display_platform( result.emplace_back( mg::SupportedDevice { nullptr, - mg::PlatformPriority::dummy, + mg::probe::dummy, nullptr }); return result; @@ -55,7 +55,7 @@ auto probe_rendering_platform( result.emplace_back( mg::SupportedDevice { nullptr, - mg::PlatformPriority::dummy, + mg::probe::dummy, nullptr }); return result; diff --git a/tests/mir_test_framework/platform_graphics_throw.cpp b/tests/mir_test_framework/platform_graphics_throw.cpp index 20973b2cb0c..c1af57c1d7a 100644 --- a/tests/mir_test_framework/platform_graphics_throw.cpp +++ b/tests/mir_test_framework/platform_graphics_throw.cpp @@ -53,7 +53,7 @@ class ExceptionThrowingPlatform : public mg::DisplayPlatform, public mg::Renderi mtd::NullEmergencyCleanup null_cleanup; mg::SupportedDevice device = { nullptr, - mg::PlatformPriority::unsupported, + mg::probe::unsupported, nullptr }; stub_render_platform = create_stub_render_platform(device, {}, mo::ProgramOption{}, null_cleanup); @@ -137,7 +137,7 @@ auto probe_display_platform( result.emplace_back( mg::SupportedDevice { nullptr, - mg::PlatformPriority::unsupported, + mg::probe::unsupported, nullptr }); return result; @@ -154,7 +154,7 @@ auto probe_rendering_platform( result.emplace_back( mg::SupportedDevice { nullptr, - mg::PlatformPriority::unsupported, + mg::probe::unsupported, nullptr }); return result; diff --git a/tests/platform_test_harness/graphics_platform_test_harness.cpp b/tests/platform_test_harness/graphics_platform_test_harness.cpp index 88d730b8738..7da4fe12bb2 100644 --- a/tests/platform_test_harness/graphics_platform_test_harness.cpp +++ b/tests/platform_test_harness/graphics_platform_test_harness.cpp @@ -109,33 +109,33 @@ class MinimalServerEnvironment : private mir::DefaultServerConfiguration }; char const* MinimalServerEnvironment::argv[] = {"graphics_platform_test_harness", nullptr}; -std::string describe_probe_result(mg::PlatformPriority priority) +std::string describe_probe_result(mg::probe::Result priority) { - if (priority == mg::PlatformPriority::unsupported) + if (priority == mg::probe::unsupported) { return "UNSUPPORTED"; } - else if (priority == mg::PlatformPriority::dummy) + else if (priority == mg::probe::dummy) { return "DUMMY"; } - else if (priority < mg::PlatformPriority::supported) + else if (priority < mg::probe::supported) { - return std::string{"SUPPORTED - "} + std::to_string(mg::PlatformPriority::supported - priority); + return std::string{"SUPPORTED - "} + std::to_string(mg::probe::supported - priority); } - else if (priority == mg::PlatformPriority::supported) + else if (priority == mg::probe::supported) { return "SUPPORTED"; } - else if (priority < mg::PlatformPriority::best) + else if (priority < mg::probe::best) { - return std::string{"SUPPORTED + "} + std::to_string(priority - mg::PlatformPriority::supported); + return std::string{"SUPPORTED + "} + std::to_string(priority - mg::probe::supported); } - else if (priority == mg::PlatformPriority::best) + else if (priority == mg::probe::best) { return "BEST"; } - return std::string{"BEST + "} + std::to_string(priority - mg::PlatformPriority::best); + return std::string{"BEST + "} + std::to_string(priority - mg::probe::best); } auto test_probe(mir::SharedLibrary const& dso, MinimalServerEnvironment& config) -> std::vector @@ -173,7 +173,7 @@ auto test_platform_construction(mir::SharedLibrary const& dso, std::vector= mg::PlatformPriority::supported) + if (device.support_level >= mg::probe::supported) { auto display = create_display_platform( device, diff --git a/tests/unit-tests/graphics/test_platform_prober.cpp b/tests/unit-tests/graphics/test_platform_prober.cpp index 9ff44a65506..2d08f51cc49 100644 --- a/tests/unit-tests/graphics/test_platform_prober.cpp +++ b/tests/unit-tests/graphics/test_platform_prober.cpp @@ -216,7 +216,7 @@ TEST_F(ServerPlatformProbeMockDRM, DoesNotLoadDummyPlatformWhenBetterPlatformExi EXPECT_THAT(selection_result, Not(IsEmpty())); for (auto& [device, module] : selection_result) { - EXPECT_THAT(device.support_level, Gt(mir::graphics::PlatformPriority::dummy)); + EXPECT_THAT(device.support_level, Gt(mir::graphics::probe::dummy)); } } #endif diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_platform.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_platform.cpp index fd5a84d2016..a157b6c4628 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_platform.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_platform.cpp @@ -167,7 +167,7 @@ TEST_F(MesaGraphicsPlatform, display_probe_returns_unsupported_when_no_drm_udev_ auto probe = platform_lib.load_function(display_platform_probe_symbol); EXPECT_THAT( probe(stub_vt, udev, options), - Each(SupportLevelIs(mg::PlatformPriority::unsupported))); + Each(SupportLevelIs(mg::probe::unsupported))); } TEST_F(MesaGraphicsPlatform, display_probe_returns_best_when_master) @@ -186,7 +186,7 @@ TEST_F(MesaGraphicsPlatform, display_probe_returns_best_when_master) auto probe = platform_lib.load_function(display_platform_probe_symbol); EXPECT_THAT( probe(stub_vt, udev, options), - Each(SupportLevelIs(mg::PlatformPriority::best))); + Each(SupportLevelIs(mg::probe::best))); } TEST_F(MesaGraphicsPlatform, probe_returns_unsupported_when_modesetting_is_not_supported) @@ -204,7 +204,7 @@ TEST_F(MesaGraphicsPlatform, probe_returns_unsupported_when_modesetting_is_not_s auto probe = platform_lib.load_function(display_platform_probe_symbol); EXPECT_THAT( probe(stub_vt, udev, options), - Each(SupportLevelIs(mg::PlatformPriority::unsupported))); + Each(SupportLevelIs(mg::probe::unsupported))); } TEST_F(MesaGraphicsPlatform, probe_returns_supported_when_cannot_determine_kms_support) @@ -222,7 +222,7 @@ TEST_F(MesaGraphicsPlatform, probe_returns_supported_when_cannot_determine_kms_s auto probe = platform_lib.load_function(display_platform_probe_symbol); EXPECT_THAT( probe(stub_vt, udev, options), - Each(SupportLevelIs(mg::PlatformPriority::supported))); + Each(SupportLevelIs(mg::probe::supported))); } TEST_F(MesaGraphicsPlatform, probe_returns_supported_when_unexpected_error_returned) @@ -240,7 +240,7 @@ TEST_F(MesaGraphicsPlatform, probe_returns_supported_when_unexpected_error_retur auto probe = platform_lib.load_function(display_platform_probe_symbol); EXPECT_THAT( probe(stub_vt, udev, options), - Each(SupportLevelIs(mg::PlatformPriority::supported))); + Each(SupportLevelIs(mg::probe::supported))); } TEST_F(MesaGraphicsPlatform, probe_returns_supported_when_cannot_determine_busid) @@ -258,7 +258,7 @@ TEST_F(MesaGraphicsPlatform, probe_returns_supported_when_cannot_determine_busid auto probe = platform_lib.load_function(display_platform_probe_symbol); EXPECT_THAT( probe(stub_vt, udev, options), - Each(SupportLevelIs(mg::PlatformPriority::supported))); + Each(SupportLevelIs(mg::probe::supported))); } TEST_F(MesaGraphicsPlatform, display_probe_returns_supported_when_KMS_probe_is_overridden) @@ -277,7 +277,7 @@ TEST_F(MesaGraphicsPlatform, display_probe_returns_supported_when_KMS_probe_is_o auto probe = platform_lib.load_function(display_platform_probe_symbol); EXPECT_THAT( probe(stub_vt, udev, options), - Each(SupportLevelIs(mg::PlatformPriority::supported))); + Each(SupportLevelIs(mg::probe::supported))); } TEST_F(MesaGraphicsPlatform, display_probe_does_not_touch_quirked_device) From 61d74cb0a10f99cd102ac9efe45c1a931f196fed Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Tue, 17 Oct 2023 09:48:09 +0100 Subject: [PATCH 103/108] Rename `DisplayInterfaceBase` -> `DisplayProvider` --- include/platform/mir/graphics/platform.h | 34 +++++++++---------- .../server/eglstream_interface_provider.cpp | 16 ++++----- .../server/eglstream_interface_provider.h | 4 +-- src/platforms/gbm-kms/server/kms/platform.cpp | 4 +-- .../wayland/wl_egl_display_provider.cpp | 4 +-- .../wayland/wl_egl_display_provider.h | 4 +-- src/platforms/x11/graphics/platform.cpp | 4 +-- .../compositor/basic_screen_shooter.cpp | 4 +-- .../stubbed_graphics_platform.cpp | 4 +-- 9 files changed, 39 insertions(+), 39 deletions(-) diff --git a/include/platform/mir/graphics/platform.h b/include/platform/mir/graphics/platform.h index cddc8284d1a..a0fe6450981 100644 --- a/include/platform/mir/graphics/platform.h +++ b/include/platform/mir/graphics/platform.h @@ -244,7 +244,7 @@ class RenderingPlatform RenderingProvider::Tag const& type_tag) -> std::shared_ptr = 0; }; -class DisplayInterfaceBase +class DisplayProvider { public: class Tag @@ -254,7 +254,7 @@ class DisplayInterfaceBase virtual ~Tag() = default; }; - virtual ~DisplayInterfaceBase() = default; + virtual ~DisplayProvider() = default; }; @@ -271,10 +271,10 @@ class Framebuffer }; -class CPUAddressableDisplayProvider : public DisplayInterfaceBase +class CPUAddressableDisplayProvider : public DisplayProvider { public: - class Tag : public DisplayInterfaceBase::Tag + class Tag : public DisplayProvider::Tag { }; @@ -294,10 +294,10 @@ class CPUAddressableDisplayProvider : public DisplayInterfaceBase -> std::unique_ptr = 0; }; -class GBMDisplayProvider : public DisplayInterfaceBase +class GBMDisplayProvider : public DisplayProvider { public: - class Tag : public DisplayInterfaceBase::Tag + class Tag : public DisplayProvider::Tag { }; @@ -351,10 +351,10 @@ class GBMDisplayProvider : public DisplayInterfaceBase class DmaBufBuffer; -class DmaBufDisplayProvider : public DisplayInterfaceBase +class DmaBufDisplayProvider : public DisplayProvider { public: - class Tag : public DisplayInterfaceBase::Tag + class Tag : public DisplayProvider::Tag { }; @@ -366,10 +366,10 @@ class DmaBufDisplayProvider : public DisplayInterfaceBase typedef void* EGLStreamKHR; #endif -class EGLStreamDisplayProvider : public DisplayInterfaceBase +class EGLStreamDisplayProvider : public DisplayProvider { public: - class Tag : public DisplayInterfaceBase::Tag + class Tag : public DisplayProvider::Tag { }; @@ -378,10 +378,10 @@ class EGLStreamDisplayProvider : public DisplayInterfaceBase virtual auto claim_stream() -> EGLStreamKHR = 0; }; -class GenericEGLDisplayProvider : public DisplayInterfaceBase +class GenericEGLDisplayProvider : public DisplayProvider { public: - class Tag : public DisplayInterfaceBase::Tag + class Tag : public DisplayProvider::Tag { }; @@ -424,8 +424,8 @@ class DisplayInterfaceProvider : public std::enable_shared_from_this std::shared_ptr { static_assert( - std::is_convertible_v, - "Can only acquire a Display interface; Interface must implement DisplayInterfaceBase"); + std::is_convertible_v, + "Can only acquire a Display interface; Interface must implement DisplayProvider"); if (auto const base_interface = maybe_create_interface(typename Interface::Tag{})) { @@ -453,11 +453,11 @@ class DisplayInterfaceProvider : public std::enable_shared_from_this this to * discover the specific interface being requested. - * \return A pointer to an implementation of the DisplayInterfaceBase-derived + * \return A pointer to an implementation of the DisplayProvider-derived * interface that corresponds to the most-derived type of tag_type. */ - virtual auto maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr = 0; + virtual auto maybe_create_interface(DisplayProvider::Tag const& type_tag) + -> std::shared_ptr = 0; }; class DisplayPlatform : public std::enable_shared_from_this diff --git a/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp b/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp index 52c29432c26..9123ed603df 100644 --- a/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp +++ b/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp @@ -26,10 +26,10 @@ namespace mg = mir::graphics; namespace { -class DisplayProvider : public mg::EGLStreamDisplayProvider +class DisplayProviderImpl : public mg::EGLStreamDisplayProvider { public: - DisplayProvider(EGLDisplay dpy, std::optional stream); + DisplayProviderImpl(EGLDisplay dpy, std::optional stream); auto get_egl_display() const -> EGLDisplay override; @@ -39,18 +39,18 @@ class DisplayProvider : public mg::EGLStreamDisplayProvider std::optional stream; }; -DisplayProvider::DisplayProvider(EGLDisplay dpy, std::optional stream) +DisplayProviderImpl::DisplayProviderImpl(EGLDisplay dpy, std::optional stream) : dpy{dpy}, stream{stream} { } -auto DisplayProvider::get_egl_display() const -> EGLDisplay +auto DisplayProviderImpl::get_egl_display() const -> EGLDisplay { return dpy; } -auto DisplayProvider::claim_stream() -> EGLStreamKHR +auto DisplayProviderImpl::claim_stream() -> EGLStreamKHR { if (stream) { @@ -71,12 +71,12 @@ mg::eglstream::InterfaceProvider::InterfaceProvider(InterfaceProvider const& fro { } -auto mg::eglstream::InterfaceProvider::maybe_create_interface(DisplayInterfaceBase::Tag const& tag) - -> std::shared_ptr +auto mg::eglstream::InterfaceProvider::maybe_create_interface(DisplayProvider::Tag const& tag) + -> std::shared_ptr { if (dynamic_cast(&tag)) { - return std::make_shared(dpy, stream); + return std::make_shared(dpy, stream); } return nullptr; } diff --git a/src/platforms/eglstream-kms/server/eglstream_interface_provider.h b/src/platforms/eglstream-kms/server/eglstream_interface_provider.h index dd41dafc0b1..313fdf09058 100644 --- a/src/platforms/eglstream-kms/server/eglstream_interface_provider.h +++ b/src/platforms/eglstream-kms/server/eglstream_interface_provider.h @@ -30,8 +30,8 @@ class InterfaceProvider : public DisplayInterfaceProvider InterfaceProvider(InterfaceProvider const& from, EGLStreamKHR with_stream); protected: - auto maybe_create_interface(DisplayInterfaceBase::Tag const& tag) - -> std::shared_ptr override; + auto maybe_create_interface(DisplayProvider::Tag const& tag) + -> std::shared_ptr override; private: EGLDisplay dpy; diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 03cd222fcb7..940f587e71f 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -374,8 +374,8 @@ class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfacePr } protected: - auto maybe_create_interface(mg::DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr + auto maybe_create_interface(mg::DisplayProvider::Tag const& type_tag) + -> std::shared_ptr { if (dynamic_cast(&type_tag)) { diff --git a/src/platforms/wayland/wl_egl_display_provider.cpp b/src/platforms/wayland/wl_egl_display_provider.cpp index 64206f7918b..ed022738214 100644 --- a/src/platforms/wayland/wl_egl_display_provider.cpp +++ b/src/platforms/wayland/wl_egl_display_provider.cpp @@ -209,8 +209,8 @@ auto mgw::WlDisplayProvider::get_egl_display() const -> EGLDisplay return egl_provider->get_egl_display(); } -auto mgw::WlDisplayProvider::maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr +auto mgw::WlDisplayProvider::maybe_create_interface(DisplayProvider::Tag const& type_tag) + -> std::shared_ptr { if (dynamic_cast(&type_tag)) { diff --git a/src/platforms/wayland/wl_egl_display_provider.h b/src/platforms/wayland/wl_egl_display_provider.h index b4caf90242f..9fd29fbde5f 100644 --- a/src/platforms/wayland/wl_egl_display_provider.h +++ b/src/platforms/wayland/wl_egl_display_provider.h @@ -18,8 +18,8 @@ class WlDisplayProvider : public DisplayInterfaceProvider auto get_egl_display() const -> EGLDisplay; - auto maybe_create_interface(DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr override; + auto maybe_create_interface(DisplayProvider::Tag const& type_tag) + -> std::shared_ptr override; class Framebuffer : public GenericEGLDisplayProvider::EGLFramebuffer { diff --git a/src/platforms/x11/graphics/platform.cpp b/src/platforms/x11/graphics/platform.cpp index 16bd5fe4348..8a2adabb2f2 100644 --- a/src/platforms/x11/graphics/platform.cpp +++ b/src/platforms/x11/graphics/platform.cpp @@ -182,8 +182,8 @@ class mgx::Platform::InterfaceProvider : public mg::DisplayInterfaceProvider std::optional const x_win; }; - auto maybe_create_interface(mir::graphics::DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr + auto maybe_create_interface(mir::graphics::DisplayProvider::Tag const& type_tag) + -> std::shared_ptr { if (dynamic_cast(&type_tag)) { diff --git a/src/server/compositor/basic_screen_shooter.cpp b/src/server/compositor/basic_screen_shooter.cpp index cc3c1076dbc..8bffc25d439 100644 --- a/src/server/compositor/basic_screen_shooter.cpp +++ b/src/server/compositor/basic_screen_shooter.cpp @@ -108,8 +108,8 @@ class InterfaceProvider : public mg::DisplayInterfaceProvider { } protected: - auto maybe_create_interface(mg::DisplayInterfaceBase::Tag const& type_tag) - -> std::shared_ptr override + auto maybe_create_interface(mg::DisplayProvider::Tag const& type_tag) + -> std::shared_ptr override { if (dynamic_cast(&type_tag)) { diff --git a/tests/mir_test_framework/stubbed_graphics_platform.cpp b/tests/mir_test_framework/stubbed_graphics_platform.cpp index 88cc45b0df2..fbef382dad2 100644 --- a/tests/mir_test_framework/stubbed_graphics_platform.cpp +++ b/tests/mir_test_framework/stubbed_graphics_platform.cpp @@ -106,8 +106,8 @@ auto mtf::StubGraphicPlatform::interface_for() class NullInterfaceProvider : public mg::DisplayInterfaceProvider { protected: - auto maybe_create_interface(mg::DisplayInterfaceBase::Tag const&) - -> std::shared_ptr + auto maybe_create_interface(mg::DisplayProvider::Tag const&) + -> std::shared_ptr { return nullptr; } From 743c278425d51eff692df7790e7e7edf38a9637d Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Tue, 17 Oct 2023 09:52:14 +0100 Subject: [PATCH 104/108] Missing header comment and header guards --- .../wayland/wl_egl_display_provider.h | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/platforms/wayland/wl_egl_display_provider.h b/src/platforms/wayland/wl_egl_display_provider.h index 9fd29fbde5f..c0cdc0af0ce 100644 --- a/src/platforms/wayland/wl_egl_display_provider.h +++ b/src/platforms/wayland/wl_egl_display_provider.h @@ -1,3 +1,21 @@ +/* + * 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 . + */ + +#ifndef MIR_PLATFORM_WAYLAND_DISPLAY_PROVIDER_H_ +#define MIR_PLATFORM_WAYLAND_DISPLAY_PROVIDER_H_ #include "mir/graphics/platform.h" @@ -53,3 +71,5 @@ class WlDisplayProvider : public DisplayInterfaceProvider std::shared_ptr const egl_provider; }; } + +#endif // MIR_PLATFORM_WAYLAND_DISPLAY_PROVIDER_H_ \ No newline at end of file From 6a40e4ddae52a3fe4814738a4a68cb0861fc5e58 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Tue, 17 Oct 2023 17:09:09 +0100 Subject: [PATCH 105/108] Fix the feature flag to support multiple devices on the same platform --- src/server/graphics/default_configuration.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/server/graphics/default_configuration.cpp b/src/server/graphics/default_configuration.cpp index dd172769f37..2cd37f0efb8 100644 --- a/src/server/graphics/default_configuration.cpp +++ b/src/server/graphics/default_configuration.cpp @@ -144,7 +144,13 @@ void hybrid_check(std::vector r.first.support_level; }); - platform_modules.resize(std::min(1uz, platform_modules.size())); + if (!platform_modules.empty()) + { + auto const erase_begin = std::remove_if(platform_modules.begin(), platform_modules.end(), + [platform=platform_modules.front().second](auto const& pm) { return pm.second != platform; }); + + platform_modules.erase(erase_begin, platform_modules.end()); + } } } } From 63a2fd70b77a5f41b1d1a8bd3174024c26b81054 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Tue, 17 Oct 2023 13:31:35 -0400 Subject: [PATCH 106/108] Adding DisplayConfigurationPolicy::confirm so that the display configuration is only written once --- .../mir/graphics/display_configuration_policy.h | 1 + .../graphics/default_display_configuration_policy.h | 9 ++++++--- src/miral/display_configuration_option.cpp | 12 ++++++++++++ src/miral/static_display_config.cpp | 8 +++++--- src/miral/static_display_config.h | 3 ++- src/miroil/persist_display_config.cpp | 4 ++++ .../default_display_configuration_policy.cpp | 11 +++++++++++ src/server/graphics/multiplexing_display.cpp | 1 + .../test/doubles/null_display_configuration_policy.h | 1 + .../graphics/test_multiplexing_display.cpp | 4 ++++ .../gbm-kms/kms/test_display_multi_monitor.cpp | 8 ++++++++ .../scene/test_mediating_display_changer.cpp | 1 + 12 files changed, 56 insertions(+), 7 deletions(-) diff --git a/include/platform/mir/graphics/display_configuration_policy.h b/include/platform/mir/graphics/display_configuration_policy.h index 3caa300e5d7..261894f6f64 100644 --- a/include/platform/mir/graphics/display_configuration_policy.h +++ b/include/platform/mir/graphics/display_configuration_policy.h @@ -30,6 +30,7 @@ class DisplayConfigurationPolicy virtual ~DisplayConfigurationPolicy() = default; virtual void apply_to(DisplayConfiguration& conf) = 0; + virtual void confirm(DisplayConfiguration const& conf) = 0; protected: DisplayConfigurationPolicy() = default; diff --git a/src/include/server/mir/graphics/default_display_configuration_policy.h b/src/include/server/mir/graphics/default_display_configuration_policy.h index fca7812ff09..c81ed612523 100644 --- a/src/include/server/mir/graphics/default_display_configuration_policy.h +++ b/src/include/server/mir/graphics/default_display_configuration_policy.h @@ -29,21 +29,24 @@ namespace graphics class CloneDisplayConfigurationPolicy : public DisplayConfigurationPolicy { public: - void apply_to(DisplayConfiguration& conf); + void apply_to(DisplayConfiguration& conf) override; + void confirm(DisplayConfiguration const& conf) override; }; /// Each screen placed to the right of the previous one class SideBySideDisplayConfigurationPolicy : public DisplayConfigurationPolicy { public: - void apply_to(graphics::DisplayConfiguration& conf); + void apply_to(graphics::DisplayConfiguration& conf) override; + void confirm(DisplayConfiguration const& conf) override; }; /// Just use the first screen class SingleDisplayConfigurationPolicy : public DisplayConfigurationPolicy { public: - void apply_to(graphics::DisplayConfiguration& conf); + void apply_to(graphics::DisplayConfiguration& conf) override; + void confirm(DisplayConfiguration const& conf) override; }; /** @} */ } diff --git a/src/miral/display_configuration_option.cpp b/src/miral/display_configuration_option.cpp index 69d390cbc6e..e89c054ed9c 100644 --- a/src/miral/display_configuration_option.cpp +++ b/src/miral/display_configuration_option.cpp @@ -51,6 +51,7 @@ class PixelFormatSelector : public mg::DisplayConfigurationPolicy public: PixelFormatSelector(std::shared_ptr const& base_policy, bool with_alpha); virtual void apply_to(mg::DisplayConfiguration& conf); + virtual void confirm(mg::DisplayConfiguration const& conf); private: std::shared_ptr const base_policy; bool const with_alpha; @@ -94,6 +95,11 @@ void PixelFormatSelector::apply_to(mg::DisplayConfiguration& conf) }); } +void PixelFormatSelector::confirm(mg::DisplayConfiguration const& conf) +{ + base_policy->confirm(conf); +} + class ScaleSetter : public mg::DisplayConfigurationPolicy { public: @@ -104,6 +110,7 @@ class ScaleSetter : public mg::DisplayConfigurationPolicy } void apply_to(mg::DisplayConfiguration& conf) override; + void confirm(mg::DisplayConfiguration const& conf) override; private: std::shared_ptr const base_policy; float const with_scale; @@ -119,6 +126,11 @@ void ScaleSetter::apply_to(mg::DisplayConfiguration& conf) }); } +void ScaleSetter::confirm(mg::DisplayConfiguration const& conf) +{ + base_policy->confirm(conf); +} + void miral::display_configuration_options(mir::Server& server) { // Add choice of monitor configuration diff --git a/src/miral/static_display_config.cpp b/src/miral/static_display_config.cpp index 73be9049f50..0e2ebdc2f45 100644 --- a/src/miral/static_display_config.cpp +++ b/src/miral/static_display_config.cpp @@ -328,6 +328,10 @@ void miral::YamlFileDisplayConfig::apply_to(mg::DisplayConfiguration& conf) mir::log_info(out.str()); } +void miral::YamlFileDisplayConfig::confirm(mir::graphics::DisplayConfiguration const&) +{ +} + void miral::YamlFileDisplayConfig::apply_default_configuration(mg::DisplayConfiguration& conf) { conf.for_each_output([config=Config{}](mg::UserDisplayConfigurationOutput& conf_output) @@ -549,7 +553,7 @@ void miral::ReloadingYamlFileDisplayConfig::config_path(std::string newpath) check_for_layout_override(); } -void miral::ReloadingYamlFileDisplayConfig::apply_to(mir::graphics::DisplayConfiguration& conf) +void miral::ReloadingYamlFileDisplayConfig::confirm(mir::graphics::DisplayConfiguration const& conf) { std::lock_guard lock{mutex}; if (!config_path_) @@ -584,8 +588,6 @@ void miral::ReloadingYamlFileDisplayConfig::apply_to(mir::graphics::DisplayConfi filename.c_str()); } } - - YamlFileDisplayConfig::apply_to(conf); } auto miral::ReloadingYamlFileDisplayConfig::the_main_loop() const -> std::shared_ptr diff --git a/src/miral/static_display_config.h b/src/miral/static_display_config.h index b01527b7f4a..1df3a1cd984 100644 --- a/src/miral/static_display_config.h +++ b/src/miral/static_display_config.h @@ -45,6 +45,7 @@ class YamlFileDisplayConfig : public mir::graphics::DisplayConfigurationPolicy void load_config(std::istream& config_file, std::string const& filename); void apply_to(mir::graphics::DisplayConfiguration& conf) override; + virtual void confirm(mir::graphics::DisplayConfiguration const& conf) override; void select_layout(std::string const& layout); @@ -94,7 +95,7 @@ class ReloadingYamlFileDisplayConfig : public YamlFileDisplayConfig void config_path(std::string newpath); - void apply_to(mir::graphics::DisplayConfiguration& conf) override; + void confirm(mir::graphics::DisplayConfiguration const& conf) override; void check_for_layout_override(); diff --git a/src/miroil/persist_display_config.cpp b/src/miroil/persist_display_config.cpp index 4426163f4f1..404e008d976 100644 --- a/src/miroil/persist_display_config.cpp +++ b/src/miroil/persist_display_config.cpp @@ -77,6 +77,10 @@ struct DisplayConfigurationPolicyAdapter : mg::DisplayConfigurationPolicy self->apply_to(conf, *wrapped_policy, *custom_policy); } + void confirm(mg::DisplayConfiguration const&) override + { + } + std::shared_ptr const self; std::shared_ptr const wrapped_policy; std::shared_ptr const custom_policy; diff --git a/src/server/graphics/default_display_configuration_policy.cpp b/src/server/graphics/default_display_configuration_policy.cpp index 19ca41c12a4..7f333a1f9db 100644 --- a/src/server/graphics/default_display_configuration_policy.cpp +++ b/src/server/graphics/default_display_configuration_policy.cpp @@ -88,6 +88,10 @@ void mg::CloneDisplayConfigurationPolicy::apply_to(DisplayConfiguration& conf) }); } +void mg::CloneDisplayConfigurationPolicy::confirm(mir::graphics::DisplayConfiguration const&) +{ +} + void mg::SideBySideDisplayConfigurationPolicy::apply_to(graphics::DisplayConfiguration& conf) { int max_x = 0; @@ -113,6 +117,9 @@ void mg::SideBySideDisplayConfigurationPolicy::apply_to(graphics::DisplayConfigu }); } +void mg::SideBySideDisplayConfigurationPolicy::confirm(mir::graphics::DisplayConfiguration const&) +{ +} void mg::SingleDisplayConfigurationPolicy::apply_to(graphics::DisplayConfiguration& conf) { @@ -137,3 +144,7 @@ void mg::SingleDisplayConfigurationPolicy::apply_to(graphics::DisplayConfigurati } }); } + +void mg::SingleDisplayConfigurationPolicy::confirm(mir::graphics::DisplayConfiguration const&) +{ +} \ No newline at end of file diff --git a/src/server/graphics/multiplexing_display.cpp b/src/server/graphics/multiplexing_display.cpp index 7966392edba..0981f5ccf83 100644 --- a/src/server/graphics/multiplexing_display.cpp +++ b/src/server/graphics/multiplexing_display.cpp @@ -33,6 +33,7 @@ mg::MultiplexingDisplay::MultiplexingDisplay( { auto conf = configuration(); initial_configuration_policy.apply_to(*conf); + initial_configuration_policy.confirm(*conf); configure(*conf); } diff --git a/tests/include/mir/test/doubles/null_display_configuration_policy.h b/tests/include/mir/test/doubles/null_display_configuration_policy.h index 54907ffd1b8..d62f905791d 100644 --- a/tests/include/mir/test/doubles/null_display_configuration_policy.h +++ b/tests/include/mir/test/doubles/null_display_configuration_policy.h @@ -29,6 +29,7 @@ class NullDisplayConfigurationPolicy : public graphics::DisplayConfigurationPoli { public: void apply_to(graphics::DisplayConfiguration&) override {} + void confirm(graphics::DisplayConfiguration const&) override {} }; } } diff --git a/tests/unit-tests/graphics/test_multiplexing_display.cpp b/tests/unit-tests/graphics/test_multiplexing_display.cpp index dfa83582d96..34321b7b32c 100644 --- a/tests/unit-tests/graphics/test_multiplexing_display.cpp +++ b/tests/unit-tests/graphics/test_multiplexing_display.cpp @@ -679,6 +679,10 @@ TEST(MultiplexingDisplay, applies_initial_display_configuration) }); } + void confirm(mg::DisplayConfiguration const&) override + { + } + auto expected_location_for_display(mg::DisplayConfigurationOutput const& display) -> geom::Point { auto it = std::find_if( diff --git a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp index 8f34ccd195e..111a1dcc7fa 100644 --- a/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp +++ b/tests/unit-tests/platforms/gbm-kms/kms/test_display_multi_monitor.cpp @@ -76,6 +76,10 @@ class ClonedDisplayConfigurationPolicy : public mg::DisplayConfigurationPolicy } }); } + + void confirm(mg::DisplayConfiguration const&) + { + } }; class SideBySideDisplayConfigurationPolicy : public mg::DisplayConfigurationPolicy @@ -105,6 +109,10 @@ class SideBySideDisplayConfigurationPolicy : public mg::DisplayConfigurationPoli } }); } + + void confirm(mg::DisplayConfiguration const&) + { + } }; class MesaDisplayMultiMonitorTest : public ::testing::Test diff --git a/tests/unit-tests/scene/test_mediating_display_changer.cpp b/tests/unit-tests/scene/test_mediating_display_changer.cpp index e726846f030..671c45a5441 100644 --- a/tests/unit-tests/scene/test_mediating_display_changer.cpp +++ b/tests/unit-tests/scene/test_mediating_display_changer.cpp @@ -55,6 +55,7 @@ class MockDisplayConfigurationPolicy : public mg::DisplayConfigurationPolicy public: ~MockDisplayConfigurationPolicy() noexcept {} MOCK_METHOD(void, apply_to, (mg::DisplayConfiguration&)); + MOCK_METHOD(void, confirm, (mg::DisplayConfiguration const&)); }; From 72850520d86fe1dfc4aae5b3349278bdddde2109 Mon Sep 17 00:00:00 2001 From: Alan Griffiths Date: Wed, 18 Oct 2023 09:50:00 +0100 Subject: [PATCH 107/108] Fixup --- src/miral/display_configuration_option.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/miral/display_configuration_option.cpp b/src/miral/display_configuration_option.cpp index 512acc6973c..47f6dba523a 100644 --- a/src/miral/display_configuration_option.cpp +++ b/src/miral/display_configuration_option.cpp @@ -145,6 +145,7 @@ class AutoscaleSetter : public mg::DisplayConfigurationPolicy } void apply_to(mg::DisplayConfiguration& conf) override; + void confirm(mg::DisplayConfiguration const& conf) override; private: void apply_to(mg::UserDisplayConfigurationOutput& output); std::shared_ptr const base_policy; @@ -158,6 +159,11 @@ void AutoscaleSetter::apply_to(mg::DisplayConfiguration& conf) [this](mg::UserDisplayConfigurationOutput& output) { if (output.connected) apply_to(output); }); } +void AutoscaleSetter::confirm(mg::DisplayConfiguration const& conf) +{ + base_policy->confirm(conf); +} + void AutoscaleSetter::apply_to(mg::UserDisplayConfigurationOutput& output) { auto const output_height = From 926748ee0b06c7e6b014ec056a94541ecd37d44c Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Wed, 18 Oct 2023 04:55:44 -0400 Subject: [PATCH 108/108] Fix for mir_performance_tests failure due to not cleaning up our displays (#3080) --- src/platforms/eglstream-kms/server/display.cpp | 15 +++++++++++++++ src/platforms/eglstream-kms/server/platform.cpp | 8 ++++++++ src/platforms/eglstream-kms/server/platform.h | 2 ++ 3 files changed, 25 insertions(+) diff --git a/src/platforms/eglstream-kms/server/display.cpp b/src/platforms/eglstream-kms/server/display.cpp index 46cef442c0d..effe77c4b7e 100644 --- a/src/platforms/eglstream-kms/server/display.cpp +++ b/src/platforms/eglstream-kms/server/display.cpp @@ -181,6 +181,21 @@ class DisplayBuffer pending_flip = satisfied_promise.get_future(); } + ~DisplayBuffer() + { + if (output_stream != EGL_NO_STREAM_KHR) + { + eglDestroyStreamKHR(dpy, output_stream); + output_stream = nullptr; + } + + if (ctx != EGL_NO_CONTEXT) + { + eglDestroyContext(dpy, ctx); + ctx = nullptr; + } + } + mir::geometry::Rectangle view_area() const override { return view_area_; diff --git a/src/platforms/eglstream-kms/server/platform.cpp b/src/platforms/eglstream-kms/server/platform.cpp index dae3f89f80d..777b0561a34 100644 --- a/src/platforms/eglstream-kms/server/platform.cpp +++ b/src/platforms/eglstream-kms/server/platform.cpp @@ -248,6 +248,14 @@ mge::DisplayPlatform::DisplayPlatform( provider = std::make_shared(display); } +mge::DisplayPlatform::~DisplayPlatform() +{ + if (display != EGL_NO_DISPLAY) + { + eglTerminate(display); + } +} + auto mge::DisplayPlatform::create_display( std::shared_ptr const& configuration_policy, std::shared_ptr const& gl_config) diff --git a/src/platforms/eglstream-kms/server/platform.h b/src/platforms/eglstream-kms/server/platform.h index 53371c2ff64..1eee5f51cf1 100644 --- a/src/platforms/eglstream-kms/server/platform.h +++ b/src/platforms/eglstream-kms/server/platform.h @@ -65,6 +65,8 @@ class DisplayPlatform : public graphics::DisplayPlatform EGLDeviceEXT device, std::shared_ptr display_report); + ~DisplayPlatform(); + auto create_display( std::shared_ptr const& initial_conf_policy, std::shared_ptr const& gl_config)