1 // Copyright (C) 2024 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #pragma once
16 
17 #include <memory>
18 #include <optional>
19 #include <string>
20 #include <vector>
21 
22 #include <android-base/expected.h>
23 #include <android/native_window_jni.h>
24 #define VULKAN_HPP_NAMESPACE vkhpp
25 #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
26 #define VULKAN_HPP_ENABLE_DYNAMIC_LOADER_TOOL 1
27 #define VULKAN_HPP_NO_CONSTRUCTORS
28 #define VULKAN_HPP_NO_EXCEPTIONS
29 #define VULKAN_HPP_ASSERT_ON_RESULT
30 #include <vulkan/vulkan.hpp>
31 #include <vulkan/vulkan_to_string.hpp>
32 
33 #include "common.h"
34 
35 namespace cuttlefish {
36 
37 template <typename T>
38 using Result = android::base::expected<T, std::string>;
39 
40 // Empty object for `Result<Ok>` that allows using the below macros.
41 struct Ok {};
42 
Err(const std::string & msg)43 inline android::base::unexpected<std::string> Err(const std::string& msg) {
44   return android::base::unexpected(msg);
45 }
46 
47 #define VK_ASSERT(x)                                          \
48   ({                                                          \
49     auto result = (x);                                        \
50     if (!result.ok()) {                                       \
51       ALOGE("Failed to " #x ": %s.", result.error().c_str()); \
52       std::abort();                                           \
53     };                                                        \
54     std::move(result.value());                                \
55   })
56 
57 #define VK_EXPECT(x)                \
58   ({                                \
59     auto expected = (x);            \
60     if (!expected.ok()) {           \
61       return Err(expected.error()); \
62     };                              \
63     std::move(expected.value());    \
64   })
65 
66 #define VK_EXPECT_RESULT(x)                          \
67   do {                                               \
68     vkhpp::Result result = (x);                      \
69     if (result != vkhpp::Result::eSuccess) {         \
70       return Err(std::string("Failed to " #x ": ") + \
71                  vkhpp::to_string(result));          \
72     }                                                \
73   } while (0);
74 
75 #define VK_EXPECT_RV(x)                               \
76   ({                                                  \
77     auto vkhpp_rv = (x);                              \
78     if (vkhpp_rv.result != vkhpp::Result::eSuccess) { \
79       return Err(std::string("Failed to " #x ": ") +  \
80                  vkhpp::to_string(vkhpp_rv.result));  \
81     };                                                \
82     std::move(vkhpp_rv.value);                        \
83   })
84 
85 #define VK_TRY(x)                                    \
86   do {                                               \
87     vkhpp::Result result = (x);                      \
88     if (result != vkhpp::Result::eSuccess) {         \
89       return Err(std::string("Failed to " #x ": ") + \
90                  vkhpp::to_string(result));          \
91     }                                                \
92   } while (0);
93 
94 #define VK_TRY_RV(x)                                  \
95   ({                                                  \
96     auto vkhpp_rv = (x);                              \
97     if (vkhpp_rv.result != vkhpp::Result::eSuccess) { \
98       return Err(std::string("Failed to " #x ": ") +  \
99                  vkhpp::to_string(vkhpp_rv.result));  \
100     };                                                \
101     std::move(vkhpp_rv.value);                        \
102   })
103 
104 class SampleBase {
105  public:
~SampleBase()106   virtual ~SampleBase() {}
107 
108   SampleBase(const SampleBase&) = delete;
109   SampleBase& operator=(const SampleBase&) = delete;
110 
111   SampleBase(SampleBase&&) = default;
112   SampleBase& operator=(SampleBase&&) = default;
113 
114   virtual Result<Ok> StartUp() = 0;
115   virtual Result<Ok> CleanUp() = 0;
116 
117   struct SwapchainInfo {
118     vkhpp::Format swapchainFormat;
119     vkhpp::Extent2D swapchainExtent;
120     std::vector<vkhpp::ImageView> swapchainImageViews;
121   };
CreateSwapchainDependents(const SwapchainInfo &)122   virtual Result<Ok> CreateSwapchainDependents(const SwapchainInfo& /*info*/) {
123     return Ok{};
124   }
125 
DestroySwapchainDependents()126   virtual Result<Ok> DestroySwapchainDependents() { return Ok{}; }
127 
128   struct FrameInfo {
129     uint32_t swapchainImageIndex = -1;
130     vkhpp::CommandBuffer commandBuffer;
131   };
RecordFrame(const FrameInfo &)132   virtual Result<Ok> RecordFrame(const FrameInfo& /*frame*/) { return Ok{}; }
133 
134   Result<Ok> Render();
135 
136   Result<Ok> SetWindow(ANativeWindow* window = nullptr);
137 
138  protected:
139   SampleBase() = default;
140 
141   Result<Ok> StartUpBase(const std::vector<std::string>& instance_extensions =
142                              {
143                                  VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
144                                  VK_KHR_SURFACE_EXTENSION_NAME,
145                              },
146                          const std::vector<std::string>& instance_layers = {},
147                          const std::vector<std::string>& device_extensions = {
148                              VK_KHR_SWAPCHAIN_EXTENSION_NAME,
149                          });
150   Result<Ok> CleanUpBase();
151 
152   Result<Ok> CreateSurface();
153   Result<Ok> DestroySurface();
154 
155   Result<Ok> CreateSwapchain();
156   Result<Ok> DestroySwapchain();
157   Result<Ok> RecreateSwapchain();
158 
159   struct BufferWithMemory {
160     vkhpp::UniqueBuffer buffer;
161     vkhpp::UniqueDeviceMemory bufferMemory;
162   };
163   Result<BufferWithMemory> CreateBuffer(
164       vkhpp::DeviceSize buffer_size, vkhpp::BufferUsageFlags buffer_usages,
165       vkhpp::MemoryPropertyFlags buffer_memory_properties);
166   Result<BufferWithMemory> CreateBufferWithData(
167       vkhpp::DeviceSize buffer_size, vkhpp::BufferUsageFlags buffer_usages,
168       vkhpp::MemoryPropertyFlags buffer_memory_properties,
169       const uint8_t* buffer_data);
170 
171   Result<Ok> DoCommandsImmediate(
172       const std::function<Result<Ok>(vkhpp::UniqueCommandBuffer&)>& func,
173       const std::vector<vkhpp::UniqueSemaphore>& semaphores_wait = {},
174       const std::vector<vkhpp::UniqueSemaphore>& semaphores_signal = {});
175 
176   struct ImageWithMemory {
177     vkhpp::UniqueImage image;
178     vkhpp::UniqueDeviceMemory imageMemory;
179     vkhpp::UniqueImageView imageView;
180   };
181   Result<ImageWithMemory> CreateImage(
182       uint32_t width, uint32_t height, vkhpp::Format format,
183       vkhpp::ImageUsageFlags usages,
184       vkhpp::MemoryPropertyFlags memory_properties,
185       vkhpp::ImageLayout returned_layout);
186 
187   Result<Ok> LoadImage(const vkhpp::UniqueImage& image, uint32_t width,
188                        uint32_t height, const std::vector<uint8_t>& imageData,
189                        vkhpp::ImageLayout currentLayout,
190                        vkhpp::ImageLayout returnedLayout);
191 
192   Result<std::vector<uint8_t>> DownloadImage(
193       uint32_t width, uint32_t height, const vkhpp::UniqueImage& image,
194       vkhpp::ImageLayout current_layout, vkhpp::ImageLayout returned_layout);
195 
196   struct YuvImageWithMemory {
197     vkhpp::UniqueSamplerYcbcrConversion imageSamplerConversion;
198     vkhpp::UniqueSampler imageSampler;
199     vkhpp::UniqueDeviceMemory imageMemory;
200     vkhpp::UniqueImage image;
201     vkhpp::UniqueImageView imageView;
202   };
203   Result<YuvImageWithMemory> CreateYuvImage(
204       uint32_t width, uint32_t height, vkhpp::ImageUsageFlags usages,
205       vkhpp::MemoryPropertyFlags memory_properties,
206       vkhpp::ImageLayout returned_layout);
207 
208   Result<Ok> LoadYuvImage(const vkhpp::UniqueImage& image, uint32_t width,
209                           uint32_t height,
210                           const std::vector<uint8_t>& image_data_y,
211                           const std::vector<uint8_t>& image_data_u,
212                           const std::vector<uint8_t>& image_data_v,
213                           vkhpp::ImageLayout current_layout,
214                           vkhpp::ImageLayout returned_layout);
215 
216   struct FramebufferWithAttachments {
217     std::optional<ImageWithMemory> colorAttachment;
218     std::optional<ImageWithMemory> depthAttachment;
219     vkhpp::UniqueRenderPass renderpass;
220     vkhpp::UniqueFramebuffer framebuffer;
221   };
222   Result<FramebufferWithAttachments> CreateFramebuffer(
223       uint32_t width, uint32_t height,
224       vkhpp::Format colorAttachmentFormat = vkhpp::Format::eUndefined,
225       vkhpp::Format depthAttachmentFormat = vkhpp::Format::eUndefined);
226 
227  private:
228   vkhpp::DynamicLoader mLoader;
229   vkhpp::UniqueInstance mInstance;
230   std::optional<vkhpp::UniqueDebugUtilsMessengerEXT> mDebugMessenger;
231 
232  protected:
233   vkhpp::PhysicalDevice mPhysicalDevice;
234   vkhpp::UniqueDevice mDevice;
235   vkhpp::Queue mQueue;
236   uint32_t mQueueFamilyIndex = 0;
237 
238  private:
239   static constexpr const VkDeviceSize kStagingBufferSize = 32 * 1024 * 1024;
240   BufferWithMemory mStagingBuffer;
241 
242   struct PerFrameObjects {
243     vkhpp::UniqueFence readyFence;
244     vkhpp::UniqueSemaphore readyForRender;
245     vkhpp::UniqueSemaphore readyForPresent;
246     vkhpp::UniqueCommandPool commandPool;
247     vkhpp::UniqueCommandBuffer commandBuffer;
248   };
249   static constexpr const uint32_t kMaxFramesInFlight = 3;
250   uint32_t mCurrentFrame = 0;
251   std::vector<PerFrameObjects> mFrameObjects;
252 
253   ANativeWindow* mWindow = nullptr;
254 
255   std::optional<vkhpp::SurfaceKHR> mSurface;
256 
257   struct SwapchainObjects {
258     vkhpp::SurfaceFormatKHR swapchainFormat;
259     vkhpp::Extent2D swapchainExtent;
260     vkhpp::UniqueSwapchainKHR swapchain;
261     std::vector<vkhpp::Image> swapchainImages;
262     std::vector<vkhpp::UniqueImageView> swapchainImageViews;
263   };
264   std::optional<SwapchainObjects> mSwapchainObjects;
265 };
266 
267 Result<std::unique_ptr<SampleBase>> BuildVulkanSampleApp();
268 
269 }  // namespace cuttlefish
270