1 // Copyright 2017 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 <drm_fourcc.h>
6 #include <fcntl.h>
7 #include <gbm.h>
8 #include <sys/mman.h>
9 
10 #include "base/at_exit.h"
11 #include "base/command_line.h"
12 #include "base/message_loop/message_pump_type.h"
13 #include "base/task/single_thread_task_executor.h"
14 #include "components/exo/wayland/clients/client_base.h"
15 #include "components/exo/wayland/clients/client_helper.h"
16 
17 namespace exo {
18 namespace wayland {
19 namespace clients {
20 namespace {
21 
FrameCallback(void * data,wl_callback * callback,uint32_t time)22 void FrameCallback(void* data, wl_callback* callback, uint32_t time) {
23   bool* callback_pending = static_cast<bool*>(data);
24   *callback_pending = false;
25 }
26 
27 }  // namespace
28 
29 class YuvClient : public ClientBase {
30  public:
YuvClient()31   YuvClient() {}
32 
33   bool WriteSolidColor(gbm_bo* bo, SkColor color);
34 
35   void Run(const ClientBase::InitParams& params);
36 };
37 
WriteSolidColor(gbm_bo * bo,SkColor color)38 bool YuvClient::WriteSolidColor(gbm_bo* bo, SkColor color) {
39   for (size_t i = 0; i < static_cast<size_t>(gbm_bo_get_plane_count(bo)); ++i) {
40     base::ScopedFD fd(gbm_bo_get_plane_fd(bo, i));
41     uint32_t stride = gbm_bo_get_stride_for_plane(bo, i);
42     uint32_t offset = gbm_bo_get_offset(bo, i);
43     uint32_t map_size = gbm_bo_get_plane_size(bo, i) + offset;
44     void* void_data = mmap(nullptr, map_size, (PROT_READ | PROT_WRITE),
45                            MAP_SHARED, fd.get(), 0);
46     if (void_data == MAP_FAILED) {
47       LOG(ERROR) << "Failed mmap().";
48       return false;
49     }
50     uint8_t* data = static_cast<uint8_t*>(void_data) + offset;
51     uint8_t yuv[] = {
52         (0.257 * SkColorGetR(color)) + (0.504 * SkColorGetG(color)) +
53             (0.098 * SkColorGetB(color)) + 16,
54         -(0.148 * SkColorGetR(color)) - (0.291 * SkColorGetG(color)) +
55             (0.439 * SkColorGetB(color)) + 128,
56         (0.439 * SkColorGetR(color)) - (0.368 * SkColorGetG(color)) -
57             (0.071 * SkColorGetB(color)) + 128};
58     if (i == 0) {
59       for (int y = 0; y < size_.height(); ++y) {
60         for (int x = 0; x < size_.width(); ++x) {
61           data[stride * y + x] = yuv[0];
62         }
63       }
64     } else {
65       for (int y = 0; y < size_.height() / 2; ++y) {
66         for (int x = 0; x < size_.width() / 2; ++x) {
67           data[stride * y + x * 2] = yuv[1];
68           data[stride * y + x * 2 + 1] = yuv[2];
69         }
70       }
71     }
72     int ret = munmap(void_data, map_size);
73     if (ret) {
74       LOG(ERROR) << "Failed munmap().";
75       return false;
76     }
77   }
78   return true;
79 }
80 
Run(const ClientBase::InitParams & params)81 void YuvClient::Run(const ClientBase::InitParams& params) {
82   if (!ClientBase::Init(params))
83     return;
84   bool callback_pending = false;
85   std::unique_ptr<wl_callback> frame_callback;
86   wl_callback_listener frame_listener = {FrameCallback};
87 
88   size_t frame_number = 0;
89   do {
90     if (callback_pending)
91       continue;
92     frame_number++;
93 
94     Buffer* buffer = DequeueBuffer();
95     if (!buffer) {
96       LOG(ERROR) << "Can't find free buffer";
97       return;
98     }
99     const SkColor kColors[] = {SK_ColorBLUE,   SK_ColorGREEN, SK_ColorRED,
100                                SK_ColorYELLOW, SK_ColorCYAN,  SK_ColorMAGENTA};
101     if (!WriteSolidColor(buffer->bo.get(),
102                          kColors[frame_number % buffers_.size()]))
103       return;
104 
105     wl_surface_set_buffer_scale(surface_.get(), scale_);
106     wl_surface_set_buffer_transform(surface_.get(), transform_);
107     wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
108                       surface_size_.height());
109     wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0);
110 
111     frame_callback.reset(wl_surface_frame(surface_.get()));
112     wl_callback_add_listener(frame_callback.get(), &frame_listener,
113                              &callback_pending);
114     callback_pending = true;
115     wl_surface_commit(surface_.get());
116     wl_display_flush(display_.get());
117   } while (wl_display_dispatch(display_.get()) != -1);
118 }
119 
120 }  // namespace clients
121 }  // namespace wayland
122 }  // namespace exo
123 
main(int argc,char * argv[])124 int main(int argc, char* argv[]) {
125   base::AtExitManager exit_manager;
126   base::CommandLine::Init(argc, argv);
127   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
128 
129   exo::wayland::clients::ClientBase::InitParams params;
130   params.use_drm = true;
131   params.num_buffers = 8;  // Allow up to 8 buffers by default.
132   if (!params.FromCommandLine(*command_line))
133     return 1;
134 
135   if (!params.use_drm) {
136     LOG(ERROR) << "Missing --use-drm parameter which is required for buffer "
137                   "allocation";
138     return 1;
139   }
140 
141   // TODO(dcastagna): Support other YUV formats.
142   params.drm_format = DRM_FORMAT_NV12;
143   params.bo_usage =
144       GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR | GBM_BO_USE_TEXTURING;
145 
146   base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
147   exo::wayland::clients::YuvClient client;
148   client.Run(params);
149   return 0;
150 }
151