summaryrefslogtreecommitdiff
path: root/stream-servers/DisplayVk.cpp
diff options
context:
space:
mode:
authorKaiyi Li <kaiyili@google.com>2021-02-03 12:52:23 -0800
committerKaiyi Li <kaiyili@google.com>2021-02-06 13:13:20 -0800
commitea146f7ef6886373d4d53e2cca5d939a4621a5cc (patch)
treea9deceae4b863b950fea2c74a0ae337f2fe5cb62 /stream-servers/DisplayVk.cpp
parent38514f1650b7abc78a98a692ab16a41784cf2fd8 (diff)
downloadvulkan-cereal-ea146f7ef6886373d4d53e2cca5d939a4621a5cc.tar.gz
Native VK Swapchain: add DisplayVk to orchestrate the CompositorVk and SwapChainStateVk
DisplayVk is used to connect the compositor, swapchain and gfxstream renderer. Similar to FrameBuffer, DisplayVk also has a two stage initialization, first initialized by the constructor, then use the bindToSurface with a VkSurfaceKHR to complete the initialization. To draw a VkImage, first call importVkImage to specify an id to this image. Then call post with this id to draw. When using with FrameBuffer, the id is supposed to be the color buffer id. And post is supposed to be called in FrameBuffer::post() with the same id. DisplayVk::importVkImage and DisplayVk::releaseImport is supposed to track the lifetime of a VkImage backed ColorBuffer. In DisplayVk::post, a transform to stretch the ColorBuffer to fill the entire window is used. DisplayVk::post waits until the command buffer finishes execution. To change this function into an asynchronous function, add extra parameters to pass in sync objects, and signal those objects once the execution completes, but let the function returns once the command buffer is submitted. Test: DisplayVk_unittests Change-Id: I90c0e46947dfd3cd4c5c5c2c71ffbd987ca1c89d
Diffstat (limited to 'stream-servers/DisplayVk.cpp')
-rw-r--r--stream-servers/DisplayVk.cpp234
1 files changed, 234 insertions, 0 deletions
diff --git a/stream-servers/DisplayVk.cpp b/stream-servers/DisplayVk.cpp
new file mode 100644
index 00000000..8822e763
--- /dev/null
+++ b/stream-servers/DisplayVk.cpp
@@ -0,0 +1,234 @@
+#include "DisplayVk.h"
+
+#include "ErrorLog.h"
+#include "NativeSubWindow.h"
+
+#define GLM_FORCE_RADIANS
+#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES
+#include <glm/glm.hpp>
+#include <glm/gtx/matrix_transform_2d.hpp>
+
+static void repaintCallback(void *) {}
+
+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_swapChainVkQueue(swapChainVkqueue),
+ m_vkDevice(vkDevice),
+ m_compositorVkQueue(compositorVkQueue),
+ 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 = {};
+ commandPoolCi.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ commandPoolCi.queueFamilyIndex = m_compositorQueueFamilyIndex;
+ VK_CHECK(m_vk.vkCreateCommandPool(m_vkDevice, &commandPoolCi, nullptr,
+ &m_vkCommandPool));
+ VkFenceCreateInfo fenceCi = {};
+ fenceCi.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ fenceCi.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+ VK_CHECK(m_vk.vkCreateFence(m_vkDevice, &fenceCi, nullptr,
+ &m_frameDrawCompleteFence));
+ VkSemaphoreCreateInfo semaphoreCi = {};
+ 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 = {};
+ samplerCi.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ samplerCi.magFilter = VK_FILTER_NEAREST;
+ samplerCi.minFilter = VK_FILTER_NEAREST;
+ samplerCi.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ samplerCi.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ samplerCi.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ samplerCi.anisotropyEnable = VK_FALSE;
+ samplerCi.maxAnisotropy = 1.0f;
+ samplerCi.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
+ samplerCi.unnormalizedCoordinates = VK_FALSE;
+ samplerCi.compareEnable = VK_FALSE;
+ samplerCi.compareOp = VK_COMPARE_OP_ALWAYS;
+ samplerCi.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ samplerCi.mipLodBias = 0.0f;
+ samplerCi.minLod = 0.0f;
+ samplerCi.maxLod = 0.0f;
+ 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);
+}
+
+void DisplayVk::importVkImage(HandleType id, VkImage image, VkFormat format,
+ uint32_t width, uint32_t height) {
+ if (m_colorBuffers.find(id) != m_colorBuffers.end()) {
+ ERR("%s(%s:%d): DisplayVk can't import VkImage with duplicate id = "
+ "%" PRIu64 ".\n",
+ __FUNCTION__, __FILE__, static_cast<int>(__LINE__),
+ static_cast<uint64_t>(id));
+ ::abort();
+ }
+ ColorBufferInfo cbInfo = {};
+ cbInfo.m_width = width;
+ cbInfo.m_height = height;
+ cbInfo.m_vkFormat = format;
+
+ VkImageViewCreateInfo imageViewCi = {};
+ imageViewCi.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ imageViewCi.image = image;
+ imageViewCi.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ imageViewCi.format = format;
+ imageViewCi.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
+ imageViewCi.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
+ imageViewCi.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
+ imageViewCi.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
+ imageViewCi.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ imageViewCi.subresourceRange.baseMipLevel = 0;
+ imageViewCi.subresourceRange.levelCount = 1;
+ imageViewCi.subresourceRange.baseArrayLayer = 0;
+ imageViewCi.subresourceRange.layerCount = 1;
+ VK_CHECK(m_vk.vkCreateImageView(m_vkDevice, &imageViewCi, nullptr,
+ &cbInfo.m_vkImageView));
+ m_colorBuffers.emplace(id, cbInfo);
+}
+
+void DisplayVk::releaseImport(HandleType id) {
+ auto it = m_colorBuffers.find(id);
+ if (it == m_colorBuffers.end()) {
+ return;
+ }
+ m_vk.vkDestroyImageView(m_vkDevice, it->second.m_vkImageView, nullptr);
+ m_colorBuffers.erase(it);
+}
+
+void DisplayVk::post(HandleType id) {
+ 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;
+ }
+ const auto &surfaceState = *m_surfaceState;
+
+ 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));
+ if (!surfaceState.m_prevColorBuffer.has_value() ||
+ surfaceState.m_prevColorBuffer.value() != id) {
+ const auto &cb = m_colorBuffers.at(id);
+ if (!canComposite(cb.m_vkFormat)) {
+ ERR("%s(%s:%d): Can't composite the ColorBuffer(id = %" PRIu64
+ "). The image(VkFormat = %" PRIu64 ") can be sampled from.\n",
+ __FUNCTION__, __FILE__, static_cast<int>(__LINE__),
+ static_cast<uint64_t>(id),
+ static_cast<uint64_t>(cb.m_vkFormat));
+ return;
+ }
+ auto composition = std::make_unique<Composition>(
+ cb.m_vkImageView, m_compositionVkSampler, cb.m_width, cb.m_height);
+ composition->m_transform =
+ glm::scale(glm::mat3(1.0),
+ glm::vec2(static_cast<float>(surfaceState.m_width) /
+ static_cast<float>(cb.m_width),
+ static_cast<float>(surfaceState.m_height) /
+ static_cast<float>(cb.m_height)));
+ m_compositorVk->setComposition(imageIndex, std::move(composition));
+ }
+
+ 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 = {};
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 1;
+ submitInfo.pWaitSemaphores = &m_imageReadySem;
+ submitInfo.pWaitDstStageMask = waitStages;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &cmdBuff;
+ submitInfo.signalSemaphoreCount = 1;
+ submitInfo.pSignalSemaphores = &m_frameDrawCompleteSem;
+ VK_CHECK(m_vk.vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo,
+ m_frameDrawCompleteFence));
+
+ auto swapChain = m_swapChainStateVk->getSwapChain();
+ VkPresentInfoKHR presentInfo = {};
+ presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+ presentInfo.waitSemaphoreCount = 1;
+ presentInfo.pWaitSemaphores = &m_frameDrawCompleteSem;
+ presentInfo.swapchainCount = 1;
+ presentInfo.pSwapchains = &swapChain;
+ presentInfo.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;
+} \ No newline at end of file