1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/ozone/platform/drm/gpu/drm_window.h"
6 
7 #include <drm_fourcc.h>
8 #include <stdint.h>
9 #include <xf86drm.h>
10 #include <memory>
11 #include <utility>
12 #include <vector>
13 
14 #include "base/bind.h"
15 #include "base/files/platform_file.h"
16 #include "base/macros.h"
17 #include "base/test/task_environment.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/skia/include/core/SkCanvas.h"
20 #include "third_party/skia/include/core/SkColor.h"
21 #include "third_party/skia/include/core/SkImageInfo.h"
22 #include "third_party/skia/include/core/SkSurface.h"
23 #include "ui/gfx/gpu_fence.h"
24 #include "ui/gfx/linux/gbm_buffer.h"
25 #include "ui/gfx/linux/test/mock_gbm_device.h"
26 #include "ui/gfx/presentation_feedback.h"
27 #include "ui/ozone/platform/drm/gpu/drm_device_generator.h"
28 #include "ui/ozone/platform/drm/gpu/drm_device_manager.h"
29 #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h"
30 #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h"
31 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
32 #include "ui/ozone/platform/drm/gpu/screen_manager.h"
33 #include "ui/ozone/public/surface_ozone_canvas.h"
34 
35 namespace {
36 
37 // Mode of size 6x4.
38 const drmModeModeInfo kDefaultMode = {0, 6, 0, 0, 0, 0, 4,     0,
39                                       0, 0, 0, 0, 0, 0, {'\0'}};
40 
41 const gfx::AcceleratedWidget kDefaultWidgetHandle = 1;
42 const uint32_t kDefaultCrtc = 1;
43 const uint32_t kDefaultConnector = 2;
44 const int kDefaultCursorSize = 64;
45 
GetCursorBuffers(const scoped_refptr<ui::MockDrmDevice> drm)46 std::vector<sk_sp<SkSurface>> GetCursorBuffers(
47     const scoped_refptr<ui::MockDrmDevice> drm) {
48   std::vector<sk_sp<SkSurface>> cursor_buffers;
49   for (const auto& cursor_buffer : drm->buffers()) {
50     if (cursor_buffer && cursor_buffer->width() == kDefaultCursorSize &&
51         cursor_buffer->height() == kDefaultCursorSize) {
52       cursor_buffers.push_back(cursor_buffer);
53     }
54   }
55 
56   return cursor_buffers;
57 }
58 
AllocateBitmap(const gfx::Size & size)59 SkBitmap AllocateBitmap(const gfx::Size& size) {
60   SkBitmap image;
61   SkImageInfo info = SkImageInfo::Make(size.width(), size.height(),
62                                        kN32_SkColorType, kPremul_SkAlphaType);
63   image.allocPixels(info);
64   image.eraseColor(SK_ColorWHITE);
65   return image;
66 }
67 
68 }  // namespace
69 
70 class DrmWindowTest : public testing::Test {
71  public:
72   DrmWindowTest() = default;
73 
74   void SetUp() override;
75   void TearDown() override;
76 
OnSubmission(gfx::SwapResult result,std::unique_ptr<gfx::GpuFence> out_fence)77   void OnSubmission(gfx::SwapResult result,
78                     std::unique_ptr<gfx::GpuFence> out_fence) {
79     last_swap_buffers_result_ = result;
80   }
81 
OnPresentation(const gfx::PresentationFeedback & feedback)82   void OnPresentation(const gfx::PresentationFeedback& feedback) {
83     on_swap_buffers_count_++;
84     last_presentation_feedback_ = feedback;
85   }
86 
87  protected:
88   void InitializeDrmState(ui::MockDrmDevice* drm, bool is_atomic = true);
89 
90   base::test::SingleThreadTaskEnvironment task_environment_{
91       base::test::SingleThreadTaskEnvironment::MainThreadType::UI};
92   scoped_refptr<ui::MockDrmDevice> drm_;
93   std::unique_ptr<ui::ScreenManager> screen_manager_;
94   std::unique_ptr<ui::DrmDeviceManager> drm_device_manager_;
95 
96   int on_swap_buffers_count_;
97   gfx::SwapResult last_swap_buffers_result_;
98   gfx::PresentationFeedback last_presentation_feedback_;
99 
100  private:
101   struct PlaneState {
102     std::vector<uint32_t> formats;
103   };
104 
105   struct CrtcState {
106     std::vector<PlaneState> planes;
107   };
108 
109   DISALLOW_COPY_AND_ASSIGN(DrmWindowTest);
110 };
111 
SetUp()112 void DrmWindowTest::SetUp() {
113   on_swap_buffers_count_ = 0;
114   last_swap_buffers_result_ = gfx::SwapResult::SWAP_FAILED;
115 
116   auto gbm_device = std::make_unique<ui::MockGbmDevice>();
117   drm_ = new ui::MockDrmDevice(std::move(gbm_device));
118   screen_manager_ = std::make_unique<ui::ScreenManager>();
119 
120   InitializeDrmState(drm_.get());
121 
122   screen_manager_->AddDisplayController(drm_, kDefaultCrtc, kDefaultConnector);
123   std::vector<ui::ScreenManager::ControllerConfigParams> controllers_to_enable;
124   controllers_to_enable.emplace_back(
125       1 /*display_id*/, drm_, kDefaultCrtc, kDefaultConnector, gfx::Point(),
126       std::make_unique<drmModeModeInfo>(kDefaultMode));
127   screen_manager_->ConfigureDisplayControllers(controllers_to_enable);
128 
129   drm_device_manager_ = std::make_unique<ui::DrmDeviceManager>(nullptr);
130 
131   std::unique_ptr<ui::DrmWindow> window(new ui::DrmWindow(
132       kDefaultWidgetHandle, drm_device_manager_.get(), screen_manager_.get()));
133   window->Initialize();
134   window->SetBounds(
135       gfx::Rect(gfx::Size(kDefaultMode.hdisplay, kDefaultMode.vdisplay)));
136   screen_manager_->AddWindow(kDefaultWidgetHandle, std::move(window));
137 }
138 
TearDown()139 void DrmWindowTest::TearDown() {
140   std::unique_ptr<ui::DrmWindow> window =
141       screen_manager_->RemoveWindow(kDefaultWidgetHandle);
142   window->Shutdown();
143 }
144 
InitializeDrmState(ui::MockDrmDevice * drm,bool is_atomic)145 void DrmWindowTest::InitializeDrmState(ui::MockDrmDevice* drm, bool is_atomic) {
146   // A Sample of CRTC states.
147   std::vector<CrtcState> crtc_states = {
148       {
149           /* .planes = */
150           {{/* .formats = */ {DRM_FORMAT_XRGB8888}}},
151       },
152       {
153           /* .planes = */
154           {{/* .formats = */ {DRM_FORMAT_XRGB8888}}},
155       },
156   };
157 
158   constexpr uint32_t kPlaneIdBase = 300;
159   constexpr uint32_t kInFormatsBlobPropIdBase = 400;
160 
161   std::vector<ui::MockDrmDevice::CrtcProperties> crtc_properties(
162       crtc_states.size());
163   std::map<uint32_t, std::string> crtc_property_names = {
164       {1000, "ACTIVE"},
165       {1001, "MODE_ID"},
166   };
167 
168   std::vector<ui::MockDrmDevice::ConnectorProperties> connector_properties(3);
169   std::map<uint32_t, std::string> connector_property_names = {
170       {2000, "CRTC_ID"},
171   };
172   for (size_t i = 0; i < connector_properties.size(); ++i) {
173     connector_properties[i].id = kDefaultConnector + i;
174     for (const auto& pair : connector_property_names) {
175       connector_properties[i].properties.push_back(
176           {/* .id = */ pair.first, /* .value = */ 0});
177     }
178   }
179 
180   std::vector<ui::MockDrmDevice::PlaneProperties> plane_properties;
181   std::map<uint32_t, std::string> plane_property_names = {
182       // Add all required properties.
183       {3000, "CRTC_ID"},
184       {3001, "CRTC_X"},
185       {3002, "CRTC_Y"},
186       {3003, "CRTC_W"},
187       {3004, "CRTC_H"},
188       {3005, "FB_ID"},
189       {3006, "SRC_X"},
190       {3007, "SRC_Y"},
191       {3008, "SRC_W"},
192       {3009, "SRC_H"},
193       // Defines some optional properties we use for convenience.
194       {3010, "type"},
195       {3011, "IN_FORMATS"},
196   };
197 
198   uint32_t plane_id = kPlaneIdBase;
199   uint32_t property_id = kInFormatsBlobPropIdBase;
200 
201   for (size_t crtc_idx = 0; crtc_idx < crtc_states.size(); ++crtc_idx) {
202     crtc_properties[crtc_idx].id = kDefaultCrtc + crtc_idx;
203     for (const auto& pair : crtc_property_names) {
204       crtc_properties[crtc_idx].properties.push_back(
205           {/* .id = */ pair.first, /* .value = */ 0});
206     }
207 
208     std::vector<ui::MockDrmDevice::PlaneProperties> crtc_plane_properties(
209         crtc_states[crtc_idx].planes.size());
210     for (size_t plane_idx = 0; plane_idx < crtc_states[crtc_idx].planes.size();
211          ++plane_idx) {
212       crtc_plane_properties[plane_idx].id = plane_id++;
213       crtc_plane_properties[plane_idx].crtc_mask = 1 << crtc_idx;
214 
215       for (const auto& pair : plane_property_names) {
216         uint64_t value = 0;
217         if (pair.first == 3010) {
218           value =
219               plane_idx == 0 ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
220         } else if (pair.first == 3011) {
221           value = property_id++;
222           drm->SetPropertyBlob(ui::MockDrmDevice::AllocateInFormatsBlob(
223               value, crtc_states[crtc_idx].planes[plane_idx].formats,
224               std::vector<drm_format_modifier>()));
225         } else if (pair.first >= 3001 && pair.first <= 3009) {
226           value = 27;
227         }
228 
229         crtc_plane_properties[plane_idx].properties.push_back(
230             {/* .id = */ pair.first, /* .value = */ value});
231       }
232     }
233 
234     plane_properties.insert(plane_properties.end(),
235                             crtc_plane_properties.begin(),
236                             crtc_plane_properties.end());
237   }
238 
239   std::map<uint32_t, std::string> property_names;
240   property_names.insert(crtc_property_names.begin(), crtc_property_names.end());
241   property_names.insert(connector_property_names.begin(),
242                         connector_property_names.end());
243   property_names.insert(plane_property_names.begin(),
244                         plane_property_names.end());
245   drm->InitializeState(crtc_properties, connector_properties, plane_properties,
246                        property_names, /*is_atomic=*/false);
247 }
248 
TEST_F(DrmWindowTest,SetCursorImage)249 TEST_F(DrmWindowTest, SetCursorImage) {
250   const gfx::Size cursor_size(6, 4);
251   screen_manager_->GetWindow(kDefaultWidgetHandle)
252       ->SetCursor(std::vector<SkBitmap>(1, AllocateBitmap(cursor_size)),
253                   gfx::Point(4, 2), 0);
254 
255   SkBitmap cursor;
256   std::vector<sk_sp<SkSurface>> cursor_buffers = GetCursorBuffers(drm_);
257   EXPECT_EQ(2u, cursor_buffers.size());
258 
259   // Buffers 1 is the cursor backbuffer we just drew in.
260   cursor.allocPixels(cursor_buffers[1]->getCanvas()->imageInfo());
261   EXPECT_TRUE(cursor_buffers[1]->getCanvas()->readPixels(cursor, 0, 0));
262 
263   // Check that the frontbuffer is displaying the right image as set above.
264   for (int i = 0; i < cursor.height(); ++i) {
265     for (int j = 0; j < cursor.width(); ++j) {
266       if (j < cursor_size.width() && i < cursor_size.height())
267         EXPECT_EQ(SK_ColorWHITE, cursor.getColor(j, i));
268       else
269         EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
270                   cursor.getColor(j, i));
271     }
272   }
273 }
274 
TEST_F(DrmWindowTest,CheckCursorSurfaceAfterChangingDevice)275 TEST_F(DrmWindowTest, CheckCursorSurfaceAfterChangingDevice) {
276   const gfx::Size cursor_size(6, 4);
277   screen_manager_->GetWindow(kDefaultWidgetHandle)
278       ->SetCursor(std::vector<SkBitmap>(1, AllocateBitmap(cursor_size)),
279                   gfx::Point(4, 2), 0);
280 
281   // Add another device.
282   auto gbm_device = std::make_unique<ui::MockGbmDevice>();
283   scoped_refptr<ui::MockDrmDevice> drm =
284       new ui::MockDrmDevice(std::move(gbm_device));
285   InitializeDrmState(drm.get());
286 
287   screen_manager_->AddDisplayController(drm, kDefaultCrtc, kDefaultConnector);
288 
289   std::vector<ui::ScreenManager::ControllerConfigParams> controllers_to_enable;
290   controllers_to_enable.emplace_back(
291       2 /*display_id*/, drm, kDefaultCrtc, kDefaultConnector,
292       gfx::Point(0, kDefaultMode.vdisplay),
293       std::make_unique<drmModeModeInfo>(kDefaultMode));
294   screen_manager_->ConfigureDisplayControllers(controllers_to_enable);
295 
296   // Move window to the display on the new device.
297   screen_manager_->GetWindow(kDefaultWidgetHandle)
298       ->SetBounds(gfx::Rect(0, kDefaultMode.vdisplay, kDefaultMode.hdisplay,
299                             kDefaultMode.vdisplay));
300 
301   EXPECT_EQ(2u, GetCursorBuffers(drm).size());
302   // Make sure the cursor is showing on the new display.
303   EXPECT_NE(0u, drm->get_cursor_handle_for_crtc(kDefaultCrtc));
304 }
305 
TEST_F(DrmWindowTest,CheckDeathOnFailedSwap)306 TEST_F(DrmWindowTest, CheckDeathOnFailedSwap) {
307   const gfx::Size window_size(6, 4);
308   ui::DrmWindow* window = screen_manager_->GetWindow(kDefaultWidgetHandle);
309 
310   std::unique_ptr<ui::GbmBuffer> buffer = drm_->gbm_device()->CreateBuffer(
311       DRM_FORMAT_XRGB8888, window_size, GBM_BO_USE_SCANOUT);
312   ASSERT_TRUE(buffer);
313   scoped_refptr<ui::DrmFramebuffer> framebuffer =
314       ui::DrmFramebuffer::AddFramebuffer(drm_, buffer.get(), window_size);
315   ui::DrmOverlayPlane plane(framebuffer, nullptr);
316 
317   drm_->set_page_flip_expectation(false);
318 
319   ui::DrmOverlayPlaneList planes;
320   planes.push_back(plane.Clone());
321 
322   // Window was re-sized, so the expectation is to re-create the buffers first.
323   window->SchedulePageFlip(
324       ui::DrmOverlayPlane::Clone(planes),
325       base::BindOnce(&DrmWindowTest::OnSubmission, base::Unretained(this)),
326       base::BindOnce(&DrmWindowTest::OnPresentation, base::Unretained(this)));
327   EXPECT_EQ(1, on_swap_buffers_count_);
328   EXPECT_EQ(gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS,
329             last_swap_buffers_result_);
330   EXPECT_EQ(static_cast<uint32_t>(gfx::PresentationFeedback::Flags::kFailure),
331             last_presentation_feedback_.flags);
332 
333   EXPECT_DEATH_IF_SUPPORTED(
334       window->SchedulePageFlip(
335           ui::DrmOverlayPlane::Clone(planes),
336           base::BindOnce(&DrmWindowTest::OnSubmission, base::Unretained(this)),
337           base::BindOnce(&DrmWindowTest::OnPresentation,
338                          base::Unretained(this))),
339       "SchedulePageFlip failed");
340 }
341