summaryrefslogtreecommitdiff
path: root/libs/hwui/renderthread/VulkanManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/renderthread/VulkanManager.cpp')
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp239
1 files changed, 209 insertions, 30 deletions
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 038e13c513fd..b0d45052ca49 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -16,7 +16,8 @@
#include "VulkanManager.h"
-#include "DeviceInfo.h"
+#include <private/gui/SyncFeatures.h>
+
#include "Properties.h"
#include "RenderThread.h"
#include "renderstate/RenderState.h"
@@ -40,9 +41,12 @@ namespace renderthread {
VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
void VulkanManager::destroy() {
- mRenderThread.renderState().onContextDestroyed();
mRenderThread.setGrContext(nullptr);
+ // We don't need to explicitly free the command buffer since it automatically gets freed when we
+ // delete the VkCommandPool below.
+ mDummyCB = VK_NULL_HANDLE;
+
if (VK_NULL_HANDLE != mCommandPool) {
mDestroyCommandPool(mDevice, mCommandPool, nullptr);
mCommandPool = VK_NULL_HANDLE;
@@ -74,7 +78,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
0, // applicationVersion
"android framework", // pEngineName
0, // engineVerison
- VK_MAKE_VERSION(1, 0, 0), // apiVersion
+ VK_MAKE_VERSION(1, 1, 0), // apiVersion
};
std::vector<const char*> instanceExtensions;
@@ -129,6 +133,7 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
GET_INST_PROC(DestroyInstance);
GET_INST_PROC(EnumeratePhysicalDevices);
+ GET_INST_PROC(GetPhysicalDeviceProperties);
GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
GET_INST_PROC(GetPhysicalDeviceFeatures2);
GET_INST_PROC(CreateDevice);
@@ -160,6 +165,13 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
return false;
}
+ VkPhysicalDeviceProperties physDeviceProperties;
+ mGetPhysicalDeviceProperties(mPhysicalDevice, &physDeviceProperties);
+ if (physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
+ this->destroy();
+ return false;
+ }
+
// query to get the initial queue props size
uint32_t queueCount;
mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
@@ -228,6 +240,11 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(),
instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data());
+ if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
+ this->destroy();
+ return false;
+ }
+
memset(&features, 0, sizeof(VkPhysicalDeviceFeatures2));
features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
features.pNext = nullptr;
@@ -315,6 +332,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe
GET_DEV_PROC(DeviceWaitIdle);
GET_DEV_PROC(CreateSemaphore);
GET_DEV_PROC(DestroySemaphore);
+ GET_DEV_PROC(ImportSemaphoreFdKHR);
+ GET_DEV_PROC(GetSemaphoreFdKHR);
GET_DEV_PROC(CreateFence);
GET_DEV_PROC(DestroyFence);
GET_DEV_PROC(WaitForFences);
@@ -386,6 +405,14 @@ void VulkanManager::initialize() {
&mCommandPool);
SkASSERT(VK_SUCCESS == res);
}
+ LOG_ALWAYS_FATAL_IF(mCommandPool == VK_NULL_HANDLE);
+
+ if (!setupDummyCommandBuffer()) {
+ this->destroy();
+ return;
+ }
+ LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
@@ -399,13 +426,9 @@ void VulkanManager::initialize() {
free_features_extensions_structs(features);
- DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
-
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
mSwapBehavior = SwapBehavior::BufferAge;
}
-
- mRenderThread.renderState().onContextCreated();
}
// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
@@ -603,7 +626,8 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt
VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
- kRGBA_8888_SkColorType, nullptr, &props);
+ surface->mColorMode == ColorMode::WideColorGamut ? kRGBA_F16_SkColorType
+ : kRGBA_8888_SkColorType, nullptr, &props);
}
SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -718,24 +742,22 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) {
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
- // Pick our surface format. For now, just make sure it matches our sRGB request:
- VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
+ VkFormat surfaceFormat = VK_FORMAT_R8G8B8A8_UNORM;
VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
-
- bool wantSRGB = false;
-#ifdef ANDROID_ENABLE_LINEAR_BLENDING
- wantSRGB = true;
-#endif
+ if (surface->mColorMode == ColorMode::WideColorGamut) {
+ surfaceFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
+ colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
+ }
+ bool foundSurfaceFormat = false;
for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
- // We are assuming we can get either R8G8B8A8_UNORM or R8G8B8A8_SRGB
- VkFormat desiredFormat = wantSRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
- if (desiredFormat == surfaceFormats[i].format) {
- surfaceFormat = surfaceFormats[i].format;
- colorSpace = surfaceFormats[i].colorSpace;
+ if (surfaceFormat == surfaceFormats[i].format
+ && colorSpace == surfaceFormats[i].colorSpace) {
+ foundSurfaceFormat = true;
+ break;
}
}
- if (VK_FORMAT_UNDEFINED == surfaceFormat) {
+ if (!foundSurfaceFormat) {
return false;
}
@@ -797,14 +819,14 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) {
return true;
}
-VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) {
+VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode) {
initialize();
if (!window) {
return nullptr;
}
- VulkanSurface* surface = new VulkanSurface();
+ VulkanSurface* surface = new VulkanSurface(colorMode);
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
@@ -982,19 +1004,176 @@ int VulkanManager::getAge(VulkanSurface* surface) {
return surface->mCurrentTime - lastUsed;
}
+bool VulkanManager::setupDummyCommandBuffer() {
+ if (mDummyCB != VK_NULL_HANDLE) {
+ return true;
+ }
+
+ VkCommandBufferAllocateInfo commandBuffersInfo;
+ memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo));
+ commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ commandBuffersInfo.pNext = nullptr;
+ commandBuffersInfo.commandPool = mCommandPool;
+ commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ commandBuffersInfo.commandBufferCount = 1;
+
+ VkResult err = mAllocateCommandBuffers(mDevice, &commandBuffersInfo, &mDummyCB);
+ if (err != VK_SUCCESS) {
+ // It is probably unnecessary to set this back to VK_NULL_HANDLE, but we set it anyways to
+ // make sure the driver didn't set a value and then return a failure.
+ mDummyCB = VK_NULL_HANDLE;
+ return false;
+ }
+
+ VkCommandBufferBeginInfo beginInfo;
+ memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo));
+ beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
+
+ mBeginCommandBuffer(mDummyCB, &beginInfo);
+ mEndCommandBuffer(mDummyCB);
+ return true;
+}
+
status_t VulkanManager::fenceWait(sp<Fence>& fence) {
- //TODO: Insert a wait on fence command into the Vulkan command buffer.
- // Block CPU on the fence.
- status_t err = fence->waitForever("VulkanManager::fenceWait");
- if (err != NO_ERROR) {
- ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err);
- return err;
+ if (!hasVkContext()) {
+ ALOGE("VulkanManager::fenceWait: VkDevice not initialized");
+ return INVALID_OPERATION;
+ }
+
+ if (SyncFeatures::getInstance().useWaitSync() &&
+ SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Block GPU on the fence.
+ int fenceFd = fence->dup();
+ if (fenceFd == -1) {
+ ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = nullptr;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+ VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("Failed to create import semaphore, err: %d", err);
+ return UNKNOWN_ERROR;
+ }
+ VkImportSemaphoreFdInfoKHR importInfo;
+ importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+ importInfo.pNext = nullptr;
+ importInfo.semaphore = semaphore;
+ importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
+ importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+ importInfo.fd = fenceFd;
+
+ err = mImportSemaphoreFdKHR(mDevice, &importInfo);
+ if (VK_SUCCESS != err) {
+ ALOGE("Failed to import semaphore, err: %d", err);
+ return UNKNOWN_ERROR;
+ }
+
+ LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
+ VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 1;
+ // Wait to make sure aquire semaphore set above has signaled.
+ submitInfo.pWaitSemaphores = &semaphore;
+ submitInfo.pWaitDstStageMask = &waitDstStageFlags;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &mDummyCB;
+ submitInfo.signalSemaphoreCount = 0;
+
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
+
+ // On Android when we import a semaphore, it is imported using temporary permanence. That
+ // means as soon as we queue the semaphore for a wait it reverts to its previous permanent
+ // state before importing. This means it will now be in an idle state with no pending
+ // signal or wait operations, so it is safe to immediately delete it.
+ mDestroySemaphore(mDevice, semaphore, nullptr);
+ } else {
+ // Block CPU on the fence.
+ status_t err = fence->waitForever("VulkanManager::fenceWait");
+ if (err != NO_ERROR) {
+ ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err);
+ return err;
+ }
}
return OK;
}
status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence) {
- //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed.
+ if (!hasVkContext()) {
+ ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized");
+ return INVALID_OPERATION;
+ }
+
+ if (SyncFeatures::getInstance().useFenceSync()) {
+ ALOGE("VulkanManager::createReleaseFence: Vk backend doesn't support non-native fences");
+ return INVALID_OPERATION;
+ }
+
+ if (!SyncFeatures::getInstance().useNativeFenceSync()) {
+ return OK;
+ }
+
+ VkExportSemaphoreCreateInfo exportInfo;
+ exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+ exportInfo.pNext = nullptr;
+ exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = &exportInfo;
+ semaphoreInfo.flags = 0;
+ VkSemaphore semaphore;
+ VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("VulkanManager::createReleaseFence: Failed to create semaphore");
+ return INVALID_OPERATION;
+ }
+
+ LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE);
+
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 0;
+ submitInfo.pWaitSemaphores = nullptr;
+ submitInfo.pWaitDstStageMask = nullptr;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &mDummyCB;
+ submitInfo.signalSemaphoreCount = 1;
+ submitInfo.pSignalSemaphores = &semaphore;
+
+ mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
+
+ VkSemaphoreGetFdInfoKHR getFdInfo;
+ getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+ getFdInfo.pNext = nullptr;
+ getFdInfo.semaphore = semaphore;
+ getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ int fenceFd = 0;
+
+ err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+ if (VK_SUCCESS != err) {
+ ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd");
+ return INVALID_OPERATION;
+ }
+ nativeFence = new Fence(fenceFd);
+
+ // Exporting a semaphore with copy transference via vkGetSemahporeFdKHR, has the same effect of
+ // destroying the semaphore and creating a new one with the same handle, and the payloads
+ // ownership is move to the Fd we created. Thus the semahpore is in a state that we can delete
+ // it and we don't need to wait on the command buffer we submitted to finish.
+ mDestroySemaphore(mDevice, semaphore, nullptr);
+
return OK;
}