diff --git a/src/platforms/common/server/CMakeLists.txt b/src/platforms/common/server/CMakeLists.txt index 0fc0d9b8eb9..5940a26ba92 100644 --- a/src/platforms/common/server/CMakeLists.txt +++ b/src/platforms/common/server/CMakeLists.txt @@ -14,6 +14,11 @@ add_library(server_platform_common STATIC one_shot_device_observer.cpp cpu_copy_output_surface.cpp cpu_copy_output_surface.h + kms_cpu_addressable_display_provider.cpp + kms_cpu_addressable_display_provider.h + cpu_addressable_fb.cpp + cpu_addressable_fb.h + kms_framebuffer.h options_parsing_helpers.h options_parsing_helpers.cpp ) diff --git a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp b/src/platforms/common/server/cpu_addressable_fb.cpp similarity index 91% rename from src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp rename to src/platforms/common/server/cpu_addressable_fb.cpp index dacf1932a60..3e938f5a409 100644 --- a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.cpp +++ b/src/platforms/common/server/cpu_addressable_fb.cpp @@ -16,7 +16,6 @@ #include "cpu_addressable_fb.h" -#include "mir/geometry/forward.h" #include "mir/log.h" #include "mir_toolkit/common.h" @@ -26,9 +25,8 @@ #include namespace mg = mir::graphics; -namespace mgg = mg::gbm; -class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappableBuffer +class mg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappableBuffer { template class Mapping : public mir::renderer::software::Mapping @@ -53,7 +51,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable 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); + log_error("Failed to unmap CPU buffer: %s (%i)", strerror(errno), errno); } } @@ -102,7 +100,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable 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); + log_error("Failed destroy CPU-accessible buffer: %s (%i)", strerror(-err), -err); } } @@ -248,7 +246,7 @@ class mgg::CPUAddressableFB::Buffer : public mir::renderer::software::RWMappable size_t const size_; }; -mgg::CPUAddressableFB::CPUAddressableFB( +mg::CPUAddressableFB::CPUAddressableFB( mir::Fd const& drm_fd, bool supports_modifiers, DRMFormat format, @@ -257,12 +255,12 @@ mgg::CPUAddressableFB::CPUAddressableFB( { } -mgg::CPUAddressableFB::~CPUAddressableFB() +mg::CPUAddressableFB::~CPUAddressableFB() { drmModeRmFB(drm_fd, fb_id); } -mgg::CPUAddressableFB::CPUAddressableFB( +mg::CPUAddressableFB::CPUAddressableFB( mir::Fd drm_fd, bool supports_modifiers, DRMFormat format, @@ -273,32 +271,32 @@ mgg::CPUAddressableFB::CPUAddressableFB( { } -auto mgg::CPUAddressableFB::map_writeable() -> std::unique_ptr> +auto mg::CPUAddressableFB::map_writeable() -> std::unique_ptr> { return buffer->map_writeable(); } -auto mgg::CPUAddressableFB::format() const -> MirPixelFormat +auto mg::CPUAddressableFB::format() const -> MirPixelFormat { return buffer->format(); } -auto mgg::CPUAddressableFB::stride() const -> geometry::Stride +auto mg::CPUAddressableFB::stride() const -> geometry::Stride { return buffer->stride(); } -auto mgg::CPUAddressableFB::size() const -> geometry::Size +auto mg::CPUAddressableFB::size() const -> geometry::Size { return buffer->size(); } -mgg::CPUAddressableFB::operator uint32_t() const +mg::CPUAddressableFB::operator uint32_t() const { return fb_id; } -auto mgg::CPUAddressableFB::fb_id_for_buffer( +auto mg::CPUAddressableFB::fb_id_for_buffer( mir::Fd const &drm_fd, bool supports_modifiers, DRMFormat format, diff --git a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h b/src/platforms/common/server/cpu_addressable_fb.h similarity index 92% rename from src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h rename to src/platforms/common/server/cpu_addressable_fb.h index 9d2b3494168..a836b51243f 100644 --- a/src/platforms/gbm-kms/server/kms/cpu_addressable_fb.h +++ b/src/platforms/common/server/cpu_addressable_fb.h @@ -14,15 +14,15 @@ * along with this program. If not, see . */ -#ifndef MIR_GRAPHICS_GBM_FB_H_ -#define MIR_GRAPHICS_GBM_FB_H_ +#ifndef MIR_GRAPHICS_CPU_ADDRESSABLE_FB_H_ +#define MIR_GRAPHICS_CPU_ADDRESSABLE_FB_H_ #include "mir/fd.h" #include "mir/graphics/platform.h" #include "kms_framebuffer.h" -namespace mir::graphics::gbm +namespace mir::graphics { class CPUAddressableFB : public FBHandle, public CPUAddressableDisplayProvider::MappableFB { @@ -65,4 +65,4 @@ class CPUAddressableFB : public FBHandle, public CPUAddressableDisplayProvider:: } -#endif //MIR_GRAPHICS_GBM_FB_H_ +#endif //MIR_GRAPHICS_CPU_ADDRESSABLE_FB_H_ diff --git a/src/platforms/common/server/kms_cpu_addressable_display_provider.cpp b/src/platforms/common/server/kms_cpu_addressable_display_provider.cpp new file mode 100644 index 00000000000..e64c9f24603 --- /dev/null +++ b/src/platforms/common/server/kms_cpu_addressable_display_provider.cpp @@ -0,0 +1,72 @@ +/* + * 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 "kms_cpu_addressable_display_provider.h" +#include "cpu_addressable_fb.h" +#include +#include + +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)) + { + BOOST_THROW_EXCEPTION(( + std::system_error{ + errno, + std::system_category(), + "Failed to query DRM capabilities"})); + } + return value; +} +} + +namespace mg = mir::graphics; +namespace geom = mir::geometry; + +mg::kms::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 mg::kms::CPUAddressableDisplayProvider::supported_formats() const +-> std::vector +{ + // TODO: Pull out of DRM info + return {mg::DRMFormat{DRM_FORMAT_XRGB8888}, mg::DRMFormat{DRM_FORMAT_ARGB8888}}; +} + +auto mg::kms::CPUAddressableDisplayProvider::alloc_fb( + geom::Size size, DRMFormat format) -> std::unique_ptr +{ + return std::make_unique(drm_fd, supports_modifiers, format, size); +} + +auto mir::graphics::kms::CPUAddressableDisplayProvider::create_if_supported(mir::Fd const& drm_fd) +-> std::shared_ptr +{ + if (drm_get_cap_checked(drm_fd, DRM_CAP_DUMB_BUFFER)) + { + return std::shared_ptr(new CPUAddressableDisplayProvider{drm_fd}); + } + else + { + return {}; + } +} diff --git a/src/platforms/common/server/kms_cpu_addressable_display_provider.h b/src/platforms/common/server/kms_cpu_addressable_display_provider.h new file mode 100644 index 00000000000..26ec2428526 --- /dev/null +++ b/src/platforms/common/server/kms_cpu_addressable_display_provider.h @@ -0,0 +1,52 @@ +/* + * 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_BASIC_CPU_ADDRESSABLE_DISPLAY_PROVIDER_H +#define MIR_GRAPHICS_BASIC_CPU_ADDRESSABLE_DISPLAY_PROVIDER_H + +#include "mir/graphics/platform.h" +#include + +namespace mir +{ +namespace graphics +{ +namespace kms +{ +class CPUAddressableDisplayProvider : public graphics::CPUAddressableDisplayProvider +{ +public: + /// Create an CPUAddressableDisplayProvider if and only if supported by the device + /// \return the provider, or an empty pointer + static auto create_if_supported(mir::Fd const& drm_fd) -> std::shared_ptr; + + auto supported_formats() const + -> std::vector override; + + auto alloc_fb(geometry::Size pixel_size, DRMFormat format) + -> std::unique_ptr override; + +private: + explicit CPUAddressableDisplayProvider(mir::Fd drm_fd); + + mir::Fd const drm_fd; + bool const supports_modifiers; +}; +} +} +} + +#endif //MIR_GRAPHICS_BASIC_CPU_ADDRESSABLE_DISPLAY_PROVIDER_H diff --git a/src/platforms/gbm-kms/server/kms/kms_framebuffer.h b/src/platforms/common/server/kms_framebuffer.h similarity index 98% rename from src/platforms/gbm-kms/server/kms/kms_framebuffer.h rename to src/platforms/common/server/kms_framebuffer.h index 32b16c2ebf6..3bf50e84dea 100644 --- a/src/platforms/gbm-kms/server/kms/kms_framebuffer.h +++ b/src/platforms/common/server/kms_framebuffer.h @@ -23,8 +23,6 @@ namespace mir { namespace graphics { -namespace gbm -{ class FBHandle : public Framebuffer { public: @@ -35,7 +33,6 @@ class FBHandle : public Framebuffer } } -} #endif //MIR_GRAPHICS_GBM_KMS_FRAMEBUFFER_H_ diff --git a/src/platforms/eglstream-kms/server/display.cpp b/src/platforms/eglstream-kms/server/display.cpp index effe77c4b7e..18fc77b1245 100644 --- a/src/platforms/eglstream-kms/server/display.cpp +++ b/src/platforms/eglstream-kms/server/display.cpp @@ -14,10 +14,13 @@ * along with this program. If not, see . */ +#include #include +#include #include "display.h" #include "egl_output.h" +#include "kms_framebuffer.h" #include "kms-utils/drm_mode_resources.h" #include "mir/graphics/platform.h" @@ -130,16 +133,12 @@ class DisplayBuffer EGLContext ctx, EGLConfig config, std::shared_ptr event_handler, - mge::kms::EGLOutput const& output, + std::shared_ptr output, std::shared_ptr display_report) : owner{std::move(owner)}, dpy{dpy}, ctx{create_context(dpy, config, ctx)}, - layer{output.output_layer()}, - crtc_id{output.crtc_id()}, - view_area_{output.extents()}, - output_size{output.size()}, - transform{output.transformation()}, + output{output}, drm_node{std::move(drm_node)}, event_handler{std::move(event_handler)}, display_report{std::move(display_report)} @@ -156,17 +155,17 @@ class DisplayBuffer } EGLAttrib swap_interval; - if (eglQueryOutputLayerAttribEXT(dpy, layer, EGL_SWAP_INTERVAL_EXT, &swap_interval) != EGL_TRUE) + if (eglQueryOutputLayerAttribEXT(dpy, output->output_layer(), EGL_SWAP_INTERVAL_EXT, &swap_interval) != EGL_TRUE) { BOOST_THROW_EXCEPTION(mg::egl_error("Failed to query swap interval")); } - if (eglOutputLayerAttribEXT(dpy, layer, EGL_SWAP_INTERVAL_EXT, 1) != EGL_TRUE) + if (eglOutputLayerAttribEXT(dpy, output->output_layer(), EGL_SWAP_INTERVAL_EXT, 1) != EGL_TRUE) { BOOST_THROW_EXCEPTION(mg::egl_error("Failed to set swap interval")); } - if (eglStreamConsumerOutputEXT(dpy, output_stream, output.output_layer()) == EGL_FALSE) + if (eglStreamConsumerOutputEXT(dpy, output_stream, output->output_layer()) == EGL_FALSE) { BOOST_THROW_EXCEPTION(mg::egl_error("Failed to attach EGLStream to output")); }; @@ -198,12 +197,12 @@ class DisplayBuffer mir::geometry::Rectangle view_area() const override { - return view_area_; + return output->extents(); } glm::mat2 transformation() const override { - return transform; + return output->transformation(); } void for_each_display_buffer(const std::function& f) override @@ -215,19 +214,87 @@ class DisplayBuffer { // Wait for the last flip to finish, if it hasn't already. pending_flip.get(); - pending_flip = event_handler->expect_flip_event( - crtc_id, + output->crtc_id(), [this](unsigned frame_count, std::chrono::milliseconds frame_time) { // TODO: Um, why does NVIDIA always call this with 0, 0ms? display_report->report_vsync( - crtc_id, + output->crtc_id(), mg::Frame { frame_count, mir::time::PosixTimestamp(CLOCK_MONOTONIC, frame_time)}); }); + if (next_swap) // This is the KMS route + { + visible_fb = std::move(scheduled_fb); + scheduled_fb = nullptr; + + scheduled_fb = std::move(next_swap); + next_swap = nullptr; + + // Arbitrarily pick a 10s deadline for modesetting + auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds{10}; + while (auto error = output->queue_atomic_flip(*scheduled_fb, event_handler->drm_event_data())) + { + if (error->value() == EPERM || error->value() == EACCES) + { + /* We don't have modesetting permissions + * This is presumably because we've just been VT-switched-away, and so + * will pause soon. + * + * We'll assume that this is transitory, log in case it's an acutal issue, and continue. + * + * We've failed to submit the flip, though, so cancel the pending flip event + */ + mir::log_info("Failed to submit page flip (%s (%i))", + error->message().c_str(), + error->value()); + event_handler->cancel_flip_events(output->crtc_id()); + return; + } + if (error->value() == EAGAIN) + { + /* Sigh. + * So, if we try and schedule a flip when something is already in progress, we'll + * get EAGAIN. + * + * *Mostly* we shouldn't, because we're waiting for the page flip event before trying + * to submit a new one, but it's possible for the KMS driver to cause this for opaque + * (to us) reasons even if we're doing everything correctly. + * + * So, in this case, wait a bit(!) and try again. + */ + if (std::chrono::steady_clock::now() > deadline) + { + BOOST_THROW_EXCEPTION((std::runtime_error{"Timeout waiting for modeset"})); + } + /* Sleep for 5ms; this would correspond to ~200Hz, which is fast enough that we + * won't notice it, while being long enough to make it likely the previous work is + * done + */ + std::this_thread::sleep_for(std::chrono::milliseconds{5}); + } + if (error->value() == EINVAL) + { + /* This *should* be just for us doing the wrong thing, but it *also* + * occurs when VT switching has disabled the damn output behind our back + * so we can't do a non-ALLOW_MODESET commit (and we don't want to do that) + * anyway. + * + * Assume this is a transient VT-switch failure :( + */ + mir::log_info("Failed to submit page flip (%s (%i))", + error->message().c_str(), + error->value()); + event_handler->cancel_flip_events(output->crtc_id()); + return; + } + } + return; + } + EGLAttrib const acquire_attribs[] = { EGL_DRM_FLIP_EVENT_DATA_NV, reinterpret_cast(event_handler->drm_event_data()), EGL_NONE @@ -235,6 +302,12 @@ class DisplayBuffer if (nv_stream(dpy).eglStreamConsumerAcquireAttribNV(dpy, output_stream, acquire_attribs) != EGL_TRUE) { auto error = eglGetError(); + /* TODO: Handle error 0x3353 (EGL_RESOURCE_BUSY_EXT) here. + * That can be triggered by VT switch, but the naive approach direct KMS uses above doesn't work: + * We can't return to the compositor without *really* consuming the buffer because then + * eglSwapBuffers in EGLStreamOutputSurface::commit() blocks (because it can't push a new frame + * into the EGLStream). + */ EGLAttrib stream_state{0}; eglQueryStreamAttribKHR(dpy, output_stream, EGL_STREAM_STATE_KHR, &stream_state); std::string state; @@ -317,25 +390,36 @@ class DisplayBuffer return false; } + if (auto fb = std::dynamic_pointer_cast(renderable_list[0].buffer)) + { + next_swap = std::move(fb); + return true; + } // TODO: Validate that the submitted "framebuffer" is *actually* our EGLStream return true; } + + void resume() + { + output->set_flags_for_next_flip(DRM_MODE_ATOMIC_ALLOW_MODESET); + } private: std::shared_ptr const owner; EGLDisplay dpy; EGLContext ctx; - EGLOutputLayerEXT layer; - uint32_t crtc_id; - mir::geometry::Rectangle const view_area_; - mir::geometry::Size const output_size; - glm::mat2 const transform; + std::shared_ptr output; EGLStreamKHR output_stream; mir::Fd const drm_node; std::shared_ptr const event_handler; std::future pending_flip; mg::EGLExtensions::LazyDisplayExtensions nv_stream; std::shared_ptr const display_report; + + /// Used only for the KMS case + std::shared_ptr next_swap{nullptr}; + std::shared_ptr scheduled_fb{nullptr}; + std::shared_ptr visible_fb{nullptr}; }; mge::KMSDisplayConfiguration create_display_configuration( @@ -345,8 +429,7 @@ mge::KMSDisplayConfiguration create_display_configuration( { if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, context) == EGL_FALSE) { - BOOST_THROW_EXCEPTION(( - mg::egl_error("Failed to make EGL context current for display construction"))); + BOOST_THROW_EXCEPTION((mg::egl_error("Failed to make EGL context current for display construction"))); } return mge::KMSDisplayConfiguration{drm_node, dpy}; } @@ -403,11 +486,11 @@ void mge::Display::configure(DisplayConfiguration const& conf) { auto kms_conf = dynamic_cast(conf); active_sync_groups.clear(); - kms_conf.for_each_output([this](kms::EGLOutput const& output) + kms_conf.for_each_output([this](std::shared_ptr const& output) { - if (output.used) + if (output->used) { - const_cast(output).configure(output.current_mode_index); + output->configure(output->current_mode_index); active_sync_groups.emplace_back( std::make_unique<::DisplayBuffer>( provider, @@ -462,7 +545,10 @@ void mge::Display::pause() void mge::Display::resume() { - + for (auto& group : active_sync_groups) + { + dynamic_cast<::DisplayBuffer*>(group.get())->resume(); + } } std::shared_ptr mge::Display::create_hardware_cursor() diff --git a/src/platforms/eglstream-kms/server/drm_event_handler.h b/src/platforms/eglstream-kms/server/drm_event_handler.h index 06e4e208818..d2f08ca26b7 100644 --- a/src/platforms/eglstream-kms/server/drm_event_handler.h +++ b/src/platforms/eglstream-kms/server/drm_event_handler.h @@ -40,6 +40,16 @@ class DRMEventHandler virtual std::future expect_flip_event( KMSCrtcId id, std::function on_flip) = 0; + + /** + * Cancel any pending flip events on this CRTC + * + * This is useful when we know that we're not going to get the flip event we requested, + * such as when a VT switch has occurred. + * + * This marks all pending flip futures as completed. + */ + virtual void cancel_flip_events(KMSCrtcId id) = 0; }; } } diff --git a/src/platforms/eglstream-kms/server/egl_output.cpp b/src/platforms/eglstream-kms/server/egl_output.cpp index e9b9b3100ce..0a246d1e9af 100644 --- a/src/platforms/eglstream-kms/server/egl_output.cpp +++ b/src/platforms/eglstream-kms/server/egl_output.cpp @@ -16,6 +16,7 @@ #include "mir/log.h" +#include #include #include "egl_output.h" @@ -26,6 +27,7 @@ #include #include #include +#include #include #include @@ -191,6 +193,8 @@ void mgek::EGLOutput::reset() drmModeFreeProperty(prop); } } + + current_crtc = nullptr; } geom::Size mgek::EGLOutput::size() const @@ -208,21 +212,15 @@ int mgek::EGLOutput::max_refresh_rate() const void mgek::EGLOutput::configure(size_t kms_mode_index) { mode_index = kms_mode_index; - auto const width = connector->modes[kms_mode_index].hdisplay; - auto const height = connector->modes[kms_mode_index].vdisplay; - - std::unique_ptr - request{drmModeAtomicAlloc(), &drmModeAtomicFree}; + refresh_connector(drm_fd, connector); 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; + crtc_id_ = crtc->crtc_id; + plane_id = plane->plane_id; + plane_props = std::make_unique(drm_fd, plane_id, DRM_MODE_OBJECT_PLANE); - uint32_t mode_id{0}; auto ret = drmModeCreatePropertyBlob( drm_fd, &connector->modes[kms_mode_index], @@ -235,50 +233,13 @@ void mgek::EGLOutput::configure(size_t kms_mode_index) std::system_error(-ret, std::system_category(), "Failed to create DRM Mode property blob")); } + auto const width = connector->modes[mode_index].hdisplay; + auto const height = connector->modes[mode_index].vdisplay; CPUAddressableFb dummy{drm_fd, width, height}; - - mgk::ObjectProperties crtc_props{drm_fd, crtc_id, DRM_MODE_OBJECT_CRTC}; - - /* Activate the CRTC and set the mode */ - drmModeAtomicAddProperty(request.get(), crtc_id, crtc_props.id_for("MODE_ID"), mode_id); - drmModeAtomicAddProperty(request.get(), crtc_id, crtc_props.id_for("ACTIVE"), 1); - - /* Set CRTC for the output */ - auto const connector_id = connector->connector_id; - mgk::ObjectProperties connector_props{drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR}; - drmModeAtomicAddProperty(request.get(), connector_id, connector_props.id_for("CRTC_ID"), crtc_id); - - /* Set up the output plane... */ - auto const plane_id = plane->plane_id; - mgk::ObjectProperties plane_props{drm_fd, plane_id, DRM_MODE_OBJECT_PLANE}; - - /* Source viewport. Coordinates are 16.16 fixed point format */ - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("SRC_X"), 0); - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("SRC_Y"), 0); - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("SRC_W"), width << 16); - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("SRC_H"), height << 16); - - /* Destination viewport. Coordinates are *not* 16.16 */ - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_X"), 0); - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_Y"), 0); - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_W"), width); - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_H"), height); - - /* Set a surface for the plane, and connect to the CRTC */ - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("FB_ID"), dummy.id()); - drmModeAtomicAddProperty(request.get(), plane_id, plane_props.id_for("CRTC_ID"), crtc_id); - - /* We don't monitor the DRM events (yet), so have no userdata */ - ret = drmModeAtomicCommit(drm_fd, request.get(), DRM_MODE_ATOMIC_ALLOW_MODESET, nullptr); - - if (ret != 0) - { - BOOST_THROW_EXCEPTION( - std::system_error(-ret, std::system_category(), "Failed to commit atomic KMS configuration change")); - } + atomic_commit(dummy.id(), nullptr, DRM_MODE_ATOMIC_ALLOW_MODESET); EGLAttrib const crtc_filter[] = { - EGL_DRM_CRTC_EXT, static_cast(crtc_id), + EGL_DRM_CRTC_EXT, static_cast(crtc_id_), EGL_NONE}; int found_layers{0}; if (eglGetOutputLayersEXT(display, crtc_filter, &layer, 1, &found_layers) != EGL_TRUE) @@ -286,16 +247,14 @@ void mgek::EGLOutput::configure(size_t kms_mode_index) BOOST_THROW_EXCEPTION(( mg::egl_error( std::string{"Failed to find EGLOutputEXT corresponding to DRM CRTC "} + - std::to_string(crtc_id)))); + std::to_string(crtc_id_)))); } if (found_layers != 1) { BOOST_THROW_EXCEPTION(std::runtime_error{ std::string{"Failed to find EGLOutputEXT corresponding to DRM CRTC "} + - std::to_string(crtc_id)}); + std::to_string(crtc_id_)}); } - - using_saved_crtc = false; } EGLOutputLayerEXT mgek::EGLOutput::output_layer() const @@ -316,6 +275,65 @@ uint32_t mgek::EGLOutput::crtc_id() const return static_cast(crtc_id); } +int mgek::EGLOutput::atomic_commit(uint64_t fb, const void *drm_event_userdata, uint32_t flags) +{ + auto const width = connector->modes[mode_index].hdisplay; + auto const height = connector->modes[mode_index].vdisplay; + + std::unique_ptr + request{drmModeAtomicAlloc(), &drmModeAtomicFree}; + + if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) + { + mgk::ObjectProperties crtc_props{drm_fd, crtc_id_, DRM_MODE_OBJECT_CRTC}; + + /* Activate the CRTC and set the mode */ + drmModeAtomicAddProperty(request.get(), crtc_id_, crtc_props.id_for("MODE_ID"), mode_id); + drmModeAtomicAddProperty(request.get(), crtc_id_, crtc_props.id_for("ACTIVE"), 1); + + /* Set CRTC for the output */ + auto const connector_id = connector->connector_id; + mgk::ObjectProperties connector_props{drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR}; + drmModeAtomicAddProperty(request.get(), connector_id, connector_props.id_for("CRTC_ID"), crtc_id_); + } + + /* Source viewport. Coordinates are 16.16 fixed point format */ + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("SRC_X"), 0); + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("SRC_Y"), 0); + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("SRC_W"), width << 16); + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("SRC_H"), height << 16); + + /* Destination viewport. Coordinates are *not* 16.16 */ + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("CRTC_X"), 0); + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("CRTC_Y"), 0); + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("CRTC_W"), width); + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("CRTC_H"), height); + + /* Set a surface for the plane */ + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("CRTC_ID"), crtc_id_); + drmModeAtomicAddProperty(request.get(), plane_id, plane_props->id_for("FB_ID"), fb); + + auto ret = drmModeAtomicCommit(drm_fd, request.get(), flags, const_cast(drm_event_userdata)); + if (ret != 0) + { + return ret; + } + + using_saved_crtc = false; + return 0; +} + +auto mgek::EGLOutput::queue_atomic_flip(FBHandle const& fb, void const* drm_event_userdata) -> std::optional +{ + uint32_t flags = flags_for_next_flip | DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT; + flags_for_next_flip = 0; + if (auto err = atomic_commit(fb, drm_event_userdata, flags)) + { + return std::error_code{-err, std::system_category()}; + } + return std::nullopt; +} + void mgek::EGLOutput::clear_crtc() { using namespace std::string_literals; @@ -346,6 +364,8 @@ void mgek::EGLOutput::clear_crtc() std::system_category(), "Couldn't clear output "s + mgk::connector_name(connector)})); } + + current_crtc = nullptr; } void mgek::EGLOutput::restore_saved_crtc() @@ -382,3 +402,8 @@ void mgek::EGLOutput::set_power_mode(MirPowerMode mode) BOOST_THROW_EXCEPTION((std::system_error{-ret, std::system_category(), "Failed to set output power mode"})); } } + +void mgek::EGLOutput::set_flags_for_next_flip(uint32_t flags) +{ + flags_for_next_flip = flags; +} \ No newline at end of file diff --git a/src/platforms/eglstream-kms/server/egl_output.h b/src/platforms/eglstream-kms/server/egl_output.h index 605e1f84830..29dca8fec7f 100644 --- a/src/platforms/eglstream-kms/server/egl_output.h +++ b/src/platforms/eglstream-kms/server/egl_output.h @@ -18,6 +18,7 @@ #define MIR_GRAPHICS_EGLSTREAM_KMS_OUTPUT_H_ #include "kms-utils/drm_mode_resources.h" +#include "kms_framebuffer.h" #include "mir/geometry/size.h" #include "mir/geometry/point.h" #include "mir/geometry/displacement.h" @@ -48,12 +49,15 @@ class EGLOutput : public DisplayConfigurationOutput EGLOutputLayerEXT output_layer() const; uint32_t crtc_id() const; + auto queue_atomic_flip(FBHandle const& fb, void const* drm_event_userdata) -> std::optional; void clear_crtc(); void set_power_mode(MirPowerMode mode); + void set_flags_for_next_flip(uint32_t flags); private: void restore_saved_crtc(); + int atomic_commit(uint64_t fb, void const* drm_event_userdata, uint32_t flags); int const drm_fd; @@ -63,11 +67,19 @@ class EGLOutput : public DisplayConfigurationOutput graphics::kms::DRMModeConnectorUPtr connector; + mir::graphics::kms::DRMModeCrtcUPtr current_crtc; size_t mode_index; drmModeCrtc saved_crtc; bool using_saved_crtc; + uint32_t plane_id; + std::unique_ptr plane_props; + uint32_t crtc_id_; + + uint32_t mode_id; + int dpms_enum_id; + uint32_t flags_for_next_flip = 0; }; } diff --git a/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp b/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp index 9123ed603df..7d2c3e0b7d4 100644 --- a/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp +++ b/src/platforms/eglstream-kms/server/eglstream_interface_provider.cpp @@ -16,6 +16,7 @@ #include "eglstream_interface_provider.h" #include "mir/graphics/platform.h" +#include "kms_cpu_addressable_display_provider.h" #include #include @@ -60,14 +61,16 @@ auto DisplayProviderImpl::claim_stream() -> EGLStreamKHR } } -mg::eglstream::InterfaceProvider::InterfaceProvider(EGLDisplay dpy) - : dpy{dpy} +mg::eglstream::InterfaceProvider::InterfaceProvider(EGLDisplay dpy, mir::Fd drm_fd) + : dpy{dpy}, + drm_fd{drm_fd} { } mg::eglstream::InterfaceProvider::InterfaceProvider(InterfaceProvider const& from, EGLStreamKHR with_stream) : dpy{from.dpy}, - stream{with_stream} + stream{with_stream}, + drm_fd{from.drm_fd} { } @@ -78,5 +81,9 @@ auto mg::eglstream::InterfaceProvider::maybe_create_interface(DisplayProvider::T { return std::make_shared(dpy, stream); } + if (dynamic_cast(&tag)) + { + return mg::kms::CPUAddressableDisplayProvider::create_if_supported(drm_fd); + } 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 313fdf09058..8669fd5d268 100644 --- a/src/platforms/eglstream-kms/server/eglstream_interface_provider.h +++ b/src/platforms/eglstream-kms/server/eglstream_interface_provider.h @@ -18,6 +18,7 @@ #define MIR_GRAPHICS_EGLSTREAM_INTERFACE_PROVIDER_H_ #include "mir/graphics/platform.h" +#include "mir/fd.h" #include @@ -26,7 +27,7 @@ namespace mir::graphics::eglstream class InterfaceProvider : public DisplayInterfaceProvider { public: - InterfaceProvider(EGLDisplay dpy); + InterfaceProvider(EGLDisplay dpy, mir::Fd drm_fd); InterfaceProvider(InterfaceProvider const& from, EGLStreamKHR with_stream); protected: @@ -36,6 +37,7 @@ class InterfaceProvider : public DisplayInterfaceProvider private: EGLDisplay dpy; std::optional stream; + mir::Fd drm_fd; }; ; diff --git a/src/platforms/eglstream-kms/server/kms_display_configuration.cpp b/src/platforms/eglstream-kms/server/kms_display_configuration.cpp index dddabf452f4..6b81069d1dc 100644 --- a/src/platforms/eglstream-kms/server/kms_display_configuration.cpp +++ b/src/platforms/eglstream-kms/server/kms_display_configuration.cpp @@ -267,11 +267,11 @@ void mge::KMSDisplayConfiguration::update() } void mge::KMSDisplayConfiguration::for_each_output( - std::function const& f) const + std::function const&)> const& f) const { for (auto const& output : outputs) { - f(*output); + f(output); } } diff --git a/src/platforms/eglstream-kms/server/kms_display_configuration.h b/src/platforms/eglstream-kms/server/kms_display_configuration.h index 50477a5ae6b..6150e0a0bb7 100644 --- a/src/platforms/eglstream-kms/server/kms_display_configuration.h +++ b/src/platforms/eglstream-kms/server/kms_display_configuration.h @@ -45,7 +45,7 @@ class KMSDisplayConfiguration : public DisplayConfiguration size_t get_kms_mode_index(DisplayConfigurationOutputId id, size_t conf_mode_index) const; void update(); - void for_each_output(std::function const& f) const; + void for_each_output(std::function const&)> const& f) const; private: void update_output( diff --git a/src/platforms/eglstream-kms/server/platform.cpp b/src/platforms/eglstream-kms/server/platform.cpp index 777b0561a34..d82f74ce761 100644 --- a/src/platforms/eglstream-kms/server/platform.cpp +++ b/src/platforms/eglstream-kms/server/platform.cpp @@ -245,7 +245,7 @@ mge::DisplayPlatform::DisplayPlatform( std::to_string(std::get<0>(egl_version)) + "." + std::to_string(std::get<1>(egl_version))})); } - provider = std::make_shared(display); + provider = std::make_shared(display, drm_node); } mge::DisplayPlatform::~DisplayPlatform() diff --git a/src/platforms/eglstream-kms/server/threaded_drm_event_handler.cpp b/src/platforms/eglstream-kms/server/threaded_drm_event_handler.cpp index 7b4be5b0108..33db6bff38d 100644 --- a/src/platforms/eglstream-kms/server/threaded_drm_event_handler.cpp +++ b/src/platforms/eglstream-kms/server/threaded_drm_event_handler.cpp @@ -15,23 +15,80 @@ */ #include "threaded_drm_event_handler.h" +#include "mir/log.h" +#include "mir/logging/logger.h" #include #include #include +#include #include #include #include +#include #include namespace mge = mir::graphics::eglstream; + + +class mge::ThreadedDRMEventHandler::Pipe +{ +public: + Pipe() + : Pipe(checked_pipe(O_CLOEXEC)) + { + } + + auto read_fd() const -> int + { + return read; + } + + auto read_from() -> uint32_t + { + uint32_t buffer; + if (::read(read, &buffer, sizeof(buffer)) != sizeof(buffer)) + { + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to read from pipe"})); + } + return buffer; + } + + void write_to(uint32_t data) + { + if (::write(write, &data, sizeof(data)) != sizeof(data)) + { + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to write to pipe"})); + } + } +private: + static auto checked_pipe(int flags) -> std::array + { + std::array pipefds; + if (pipe2(pipefds.data(), flags)) + { + BOOST_THROW_EXCEPTION((std::system_error{errno, std::system_category(), "Failed to create pipe()"})); + } + return pipefds; + } + + Pipe(std::array pipefds) + : read{mir::Fd{pipefds[0]}}, + write{mir::Fd{pipefds[1]}} + { + } + + mir::Fd const read; + mir::Fd const write; +}; + mge::ThreadedDRMEventHandler::ThreadedDRMEventHandler(mir::Fd drm_fd) : drm_fd{std::move(drm_fd)}, + cancel_pipe{std::make_unique()}, dispatch_thread{[this]() { event_loop(); }} { - } mge::ThreadedDRMEventHandler::~ThreadedDRMEventHandler() @@ -97,6 +154,11 @@ std::future mge::ThreadedDRMEventHandler::expect_flip_event( return pending_expectations.back()->completion.get_future(); } +void mge::ThreadedDRMEventHandler::cancel_flip_events(KMSCrtcId id) +{ + cancel_pipe->write_to(id); +} + void mge::ThreadedDRMEventHandler::event_loop() noexcept { drmEventContext ctx; @@ -137,11 +199,16 @@ void mge::ThreadedDRMEventHandler::event_loop() noexcept */ lock.unlock(); - pollfd events; - events.fd = drm_fd; - events.events = POLLIN; + pollfd events[2]; + // DRM events + events[0].fd = drm_fd; + events[0].events = POLLIN; + // Cancel events + events[1].fd = cancel_pipe->read_fd(); + events[1].events = POLLIN; + // TODO: Maybe have a non-infinite timeout here? - auto result = ::poll(&events, 1, -1); + auto result = ::poll(events, 2, -1); // We've got some events; reclaim the lock and process them. lock.lock(); @@ -166,9 +233,24 @@ void mge::ThreadedDRMEventHandler::event_loop() noexcept } } } - else if (events.revents & POLLIN) - { - drmHandleEvent(drm_fd, &ctx); + else + { // DRM event + if (events[0].revents & POLLIN) + { + drmHandleEvent(drm_fd, &ctx); + } // Cancel event + if (events[1].revents & POLLIN) + { + auto crtc_id = cancel_pipe->read_from(); + for (auto& slot :pending_expectations) + { + if (slot && slot->id == crtc_id) + { + slot->completion.set_value(); + slot = {}; + } + } + } } } } diff --git a/src/platforms/eglstream-kms/server/threaded_drm_event_handler.h b/src/platforms/eglstream-kms/server/threaded_drm_event_handler.h index a2dce273e7d..72cc88ea7e3 100644 --- a/src/platforms/eglstream-kms/server/threaded_drm_event_handler.h +++ b/src/platforms/eglstream-kms/server/threaded_drm_event_handler.h @@ -45,6 +45,7 @@ class ThreadedDRMEventHandler : public DRMEventHandler KMSCrtcId id, std::function on_flip) override; + void cancel_flip_events(KMSCrtcId id) override; private: void event_loop() noexcept; @@ -57,6 +58,8 @@ class ThreadedDRMEventHandler : public DRMEventHandler void* data) noexcept; mir::Fd const drm_fd; + class Pipe; + std::unique_ptr const cancel_pipe; struct FlipEventData { diff --git a/src/platforms/gbm-kms/server/kms/CMakeLists.txt b/src/platforms/gbm-kms/server/kms/CMakeLists.txt index fac0b24655f..e066a003aa2 100644 --- a/src/platforms/gbm-kms/server/kms/CMakeLists.txt +++ b/src/platforms/gbm-kms/server/kms/CMakeLists.txt @@ -37,9 +37,6 @@ add_library( egl_helper.cpp quirks.cpp quirks.h - kms_framebuffer.h - cpu_addressable_fb.cpp - cpu_addressable_fb.h ) target_link_libraries( diff --git a/src/platforms/gbm-kms/server/kms/display.cpp b/src/platforms/gbm-kms/server/kms/display.cpp index e250fb1b522..12a445c3cf4 100644 --- a/src/platforms/gbm-kms/server/kms/display.cpp +++ b/src/platforms/gbm-kms/server/kms/display.cpp @@ -23,12 +23,11 @@ #include "kms_display_configuration.h" #include "kms_output.h" #include "kms_page_flipper.h" -#include "kms_framebuffer.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" +#include "kms_framebuffer.h" #include "mir/graphics/display_report.h" #include "mir/graphics/display_configuration_policy.h" #include "mir/graphics/transformation.h" @@ -445,42 +444,6 @@ void mgg::Display::configure_locked( 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)) - { - BOOST_THROW_EXCEPTION(( - std::system_error{ - errno, - std::system_category(), - "Failed to query DRM capabilities"})); - } - return value; -} -} - -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::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, DRMFormat format) -> std::unique_ptr -{ - return std::make_unique(drm_fd, supports_modifiers, format, size); -} - namespace { auto gbm_create_device_checked(mir::Fd fd) -> std::shared_ptr @@ -591,7 +554,7 @@ namespace { using LockedFrontBuffer = std::unique_ptr>; -class GBMBoFramebuffer : public mgg::FBHandle +class GBMBoFramebuffer : public mg::FBHandle { public: static auto framebuffer_for_frontbuffer(mir::Fd const& drm_fd, LockedFrontBuffer bo) -> std::unique_ptr diff --git a/src/platforms/gbm-kms/server/kms/display.h b/src/platforms/gbm-kms/server/kms/display.h index be6cf74ac86..d1d6f184e09 100644 --- a/src/platforms/gbm-kms/server/kms/display.h +++ b/src/platforms/gbm-kms/server/kms/display.h @@ -104,22 +104,6 @@ class Display : public graphics::Display std::weak_ptr cursor; }; -class CPUAddressableDisplayProvider : public graphics::CPUAddressableDisplayProvider -{ -public: - explicit CPUAddressableDisplayProvider(mir::Fd drm_fd); - - auto supported_formats() const - -> std::vector override; - - auto alloc_fb(geometry::Size pixel_size, DRMFormat format) - -> std::unique_ptr override; - -private: - mir::Fd const drm_fd; - bool const supports_modifiers; -}; - class GBMDisplayProvider : public graphics::GBMDisplayProvider { public: diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.cpp b/src/platforms/gbm-kms/server/kms/display_buffer.cpp index 130ae5f523f..17b5f4af791 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.cpp +++ b/src/platforms/gbm-kms/server/kms/display_buffer.cpp @@ -76,7 +76,7 @@ mgg::DisplayBuffer::DisplayBuffer( { 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( + auto initial_fb = std::make_shared( std::move(drm_fd), false, DRMFormat{DRM_FORMAT_XRGB8888}, @@ -132,7 +132,7 @@ bool mgg::DisplayBuffer::overlay(std::vector const& renderable_l return false; } - if (auto fb = std::dynamic_pointer_cast(renderable_list[0].buffer)) + if (auto fb = std::dynamic_pointer_cast(renderable_list[0].buffer)) { next_swap = std::move(fb); return true; diff --git a/src/platforms/gbm-kms/server/kms/display_buffer.h b/src/platforms/gbm-kms/server/kms/display_buffer.h index f30523dd728..e368966f490 100644 --- a/src/platforms/gbm-kms/server/kms/display_buffer.h +++ b/src/platforms/gbm-kms/server/kms/display_buffer.h @@ -40,7 +40,6 @@ namespace gbm { class Platform; -class FBHandle; class KMSOutput; class NativeBuffer; diff --git a/src/platforms/gbm-kms/server/kms/kms_output.h b/src/platforms/gbm-kms/server/kms/kms_output.h index f68a59c6b6a..37495b99cdf 100644 --- a/src/platforms/gbm-kms/server/kms/kms_output.h +++ b/src/platforms/gbm-kms/server/kms/kms_output.h @@ -33,12 +33,11 @@ namespace mir namespace graphics { class DisplayConfigurationOutput; +class FBHandle; namespace gbm { -class FBHandle; - class KMSOutput { public: diff --git a/src/platforms/gbm-kms/server/kms/platform.cpp b/src/platforms/gbm-kms/server/kms/platform.cpp index 940f587e71f..41b16d5c4a9 100644 --- a/src/platforms/gbm-kms/server/kms/platform.cpp +++ b/src/platforms/gbm-kms/server/kms/platform.cpp @@ -30,6 +30,7 @@ #include "one_shot_device_observer.h" #include "mir/graphics/linux_dmabuf.h" #include "mir/graphics/egl_context_executor.h" +#include "kms_cpu_addressable_display_provider.h" #include "surfaceless_egl_context.h" #include #include @@ -383,7 +384,7 @@ class mgg::Platform::KMSDisplayInterfaceProvider : public mg::DisplayInterfacePr } if (dynamic_cast(&type_tag)) { - return std::make_shared(drm_fd); + return mg::kms::CPUAddressableDisplayProvider::create_if_supported(drm_fd); } return {}; } 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 d457d465b36..16e37cc8fa5 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 @@ -34,20 +34,20 @@ struct MockKMSOutput : public graphics::gbm::KMSOutput MOCK_CONST_METHOD0(size, geometry::Size()); MOCK_CONST_METHOD0(max_refresh_rate, int()); - bool set_crtc(graphics::gbm::FBHandle const& fb) override + bool set_crtc(graphics::FBHandle const& fb) override { return set_crtc_thunk(&fb); } MOCK_METHOD0(has_crtc_mismatch, bool()); - MOCK_METHOD1(set_crtc_thunk, bool(graphics::gbm::FBHandle const*)); + MOCK_METHOD1(set_crtc_thunk, bool(graphics::FBHandle const*)); MOCK_METHOD0(clear_crtc, void()); - bool schedule_page_flip(graphics::gbm::FBHandle const& fb) override + bool schedule_page_flip(graphics::FBHandle const& fb) override { return schedule_page_flip_thunk(&fb); } - MOCK_METHOD1(schedule_page_flip_thunk, bool(graphics::gbm::FBHandle const*)); + MOCK_METHOD1(schedule_page_flip_thunk, bool(graphics::FBHandle const*)); MOCK_METHOD0(wait_for_page_flip, void()); MOCK_CONST_METHOD0(last_frame, graphics::Frame()); @@ -63,8 +63,8 @@ struct MockKMSOutput : public graphics::gbm::KMSOutput MOCK_METHOD0(refresh_hardware_state, void()); MOCK_CONST_METHOD1(update_from_hardware_state, void(graphics::DisplayConfigurationOutput&)); - MOCK_CONST_METHOD1(fb_for, std::shared_ptr(gbm_bo*)); - MOCK_CONST_METHOD1(fb_for, std::shared_ptr(graphics::DMABufBuffer const&)); + MOCK_CONST_METHOD1(fb_for, std::shared_ptr(gbm_bo*)); + MOCK_CONST_METHOD1(fb_for, std::shared_ptr(graphics::DMABufBuffer const&)); MOCK_CONST_METHOD1(buffer_requires_migration, bool(gbm_bo*)); MOCK_CONST_METHOD0(drm_fd, int()); }; 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 111a1dcc7fa..586272e269c 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 @@ -345,6 +345,9 @@ TEST_F(MesaDisplayMultiMonitorTest, flip_flips_all_connected_crtcs) EXPECT_CALL(mock_drm, drmModeAddFB2(mtd::IsFdOfDevice(drm_device), _, _, _, _, _, _, _, _)) .WillRepeatedly(DoAll(SetArgPointee<7>(fb_id), Return(0))); + EXPECT_CALL(mock_drm, drmModeAddFB2WithModifiers(mtd::IsFdOfDevice(drm_device), + _, _, _, _, _, _, _, _, _)) + .WillRepeatedly(DoAll(SetArgPointee<8>(fb_id), Return(0))); /* All crtcs are flipped */ for (int i = 0; i < num_connected_outputs; i++) 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 fe7583f8c69..01d3c03f9b1 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,7 +14,7 @@ * along with this program. If not, see . */ -#include "kms/kms_framebuffer.h" +#include "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" @@ -55,7 +55,7 @@ class MockPageFlipper : public mgg::PageFlipper MOCK_METHOD1(wait_for_flip, mg::Frame(uint32_t)); }; -class MockKMSFramebuffer : public mgg::FBHandle +class MockKMSFramebuffer : public mg::FBHandle { public: MockKMSFramebuffer(uint32_t fb_id)