| #include "DisplayVk.h" |
| |
| #include <glm/glm.hpp> |
| #include <glm/gtx/matrix_transform_2d.hpp> |
| |
| #include "ErrorLog.h" |
| |
| DisplayVk::DisplayVk(const goldfish_vk::VulkanDispatch &vk, |
| VkPhysicalDevice vkPhysicalDevice, |
| uint32_t swapChainQueueFamilyIndex, |
| uint32_t compositorQueueFamilyIndex, VkDevice vkDevice, |
| VkQueue compositorVkQueue, VkQueue swapChainVkqueue) |
| : m_vk(vk), |
| m_vkPhysicalDevice(vkPhysicalDevice), |
| m_swapChainQueueFamilyIndex(swapChainQueueFamilyIndex), |
| m_compositorQueueFamilyIndex(compositorQueueFamilyIndex), |
| m_vkDevice(vkDevice), |
| m_compositorVkQueue(compositorVkQueue), |
| m_swapChainVkQueue(swapChainVkqueue), |
| m_vkCommandPool(VK_NULL_HANDLE), |
| m_swapChainStateVk(nullptr), |
| m_compositorVk(nullptr), |
| m_surfaceState(nullptr), |
| m_canComposite() { |
| // TODO(kaiyili): validate the capabilites of the passed in Vulkan |
| // components. |
| VkCommandPoolCreateInfo commandPoolCi = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, |
| .queueFamilyIndex = m_compositorQueueFamilyIndex, |
| }; |
| VK_CHECK(m_vk.vkCreateCommandPool(m_vkDevice, &commandPoolCi, nullptr, |
| &m_vkCommandPool)); |
| VkFenceCreateInfo fenceCi = {.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, |
| .flags = VK_FENCE_CREATE_SIGNALED_BIT}; |
| VK_CHECK(m_vk.vkCreateFence(m_vkDevice, &fenceCi, nullptr, |
| &m_frameDrawCompleteFence)); |
| VkSemaphoreCreateInfo semaphoreCi = { |
| .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO}; |
| VK_CHECK(m_vk.vkCreateSemaphore(m_vkDevice, &semaphoreCi, nullptr, |
| &m_imageReadySem)); |
| VK_CHECK(m_vk.vkCreateSemaphore(m_vkDevice, &semaphoreCi, nullptr, |
| &m_frameDrawCompleteSem)); |
| |
| VkSamplerCreateInfo samplerCi = { |
| .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, |
| .magFilter = VK_FILTER_NEAREST, |
| .minFilter = VK_FILTER_NEAREST, |
| .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, |
| .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, |
| .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, |
| .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, |
| .mipLodBias = 0.0f, |
| .anisotropyEnable = VK_FALSE, |
| .maxAnisotropy = 1.0f, |
| .compareEnable = VK_FALSE, |
| .compareOp = VK_COMPARE_OP_ALWAYS, |
| .minLod = 0.0f, |
| .maxLod = 0.0f, |
| .borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK, |
| .unnormalizedCoordinates = VK_FALSE}; |
| VK_CHECK(m_vk.vkCreateSampler(m_vkDevice, &samplerCi, nullptr, |
| &m_compositionVkSampler)); |
| } |
| |
| DisplayVk::~DisplayVk() { |
| m_vk.vkDestroySampler(m_vkDevice, m_compositionVkSampler, nullptr); |
| m_vk.vkDestroySemaphore(m_vkDevice, m_imageReadySem, nullptr); |
| m_vk.vkDestroySemaphore(m_vkDevice, m_frameDrawCompleteSem, nullptr); |
| m_vk.vkDestroyFence(m_vkDevice, m_frameDrawCompleteFence, nullptr); |
| m_compositorVk.reset(); |
| m_swapChainStateVk.reset(); |
| m_vk.vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr); |
| } |
| |
| void DisplayVk::bindToSurface(VkSurfaceKHR surface, uint32_t width, |
| uint32_t height) { |
| if (!SwapChainStateVk::validateQueueFamilyProperties( |
| m_vk, m_vkPhysicalDevice, surface, m_swapChainQueueFamilyIndex)) { |
| ERR("%s(%s:%d): DisplayVk can't create VkSwapchainKHR with given " |
| "VkDevice and VkSurfaceKHR.\n", |
| __FUNCTION__, __FILE__, static_cast<int>(__LINE__)); |
| ::abort(); |
| } |
| auto swapChainCi = SwapChainStateVk::createSwapChainCi( |
| m_vk, surface, m_vkPhysicalDevice, width, height, |
| {m_swapChainQueueFamilyIndex, m_compositorQueueFamilyIndex}); |
| VkFormatProperties formatProps; |
| m_vk.vkGetPhysicalDeviceFormatProperties( |
| m_vkPhysicalDevice, swapChainCi->imageFormat, &formatProps); |
| if (!(formatProps.optimalTilingFeatures & |
| VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) { |
| ERR("%s(%s:%d): DisplayVk: The image format chosen for present VkImage " |
| "can't be used as the color attachment, and therefore can't be " |
| "used as the render target of CompositorVk.\n", |
| __FUNCTION__, __FILE__, static_cast<int>(__LINE__)); |
| ::abort(); |
| } |
| m_swapChainStateVk = |
| std::make_unique<SwapChainStateVk>(m_vk, m_vkDevice, *swapChainCi); |
| m_compositorVk = CompositorVk::create( |
| m_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue, |
| m_swapChainStateVk->getFormat(), VK_IMAGE_LAYOUT_UNDEFINED, |
| VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, width, height, |
| m_swapChainStateVk->getVkImageViews(), m_vkCommandPool); |
| auto surfaceState = std::make_unique<SurfaceState>(); |
| surfaceState->m_height = height; |
| surfaceState->m_width = width; |
| m_surfaceState = std::move(surfaceState); |
| } |
| |
| std::shared_ptr<DisplayVk::DisplayBufferInfo> DisplayVk::createDisplayBuffer( |
| VkImage image, VkFormat format, uint32_t width, uint32_t height) { |
| return std::shared_ptr<DisplayBufferInfo>( |
| new DisplayBufferInfo(m_vk, m_vkDevice, width, height, format, image)); |
| } |
| |
| void DisplayVk::post( |
| const std::shared_ptr<DisplayBufferInfo> &displayBufferPtr) { |
| if (m_swapChainStateVk == nullptr || m_compositorVk == nullptr) { |
| ERR("%s(%s:%d): Haven't bound to a surface, can't post color buffer.\n", |
| __FUNCTION__, __FILE__, static_cast<int>(__LINE__)); |
| return; |
| } |
| auto &surfaceState = *m_surfaceState; |
| const auto &db = *displayBufferPtr; |
| |
| VK_CHECK(m_vk.vkWaitForFences(m_vkDevice, 1, &m_frameDrawCompleteFence, |
| VK_TRUE, UINT64_MAX)); |
| uint32_t imageIndex; |
| VK_CHECK(m_vk.vkAcquireNextImageKHR( |
| m_vkDevice, m_swapChainStateVk->getSwapChain(), UINT64_MAX, |
| m_imageReadySem, VK_NULL_HANDLE, &imageIndex)); |
| auto maybePrevDisplayBuffer = surfaceState.m_prevDisplayBuffer.lock(); |
| if (!maybePrevDisplayBuffer || maybePrevDisplayBuffer != displayBufferPtr) { |
| if (!canComposite(db.m_vkFormat)) { |
| ERR("%s(%s:%d): Can't composite the DisplayBuffer(0x%" PRIxPTR |
| "). The image(VkFormat = %" PRIu64 ") can be sampled from.\n", |
| __FUNCTION__, __FILE__, static_cast<int>(__LINE__), |
| reinterpret_cast<uintptr_t>(displayBufferPtr.get()), |
| static_cast<uint64_t>(db.m_vkFormat)); |
| return; |
| } |
| auto composition = std::make_unique<Composition>( |
| db.m_vkImageView, m_compositionVkSampler, db.m_width, db.m_height); |
| composition->m_transform = |
| glm::scale(glm::mat3(1.0), |
| glm::vec2(static_cast<float>(surfaceState.m_width) / |
| static_cast<float>(db.m_width), |
| static_cast<float>(surfaceState.m_height) / |
| static_cast<float>(db.m_height))); |
| m_compositorVk->setComposition(imageIndex, std::move(composition)); |
| surfaceState.m_prevDisplayBuffer = displayBufferPtr; |
| } |
| |
| auto cmdBuff = m_compositorVk->getCommandBuffer(imageIndex); |
| |
| VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &m_frameDrawCompleteFence)); |
| VkPipelineStageFlags waitStages[] = { |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; |
| VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| .waitSemaphoreCount = 1, |
| .pWaitSemaphores = &m_imageReadySem, |
| .pWaitDstStageMask = waitStages, |
| .commandBufferCount = 1, |
| .pCommandBuffers = &cmdBuff, |
| .signalSemaphoreCount = 1, |
| .pSignalSemaphores = &m_frameDrawCompleteSem}; |
| VK_CHECK(m_vk.vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo, |
| m_frameDrawCompleteFence)); |
| |
| auto swapChain = m_swapChainStateVk->getSwapChain(); |
| VkPresentInfoKHR presentInfo = {.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, |
| .waitSemaphoreCount = 1, |
| .pWaitSemaphores = &m_frameDrawCompleteSem, |
| .swapchainCount = 1, |
| .pSwapchains = &swapChain, |
| .pImageIndices = &imageIndex}; |
| VK_CHECK(m_vk.vkQueuePresentKHR(m_swapChainVkQueue, &presentInfo)); |
| VK_CHECK(m_vk.vkWaitForFences(m_vkDevice, 1, &m_frameDrawCompleteFence, |
| VK_TRUE, UINT64_MAX)); |
| } |
| |
| bool DisplayVk::canComposite(VkFormat format) { |
| auto it = m_canComposite.find(format); |
| if (it != m_canComposite.end()) { |
| return it->second; |
| } |
| VkFormatProperties formatProps = {}; |
| m_vk.vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, format, |
| &formatProps); |
| bool res = |
| formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; |
| m_canComposite.emplace(format, res); |
| return res; |
| } |
| |
| DisplayVk::DisplayBufferInfo::DisplayBufferInfo( |
| const goldfish_vk::VulkanDispatch &vk, VkDevice vkDevice, uint32_t width, |
| uint32_t height, VkFormat format, VkImage image) |
| : m_vk(vk), |
| m_vkDevice(vkDevice), |
| m_width(width), |
| m_height(height), |
| m_vkFormat(format), |
| m_vkImageView(VK_NULL_HANDLE) { |
| VkImageViewCreateInfo imageViewCi = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
| .image = image, |
| .viewType = VK_IMAGE_VIEW_TYPE_2D, |
| .format = format, |
| .components = {.r = VK_COMPONENT_SWIZZLE_IDENTITY, |
| .g = VK_COMPONENT_SWIZZLE_IDENTITY, |
| .b = VK_COMPONENT_SWIZZLE_IDENTITY, |
| .a = VK_COMPONENT_SWIZZLE_IDENTITY}, |
| .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .baseMipLevel = 0, |
| .levelCount = 1, |
| .baseArrayLayer = 0, |
| .layerCount = 1}}; |
| VK_CHECK(m_vk.vkCreateImageView(m_vkDevice, &imageViewCi, nullptr, |
| &m_vkImageView)); |
| } |
| |
| DisplayVk::DisplayBufferInfo::~DisplayBufferInfo() { |
| m_vk.vkDestroyImageView(m_vkDevice, m_vkImageView, nullptr); |
| } |