diff options
-rw-r--r-- | libs/hwui/Android.bp | 2 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp | 22 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaVulkanPipeline.h | 1 | ||||
-rw-r--r-- | libs/hwui/renderthread/VulkanManager.cpp | 712 | ||||
-rw-r--r-- | libs/hwui/renderthread/VulkanManager.h | 101 | ||||
-rw-r--r-- | libs/hwui/renderthread/VulkanSurface.cpp | 536 | ||||
-rw-r--r-- | libs/hwui/renderthread/VulkanSurface.h | 127 |
7 files changed, 765 insertions, 736 deletions
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 793dd8d39376..4f1b2a4fcbf8 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -69,6 +69,7 @@ cc_defaults { "libminikin", "libandroidfw", "libcrypto", + "libsync", ], static_libs: [ "libEGL_blobCache", @@ -180,6 +181,7 @@ cc_defaults { "renderthread/EglManager.cpp", "renderthread/ReliableSurface.cpp", "renderthread/VulkanManager.cpp", + "renderthread/VulkanSurface.cpp", "renderthread/RenderProxy.cpp", "renderthread/RenderTask.cpp", "renderthread/RenderThread.cpp", diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 87cffb52c150..edde6d3e05c0 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -55,20 +55,8 @@ MakeCurrentResult SkiaVulkanPipeline::makeCurrent() { } Frame SkiaVulkanPipeline::getFrame() { - LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, - "drawRenderNode called on a context with no surface!"); - - SkSurface* backBuffer = mVkManager.getBackbufferSurface(&mVkSurface); - LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, - "drawRenderNode called on a context with an invalid surface"); - if (backBuffer == nullptr) { - SkDebugf("failed to get backbuffer"); - return Frame(-1, -1, 0); - } - - Frame frame(mVkSurface->windowWidth(), mVkSurface->windowHeight(), - mVkManager.getAge(mVkSurface)); - return frame; + LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, "getFrame() called on a context with no surface!"); + return mVkManager.dequeueNextBuffer(mVkSurface); } bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, @@ -77,13 +65,13 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) { - sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface(); + sk_sp<SkSurface> backBuffer = mVkSurface->getCurrentSkSurface(); if (backBuffer.get() == nullptr) { return false; } SkiaPipeline::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, - backBuffer, mVkSurface->preTransform()); + backBuffer, mVkSurface->getCurrentPreTransform()); ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext()); layerUpdateQueue->clear(); @@ -113,7 +101,7 @@ bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect currentFrameInfo->markSwapBuffers(); if (*requireSwap) { - mVkManager.swapBuffers(mVkSurface); + mVkManager.swapBuffers(mVkSurface, screenDirty); } return *requireSwap; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 2c24edddb7e0..77a7ab171ee1 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -18,6 +18,7 @@ #include "SkiaPipeline.h" #include "renderthread/VulkanManager.h" +#include "renderthread/VulkanSurface.h" #include "renderstate/RenderState.h" diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 5af660c8738a..d4c6eae37630 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -16,6 +16,7 @@ #include "VulkanManager.h" +#include <android/sync.h> #include <gui/Surface.h> #include "Properties.h" @@ -23,6 +24,7 @@ #include "renderstate/RenderState.h" #include "utils/FatVector.h" +#include <GrBackendSemaphore.h> #include <GrBackendSurface.h> #include <GrContext.h> #include <GrTypes.h> @@ -142,6 +144,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe GET_INST_PROC(GetPhysicalDeviceProperties); GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties); GET_INST_PROC(GetPhysicalDeviceFeatures2); + GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2); GET_INST_PROC(CreateDevice); GET_INST_PROC(EnumerateDeviceExtensionProperties); GET_INST_PROC(CreateAndroidSurfaceKHR); @@ -318,11 +321,6 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe GET_DEV_PROC(GetDeviceQueue); GET_DEV_PROC(DeviceWaitIdle); GET_DEV_PROC(DestroyDevice); - GET_DEV_PROC(CreateSwapchainKHR); - GET_DEV_PROC(DestroySwapchainKHR); - GET_DEV_PROC(GetSwapchainImagesKHR); - GET_DEV_PROC(AcquireNextImageKHR); - GET_DEV_PROC(QueuePresentKHR); GET_DEV_PROC(CreateCommandPool); GET_DEV_PROC(DestroyCommandPool); GET_DEV_PROC(AllocateCommandBuffers); @@ -426,201 +424,102 @@ VkFunctorInitParams VulkanManager::getVkFunctorInitParams() const { }; } -// Returns the next BackbufferInfo to use for the next draw. The function will make sure all -// previous uses have finished before returning. -VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) { - SkASSERT(surface->mBackbuffers); +Frame VulkanManager::dequeueNextBuffer(VulkanSurface* surface) { - ++surface->mCurrentBackbufferIndex; - if (surface->mCurrentBackbufferIndex > surface->mImageCount) { - surface->mCurrentBackbufferIndex = 0; - } - - VulkanSurface::BackbufferInfo* backbuffer = - surface->mBackbuffers + surface->mCurrentBackbufferIndex; + VulkanSurface::NativeBufferInfo* bufferInfo = surface->dequeueNativeBuffer(); - // Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely - // reuse its commands buffers. - VkResult res = mWaitForFences(mDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX); - if (res != VK_SUCCESS) { - return nullptr; + if (bufferInfo == nullptr) { + ALOGE("VulkanSurface::dequeueNativeBuffer called with an invalid surface!"); + return Frame(-1, -1, 0); } - return backbuffer; -} + LOG_ALWAYS_FATAL_IF(!bufferInfo->dequeued); -static SkMatrix getPreTransformMatrix(int width, int height, - VkSurfaceTransformFlagBitsKHR transform) { - switch (transform) { - case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: - return SkMatrix::I(); - case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: - return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1); - case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: - return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1); - case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: - return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1); - case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR: - return SkMatrix::MakeAll(-1, 0, width, 0, 1, 0, 0, 0, 1); - case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR: - return SkMatrix::MakeAll(0, -1, height, -1, 0, width, 0, 0, 1); - case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR: - return SkMatrix::MakeAll(1, 0, 0, 0, -1, height, 0, 0, 1); - case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR: - return SkMatrix::MakeAll(0, 1, 0, 1, 0, 0, 0, 0, 1); - default: - LOG_ALWAYS_FATAL("Unsupported pre transform of swapchain."); - } - return SkMatrix::I(); -} - - -SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) { - // Recreate VulkanSurface, if ANativeWindow has been resized. - VulkanSurface* surface = *surfaceOut; - int windowWidth = 0, windowHeight = 0; - ANativeWindow* window = surface->mNativeWindow; - window->query(window, NATIVE_WINDOW_WIDTH, &windowWidth); - window->query(window, NATIVE_WINDOW_HEIGHT, &windowHeight); - if (windowWidth != surface->mWindowWidth || windowHeight != surface->mWindowHeight) { - ColorMode colorMode = surface->mColorMode; - sk_sp<SkColorSpace> colorSpace = surface->mColorSpace; - SkColorType colorType = surface->mColorType; - GrContext* grContext = surface->mGrContext; - destroySurface(surface); - *surfaceOut = createSurface(window, colorMode, colorSpace, colorType, grContext); - surface = *surfaceOut; - if (!surface) { - return nullptr; + if (bufferInfo->dequeue_fence != -1) { + int fence_clone = dup(bufferInfo->dequeue_fence); + if (fence_clone == -1) { + ALOGE("dup(fence) failed, stalling until signalled: %s (%d)", strerror(errno), errno); + sync_wait(bufferInfo->dequeue_fence, -1 /* forever */); + } else { + 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); + LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err, "Failed to create import semaphore, err: %d", + err); + + 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 = fence_clone; + + err = mImportSemaphoreFdKHR(mDevice, &importInfo); + LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err, "Failed to import semaphore, err: %d", err); + + GrBackendSemaphore backendSemaphore; + backendSemaphore.initVulkan(semaphore); + bufferInfo->skSurface->wait(1, &backendSemaphore); } } - VulkanSurface::BackbufferInfo* backbuffer = getAvailableBackbuffer(surface); - SkASSERT(backbuffer); - - VkResult res; - - res = mResetFences(mDevice, 2, backbuffer->mUsageFences); - SkASSERT(VK_SUCCESS == res); - - // The acquire will signal the attached mAcquireSemaphore. We use this to know the image has - // finished presenting and that it is safe to begin sending new commands to the returned image. - res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX, - backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, - &backbuffer->mImageIndex); - - if (VK_ERROR_SURFACE_LOST_KHR == res) { - // need to figure out how to create a new vkSurface without the platformData* - // maybe use attach somehow? but need a Window - return nullptr; - } - if (VK_ERROR_OUT_OF_DATE_KHR == res || VK_SUBOPTIMAL_KHR == res) { - // tear swapchain down and try again - if (!createSwapchain(surface)) { - return nullptr; - } - backbuffer = getAvailableBackbuffer(surface); - res = mResetFences(mDevice, 2, backbuffer->mUsageFences); - SkASSERT(VK_SUCCESS == res); - - // acquire the image - res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX, - backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, - &backbuffer->mImageIndex); + int bufferAge = (mSwapBehavior == SwapBehavior::Discard) ? 0 : surface->getCurrentBuffersAge(); + return Frame(surface->logicalWidth(), surface->logicalHeight(), bufferAge); +} - if (VK_SUCCESS != res) { - return nullptr; - } +void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) { + if (CC_UNLIKELY(Properties::waitForGpuCompletion)) { + ATRACE_NAME("Finishing GPU work"); + mDeviceWaitIdle(mDevice); } - // set up layout transfer from initial to color attachment - VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout; - SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout); - VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkAccessFlags srcAccessMask = 0; - VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - VkImageMemoryBarrier imageMemoryBarrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType - NULL, // pNext - srcAccessMask, // outputMask - dstAccessMask, // inputMask - layout, // oldLayout - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout - mPresentQueueIndex, // srcQueueFamilyIndex - mGraphicsQueueIndex, // dstQueueFamilyIndex - surface->mImages[backbuffer->mImageIndex], // image - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange - }; - mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[0], 0); - - VkCommandBufferBeginInfo info; - memset(&info, 0, sizeof(VkCommandBufferBeginInfo)); - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - info.flags = 0; - mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[0], &info); - - mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[0], srcStageMask, dstStageMask, 0, 0, - nullptr, 0, nullptr, 1, &imageMemoryBarrier); - - mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[0]); - - VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - // insert the layout transfer into the queue and wait on the acquire - 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 = &backbuffer->mAcquireSemaphore; - submitInfo.pWaitDstStageMask = &waitDstStageFlags; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[0]; - submitInfo.signalSemaphoreCount = 0; - - // Attach first fence to submission here so we can track when the command buffer finishes. - mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[0]); + VkExportSemaphoreCreateInfo exportInfo; + exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; + exportInfo.pNext = nullptr; + exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - // We need to notify Skia that we changed the layout of the wrapped VkImage - sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface; - GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget( - SkSurface::kFlushRead_BackendHandleAccess); - if (!backendRT.isValid()) { - SkASSERT(backendRT.isValid()); - return nullptr; + 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); + ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to create semaphore"); + + GrBackendSemaphore backendSemaphore; + backendSemaphore.initVulkan(semaphore); + + VulkanSurface::NativeBufferInfo* bufferInfo = surface->getCurrentBufferInfo(); + + int fenceFd = -1; + GrSemaphoresSubmitted submitted = + bufferInfo->skSurface->flush(SkSurface::BackendSurfaceAccess::kPresent, + SkSurface::kNone_FlushFlags, 1, &backendSemaphore); + if (submitted == GrSemaphoresSubmitted::kYes) { + 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; + + err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); + ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd"); + } else { + ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed"); + mQueueWaitIdle(mGraphicsQueue); } - backendRT.setVkImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - surface->mPreTransform = getPreTransformMatrix(surface->windowWidth(), - surface->windowHeight(), - surface->mTransform); + surface->presentCurrentBuffer(dirtyRect, fenceFd); - surface->mBackbuffer = std::move(skSurface); - return surface->mBackbuffer.get(); -} - -void VulkanManager::destroyBuffers(VulkanSurface* surface) { - if (surface->mBackbuffers) { - for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) { - mWaitForFences(mDevice, 2, surface->mBackbuffers[i].mUsageFences, true, UINT64_MAX); - surface->mBackbuffers[i].mImageIndex = -1; - mDestroySemaphore(mDevice, surface->mBackbuffers[i].mAcquireSemaphore, nullptr); - mDestroySemaphore(mDevice, surface->mBackbuffers[i].mRenderSemaphore, nullptr); - mFreeCommandBuffers(mDevice, mCommandPool, 2, - surface->mBackbuffers[i].mTransitionCmdBuffers); - mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[0], 0); - mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[1], 0); - } - } - - delete[] surface->mBackbuffers; - surface->mBackbuffers = nullptr; - delete[] surface->mImageInfos; - surface->mImageInfos = nullptr; - delete[] surface->mImages; - surface->mImages = nullptr; + // Exporting a semaphore with copy transference via vkGetSemaphoreFdKHR, 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 semaphore 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); } void VulkanManager::destroySurface(VulkanSurface* surface) { @@ -630,271 +529,9 @@ void VulkanManager::destroySurface(VulkanSurface* surface) { } mDeviceWaitIdle(mDevice); - destroyBuffers(surface); - - if (VK_NULL_HANDLE != surface->mSwapchain) { - mDestroySwapchainKHR(mDevice, surface->mSwapchain, nullptr); - surface->mSwapchain = VK_NULL_HANDLE; - } - - if (VK_NULL_HANDLE != surface->mVkSurface) { - mDestroySurfaceKHR(mInstance, surface->mVkSurface, nullptr); - surface->mVkSurface = VK_NULL_HANDLE; - } delete surface; } -void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) { - mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, nullptr); - SkASSERT(surface->mImageCount); - surface->mImages = new VkImage[surface->mImageCount]; - mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, surface->mImages); - - SkSurfaceProps props(0, kUnknown_SkPixelGeometry); - - // set up initial image layouts and create surfaces - surface->mImageInfos = new VulkanSurface::ImageInfo[surface->mImageCount]; - for (uint32_t i = 0; i < surface->mImageCount; ++i) { - GrVkImageInfo info; - info.fImage = surface->mImages[i]; - info.fAlloc = GrVkAlloc(); - info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - info.fImageTiling = VK_IMAGE_TILING_OPTIMAL; - info.fFormat = format; - info.fLevelCount = 1; - - GrBackendRenderTarget backendRT(extent.width, extent.height, 0, 0, info); - - VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i]; - imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget( - surface->mGrContext, backendRT, kTopLeft_GrSurfaceOrigin, - surface->mColorType, surface->mColorSpace, &props); - } - - SkASSERT(mCommandPool != VK_NULL_HANDLE); - - // set up the backbuffers - VkSemaphoreCreateInfo semaphoreInfo; - memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo)); - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - semaphoreInfo.pNext = nullptr; - semaphoreInfo.flags = 0; - 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 = 2; - VkFenceCreateInfo fenceInfo; - memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo)); - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.pNext = nullptr; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - // we create one additional backbuffer structure here, because we want to - // give the command buffers they contain a chance to finish before we cycle back - surface->mBackbuffers = new VulkanSurface::BackbufferInfo[surface->mImageCount + 1]; - for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) { - SkDEBUGCODE(VkResult res); - surface->mBackbuffers[i].mImageIndex = -1; - SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, - &surface->mBackbuffers[i].mAcquireSemaphore); - SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, - &surface->mBackbuffers[i].mRenderSemaphore); - SkDEBUGCODE(res =) mAllocateCommandBuffers(mDevice, &commandBuffersInfo, - surface->mBackbuffers[i].mTransitionCmdBuffers); - SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr, - &surface->mBackbuffers[i].mUsageFences[0]); - SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr, - &surface->mBackbuffers[i].mUsageFences[1]); - SkASSERT(VK_SUCCESS == res); - } - surface->mCurrentBackbufferIndex = surface->mImageCount; -} - -bool VulkanManager::createSwapchain(VulkanSurface* surface) { - // check for capabilities - VkSurfaceCapabilitiesKHR caps; - VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice, - surface->mVkSurface, &caps); - if (VK_SUCCESS != res) { - return false; - } - - uint32_t surfaceFormatCount; - res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface, - &surfaceFormatCount, nullptr); - if (VK_SUCCESS != res) { - return false; - } - - FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount); - res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface, - &surfaceFormatCount, surfaceFormats.data()); - if (VK_SUCCESS != res) { - return false; - } - - uint32_t presentModeCount; - res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice, - surface->mVkSurface, &presentModeCount, nullptr); - if (VK_SUCCESS != res) { - return false; - } - - FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount); - res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice, - surface->mVkSurface, &presentModeCount, - presentModes.data()); - if (VK_SUCCESS != res) { - return false; - } - - if (!SkToBool(caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) { - return false; - } - VkSurfaceTransformFlagBitsKHR transform; - if (SkToBool(caps.supportedTransforms & caps.currentTransform) && - !SkToBool(caps.currentTransform & VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) { - transform = caps.currentTransform; - } else { - transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - } - - VkExtent2D extent = caps.currentExtent; - // clamp width; to handle currentExtent of -1 and protect us from broken hints - if (extent.width < caps.minImageExtent.width) { - extent.width = caps.minImageExtent.width; - } - SkASSERT(extent.width <= caps.maxImageExtent.width); - // clamp height - if (extent.height < caps.minImageExtent.height) { - extent.height = caps.minImageExtent.height; - } - SkASSERT(extent.height <= caps.maxImageExtent.height); - - VkExtent2D swapExtent = extent; - if (transform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR || - transform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR || - transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR || - transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR) { - swapExtent.width = extent.height; - swapExtent.height = extent.width; - } - - surface->mWindowWidth = extent.width; - surface->mWindowHeight = extent.height; - - uint32_t imageCount = std::max<uint32_t>(3, caps.minImageCount); - if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) { - // Application must settle for fewer images than desired: - imageCount = caps.maxImageCount; - } - - // Currently Skia requires the images to be color attchments and support all transfer - // operations. - VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT; - SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags); - - SkASSERT(caps.supportedCompositeAlpha & - (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)); - VkCompositeAlphaFlagBitsKHR composite_alpha = - (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) - ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR - : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - - VkFormat surfaceFormat = VK_FORMAT_R8G8B8A8_UNORM; - VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - if (surface->mColorType == SkColorType::kRGBA_F16_SkColorType) { - surfaceFormat = VK_FORMAT_R16G16B16A16_SFLOAT; - } - - if (surface->mColorMode == ColorMode::WideColorGamut) { - skcms_Matrix3x3 surfaceGamut; - LOG_ALWAYS_FATAL_IF(!surface->mColorSpace->toXYZD50(&surfaceGamut), - "Could not get gamut matrix from color space"); - if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) { - colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT; - } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) { - colorSpace = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT; - } else { - LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); - } - } - - bool foundSurfaceFormat = false; - for (uint32_t i = 0; i < surfaceFormatCount; ++i) { - if (surfaceFormat == surfaceFormats[i].format - && colorSpace == surfaceFormats[i].colorSpace) { - foundSurfaceFormat = true; - break; - } - } - - if (!foundSurfaceFormat) { - return false; - } - - // FIFO is always available and will match what we do on GL so just pick that here. - VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR; - - VkSwapchainCreateInfoKHR swapchainCreateInfo; - memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR)); - swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swapchainCreateInfo.surface = surface->mVkSurface; - swapchainCreateInfo.minImageCount = imageCount; - swapchainCreateInfo.imageFormat = surfaceFormat; - swapchainCreateInfo.imageColorSpace = colorSpace; - swapchainCreateInfo.imageExtent = swapExtent; - swapchainCreateInfo.imageArrayLayers = 1; - swapchainCreateInfo.imageUsage = usageFlags; - - uint32_t queueFamilies[] = {mGraphicsQueueIndex, mPresentQueueIndex}; - if (mGraphicsQueueIndex != mPresentQueueIndex) { - swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - swapchainCreateInfo.queueFamilyIndexCount = 2; - swapchainCreateInfo.pQueueFamilyIndices = queueFamilies; - } else { - swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - swapchainCreateInfo.queueFamilyIndexCount = 0; - swapchainCreateInfo.pQueueFamilyIndices = nullptr; - } - - swapchainCreateInfo.preTransform = transform; - swapchainCreateInfo.compositeAlpha = composite_alpha; - swapchainCreateInfo.presentMode = mode; - swapchainCreateInfo.clipped = true; - swapchainCreateInfo.oldSwapchain = surface->mSwapchain; - - res = mCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &surface->mSwapchain); - if (VK_SUCCESS != res) { - return false; - } - - surface->mTransform = transform; - - // destroy the old swapchain - if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) { - mDeviceWaitIdle(mDevice); - - destroyBuffers(surface); - - mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr); - } - - createBuffers(surface, surfaceFormat, swapExtent); - - // The window content is not updated (frozen) until a buffer of the window size is received. - // This prevents temporary stretching of the window after it is resized, but before the first - // buffer with new size is enqueued. - native_window_set_scaling_mode(surface->mNativeWindow, NATIVE_WINDOW_SCALING_MODE_FREEZE); - - return true; -} - VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, SkColorType surfaceColorType, @@ -904,185 +541,8 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode col return nullptr; } - VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace, - surfaceColorType, grContext); - - VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; - memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.pNext = nullptr; - surfaceCreateInfo.flags = 0; - surfaceCreateInfo.window = window; - - VkResult res = mCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr, - &surface->mVkSurface); - if (VK_SUCCESS != res) { - delete surface; - return nullptr; - } - - SkDEBUGCODE(VkBool32 supported; res = mGetPhysicalDeviceSurfaceSupportKHR( - mPhysicalDevice, mPresentQueueIndex, surface->mVkSurface, &supported); - // All physical devices and queue families on Android must be capable of - // presentation with any native window. - SkASSERT(VK_SUCCESS == res && supported);); - - if (!createSwapchain(surface)) { - destroySurface(surface); - return nullptr; - } - - return surface; -} - -// Helper to know which src stage flags we need to set when transitioning to the present layout -static VkPipelineStageFlags layoutToPipelineSrcStageFlags(const VkImageLayout layout) { - if (VK_IMAGE_LAYOUT_GENERAL == layout) { - return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout || - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) { - return VK_PIPELINE_STAGE_TRANSFER_BIT; - } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) { - return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout || - VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout) { - return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) { - return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) { - return VK_PIPELINE_STAGE_HOST_BIT; - } - - SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout); - return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; -} - -// Helper to know which src access mask we need to set when transitioning to the present layout -static VkAccessFlags layoutToSrcAccessMask(const VkImageLayout layout) { - VkAccessFlags flags = 0; - if (VK_IMAGE_LAYOUT_GENERAL == layout) { - flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | - VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_HOST_WRITE_BIT | - VK_ACCESS_HOST_READ_BIT; - } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) { - flags = VK_ACCESS_HOST_WRITE_BIT; - } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) { - flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout) { - flags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - } else if (VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) { - flags = VK_ACCESS_TRANSFER_WRITE_BIT; - } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout) { - flags = VK_ACCESS_TRANSFER_READ_BIT; - } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) { - flags = VK_ACCESS_SHADER_READ_BIT; - } - return flags; -} - -void VulkanManager::swapBuffers(VulkanSurface* surface) { - if (CC_UNLIKELY(Properties::waitForGpuCompletion)) { - ATRACE_NAME("Finishing GPU work"); - mDeviceWaitIdle(mDevice); - } - - SkASSERT(surface->mBackbuffers); - VulkanSurface::BackbufferInfo* backbuffer = - surface->mBackbuffers + surface->mCurrentBackbufferIndex; - - SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get(); - GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget( - SkSurface::kFlushRead_BackendHandleAccess); - SkASSERT(backendRT.isValid()); - - GrVkImageInfo imageInfo; - SkAssertResult(backendRT.getVkImageInfo(&imageInfo)); - - // Check to make sure we never change the actually wrapped image - SkASSERT(imageInfo.fImage == surface->mImages[backbuffer->mImageIndex]); - - // We need to transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR and make sure that all - // previous work is complete for before presenting. So we first add the necessary barrier here. - VkImageLayout layout = imageInfo.fImageLayout; - VkPipelineStageFlags srcStageMask = layoutToPipelineSrcStageFlags(layout); - VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - VkAccessFlags srcAccessMask = layoutToSrcAccessMask(layout); - VkAccessFlags dstAccessMask = 0; - - VkImageMemoryBarrier imageMemoryBarrier = { - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType - NULL, // pNext - srcAccessMask, // outputMask - dstAccessMask, // inputMask - layout, // oldLayout - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout - mGraphicsQueueIndex, // srcQueueFamilyIndex - mPresentQueueIndex, // dstQueueFamilyIndex - surface->mImages[backbuffer->mImageIndex], // image - {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange - }; - - mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[1], 0); - VkCommandBufferBeginInfo info; - memset(&info, 0, sizeof(VkCommandBufferBeginInfo)); - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - info.flags = 0; - mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[1], &info); - mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[1], srcStageMask, dstStageMask, 0, 0, - nullptr, 0, nullptr, 1, &imageMemoryBarrier); - mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]); - - surface->mImageInfos[backbuffer->mImageIndex].mImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - // insert the layout transfer into the queue and wait on the acquire - VkSubmitInfo submitInfo; - memset(&submitInfo, 0, sizeof(VkSubmitInfo)); - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.waitSemaphoreCount = 0; - submitInfo.pWaitDstStageMask = 0; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[1]; - submitInfo.signalSemaphoreCount = 1; - // When this command buffer finishes we will signal this semaphore so that we know it is now - // safe to present the image to the screen. - submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore; - - // Attach second fence to submission here so we can track when the command buffer finishes. - mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[1]); - - // Submit present operation to present queue. We use a semaphore here to make sure all rendering - // to the image is complete and that the layout has been change to present on the graphics - // queue. - const VkPresentInfoKHR presentInfo = { - VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType - NULL, // pNext - 1, // waitSemaphoreCount - &backbuffer->mRenderSemaphore, // pWaitSemaphores - 1, // swapchainCount - &surface->mSwapchain, // pSwapchains - &backbuffer->mImageIndex, // pImageIndices - NULL // pResults - }; - - mQueuePresentKHR(mPresentQueue, &presentInfo); - - surface->mBackbuffer.reset(); - surface->mImageInfos[backbuffer->mImageIndex].mLastUsed = surface->mCurrentTime; - surface->mImageInfos[backbuffer->mImageIndex].mInvalid = false; - surface->mCurrentTime++; -} - -int VulkanManager::getAge(VulkanSurface* surface) { - SkASSERT(surface->mBackbuffers); - VulkanSurface::BackbufferInfo* backbuffer = - surface->mBackbuffers + surface->mCurrentBackbufferIndex; - if (mSwapBehavior == SwapBehavior::Discard || - surface->mImageInfos[backbuffer->mImageIndex].mInvalid) { - return 0; - } - uint16_t lastUsed = surface->mImageInfos[backbuffer->mImageIndex].mLastUsed; - return surface->mCurrentTime - lastUsed; + return VulkanSurface::Create(window, colorMode, surfaceColorType, surfaceColorSpace, grContext, + *this); } bool VulkanManager::setupDummyCommandBuffer() { diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 95c9630fb728..c3d289176576 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -28,7 +28,9 @@ #include <ui/Fence.h> #include <utils/StrongPointer.h> #include <vk/GrVkBackendContext.h> +#include "Frame.h" #include "IRenderPipeline.h" +#include "VulkanSurface.h" class GrVkExtensions; @@ -38,66 +40,6 @@ namespace renderthread { class RenderThread; -class VulkanSurface { -public: - VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace, - SkColorType colorType, GrContext* grContext) - : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace), - mColorType(colorType), mGrContext(grContext) {} - - sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; } - - // The width and height are are the logical width and height for when submitting draws to the - // surface. In reality if the window is rotated the underlying VkImage may have the width and - // height swapped. - int windowWidth() const { return mWindowWidth; } - int windowHeight() const { return mWindowHeight; } - - SkMatrix& preTransform() { return mPreTransform; } - -private: - friend class VulkanManager; - struct BackbufferInfo { - uint32_t mImageIndex; // image this is associated with - VkSemaphore mAcquireSemaphore; // we signal on this for acquisition of image - VkSemaphore mRenderSemaphore; // we wait on this for rendering to be done - VkCommandBuffer - mTransitionCmdBuffers[2]; // to transition layout between present and render - // We use these fences to make sure the above Command buffers have finished their work - // before attempting to reuse them or destroy them. - VkFence mUsageFences[2]; - }; - - struct ImageInfo { - VkImageLayout mImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - sk_sp<SkSurface> mSurface; - uint16_t mLastUsed = 0; - bool mInvalid = true; - }; - - sk_sp<SkSurface> mBackbuffer; - - VkSurfaceKHR mVkSurface = VK_NULL_HANDLE; - VkSwapchainKHR mSwapchain = VK_NULL_HANDLE; - - BackbufferInfo* mBackbuffers = nullptr; - uint32_t mCurrentBackbufferIndex; - - uint32_t mImageCount; - VkImage* mImages = nullptr; - ImageInfo* mImageInfos; - uint16_t mCurrentTime = 0; - ColorMode mColorMode; - ANativeWindow* mNativeWindow; - int mWindowWidth = 0; - int mWindowHeight = 0; - sk_sp<SkColorSpace> mColorSpace; - SkColorType mColorType; - VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - SkMatrix mPreTransform; - GrContext* mGrContext; -}; - // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue, // which are re-used by CanvasContext. This class is created once and should be used by all vulkan // windowing contexts. The VulkanManager must be initialized before use. @@ -114,33 +56,19 @@ public: // Quick check to see if the VulkanManager has been initialized. bool hasVkContext() { return mDevice != VK_NULL_HANDLE; } - // Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new - // VulkanSurface object which is returned. + // Create and destroy functions for wrapping an ANativeWindow in a VulkanSurface VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode, sk_sp<SkColorSpace> surfaceColorSpace, SkColorType surfaceColorType, GrContext* grContext); - - // Destroy the VulkanSurface and all associated vulkan objects. void destroySurface(VulkanSurface* surface); + Frame dequeueNextBuffer(VulkanSurface* surface); + void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect); + // Cleans up all the global state in the VulkanManger. void destroy(); - // No work is needed to make a VulkanSurface current, and all functions require that a - // VulkanSurface is passed into them so we just return true here. - bool isCurrent(VulkanSurface* surface) { return true; } - - int getAge(VulkanSurface* surface); - - // Returns an SkSurface which wraps the next image returned from vkAcquireNextImageKHR. It also - // will transition the VkImage from a present layout to color attachment so that it can be used - // by the client for drawing. - SkSurface* getBackbufferSurface(VulkanSurface** surface); - - // Presents the current VkImage. - void swapBuffers(VulkanSurface* surface); - // Inserts a wait on fence command into the Vulkan command buffer. status_t fenceWait(sp<Fence>& fence); @@ -153,17 +81,10 @@ public: sk_sp<GrContext> createContext(const GrContextOptions& options); private: + friend class VulkanSurface; // Sets up the VkInstance and VkDevice objects. Also fills out the passed in // VkPhysicalDeviceFeatures struct. void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&); - - void destroyBuffers(VulkanSurface* surface); - - bool createSwapchain(VulkanSurface* surface); - void createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent); - - VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface); - bool setupDummyCommandBuffer(); // simple wrapper class that exists only to initialize a pointer to NULL @@ -190,13 +111,6 @@ private: VkPtr<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR> mGetPhysicalDeviceSurfaceFormatsKHR; VkPtr<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR> mGetPhysicalDeviceSurfacePresentModesKHR; - VkPtr<PFN_vkCreateSwapchainKHR> mCreateSwapchainKHR; - VkPtr<PFN_vkDestroySwapchainKHR> mDestroySwapchainKHR; - VkPtr<PFN_vkGetSwapchainImagesKHR> mGetSwapchainImagesKHR; - VkPtr<PFN_vkAcquireNextImageKHR> mAcquireNextImageKHR; - VkPtr<PFN_vkQueuePresentKHR> mQueuePresentKHR; - VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR; - // Instance Functions VkPtr<PFN_vkEnumerateInstanceVersion> mEnumerateInstanceVersion; VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties; @@ -207,6 +121,7 @@ private: VkPtr<PFN_vkGetPhysicalDeviceProperties> mGetPhysicalDeviceProperties; VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties; VkPtr<PFN_vkGetPhysicalDeviceFeatures2> mGetPhysicalDeviceFeatures2; + VkPtr<PFN_vkGetPhysicalDeviceImageFormatProperties2> mGetPhysicalDeviceImageFormatProperties2; VkPtr<PFN_vkCreateDevice> mCreateDevice; VkPtr<PFN_vkEnumerateDeviceExtensionProperties> mEnumerateDeviceExtensionProperties; diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp new file mode 100644 index 000000000000..c03c3a896e26 --- /dev/null +++ b/libs/hwui/renderthread/VulkanSurface.cpp @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VulkanSurface.h" + +#include <algorithm> +#include <SkSurface.h> + +#include "VulkanManager.h" +#include "utils/TraceUtils.h" +#include "utils/Color.h" + +namespace android { +namespace uirenderer { +namespace renderthread { + +static bool IsTransformSupported(int transform) { + // For now, only support pure rotations, not flip or flip-and-rotate, until we have + // more time to test them and build sample code. As far as I know we never actually + // use anything besides pure rotations anyway. + return transform == 0 + || transform == NATIVE_WINDOW_TRANSFORM_ROT_90 + || transform == NATIVE_WINDOW_TRANSFORM_ROT_180 + || transform == NATIVE_WINDOW_TRANSFORM_ROT_270; +} + +static int InvertTransform(int transform) { + switch (transform) { + case NATIVE_WINDOW_TRANSFORM_ROT_90: + return NATIVE_WINDOW_TRANSFORM_ROT_270; + case NATIVE_WINDOW_TRANSFORM_ROT_180: + return NATIVE_WINDOW_TRANSFORM_ROT_180; + case NATIVE_WINDOW_TRANSFORM_ROT_270: + return NATIVE_WINDOW_TRANSFORM_ROT_90; + default: + return 0; + } +} + +static int ConvertVkTransformToNative(VkSurfaceTransformFlagsKHR transform) { + switch (transform) { + case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_ROT_270; + case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_ROT_180; + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_ROT_90; + case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: + case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR: + default: + return 0; + } +} + +static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) { + const int width = windowSize.width(); + const int height = windowSize.height(); + + switch (transform) { + case 0: + return SkMatrix::I(); + case NATIVE_WINDOW_TRANSFORM_ROT_90: + return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1); + case NATIVE_WINDOW_TRANSFORM_ROT_180: + return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1); + case NATIVE_WINDOW_TRANSFORM_ROT_270: + return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1); + default: + LOG_ALWAYS_FATAL("Unsupported Window Transform (%d)", transform); + } + return SkMatrix::I(); +} + +void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize, + const SkISize& maxSize) { + SkISize& windowSize = windowInfo->size; + + // clamp width & height to handle currentExtent of -1 and protect us from broken hints + if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width() + || windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) { + int width = std::min(maxSize.width(), std::max(minSize.width(), windowSize.width())); + int height = std::min(maxSize.height(), std::max(minSize.height(), windowSize.height())); + ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]", + windowSize.width(), windowSize.height(), width, height); + windowSize.set(width, height); + } + + windowInfo->actualSize = windowSize; + if (windowInfo->transform & HAL_TRANSFORM_ROT_90) { + windowInfo->actualSize.set(windowSize.height(), windowSize.width()); + } + + windowInfo->preTransform = GetPreTransformMatrix(windowInfo->size, windowInfo->transform); +} + +static bool ResetNativeWindow(ANativeWindow* window) { + // -- Reset the native window -- + // The native window might have been used previously, and had its properties + // changed from defaults. That will affect the answer we get for queries + // like MIN_UNDEQUEUED_BUFFERS. Reset to a known/default state before we + // attempt such queries. + + int err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); + if (err != 0) { + ALOGW("native_window_api_connect failed: %s (%d)", strerror(-err), err); + return false; + } + + // this will match what we do on GL so pick that here. + err = window->setSwapInterval(window, 1); + if (err != 0) { + ALOGW("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err); + return false; + } + + err = native_window_set_shared_buffer_mode(window, false); + if (err != 0) { + ALOGW("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err); + return false; + } + + err = native_window_set_auto_refresh(window, false); + if (err != 0) { + ALOGW("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err); + return false; + } + + return true; +} + +class VkSurfaceAutoDeleter { +public: + VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface, + PFN_vkDestroySurfaceKHR destroySurfaceKHR) + : mInstance(instance) + , mSurface(surface) + , mDestroySurfaceKHR(destroySurfaceKHR) {} + ~VkSurfaceAutoDeleter() { + destroy(); + } + + void destroy() { + if (mSurface != VK_NULL_HANDLE) { + mDestroySurfaceKHR(mInstance, mSurface, nullptr); + mSurface = VK_NULL_HANDLE; + } + } + +private: + VkInstance mInstance; + VkSurfaceKHR mSurface; + PFN_vkDestroySurfaceKHR mDestroySurfaceKHR; +}; + +VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode, + SkColorType colorType, sk_sp<SkColorSpace> colorSpace, + GrContext* grContext, const VulkanManager& vkManager) { + + VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; + memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.pNext = nullptr; + surfaceCreateInfo.flags = 0; + surfaceCreateInfo.window = window; + + VkSurfaceKHR vkSurface = VK_NULL_HANDLE; + VkResult res = vkManager.mCreateAndroidSurfaceKHR(vkManager.mInstance, &surfaceCreateInfo, + nullptr, &vkSurface); + if (VK_SUCCESS != res) { + ALOGE("VulkanSurface::Create() vkCreateAndroidSurfaceKHR failed (%d)", res); + return nullptr; + } + + VkSurfaceAutoDeleter vkSurfaceDeleter(vkManager.mInstance, vkSurface, + vkManager.mDestroySurfaceKHR); + + SkDEBUGCODE(VkBool32 supported; res = vkManager.mGetPhysicalDeviceSurfaceSupportKHR( + vkManager.mPhysicalDevice, vkManager.mPresentQueueIndex, vkSurface, &supported); + // All physical devices and queue families on Android must be capable of + // presentation with any native window. + SkASSERT(VK_SUCCESS == res && supported);); + + // check for capabilities + VkSurfaceCapabilitiesKHR caps; + res = vkManager.mGetPhysicalDeviceSurfaceCapabilitiesKHR(vkManager.mPhysicalDevice, vkSurface, + &caps); + if (VK_SUCCESS != res) { + ALOGE("VulkanSurface::Create() vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed (%d)", res); + return nullptr; + } + + LOG_ALWAYS_FATAL_IF(0 == (caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)); + + /* + * We must destroy the VK Surface before attempting to update the window as doing so after + * will cause the native window to be modified in unexpected ways. + */ + vkSurfaceDeleter.destroy(); + + /* + * Populate Window Info struct + */ + WindowInfo windowInfo; + + windowInfo.transform = ConvertVkTransformToNative(caps.supportedTransforms); + windowInfo.size = SkISize::Make(caps.currentExtent.width, caps.currentExtent.height); + + const SkISize minSize = SkISize::Make(caps.minImageExtent.width, caps.minImageExtent.height); + const SkISize maxSize = SkISize::Make(caps.maxImageExtent.width, caps.maxImageExtent.height); + ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize); + + windowInfo.bufferCount = std::max<uint32_t>(VulkanSurface::sMaxBufferCount, caps.minImageCount); + if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) { + // Application must settle for fewer images than desired: + windowInfo.bufferCount = caps.maxImageCount; + } + + // Currently Skia requires the images to be color attachments and support all transfer + // operations. + VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + LOG_ALWAYS_FATAL_IF((caps.supportedUsageFlags & usageFlags) != usageFlags); + + windowInfo.dataspace = HAL_DATASPACE_V0_SRGB; + if (colorMode == ColorMode::WideColorGamut) { + skcms_Matrix3x3 surfaceGamut; + LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&surfaceGamut), + "Could not get gamut matrix from color space"); + if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) { + windowInfo.dataspace = HAL_DATASPACE_V0_SCRGB; + } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) { + windowInfo.dataspace = HAL_DATASPACE_DISPLAY_P3; + } else { + LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space."); + } + } + + windowInfo.pixelFormat = ColorTypeToPixelFormat(colorType); + VkFormat vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM; + if (windowInfo.pixelFormat == PIXEL_FORMAT_RGBA_FP16) { + vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + } + + uint64_t producerUsage = + AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; + uint64_t consumerUsage; + native_window_get_consumer_usage(window, &consumerUsage); + windowInfo.windowUsageFlags = consumerUsage | producerUsage; + + /* + * Now we attempt to modify the window! + */ + if (!UpdateWindow(window, windowInfo)) { + return nullptr; + } + + return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext); +} + +bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) { + ATRACE_CALL(); + + if (!ResetNativeWindow(window)) { + return false; + } + + // -- Configure the native window -- + int err = native_window_set_buffers_format(window, windowInfo.pixelFormat); + if (err != 0) { + ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)", + windowInfo.pixelFormat, strerror(-err), err); + return false; + } + + err = native_window_set_buffers_data_space(window, windowInfo.dataspace); + if (err != 0) { + ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) " + "failed: %s (%d)", windowInfo.dataspace, strerror(-err), err); + return false; + } + + const SkISize& size = windowInfo.actualSize; + err = native_window_set_buffers_dimensions(window, size.width(), size.height()); + if (err != 0) { + ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) " + "failed: %s (%d)", size.width(), size.height(), strerror(-err), err); + return false; + } + + // native_window_set_buffers_transform() expects the transform the app is requesting that + // the compositor perform during composition. With native windows, pre-transform works by + // rendering with the same transform the compositor is applying (as in Vulkan), but + // then requesting the inverse transform, so that when the compositor does + // it's job the two transforms cancel each other out and the compositor ends + // up applying an identity transform to the app's buffer. + err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform)); + if (err != 0) { + ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) " + "failed: %s (%d)", windowInfo.transform, strerror(-err), err); + return false; + } + + // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than + // HWUI's expectation + err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE); + if (err != 0) { + ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) " + "failed: %s (%d)", strerror(-err), err); + return false; + } + + // Lower layer insists that we have at least two buffers. + err = native_window_set_buffer_count(window, std::max(2, windowInfo.bufferCount)); + if (err != 0) { + ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%d) failed: %s (%d)", + windowInfo.bufferCount, strerror(-err), err); + return false; + } + + err = native_window_set_usage(window, windowInfo.windowUsageFlags); + if (err != 0) { + ALOGE("VulkanSurface::UpdateWindow() native_window_set_usage failed: %s (%d)", + strerror(-err), err); + return false; + } + + return err == 0; +} + +VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo, + SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext) + : mNativeWindow(window) + , mWindowInfo(windowInfo) + , mGrContext(grContext) + , mMinWindowSize(minWindowSize) + , mMaxWindowSize(maxWindowSize) { } + +VulkanSurface::~VulkanSurface() { + releaseBuffers(); + + // release the native window to be available for use by other clients + int err = native_window_api_disconnect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL); + ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", strerror(-err), err); +} + +void VulkanSurface::releaseBuffers() { + for (uint32_t i = 0; i < VulkanSurface::sMaxBufferCount; i++) { + VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i]; + + if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) { + int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(), + bufferInfo.dequeue_fence); + if (err != 0) { + ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err); + } + bufferInfo.dequeued = false; + + if (bufferInfo.dequeue_fence >= 0) { + close(bufferInfo.dequeue_fence); + bufferInfo.dequeue_fence = -1; + } + } + + LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued); + LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence != -1); + + bufferInfo.skSurface.reset(); + bufferInfo.buffer.clear(); + bufferInfo.hasValidContents = false; + bufferInfo.lastPresentedCount = 0; + } +} + +VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() { + // Set the dequeue index to invalid in case of error and only reset it to the correct + // value at the end of the function if everything dequeued correctly. + mDequeuedIndex = -1; + + //check if the native window has been resized or rotated and update accordingly + SkISize newSize = SkISize::MakeEmpty(); + int transformHint = 0; + mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth); + mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight); + mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint); + if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) { + WindowInfo newWindowInfo = mWindowInfo; + newWindowInfo.size = newSize; + newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0; + ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize); + + int err = 0; + if (newWindowInfo.actualSize != mWindowInfo.actualSize) { + // reset the native buffers and update the window + err = native_window_set_buffers_dimensions(mNativeWindow.get(), + newWindowInfo.actualSize.width(), + newWindowInfo.actualSize.height()); + if (err != 0) { + ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)", + newWindowInfo.actualSize.width(), + newWindowInfo.actualSize.height(), strerror(-err), err); + return nullptr; + } + // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The + // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer. + releaseBuffers(); + // TODO should we ask the nativewindow to allocate buffers? + } + + if (newWindowInfo.transform != mWindowInfo.transform) { + err = native_window_set_buffers_transform(mNativeWindow.get(), + InvertTransform(newWindowInfo.transform)); + if (err != 0) { + ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", + newWindowInfo.transform, strerror(-err), err); + newWindowInfo.transform = mWindowInfo.transform; + ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize); + } + } + + mWindowInfo = newWindowInfo; + } + + ANativeWindowBuffer* buffer; + int fence_fd; + int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd); + if (err != 0) { + ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err); + return nullptr; + } + + uint32_t idx; + for (idx = 0; idx < mWindowInfo.bufferCount; idx++) { + if (mNativeBuffers[idx].buffer.get() == buffer) { + mNativeBuffers[idx].dequeued = true; + mNativeBuffers[idx].dequeue_fence = fence_fd; + break; + } else if (mNativeBuffers[idx].buffer.get() == nullptr) { + // increasing the number of buffers we have allocated + mNativeBuffers[idx].buffer = buffer; + mNativeBuffers[idx].dequeued = true; + mNativeBuffers[idx].dequeue_fence = fence_fd; + break; + } + } + if (idx == mWindowInfo.bufferCount) { + ALOGE("dequeueBuffer returned unrecognized buffer"); + mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd); + return nullptr; + } + + VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx]; + + if (bufferInfo->skSurface.get() == nullptr) { + bufferInfo->skSurface = + SkSurface::MakeFromAHardwareBuffer(mGrContext, + ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()), + kTopLeft_GrSurfaceOrigin, DataSpaceToColorSpace(mWindowInfo.dataspace), + nullptr); + if (bufferInfo->skSurface.get() == nullptr) { + ALOGE("SkSurface::MakeFromAHardwareBuffer failed"); + mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd); + return nullptr; + } + } + + mDequeuedIndex = idx; + return bufferInfo; +} + +bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) { + if (!dirtyRect.isEmpty()) { + SkRect transformedRect; + mWindowInfo.preTransform.mapRect(&transformedRect, dirtyRect); + + SkIRect transformedIRect; + transformedRect.roundOut(&transformedIRect); + transformedIRect.intersect(0, 0, mWindowInfo.size.fWidth, mWindowInfo.size.fHeight); + + // map to bottom-left coordinate system + android_native_rect_t aRect; + aRect.left = transformedIRect.x(); + aRect.top = mWindowInfo.size.fHeight - (transformedIRect.y() + transformedIRect.height()); + aRect.right = aRect.left + transformedIRect.width(); + aRect.bottom = aRect.top - transformedIRect.height(); + + int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1); + ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err); + } + + VulkanSurface::NativeBufferInfo& currentBuffer = mNativeBuffers[mDequeuedIndex]; + int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence; + int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd); + + currentBuffer.dequeued = false; + // queueBuffer always closes fence, even on error + if (err != 0) { + ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err); + mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), + currentBuffer.dequeue_fence); + } else { + currentBuffer.hasValidContents = true; + currentBuffer.lastPresentedCount = mPresentCount; + mPresentCount++; + } + + if (currentBuffer.dequeue_fence >= 0) { + close(currentBuffer.dequeue_fence); + currentBuffer.dequeue_fence = -1; + } + + return err == 0; +} + +int VulkanSurface::getCurrentBuffersAge() { + VulkanSurface::NativeBufferInfo& currentBuffer = mNativeBuffers[mDequeuedIndex]; + return currentBuffer.hasValidContents ? (mPresentCount - currentBuffer.lastPresentedCount) : 0; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h new file mode 100644 index 000000000000..4fd9cd29b920 --- /dev/null +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <system/graphics.h> +#include <system/window.h> +#include <vulkan/vulkan.h> + +#include <SkSize.h> +#include <SkRefCnt.h> + +#include "IRenderPipeline.h" + +class SkSurface; + +namespace android { +namespace uirenderer { +namespace renderthread { + +class VulkanManager; + +class VulkanSurface { +public: + static VulkanSurface* Create(ANativeWindow* window, + ColorMode colorMode, + SkColorType colorType, + sk_sp<SkColorSpace> colorSpace, + GrContext* grContext, + const VulkanManager& vkManager); + ~VulkanSurface(); + + sk_sp<SkSurface> getCurrentSkSurface() { return mNativeBuffers[mDequeuedIndex].skSurface; } + const SkMatrix& getCurrentPreTransform() { return mWindowInfo.preTransform; } + +private: + /* + * All structs/methods in this private section are specifically for use by the VulkanManager + * + */ + friend VulkanManager; + struct NativeBufferInfo { + sk_sp<SkSurface> skSurface; + sp<ANativeWindowBuffer> buffer; + // The fence is only valid when the buffer is dequeued, and should be + // -1 any other time. When valid, we own the fd, and must ensure it is + // closed: either by closing it explicitly when queueing the buffer, + // or by passing ownership e.g. to ANativeWindow::cancelBuffer(). + int dequeue_fence = -1; + bool dequeued = false; + uint32_t lastPresentedCount = 0; + bool hasValidContents = false; + }; + + NativeBufferInfo* dequeueNativeBuffer(); + NativeBufferInfo* getCurrentBufferInfo() { return &mNativeBuffers[mDequeuedIndex]; } + bool presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd); + + // The width and height are are the logical width and height for when submitting draws to the + // surface. In reality if the window is rotated the underlying window may have the width and + // height swapped. + int logicalWidth() const { return mWindowInfo.size.width(); } + int logicalHeight() const { return mWindowInfo.size.height(); } + int getCurrentBuffersAge(); + +private: + /* + * All code below this line while logically available to VulkanManager should not be treated + * as private to this class. + * + */ + static constexpr int sMaxBufferCount = 3; + + struct WindowInfo { + SkISize size; + PixelFormat pixelFormat; + android_dataspace dataspace; + int transform; + int bufferCount; + uint64_t windowUsageFlags; + + // size of the ANativeWindow if the inverse of transform requires us to swap width/height + SkISize actualSize; + // transform to be applied to the SkSurface to map the coordinates to the provided transform + SkMatrix preTransform; + }; + + VulkanSurface(ANativeWindow* window, + const WindowInfo& windowInfo, + SkISize minWindowSize, + SkISize maxWindowSize, + GrContext* grContext); + static bool UpdateWindow(ANativeWindow* window, + const WindowInfo& windowInfo); + static void ComputeWindowSizeAndTransform(WindowInfo* windowInfo, + const SkISize& minSize, + const SkISize& maxSize); + void releaseBuffers(); + + NativeBufferInfo mNativeBuffers[VulkanSurface::sMaxBufferCount]; + + sp<ANativeWindow> mNativeWindow; + WindowInfo mWindowInfo; + GrContext* mGrContext; + + int mDequeuedIndex = -1; + uint32_t mPresentCount = 0; + + const SkISize mMinWindowSize; + const SkISize mMaxWindowSize; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */
\ No newline at end of file |