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