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