1 #ifndef VK_TEST_UTILS_H
2 #define VK_TEST_UTILS_H
3
4 #include "aemu/base/files/PathUtils.h"
5 #include "vulkan/VulkanDispatch.h"
6 #include "vulkan/vk_util.h"
7
8 namespace gfxstream {
9 namespace vk {
10
libDir()11 inline std::string libDir() {
12 using android::base::pj;
13 return
14 pj({android::base::getProgramDirectory(),
15 #ifdef _WIN32
16 // Windows uses mock Vulkan ICD.
17 "testlib64"
18 #else
19 "lib64", "vulkan"
20 #endif
21 });
22 }
23
testIcdFilename()24 inline std::string testIcdFilename() {
25 using android::base::pj;
26 return pj(libDir(),
27 #ifdef _WIN32
28 // Windows uses mock Vulkan ICD.
29 "VkICD_mock_icd.json"
30 #else
31 "vk_swiftshader_icd.json"
32 #endif
33 );
34 }
35
36 struct RenderResourceVkBase
37 : public vk_util::MultiCrtp<RenderResourceVkBase, //
38 vk_util::FindMemoryType, //
39 vk_util::RecordImageLayoutTransformCommands, //
40 vk_util::RunSingleTimeCommand> {
41 const VulkanDispatch& m_vk;
42 VkDevice m_vkDevice;
43 VkPhysicalDevice m_vkPhysicalDevice;
44 VkQueue m_vkQueue;
45 uint32_t m_width;
46 uint32_t m_height;
47
48 VkImageCreateInfo m_vkImageCreateInfo;
49 VkImage m_vkImage;
50 VkDeviceMemory m_imageVkDeviceMemory;
51 VkImageView m_vkImageView;
52
53 VkBuffer m_vkBuffer;
54 VkDeviceMemory m_bufferVkDeviceMemory;
55 uint32_t *m_memory;
56
57 VkCommandPool m_vkCommandPool;
58 VkCommandBuffer m_readCommandBuffer;
59 VkCommandBuffer m_writeCommandBuffer;
60
RenderResourceVkBaseRenderResourceVkBase61 explicit RenderResourceVkBase(const VulkanDispatch& vk)
62 : m_vk(vk),
63 m_vkImageCreateInfo({}),
64 m_vkImage(VK_NULL_HANDLE),
65 m_imageVkDeviceMemory(VK_NULL_HANDLE),
66 m_vkImageView(VK_NULL_HANDLE),
67 m_vkBuffer(VK_NULL_HANDLE),
68 m_bufferVkDeviceMemory(VK_NULL_HANDLE),
69 m_memory(nullptr),
70 m_readCommandBuffer(VK_NULL_HANDLE),
71 m_writeCommandBuffer(VK_NULL_HANDLE) {}
72 };
73
74 template <VkImageLayout imageLayout, VkImageUsageFlags imageUsage>
75 struct RenderResourceVk : public RenderResourceVkBase {
76 public:
77 static constexpr VkFormat k_vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
78 static constexpr uint32_t k_bpp = 4;
79 static constexpr VkImageLayout k_vkImageLayout = imageLayout;
80
createRenderResourceVk81 static std::unique_ptr<const RenderResourceVk<imageLayout, imageUsage>> create(
82 const VulkanDispatch& vk, VkDevice device, VkPhysicalDevice physicalDevice, VkQueue queue,
83 VkCommandPool commandPool, uint32_t width, uint32_t height) {
84 std::unique_ptr<RenderResourceVk<imageLayout, imageUsage>> res(
85 new RenderResourceVk<imageLayout, imageUsage>(vk));
86 res->m_vkDevice = device;
87 res->m_vkPhysicalDevice = physicalDevice;
88 res->m_vkQueue = queue;
89 res->m_width = width;
90 res->m_height = height;
91 res->m_vkCommandPool = commandPool;
92 if (!res->setUpImage()) {
93 return nullptr;
94 }
95 if (!res->setUpBuffer()) {
96 return nullptr;
97 }
98 if (!res->setUpCommandBuffer()) {
99 return nullptr;
100 }
101
102 return res;
103 }
104
numOfPixelsRenderResourceVk105 uint32_t numOfPixels() const { return m_width * m_height; }
106
writeRenderResourceVk107 bool write(const std::vector<uint32_t> &pixels) const {
108 if (pixels.size() != numOfPixels()) {
109 return false;
110 }
111 std::copy(pixels.begin(), pixels.end(), m_memory);
112 return submitCommandBufferAndWait(m_writeCommandBuffer);
113 }
114
readRenderResourceVk115 std::optional<std::vector<uint32_t>> read() const {
116 std::vector<uint32_t> res(numOfPixels());
117 if (!submitCommandBufferAndWait(m_readCommandBuffer)) {
118 return std::nullopt;
119 }
120 std::copy(m_memory, m_memory + numOfPixels(), res.begin());
121 return res;
122 }
123
~RenderResourceVkRenderResourceVk124 ~RenderResourceVk() {
125 std::vector<VkCommandBuffer> toFree;
126 if (m_writeCommandBuffer != VK_NULL_HANDLE) {
127 toFree.push_back(m_writeCommandBuffer);
128 }
129 if (m_readCommandBuffer != VK_NULL_HANDLE) {
130 toFree.push_back(m_readCommandBuffer);
131 }
132 if (!toFree.empty()) {
133 m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool,
134 static_cast<uint32_t>(toFree.size()),
135 toFree.data());
136 }
137 if (m_memory) {
138 m_vk.vkUnmapMemory(m_vkDevice, m_bufferVkDeviceMemory);
139 }
140 m_vk.vkFreeMemory(m_vkDevice, m_bufferVkDeviceMemory, nullptr);
141 m_vk.vkDestroyBuffer(m_vkDevice, m_vkBuffer, nullptr);
142 m_vk.vkDestroyImageView(m_vkDevice, m_vkImageView, nullptr);
143 m_vk.vkFreeMemory(m_vkDevice, m_imageVkDeviceMemory, nullptr);
144 m_vk.vkDestroyImage(m_vkDevice, m_vkImage, nullptr);
145 }
146
147 private:
RenderResourceVkRenderResourceVk148 RenderResourceVk(const VulkanDispatch& vk) : RenderResourceVkBase(vk) {}
149
setUpImageRenderResourceVk150 bool setUpImage() {
151 VkImageCreateInfo imageCi{
152 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
153 .imageType = VK_IMAGE_TYPE_2D,
154 .format = k_vkFormat,
155 .extent = {.width = m_width, .height = m_height, .depth = 1},
156 .mipLevels = 1,
157 .arrayLayers = 1,
158 .samples = VK_SAMPLE_COUNT_1_BIT,
159 .tiling = VK_IMAGE_TILING_OPTIMAL,
160 .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
161 VK_IMAGE_USAGE_TRANSFER_SRC_BIT | imageUsage,
162 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
163 .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED};
164 if (m_vk.vkCreateImage(m_vkDevice, &imageCi, nullptr, &m_vkImage) !=
165 VK_SUCCESS) {
166 m_vkImage = VK_NULL_HANDLE;
167 return false;
168 }
169 m_vkImageCreateInfo = vk_make_orphan_copy(imageCi);
170
171 VkMemoryRequirements memRequirements;
172 m_vk.vkGetImageMemoryRequirements(m_vkDevice, m_vkImage,
173 &memRequirements);
174 auto maybeMemoryTypeIndex =
175 findMemoryType(memRequirements.memoryTypeBits,
176 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
177 if (!maybeMemoryTypeIndex.has_value()) {
178 return false;
179 }
180 VkMemoryAllocateInfo allocInfo = {
181 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
182 .allocationSize = memRequirements.size,
183 .memoryTypeIndex = maybeMemoryTypeIndex.value()};
184 if (m_vk.vkAllocateMemory(m_vkDevice, &allocInfo, nullptr,
185 &m_imageVkDeviceMemory) != VK_SUCCESS) {
186 m_imageVkDeviceMemory = VK_NULL_HANDLE;
187 return false;
188 }
189 if (m_vk.vkBindImageMemory(m_vkDevice, m_vkImage, m_imageVkDeviceMemory,
190 0) != VK_SUCCESS) {
191 return false;
192 }
193
194 runSingleTimeCommands(m_vkQueue, nullptr, [this](const auto &cmdBuff) {
195 recordImageLayoutTransformCommands(
196 cmdBuff, m_vkImage, VK_IMAGE_LAYOUT_UNDEFINED, k_vkImageLayout);
197 });
198
199 VkImageViewCreateInfo imageViewCi = {
200 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
201 .image = m_vkImage,
202 .viewType = VK_IMAGE_VIEW_TYPE_2D,
203 .format = k_vkFormat,
204 .components = {.r = VK_COMPONENT_SWIZZLE_IDENTITY,
205 .g = VK_COMPONENT_SWIZZLE_IDENTITY,
206 .b = VK_COMPONENT_SWIZZLE_IDENTITY,
207 .a = VK_COMPONENT_SWIZZLE_IDENTITY},
208 .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
209 .baseMipLevel = 0,
210 .levelCount = 1,
211 .baseArrayLayer = 0,
212 .layerCount = 1}};
213 if (m_vk.vkCreateImageView(m_vkDevice, &imageViewCi, nullptr,
214 &m_vkImageView) != VK_SUCCESS) {
215 return false;
216 }
217 return true;
218 }
219
submitCommandBufferAndWaitRenderResourceVk220 bool submitCommandBufferAndWait(VkCommandBuffer cmdBuff) const {
221 VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
222 .commandBufferCount = 1,
223 .pCommandBuffers = &cmdBuff};
224 if (m_vk.vkQueueSubmit(m_vkQueue, 1, &submitInfo, VK_NULL_HANDLE) !=
225 VK_SUCCESS) {
226 return false;
227 }
228 if (m_vk.vkQueueWaitIdle(m_vkQueue) != VK_SUCCESS) {
229 return false;
230 }
231 return true;
232 }
233
setUpBufferRenderResourceVk234 bool setUpBuffer() {
235 VkBufferCreateInfo bufferCi = {
236 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
237 .size = m_width * m_height * k_bpp,
238 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
239 VK_BUFFER_USAGE_TRANSFER_DST_BIT,
240 .sharingMode = VK_SHARING_MODE_EXCLUSIVE};
241
242 if (m_vk.vkCreateBuffer(m_vkDevice, &bufferCi, nullptr, &m_vkBuffer) !=
243 VK_SUCCESS) {
244 m_vkBuffer = VK_NULL_HANDLE;
245 return false;
246 }
247
248 VkMemoryRequirements memRequirements;
249 m_vk.vkGetBufferMemoryRequirements(m_vkDevice, m_vkBuffer,
250 &memRequirements);
251 auto maybeMemoryTypeIndex =
252 findMemoryType(memRequirements.memoryTypeBits,
253 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
254 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
255 if (!maybeMemoryTypeIndex.has_value()) {
256 return false;
257 }
258 VkMemoryAllocateInfo allocInfo = {
259 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
260 .allocationSize = memRequirements.size,
261 .memoryTypeIndex = maybeMemoryTypeIndex.value()};
262 if (m_vk.vkAllocateMemory(m_vkDevice, &allocInfo, nullptr,
263 &m_bufferVkDeviceMemory) != VK_SUCCESS) {
264 m_bufferVkDeviceMemory = VK_NULL_HANDLE;
265 return false;
266 }
267 if (m_vk.vkBindBufferMemory(m_vkDevice, m_vkBuffer,
268 m_bufferVkDeviceMemory, 0) != VK_SUCCESS) {
269 return false;
270 }
271 if (m_vk.vkMapMemory(
272 m_vkDevice, m_bufferVkDeviceMemory, 0, bufferCi.size, 0,
273 reinterpret_cast<void **>(&m_memory)) != VK_SUCCESS) {
274 m_memory = nullptr;
275 return false;
276 }
277 return true;
278 }
279
setUpCommandBufferRenderResourceVk280 bool setUpCommandBuffer() {
281 std::array<VkCommandBuffer, 2> cmdBuffs;
282 VkCommandBufferAllocateInfo cmdBuffAllocInfo = {
283 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
284 .commandPool = m_vkCommandPool,
285 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
286 .commandBufferCount = static_cast<uint32_t>(cmdBuffs.size())};
287 if (m_vk.vkAllocateCommandBuffers(m_vkDevice, &cmdBuffAllocInfo,
288 cmdBuffs.data()) != VK_SUCCESS) {
289 return false;
290 }
291 m_readCommandBuffer = cmdBuffs[0];
292 m_writeCommandBuffer = cmdBuffs[1];
293
294 VkCommandBufferBeginInfo beginInfo = {
295 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
296 if (m_vk.vkBeginCommandBuffer(m_readCommandBuffer, &beginInfo) !=
297 VK_SUCCESS) {
298 return false;
299 }
300 recordImageLayoutTransformCommands(
301 m_readCommandBuffer, m_vkImage, k_vkImageLayout,
302 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
303 VkBufferImageCopy region = {
304 .bufferOffset = 0,
305 .bufferRowLength = 0,
306 .bufferImageHeight = 0,
307 .imageSubresource = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
308 .mipLevel = 0,
309 .baseArrayLayer = 0,
310 .layerCount = 1},
311 .imageOffset = {0, 0, 0},
312 .imageExtent = {m_width, m_height, 1}};
313 m_vk.vkCmdCopyImageToBuffer(m_readCommandBuffer, m_vkImage,
314 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
315 m_vkBuffer, 1, ®ion);
316 recordImageLayoutTransformCommands(m_readCommandBuffer, m_vkImage,
317 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
318 k_vkImageLayout);
319 if (m_vk.vkEndCommandBuffer(m_readCommandBuffer) != VK_SUCCESS) {
320 return false;
321 }
322
323 if (m_vk.vkBeginCommandBuffer(m_writeCommandBuffer, &beginInfo) !=
324 VK_SUCCESS) {
325 return false;
326 }
327 recordImageLayoutTransformCommands(
328 m_writeCommandBuffer, m_vkImage, k_vkImageLayout,
329 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
330 m_vk.vkCmdCopyBufferToImage(m_writeCommandBuffer, m_vkBuffer, m_vkImage,
331 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
332 ®ion);
333 recordImageLayoutTransformCommands(m_writeCommandBuffer, m_vkImage,
334 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
335 k_vkImageLayout);
336 if (m_vk.vkEndCommandBuffer(m_writeCommandBuffer) != VK_SUCCESS) {
337 return false;
338 }
339 return true;
340 }
341 };
342
343 using RenderTextureVk =
344 RenderResourceVk<VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT>;
345
346 } // namespace vk
347 } // namespace gfxstream
348
349 #endif