unplugged-system/device/generic/vulkan-cereal/stream-servers/vulkan/DisplayVk.cpp

767 lines
36 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "DisplayVk.h"
#include <algorithm>
#include <glm/glm.hpp>
#include <glm/gtx/matrix_transform_2d.hpp>
#include "host-common/GfxstreamFatalError.h"
#include "host-common/logging.h"
#include "vulkan/VkFormatUtils.h"
#include "vulkan/vk_enum_string_helper.h"
namespace gfxstream {
namespace vk {
using emugl::ABORT_REASON_OTHER;
using emugl::FatalError;
#define DISPLAY_VK_ERROR(fmt, ...) \
do { \
fprintf(stderr, "%s(%s:%d): " fmt "\n", __func__, __FILE__, __LINE__, ##__VA_ARGS__); \
fflush(stderr); \
} while (0)
#define DISPLAY_VK_ERROR_ONCE(fmt, ...) \
do { \
static bool displayVkInternalLogged = false; \
if (!displayVkInternalLogged) { \
DISPLAY_VK_ERROR(fmt, ##__VA_ARGS__); \
displayVkInternalLogged = true; \
} \
} while (0)
namespace {
bool shouldRecreateSwapchain(VkResult result) {
switch (result) {
case VK_SUBOPTIMAL_KHR:
case VK_ERROR_OUT_OF_DATE_KHR:
// b/217229121: drivers may return VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT in
// vkQueuePresentKHR even if VK_EXT_full_screen_exclusive is not enabled.
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
return true;
default:
return false;
}
}
} // namespace
DisplayVk::DisplayVk(const VulkanDispatch& vk, VkPhysicalDevice vkPhysicalDevice,
uint32_t swapChainQueueFamilyIndex, uint32_t compositorQueueFamilyIndex,
VkDevice vkDevice, VkQueue compositorVkQueue,
std::shared_ptr<android::base::Lock> compositorVkQueueLock,
VkQueue swapChainVkqueue,
std::shared_ptr<android::base::Lock> swapChainVkQueueLock)
: m_vk(vk),
m_vkPhysicalDevice(vkPhysicalDevice),
m_swapChainQueueFamilyIndex(swapChainQueueFamilyIndex),
m_compositorQueueFamilyIndex(compositorQueueFamilyIndex),
m_vkDevice(vkDevice),
m_compositorVkQueue(compositorVkQueue),
m_compositorVkQueueLock(compositorVkQueueLock),
m_swapChainVkQueue(swapChainVkqueue),
m_swapChainVkQueueLock(swapChainVkQueueLock),
m_vkCommandPool(VK_NULL_HANDLE),
m_swapChainStateVk(nullptr) {
// TODO(kaiyili): validate the capabilites of the passed in Vulkan
// components.
VkCommandPoolCreateInfo commandPoolCi = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = m_compositorQueueFamilyIndex,
};
VK_CHECK(m_vk.vkCreateCommandPool(m_vkDevice, &commandPoolCi, nullptr, &m_vkCommandPool));
constexpr size_t imageBorrowResourcePoolSize = 10;
for (size_t i = 0; i < imageBorrowResourcePoolSize; i++) {
m_imageBorrowResources.emplace_back(
ImageBorrowResource::create(m_vk, m_vkDevice, m_vkCommandPool));
}
}
DisplayVk::~DisplayVk() {
destroySwapchain();
m_imageBorrowResources.clear();
m_vk.vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr);
}
void DisplayVk::drainQueues() {
{
android::base::AutoLock lock(*m_swapChainVkQueueLock);
VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_swapChainVkQueue));
}
// We don't assume all VkCommandBuffer submitted to m_compositorVkQueueLock is always followed
// by another operation on the m_swapChainVkQueue. Therefore, only waiting for the
// m_swapChainVkQueue is not enough to guarantee all resources used are free to be destroyed.
{
android::base::AutoLock lock(*m_compositorVkQueueLock);
VK_CHECK(vk_util::waitForVkQueueIdleWithRetry(m_vk, m_compositorVkQueue));
}
}
void DisplayVk::bindToSurfaceImpl(gfxstream::DisplaySurface* surface) {
m_needToRecreateSwapChain = true;
}
void DisplayVk::surfaceUpdated(gfxstream::DisplaySurface* surface) {
m_needToRecreateSwapChain = true;
}
void DisplayVk::unbindFromSurfaceImpl() {
destroySwapchain();
}
void DisplayVk::destroySwapchain() {
drainQueues();
m_freePostResources.clear();
m_postResourceFutures.clear();
m_swapChainStateVk.reset();
m_needToRecreateSwapChain = true;
}
bool DisplayVk::recreateSwapchain() {
destroySwapchain();
const auto* surface = getBoundSurface();
if (!surface) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "DisplayVk can't create VkSwapchainKHR without a VkSurfaceKHR";
}
const auto* surfaceVk = static_cast<const DisplaySurfaceVk*>(surface->getImpl());
if (!SwapChainStateVk::validateQueueFamilyProperties(m_vk, m_vkPhysicalDevice,
surfaceVk->getSurface(),
m_swapChainQueueFamilyIndex)) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "DisplayVk can't create VkSwapchainKHR with given VkDevice and VkSurfaceKHR.";
}
INFO("Creating swapchain with size %" PRIu32 "x%" PRIu32 ".", surface->getWidth(),
surface->getHeight());
auto swapChainCi = SwapChainStateVk::createSwapChainCi(
m_vk, surfaceVk->getSurface(), m_vkPhysicalDevice, surface->getWidth(),
surface->getHeight(), {m_swapChainQueueFamilyIndex, m_compositorQueueFamilyIndex});
if (!swapChainCi) {
return false;
}
VkFormatProperties formatProps;
m_vk.vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice,
swapChainCi->mCreateInfo.imageFormat, &formatProps);
if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "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.";
}
m_swapChainStateVk =
SwapChainStateVk::createSwapChainVk(m_vk, m_vkDevice, swapChainCi->mCreateInfo);
if (m_swapChainStateVk == nullptr) return false;
int numSwapChainImages = m_swapChainStateVk->getVkImages().size();
m_postResourceFutures.resize(numSwapChainImages, std::nullopt);
for (uint32_t i = 0; i < numSwapChainImages + 1; ++i) {
m_freePostResources.emplace_back(PostResource::create(m_vk, m_vkDevice, m_vkCommandPool));
}
m_inFlightFrameIndex = 0;
m_needToRecreateSwapChain = false;
return true;
}
DisplayVk::PostResult DisplayVk::post(const BorrowedImageInfo* sourceImageInfo) {
auto completedFuture = std::async(std::launch::deferred, [] {}).share();
completedFuture.wait();
const auto* surface = getBoundSurface();
if (!surface) {
ERR("Trying to present to non-existing surface!");
return PostResult{
.success = true,
.postCompletedWaitable = completedFuture,
};
}
if (m_needToRecreateSwapChain) {
INFO("Recreating swapchain...");
constexpr const int kMaxRecreateSwapchainRetries = 8;
int retriesRemaining = kMaxRecreateSwapchainRetries;
while (retriesRemaining >= 0 && !recreateSwapchain()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
--retriesRemaining;
INFO("Swapchain recreation failed, retrying...");
}
if (retriesRemaining < 0) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "Failed to create Swapchain."
<< " w:" << surface->getWidth()
<< " h:" << surface->getHeight();
}
INFO("Recreating swapchain completed.");
}
auto result = postImpl(sourceImageInfo);
if (!result.success) {
m_needToRecreateSwapChain = true;
}
return result;
}
DisplayVk::PostResult DisplayVk::postImpl(
const BorrowedImageInfo* sourceImageInfo) {
auto completedFuture = std::async(std::launch::deferred, [] {}).share();
completedFuture.wait();
// One for acquire, one for release.
const ImageBorrowResource* imageBorrowResources[2] = {nullptr};
for (size_t i = 0; i < std::size(imageBorrowResources); i++) {
auto freeImageBorrowResource =
std::find_if(m_imageBorrowResources.begin(), m_imageBorrowResources.end(),
[this](const std::unique_ptr<ImageBorrowResource>& imageBorrowResource) {
VkResult fenceStatus = m_vk.vkGetFenceStatus(
m_vkDevice, imageBorrowResource->m_completeFence);
if (fenceStatus == VK_SUCCESS) { return true; }
if (fenceStatus == VK_NOT_READY) { return false; }
VK_CHECK(fenceStatus);
return false;
});
if (freeImageBorrowResource == m_imageBorrowResources.end()) {
freeImageBorrowResource = m_imageBorrowResources.begin();
VK_CHECK(m_vk.vkWaitForFences(
m_vkDevice, 1, &(*freeImageBorrowResource)->m_completeFence, VK_TRUE, UINT64_MAX));
}
VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &(*freeImageBorrowResource)->m_completeFence));
imageBorrowResources[i] = freeImageBorrowResource->get();
}
// We need to unconditionally acquire and release the image to satisfy the requiremment for the
// borrowed image.
const auto* sourceImageInfoVk = static_cast<const BorrowedImageInfoVk*>(sourceImageInfo);
struct ImageBorrower {
ImageBorrower(const VulkanDispatch& vk, VkQueue queue,
std::shared_ptr<android::base::Lock> queueLock, uint32_t usedQueueFamilyIndex,
const BorrowedImageInfoVk& image, const ImageBorrowResource& acquireResource,
const ImageBorrowResource& releaseResource)
: m_vk(vk),
m_vkQueue(queue),
m_queueLock(queueLock),
m_releaseResource(releaseResource) {
std::vector<VkImageMemoryBarrier> acquireQueueTransferBarriers;
std::vector<VkImageMemoryBarrier> acquireLayoutTransitionBarriers;
std::vector<VkImageMemoryBarrier> releaseLayoutTransitionBarriers;
std::vector<VkImageMemoryBarrier> releaseQueueTransferBarriers;
addNeededBarriersToUseBorrowedImage(
image, usedQueueFamilyIndex,
/*usedInitialImageLayout=*/VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
/*usedFinalImageLayout=*/VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT, &acquireQueueTransferBarriers,
&acquireLayoutTransitionBarriers, &releaseLayoutTransitionBarriers,
&releaseQueueTransferBarriers);
// Record the acquire commands.
const VkCommandBufferBeginInfo acquireBeginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
VK_CHECK(
m_vk.vkBeginCommandBuffer(acquireResource.m_vkCommandBuffer, &acquireBeginInfo));
if (!acquireQueueTransferBarriers.empty()) {
m_vk.vkCmdPipelineBarrier(
acquireResource.m_vkCommandBuffer,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr,
static_cast<uint32_t>(acquireQueueTransferBarriers.size()),
acquireQueueTransferBarriers.data());
}
if (!acquireLayoutTransitionBarriers.empty()) {
m_vk.vkCmdPipelineBarrier(
acquireResource.m_vkCommandBuffer,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr,
static_cast<uint32_t>(acquireLayoutTransitionBarriers.size()),
acquireLayoutTransitionBarriers.data());
}
VK_CHECK(m_vk.vkEndCommandBuffer(acquireResource.m_vkCommandBuffer));
// Record the release commands.
const VkCommandBufferBeginInfo releaseBeginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
VK_CHECK(
m_vk.vkBeginCommandBuffer(releaseResource.m_vkCommandBuffer, &releaseBeginInfo));
if (!releaseLayoutTransitionBarriers.empty()) {
m_vk.vkCmdPipelineBarrier(
releaseResource.m_vkCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
static_cast<uint32_t>(releaseLayoutTransitionBarriers.size()),
releaseLayoutTransitionBarriers.data());
}
if (!releaseQueueTransferBarriers.empty()) {
m_vk.vkCmdPipelineBarrier(
releaseResource.m_vkCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr,
static_cast<uint32_t>(releaseQueueTransferBarriers.size()),
releaseQueueTransferBarriers.data());
}
VK_CHECK(m_vk.vkEndCommandBuffer(releaseResource.m_vkCommandBuffer));
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 1,
.pCommandBuffers = &acquireResource.m_vkCommandBuffer,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
// Submit the acquire commands.
{
android::base::AutoLock lock(*m_queueLock);
VK_CHECK(
m_vk.vkQueueSubmit(m_vkQueue, 1, &submitInfo, acquireResource.m_completeFence));
}
}
const VulkanDispatch& m_vk;
const VkQueue m_vkQueue;
std::shared_ptr<android::base::Lock> m_queueLock;
const ImageBorrowResource& m_releaseResource;
~ImageBorrower() {
VkSubmitInfo submitInfo = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = nullptr,
.commandBufferCount = 1,
.pCommandBuffers = &m_releaseResource.m_vkCommandBuffer,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
// Submit the release commands.
{
android::base::AutoLock lock(*m_queueLock);
VK_CHECK(m_vk.vkQueueSubmit(m_vkQueue, 1, &submitInfo,
m_releaseResource.m_completeFence));
}
}
} imageBorrower(m_vk, m_compositorVkQueue, m_compositorVkQueueLock,
m_compositorQueueFamilyIndex, *sourceImageInfoVk, *imageBorrowResources[0],
*imageBorrowResources[1]);
const auto* surface = getBoundSurface();
if (!m_swapChainStateVk || !surface) {
DISPLAY_VK_ERROR("Haven't bound to a surface, can't post ColorBuffer.");
return PostResult{true, std::move(completedFuture)};
}
if (!canPost(sourceImageInfoVk->imageCreateInfo)) {
DISPLAY_VK_ERROR("Can't post ColorBuffer.");
return PostResult{true, std::move(completedFuture)};
}
for (auto& postResourceFutureOpt : m_postResourceFutures) {
if (!postResourceFutureOpt.has_value()) {
continue;
}
auto postResourceFuture = postResourceFutureOpt.value();
if (!postResourceFuture.valid()) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "Invalid postResourceFuture in m_postResourceFutures.";
}
std::future_status status = postResourceFuture.wait_for(std::chrono::seconds(0));
if (status == std::future_status::ready) {
m_freePostResources.emplace_back(postResourceFuture.get());
postResourceFutureOpt = std::nullopt;
}
}
if (m_freePostResources.empty()) {
for (auto& postResourceFutureOpt : m_postResourceFutures) {
if (!postResourceFutureOpt.has_value()) {
continue;
}
m_freePostResources.emplace_back(postResourceFutureOpt.value().get());
postResourceFutureOpt = std::nullopt;
break;
}
}
std::shared_ptr<PostResource> postResource = m_freePostResources.front();
m_freePostResources.pop_front();
VkSemaphore imageReadySem = postResource->m_swapchainImageAcquireSemaphore;
uint32_t imageIndex;
VkResult acquireRes =
m_vk.vkAcquireNextImageKHR(m_vkDevice, m_swapChainStateVk->getSwapChain(), UINT64_MAX,
imageReadySem, VK_NULL_HANDLE, &imageIndex);
if (shouldRecreateSwapchain(acquireRes)) {
return PostResult{false, std::shared_future<void>()};
}
VK_CHECK(acquireRes);
if (m_postResourceFutures[imageIndex].has_value()) {
m_freePostResources.emplace_back(m_postResourceFutures[imageIndex].value().get());
m_postResourceFutures[imageIndex] = std::nullopt;
}
VkCommandBuffer cmdBuff = postResource->m_vkCommandBuffer;
VK_CHECK(m_vk.vkResetCommandBuffer(cmdBuff, 0));
const VkCommandBufferBeginInfo beginInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
VK_CHECK(m_vk.vkBeginCommandBuffer(cmdBuff, &beginInfo));
VkImageMemoryBarrier acquireSwapchainImageBarrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.pNext = nullptr,
.srcAccessMask = VK_PIPELINE_STAGE_TRANSFER_BIT,
.dstAccessMask = VK_PIPELINE_STAGE_TRANSFER_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = m_swapChainStateVk->getVkImages()[imageIndex],
.subresourceRange =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1,
&acquireSwapchainImageBarrier);
// Note: The extent used during swapchain creation must be used here and not the
// current surface's extent as the swapchain may not have been updated after the
// surface resized. The blit must not try to write outside of the extent of the
// existing swapchain images.
const VkExtent2D swapchainImageExtent = m_swapChainStateVk->getImageExtent();
const VkImageBlit region = {
.srcSubresource = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1},
.srcOffsets = {{0, 0, 0},
{static_cast<int32_t>(sourceImageInfoVk->imageCreateInfo.extent.width),
static_cast<int32_t>(sourceImageInfoVk->imageCreateInfo.extent.height), 1}},
.dstSubresource = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1},
.dstOffsets = {{0, 0, 0},
{static_cast<int32_t>(swapchainImageExtent.width),
static_cast<int32_t>(swapchainImageExtent.height), 1}},
};
VkFormat displayBufferFormat = sourceImageInfoVk->imageCreateInfo.format;
VkImageTiling displayBufferTiling = sourceImageInfoVk->imageCreateInfo.tiling;
VkFilter filter = VK_FILTER_NEAREST;
VkFormatFeatureFlags displayBufferFormatFeatures =
getFormatFeatures(displayBufferFormat, displayBufferTiling);
if (formatIsDepthOrStencil(displayBufferFormat)) {
DISPLAY_VK_ERROR_ONCE(
"The format of the display buffer, %s, is a depth/stencil format, we can only use the "
"VK_FILTER_NEAREST filter according to VUID-vkCmdBlitImage-srcImage-00232.",
string_VkFormat(displayBufferFormat));
filter = VK_FILTER_NEAREST;
} else if (!(displayBufferFormatFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
DISPLAY_VK_ERROR_ONCE(
"The format of the display buffer, %s, with the tiling, %s, doesn't support "
"VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT, so we can only use the "
"VK_FILTER_NEAREST filter according VUID-vkCmdBlitImage-filter-02001. The supported "
"features are %s.",
string_VkFormat(displayBufferFormat), string_VkImageTiling(displayBufferTiling),
string_VkFormatFeatureFlags(displayBufferFormatFeatures).c_str());
filter = VK_FILTER_NEAREST;
} else {
filter = VK_FILTER_LINEAR;
}
m_vk.vkCmdBlitImage(cmdBuff, sourceImageInfoVk->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
m_swapChainStateVk->getVkImages()[imageIndex],
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region, filter);
VkImageMemoryBarrier releaseSwapchainImageBarrier = {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_PIPELINE_STAGE_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = m_swapChainStateVk->getVkImages()[imageIndex],
.subresourceRange =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
m_vk.vkCmdPipelineBarrier(cmdBuff, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1,
&releaseSwapchainImageBarrier);
VK_CHECK(m_vk.vkEndCommandBuffer(cmdBuff));
VkFence postCompleteFence = postResource->m_swapchainImageReleaseFence;
VK_CHECK(m_vk.vkResetFences(m_vkDevice, 1, &postCompleteFence));
VkSemaphore postCompleteSemaphore = postResource->m_swapchainImageReleaseSemaphore;
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_TRANSFER_BIT};
VkSubmitInfo submitInfo = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &imageReadySem,
.pWaitDstStageMask = waitStages,
.commandBufferCount = 1,
.pCommandBuffers = &cmdBuff,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &postCompleteSemaphore};
{
android::base::AutoLock lock(*m_compositorVkQueueLock);
VK_CHECK(m_vk.vkQueueSubmit(m_compositorVkQueue, 1, &submitInfo, postCompleteFence));
}
std::shared_future<std::shared_ptr<PostResource>> postResourceFuture =
std::async(std::launch::deferred, [postCompleteFence, postResource, this]() mutable {
VkResult res = m_vk.vkWaitForFences(m_vkDevice, 1, &postCompleteFence, VK_TRUE,
kVkWaitForFencesTimeoutNsecs);
if (res == VK_SUCCESS) {
return postResource;
}
if (res == VK_TIMEOUT) {
// Retry. If device lost, hopefully this returns immediately.
res = m_vk.vkWaitForFences(m_vkDevice, 1, &postCompleteFence, VK_TRUE,
kVkWaitForFencesTimeoutNsecs);
}
VK_CHECK(res);
return postResource;
}).share();
m_postResourceFutures[imageIndex] = postResourceFuture;
auto swapChain = m_swapChainStateVk->getSwapChain();
VkPresentInfoKHR presentInfo = {.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &postCompleteSemaphore,
.swapchainCount = 1,
.pSwapchains = &swapChain,
.pImageIndices = &imageIndex};
VkResult presentRes;
{
android::base::AutoLock lock(*m_swapChainVkQueueLock);
presentRes = m_vk.vkQueuePresentKHR(m_swapChainVkQueue, &presentInfo);
}
if (shouldRecreateSwapchain(presentRes)) {
postResourceFuture.wait();
return PostResult{false, std::shared_future<void>()};
}
VK_CHECK(presentRes);
return PostResult{true, std::async(std::launch::deferred, [postResourceFuture] {
// We can't directly wait for the VkFence here, because we
// share the VkFences on different frames, but we don't share
// the future on different frames. If we directly wait for the
// VkFence here, we may wait for a different frame if a new
// frame starts to be drawn before this future is waited.
postResourceFuture.wait();
}).share()};
}
VkFormatFeatureFlags DisplayVk::getFormatFeatures(VkFormat format, VkImageTiling tiling) {
auto i = m_vkFormatProperties.find(format);
if (i == m_vkFormatProperties.end()) {
VkFormatProperties formatProperties;
m_vk.vkGetPhysicalDeviceFormatProperties(m_vkPhysicalDevice, format, &formatProperties);
i = m_vkFormatProperties.emplace(format, formatProperties).first;
}
const VkFormatProperties& formatProperties = i->second;
VkFormatFeatureFlags formatFeatures = 0;
if (tiling == VK_IMAGE_TILING_LINEAR) {
formatFeatures = formatProperties.linearTilingFeatures;
} else if (tiling == VK_IMAGE_TILING_OPTIMAL) {
formatFeatures = formatProperties.optimalTilingFeatures;
} else {
DISPLAY_VK_ERROR("Unknown tiling %#" PRIx64 ".", static_cast<uint64_t>(tiling));
}
return formatFeatures;
}
bool DisplayVk::canPost(const VkImageCreateInfo& postImageCi) {
// According to VUID-vkCmdBlitImage-srcImage-01999, the format features of srcImage must contain
// VK_FORMAT_FEATURE_BLIT_SRC_BIT.
VkFormatFeatureFlags formatFeatures = getFormatFeatures(postImageCi.format, postImageCi.tiling);
if (!(formatFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {
DISPLAY_VK_ERROR(
"VK_FORMAT_FEATURE_BLIT_SRC_BLIT is not supported for VkImage with format %s, tilling "
"%s. Supported features are %s.",
string_VkFormat(postImageCi.format), string_VkImageTiling(postImageCi.tiling),
string_VkFormatFeatureFlags(formatFeatures).c_str());
return false;
}
// According to VUID-vkCmdBlitImage-srcImage-06421, srcImage must not use a format that requires
// a sampler YCBCR conversion.
if (formatRequiresSamplerYcbcrConversion(postImageCi.format)) {
DISPLAY_VK_ERROR("Format %s requires a sampler Y'CbCr conversion. Can't be used to post.",
string_VkFormat(postImageCi.format));
return false;
}
if (!(postImageCi.usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT)) {
// According to VUID-vkCmdBlitImage-srcImage-00219, srcImage must have been created with
// VK_IMAGE_USAGE_TRANSFER_SRC_BIT usage flag.
DISPLAY_VK_ERROR(
"The VkImage is not created with the VK_IMAGE_USAGE_TRANSFER_SRC_BIT usage flag. The "
"usage flags are %s.",
string_VkImageUsageFlags(postImageCi.usage).c_str());
return false;
}
VkFormat swapChainFormat = m_swapChainStateVk->getFormat();
if (formatIsSInt(postImageCi.format) || formatIsSInt(swapChainFormat)) {
// According to VUID-vkCmdBlitImage-srcImage-00229, if either of srcImage or dstImage was
// created with a signed integer VkFormat, the other must also have been created with a
// signed integer VkFormat.
if (!(formatIsSInt(postImageCi.format) && formatIsSInt(m_swapChainStateVk->getFormat()))) {
DISPLAY_VK_ERROR(
"The format(%s) doesn't match with the format of the presentable image(%s): either "
"of the formats is a signed integer VkFormat, but the other is not.",
string_VkFormat(postImageCi.format), string_VkFormat(swapChainFormat));
return false;
}
}
if (formatIsUInt(postImageCi.format) || formatIsUInt(swapChainFormat)) {
// According to VUID-vkCmdBlitImage-srcImage-00230, if either of srcImage or dstImage was
// created with an unsigned integer VkFormat, the other must also have been created with an
// unsigned integer VkFormat.
if (!(formatIsUInt(postImageCi.format) && formatIsUInt(swapChainFormat))) {
DISPLAY_VK_ERROR(
"The format(%s) doesn't match with the format of the presentable image(%s): either "
"of the formats is an unsigned integer VkFormat, but the other is not.",
string_VkFormat(postImageCi.format), string_VkFormat(swapChainFormat));
return false;
}
}
if (formatIsDepthOrStencil(postImageCi.format) || formatIsDepthOrStencil(swapChainFormat)) {
// According to VUID-vkCmdBlitImage-srcImage-00231, if either of srcImage or dstImage was
// created with a depth/stencil format, the other must have exactly the same format.
if (postImageCi.format != swapChainFormat) {
DISPLAY_VK_ERROR(
"The format(%s) doesn't match with the format of the presentable image(%s): either "
"of the formats is a depth/stencil VkFormat, but the other is not the same format.",
string_VkFormat(postImageCi.format), string_VkFormat(swapChainFormat));
return false;
}
}
if (postImageCi.samples != VK_SAMPLE_COUNT_1_BIT) {
// According to VUID-vkCmdBlitImage-srcImage-00233, srcImage must have been created with a
// samples value of VK_SAMPLE_COUNT_1_BIT.
DISPLAY_VK_ERROR(
"The VkImage is not created with the VK_SAMPLE_COUNT_1_BIT samples value. The samples "
"value is %s.",
string_VkSampleCountFlagBits(postImageCi.samples));
return false;
}
if (postImageCi.flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) {
// According to VUID-vkCmdBlitImage-dstImage-02545, dstImage and srcImage must not have been
// created with flags containing VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT.
DISPLAY_VK_ERROR(
"The VkImage can't be created with flags containing "
"VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT. The flags are %s.",
string_VkImageCreateFlags(postImageCi.flags).c_str());
return false;
}
return true;
}
std::shared_ptr<DisplayVk::PostResource> DisplayVk::PostResource::create(
const VulkanDispatch& vk, VkDevice vkDevice, VkCommandPool vkCommandPool) {
VkFenceCreateInfo fenceCi = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
};
VkFence fence;
VK_CHECK(vk.vkCreateFence(vkDevice, &fenceCi, nullptr, &fence));
VkSemaphore semaphores[2];
for (uint32_t i = 0; i < std::size(semaphores); i++) {
VkSemaphoreCreateInfo semaphoreCi = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
VK_CHECK(vk.vkCreateSemaphore(vkDevice, &semaphoreCi, nullptr, &semaphores[i]));
}
VkCommandBuffer commandBuffer;
VkCommandBufferAllocateInfo commandBufferAllocInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = vkCommandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
VK_CHECK(vk.vkAllocateCommandBuffers(vkDevice, &commandBufferAllocInfo, &commandBuffer));
return std::shared_ptr<PostResource>(new PostResource(
vk, vkDevice, vkCommandPool, fence, semaphores[0], semaphores[1], commandBuffer));
}
DisplayVk::PostResource::~PostResource() {
m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &m_vkCommandBuffer);
m_vk.vkDestroyFence(m_vkDevice, m_swapchainImageReleaseFence, nullptr);
m_vk.vkDestroySemaphore(m_vkDevice, m_swapchainImageAcquireSemaphore, nullptr);
m_vk.vkDestroySemaphore(m_vkDevice, m_swapchainImageReleaseSemaphore, nullptr);
}
DisplayVk::PostResource::PostResource(const VulkanDispatch& vk, VkDevice vkDevice,
VkCommandPool vkCommandPool,
VkFence swapchainImageReleaseFence,
VkSemaphore swapchainImageAcquireSemaphore,
VkSemaphore swapchainImageReleaseSemaphore,
VkCommandBuffer vkCommandBuffer)
: m_swapchainImageReleaseFence(swapchainImageReleaseFence),
m_swapchainImageAcquireSemaphore(swapchainImageAcquireSemaphore),
m_swapchainImageReleaseSemaphore(swapchainImageReleaseSemaphore),
m_vkCommandBuffer(vkCommandBuffer),
m_vk(vk),
m_vkDevice(vkDevice),
m_vkCommandPool(vkCommandPool) {}
std::unique_ptr<DisplayVk::ImageBorrowResource> DisplayVk::ImageBorrowResource::create(
const VulkanDispatch& vk, VkDevice device, VkCommandPool commandPool) {
const VkCommandBufferAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.pNext = nullptr,
.commandPool = commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
VK_CHECK(vk.vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer));
const VkFenceCreateInfo fenceCi = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.pNext = nullptr,
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
};
VkFence fence = VK_NULL_HANDLE;
VK_CHECK(vk.vkCreateFence(device, &fenceCi, nullptr, &fence));
return std::unique_ptr<ImageBorrowResource>(
new ImageBorrowResource(vk, device, commandPool, fence, commandBuffer));
}
DisplayVk::ImageBorrowResource::~ImageBorrowResource() {
m_vk.vkFreeCommandBuffers(m_vkDevice, m_vkCommandPool, 1, &m_vkCommandBuffer);
}
DisplayVk::ImageBorrowResource::ImageBorrowResource(const VulkanDispatch& vk, VkDevice device,
VkCommandPool commandPool, VkFence fence,
VkCommandBuffer commandBuffer)
: m_completeFence(fence),
m_vkCommandBuffer(commandBuffer),
m_vk(vk),
m_vkDevice(device),
m_vkCommandPool(commandPool) {}
} // namespace vk
} // namespace gfxstream