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 "base/at_exit.h"
6 #include "base/command_line.h"
7 #include "base/message_loop/message_pump_type.h"
8 #include "base/stl_util.h"
9 #include "base/task/single_thread_task_executor.h"
10 #include "components/exo/wayland/clients/client_base.h"
11 #include "components/exo/wayland/clients/client_helper.h"
12 #include "gpu/vulkan/vulkan_function_pointers.h"
13 #include "third_party/skia/include/core/SkCanvas.h"
14 #include "third_party/skia/include/core/SkSurface.h"
15 #include "third_party/skia/include/gpu/GrContext.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 VulkanClient : ClientBase {
30  public:
VulkanClient()31   VulkanClient() {}
32 
33   void Run(const ClientBase::InitParams& params);
34 
35  private:
36   friend class ScopedVulkanRenderFrame;
37 
38   DISALLOW_COPY_AND_ASSIGN(VulkanClient);
39 };
40 
41 // ScopedVulkanRenderFrame class helps setting up all the state needed to begin
42 // a new frame using vulkan, it creates a command buffer and starts a render
43 // pass. When destroyed it takes care of submitting to the queue and to wait for
44 // the work to be done.
45 class ScopedVulkanRenderFrame {
46  public:
ScopedVulkanRenderFrame(VulkanClient * client,VkFramebuffer framebuffer,SkColor clear_color)47   ScopedVulkanRenderFrame(VulkanClient* client,
48                           VkFramebuffer framebuffer,
49                           SkColor clear_color)
50       : client_(client) {
51     static const VkCommandBufferBeginInfo vk_command_buffer_begin_info{
52         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
53         .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
54     };
55 
56     VkCommandBufferAllocateInfo command_buffer_allocate_info{
57         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
58         .commandPool = client->vk_command_pool_->get(),
59         .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
60         .commandBufferCount = 1,
61     };
62 
63     VkResult result = vkAllocateCommandBuffers(client_->vk_device_->get(),
64                                                &command_buffer_allocate_info,
65                                                &command_buffer_);
66     CHECK_EQ(VK_SUCCESS, result) << "Failed to create a Vulkan command buffer.";
67 
68     result =
69         vkBeginCommandBuffer(command_buffer_, &vk_command_buffer_begin_info);
70     CHECK_EQ(VK_SUCCESS, result);
71 
72     SkColor4f sk_color = SkColor4f::FromColor(clear_color);
73     VkClearValue clear_value = {
74         .color =
75             {
76                 .float32 =
77                     {
78                         sk_color.fR, sk_color.fG, sk_color.fB, sk_color.fA,
79                     },
80             },
81     };
82     VkRenderPassBeginInfo render_pass_begin_info{
83         .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
84         .renderPass = client_->vk_render_pass_->get(),
85         .framebuffer = framebuffer,
86         .renderArea =
87             (VkRect2D){
88                 .offset = {0, 0},
89                 .extent = {client_->surface_size_.width(),
90                            client_->surface_size_.height()},
91             },
92         .clearValueCount = 1,
93         .pClearValues = &clear_value,
94     };
95     vkCmdBeginRenderPass(command_buffer_, &render_pass_begin_info,
96                          VK_SUBPASS_CONTENTS_INLINE);
97   }
~ScopedVulkanRenderFrame()98   ~ScopedVulkanRenderFrame() {
99     vkCmdEndRenderPass(command_buffer_);
100 
101     VkResult result = vkEndCommandBuffer(command_buffer_);
102     CHECK_EQ(VK_SUCCESS, result);
103     VkSubmitInfo submit_info{.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
104                              .commandBufferCount = 1,
105                              .pCommandBuffers = &command_buffer_};
106     result = vkQueueSubmit(client_->vk_queue_, 1, &submit_info, VK_NULL_HANDLE);
107     CHECK_EQ(VK_SUCCESS, result);
108 
109     result = vkQueueWaitIdle(client_->vk_queue_);
110 
111     vkFreeCommandBuffers(client_->vk_device_->get(),
112                          client_->vk_command_pool_->get(), 1, &command_buffer_);
113   }
114 
115  private:
116   VulkanClient* const client_;
117   VkCommandBuffer command_buffer_ = VK_NULL_HANDLE;
118 
119   DISALLOW_COPY_AND_ASSIGN(ScopedVulkanRenderFrame);
120 };
121 
Run(const ClientBase::InitParams & params)122 void VulkanClient::Run(const ClientBase::InitParams& params) {
123   if (!ClientBase::Init(params))
124     return;
125 
126   bool callback_pending = false;
127   std::unique_ptr<wl_callback> frame_callback;
128   wl_callback_listener frame_listener = {FrameCallback};
129 
130   size_t frame_count = 0;
131   do {
132     if (callback_pending)
133       continue;
134 
135     Buffer* buffer = DequeueBuffer();
136     if (!buffer)
137       continue;
138 
139     {
140       static const SkColor kColors[] = {SK_ColorRED, SK_ColorBLACK,
141                                         SK_ColorGREEN};
142 
143       ScopedVulkanRenderFrame vulkan_frame(
144           this, buffer->vk_framebuffer->get(),
145           kColors[++frame_count % base::size(kColors)]);
146 
147       // This is where the drawing code would go.
148       // This client is not drawing anything. Just clearing the fb.
149     }
150     ++frame_count;
151 
152     wl_surface_set_buffer_scale(surface_.get(), scale_);
153     wl_surface_set_buffer_transform(surface_.get(), transform_);
154     wl_surface_damage(surface_.get(), 0, 0, surface_size_.width(),
155                       surface_size_.height());
156     wl_surface_attach(surface_.get(), buffer->buffer.get(), 0, 0);
157 
158     frame_callback.reset(wl_surface_frame(surface_.get()));
159     wl_callback_add_listener(frame_callback.get(), &frame_listener,
160                              &callback_pending);
161     wl_surface_commit(surface_.get());
162     wl_display_flush(display_.get());
163   } while (wl_display_dispatch(display_.get()) != -1);
164 }
165 
166 }  // namespace clients
167 }  // namespace wayland
168 }  // namespace exo
169 
main(int argc,char * argv[])170 int main(int argc, char* argv[]) {
171   base::AtExitManager exit_manager;
172   base::CommandLine::Init(argc, argv);
173   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
174 
175   exo::wayland::clients::ClientBase::InitParams params;
176   if (!params.FromCommandLine(*command_line))
177     return 1;
178 
179   params.use_vulkan = true;
180   base::SingleThreadTaskExecutor main_task_executor(base::MessagePumpType::UI);
181   exo::wayland::clients::VulkanClient client;
182   client.Run(params);
183   return 1;
184 }
185