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, ®ion); 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 ®ion); 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