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