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