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