• 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     VkImageCreateInfo m_vkImageCreateInfo;
23     VkImage m_vkImage;
24     VkDeviceMemory m_imageVkDeviceMemory;
25     VkImageView m_vkImageView;
26 
27     VkBuffer m_vkBuffer;
28     VkDeviceMemory m_bufferVkDeviceMemory;
29     uint32_t *m_memory;
30 
31     VkCommandPool m_vkCommandPool;
32     VkCommandBuffer m_readCommandBuffer;
33     VkCommandBuffer m_writeCommandBuffer;
34 
RenderResourceVkBaseRenderResourceVkBase35     explicit RenderResourceVkBase(const goldfish_vk::VulkanDispatch &vk)
36         : m_vk(vk),
37           m_vkImageCreateInfo({}),
38           m_vkImage(VK_NULL_HANDLE),
39           m_imageVkDeviceMemory(VK_NULL_HANDLE),
40           m_vkImageView(VK_NULL_HANDLE),
41           m_vkBuffer(VK_NULL_HANDLE),
42           m_bufferVkDeviceMemory(VK_NULL_HANDLE),
43           m_memory(nullptr),
44           m_readCommandBuffer(VK_NULL_HANDLE),
45           m_writeCommandBuffer(VK_NULL_HANDLE) {}
46 };
47 
48 template <VkImageLayout imageLayout, VkImageUsageFlags imageUsage>
49 struct RenderResourceVk : public RenderResourceVkBase {
50    public:
51     static constexpr VkFormat k_vkFormat = VK_FORMAT_R8G8B8A8_SRGB;
52     static constexpr uint32_t k_bpp = 4;
53     static constexpr VkImageLayout k_vkImageLayout = imageLayout;
54 
55     static std::unique_ptr<const RenderResourceVk<imageLayout, imageUsage>>
createRenderResourceVk56     create(const goldfish_vk::VulkanDispatch &vk, VkDevice device,
57            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 goldfish_vk::VulkanDispatch &vk)
124         : RenderResourceVkBase(vk) {}
125 
setUpImageRenderResourceVk126     bool setUpImage() {
127         VkImageCreateInfo imageCi{
128             .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
129             .imageType = VK_IMAGE_TYPE_2D,
130             .format = k_vkFormat,
131             .extent = {.width = m_width, .height = m_height, .depth = 1},
132             .mipLevels = 1,
133             .arrayLayers = 1,
134             .samples = VK_SAMPLE_COUNT_1_BIT,
135             .tiling = VK_IMAGE_TILING_OPTIMAL,
136             .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
137                      VK_IMAGE_USAGE_TRANSFER_SRC_BIT | imageUsage,
138             .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
139             .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED};
140         if (m_vk.vkCreateImage(m_vkDevice, &imageCi, nullptr, &m_vkImage) !=
141             VK_SUCCESS) {
142             m_vkImage = VK_NULL_HANDLE;
143             return false;
144         }
145         m_vkImageCreateInfo = vk_make_orphan_copy(imageCi);
146 
147         VkMemoryRequirements memRequirements;
148         m_vk.vkGetImageMemoryRequirements(m_vkDevice, m_vkImage,
149                                           &memRequirements);
150         auto maybeMemoryTypeIndex =
151             findMemoryType(memRequirements.memoryTypeBits,
152                            VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
153         if (!maybeMemoryTypeIndex.has_value()) {
154             return false;
155         }
156         VkMemoryAllocateInfo allocInfo = {
157             .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
158             .allocationSize = memRequirements.size,
159             .memoryTypeIndex = maybeMemoryTypeIndex.value()};
160         if (m_vk.vkAllocateMemory(m_vkDevice, &allocInfo, nullptr,
161                                   &m_imageVkDeviceMemory) != VK_SUCCESS) {
162             m_imageVkDeviceMemory = VK_NULL_HANDLE;
163             return false;
164         }
165         if (m_vk.vkBindImageMemory(m_vkDevice, m_vkImage, m_imageVkDeviceMemory,
166                                    0) != VK_SUCCESS) {
167             return false;
168         }
169 
170         runSingleTimeCommands(m_vkQueue, nullptr, [this](const auto &cmdBuff) {
171             recordImageLayoutTransformCommands(
172                 cmdBuff, m_vkImage, VK_IMAGE_LAYOUT_UNDEFINED, k_vkImageLayout);
173         });
174 
175         VkImageViewCreateInfo imageViewCi = {
176             .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
177             .image = m_vkImage,
178             .viewType = VK_IMAGE_VIEW_TYPE_2D,
179             .format = k_vkFormat,
180             .components = {.r = VK_COMPONENT_SWIZZLE_IDENTITY,
181                            .g = VK_COMPONENT_SWIZZLE_IDENTITY,
182                            .b = VK_COMPONENT_SWIZZLE_IDENTITY,
183                            .a = VK_COMPONENT_SWIZZLE_IDENTITY},
184             .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
185                                  .baseMipLevel = 0,
186                                  .levelCount = 1,
187                                  .baseArrayLayer = 0,
188                                  .layerCount = 1}};
189         if (m_vk.vkCreateImageView(m_vkDevice, &imageViewCi, nullptr,
190                                    &m_vkImageView) != VK_SUCCESS) {
191             return false;
192         }
193         return true;
194     }
195 
submitCommandBufferAndWaitRenderResourceVk196     bool submitCommandBufferAndWait(VkCommandBuffer cmdBuff) const {
197         VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
198                                    .commandBufferCount = 1,
199                                    .pCommandBuffers = &cmdBuff};
200         if (m_vk.vkQueueSubmit(m_vkQueue, 1, &submitInfo, VK_NULL_HANDLE) !=
201             VK_SUCCESS) {
202             return false;
203         }
204         if (m_vk.vkQueueWaitIdle(m_vkQueue) != VK_SUCCESS) {
205             return false;
206         }
207         return true;
208     }
209 
setUpBufferRenderResourceVk210     bool setUpBuffer() {
211         VkBufferCreateInfo bufferCi = {
212             .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
213             .size = m_width * m_height * k_bpp,
214             .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
215                      VK_BUFFER_USAGE_TRANSFER_DST_BIT,
216             .sharingMode = VK_SHARING_MODE_EXCLUSIVE};
217 
218         if (m_vk.vkCreateBuffer(m_vkDevice, &bufferCi, nullptr, &m_vkBuffer) !=
219             VK_SUCCESS) {
220             m_vkBuffer = VK_NULL_HANDLE;
221             return false;
222         }
223 
224         VkMemoryRequirements memRequirements;
225         m_vk.vkGetBufferMemoryRequirements(m_vkDevice, m_vkBuffer,
226                                            &memRequirements);
227         auto maybeMemoryTypeIndex =
228             findMemoryType(memRequirements.memoryTypeBits,
229                            VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
230                                VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
231         if (!maybeMemoryTypeIndex.has_value()) {
232             return false;
233         }
234         VkMemoryAllocateInfo allocInfo = {
235             .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
236             .allocationSize = memRequirements.size,
237             .memoryTypeIndex = maybeMemoryTypeIndex.value()};
238         if (m_vk.vkAllocateMemory(m_vkDevice, &allocInfo, nullptr,
239                                   &m_bufferVkDeviceMemory) != VK_SUCCESS) {
240             m_bufferVkDeviceMemory = VK_NULL_HANDLE;
241             return false;
242         }
243         if (m_vk.vkBindBufferMemory(m_vkDevice, m_vkBuffer,
244                                     m_bufferVkDeviceMemory, 0) != VK_SUCCESS) {
245             return false;
246         }
247         if (m_vk.vkMapMemory(
248                 m_vkDevice, m_bufferVkDeviceMemory, 0, bufferCi.size, 0,
249                 reinterpret_cast<void **>(&m_memory)) != VK_SUCCESS) {
250             m_memory = nullptr;
251             return false;
252         }
253         return true;
254     }
255 
setUpCommandBufferRenderResourceVk256     bool setUpCommandBuffer() {
257         std::array<VkCommandBuffer, 2> cmdBuffs;
258         VkCommandBufferAllocateInfo cmdBuffAllocInfo = {
259             .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
260             .commandPool = m_vkCommandPool,
261             .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
262             .commandBufferCount = static_cast<uint32_t>(cmdBuffs.size())};
263         if (m_vk.vkAllocateCommandBuffers(m_vkDevice, &cmdBuffAllocInfo,
264                                           cmdBuffs.data()) != VK_SUCCESS) {
265             return false;
266         }
267         m_readCommandBuffer = cmdBuffs[0];
268         m_writeCommandBuffer = cmdBuffs[1];
269 
270         VkCommandBufferBeginInfo beginInfo = {
271             .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
272         if (m_vk.vkBeginCommandBuffer(m_readCommandBuffer, &beginInfo) !=
273             VK_SUCCESS) {
274             return false;
275         }
276         recordImageLayoutTransformCommands(
277             m_readCommandBuffer, m_vkImage, k_vkImageLayout,
278             VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
279         VkBufferImageCopy region = {
280             .bufferOffset = 0,
281             .bufferRowLength = 0,
282             .bufferImageHeight = 0,
283             .imageSubresource = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
284                                  .mipLevel = 0,
285                                  .baseArrayLayer = 0,
286                                  .layerCount = 1},
287             .imageOffset = {0, 0, 0},
288             .imageExtent = {m_width, m_height, 1}};
289         m_vk.vkCmdCopyImageToBuffer(m_readCommandBuffer, m_vkImage,
290                                     VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
291                                     m_vkBuffer, 1, &region);
292         recordImageLayoutTransformCommands(m_readCommandBuffer, m_vkImage,
293                                            VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
294                                            k_vkImageLayout);
295         if (m_vk.vkEndCommandBuffer(m_readCommandBuffer) != VK_SUCCESS) {
296             return false;
297         }
298 
299         if (m_vk.vkBeginCommandBuffer(m_writeCommandBuffer, &beginInfo) !=
300             VK_SUCCESS) {
301             return false;
302         }
303         recordImageLayoutTransformCommands(
304             m_writeCommandBuffer, m_vkImage, k_vkImageLayout,
305             VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
306         m_vk.vkCmdCopyBufferToImage(m_writeCommandBuffer, m_vkBuffer, m_vkImage,
307                                     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
308                                     &region);
309         recordImageLayoutTransformCommands(m_writeCommandBuffer, m_vkImage,
310                                            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
311                                            k_vkImageLayout);
312         if (m_vk.vkEndCommandBuffer(m_writeCommandBuffer) != VK_SUCCESS) {
313             return false;
314         }
315         return true;
316     }
317 };  // namespace emugl
318 
319 using RenderTextureVk =
320     emugl::RenderResourceVk<VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
321                             VK_IMAGE_USAGE_SAMPLED_BIT>;
322 
323 }  // namespace emugl
324 
325 #endif