Handle vulkan windowing directly in SkiaVulkanPipeline
Test: manual testing in skiavk mode Change-Id: I2fab80bae2787bfdacbc70d0402e98450e59406d
This commit is contained in:
@@ -40,6 +40,7 @@ hwui_src_files := \
|
||||
renderthread/OpenGLPipeline.cpp \
|
||||
renderthread/DrawFrameTask.cpp \
|
||||
renderthread/EglManager.cpp \
|
||||
renderthread/VulkanManager.cpp \
|
||||
renderthread/RenderProxy.cpp \
|
||||
renderthread/RenderTask.cpp \
|
||||
renderthread/RenderThread.cpp \
|
||||
|
||||
@@ -18,6 +18,7 @@ LOCAL_SHARED_LIBRARIES += \
|
||||
libutils \
|
||||
libEGL \
|
||||
libGLESv2 \
|
||||
libvulkan \
|
||||
libskia \
|
||||
libui \
|
||||
libgui \
|
||||
|
||||
@@ -23,37 +23,43 @@
|
||||
#include "SkiaPipeline.h"
|
||||
#include "SkiaProfileRenderer.h"
|
||||
|
||||
#include <SkSurface.h>
|
||||
#include <SkTypes.h>
|
||||
#include <WindowContextFactory_android.h>
|
||||
#include <VulkanWindowContext.h>
|
||||
|
||||
#include <GrContext.h>
|
||||
#include <GrTypes.h>
|
||||
#include <vk/GrVkTypes.h>
|
||||
|
||||
#include <android/native_window.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <strings.h>
|
||||
|
||||
using namespace android::uirenderer::renderthread;
|
||||
using namespace sk_app;
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
SkiaVulkanPipeline::SkiaVulkanPipeline(renderthread::RenderThread& thread)
|
||||
: SkiaPipeline(thread)
|
||||
, mVkManager(thread.vulkanManager()) {}
|
||||
|
||||
MakeCurrentResult SkiaVulkanPipeline::makeCurrent() {
|
||||
return (mWindowContext != nullptr) ?
|
||||
MakeCurrentResult::AlreadyCurrent : MakeCurrentResult::Failed;
|
||||
return MakeCurrentResult::AlreadyCurrent;
|
||||
}
|
||||
|
||||
Frame SkiaVulkanPipeline::getFrame() {
|
||||
LOG_ALWAYS_FATAL_IF(mWindowContext == nullptr, "Tried to draw into null vulkan context!");
|
||||
mBackbuffer = mWindowContext->getBackbufferSurface();
|
||||
if (mBackbuffer.get() == nullptr) {
|
||||
// try recreating the context?
|
||||
LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
|
||||
"drawRenderNode called on a context with no surface!");
|
||||
|
||||
SkSurface* backBuffer = mVkManager.getBackbufferSurface(mVkSurface);
|
||||
if (backBuffer == nullptr) {
|
||||
SkDebugf("failed to get backbuffer");
|
||||
return Frame(-1, -1, 0);
|
||||
}
|
||||
|
||||
// TODO: support buffer age if Vulkan API can do it
|
||||
Frame frame(mBackbuffer->width(), mBackbuffer->height(), 0);
|
||||
Frame frame(backBuffer->width(), backBuffer->height(), 0);
|
||||
return frame;
|
||||
}
|
||||
|
||||
@@ -66,16 +72,18 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty,
|
||||
const std::vector<sp<RenderNode>>& renderNodes,
|
||||
FrameInfoVisualizer* profiler) {
|
||||
|
||||
if (mBackbuffer.get() == nullptr) {
|
||||
sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface();
|
||||
if (backBuffer.get() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, mBackbuffer);
|
||||
SkiaPipeline::updateLighting(lightGeometry, lightInfo);
|
||||
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer);
|
||||
layerUpdateQueue->clear();
|
||||
|
||||
// Draw visual debugging features
|
||||
if (CC_UNLIKELY(Properties::showDirtyRegions
|
||||
|| ProfileType::None != Properties::getProfileType())) {
|
||||
SkCanvas* profileCanvas = mBackbuffer->getCanvas();
|
||||
SkCanvas* profileCanvas = backBuffer->getCanvas();
|
||||
SkiaProfileRenderer profileRenderer(profileCanvas);
|
||||
profiler->draw(profileRenderer);
|
||||
profileCanvas->flush();
|
||||
@@ -99,11 +107,9 @@ bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew,
|
||||
currentFrameInfo->markSwapBuffers();
|
||||
|
||||
if (*requireSwap) {
|
||||
mWindowContext->swapBuffers();
|
||||
mVkManager.swapBuffers(mVkSurface);
|
||||
}
|
||||
|
||||
mBackbuffer.reset();
|
||||
|
||||
return *requireSwap;
|
||||
}
|
||||
|
||||
@@ -113,6 +119,7 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi
|
||||
}
|
||||
|
||||
DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
|
||||
mVkManager.initialize();
|
||||
Layer* layer = new Layer(mRenderThread.renderState(), 0, 0);
|
||||
return new DeferredLayerUpdater(layer);
|
||||
}
|
||||
@@ -121,33 +128,24 @@ void SkiaVulkanPipeline::onStop() {
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
|
||||
|
||||
if (mWindowContext) {
|
||||
delete mWindowContext;
|
||||
mWindowContext = nullptr;
|
||||
if (mVkSurface) {
|
||||
mVkManager.destroySurface(mVkSurface);
|
||||
mVkSurface = nullptr;
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
DisplayParams displayParams;
|
||||
mWindowContext = window_context_factory::NewVulkanForAndroid(surface, displayParams);
|
||||
if (mWindowContext) {
|
||||
DeviceInfo::initialize(mWindowContext->getGrContext()->caps()->maxRenderTargetSize());
|
||||
}
|
||||
mVkSurface = mVkManager.createSurface(surface);
|
||||
}
|
||||
|
||||
|
||||
// this doesn't work for if there is more than one CanvasContext available at one time!
|
||||
mRenderThread.setGrContext(mWindowContext ? mWindowContext->getGrContext() : nullptr);
|
||||
|
||||
return mWindowContext != nullptr;
|
||||
return mVkSurface != nullptr;
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::isSurfaceReady() {
|
||||
return CC_LIKELY(mWindowContext != nullptr) && mWindowContext->isValid();
|
||||
return CC_UNLIKELY(mVkSurface != nullptr);
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::isContextReady() {
|
||||
return CC_LIKELY(mWindowContext != nullptr);
|
||||
return CC_LIKELY(mVkManager.hasVkContext());
|
||||
}
|
||||
|
||||
void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) {
|
||||
|
||||
@@ -17,11 +17,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "SkiaPipeline.h"
|
||||
#include <SkSurface.h>
|
||||
|
||||
namespace sk_app {
|
||||
class WindowContext;
|
||||
}
|
||||
#include "renderthread/VulkanManager.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -29,7 +25,7 @@ namespace skiapipeline {
|
||||
|
||||
class SkiaVulkanPipeline : public SkiaPipeline {
|
||||
public:
|
||||
SkiaVulkanPipeline(renderthread::RenderThread& thread) : SkiaPipeline(thread) {}
|
||||
SkiaVulkanPipeline(renderthread::RenderThread& thread);
|
||||
virtual ~SkiaVulkanPipeline() {}
|
||||
|
||||
renderthread::MakeCurrentResult makeCurrent() override;
|
||||
@@ -53,8 +49,8 @@ public:
|
||||
static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor);
|
||||
|
||||
private:
|
||||
sk_app::WindowContext* mWindowContext = nullptr;
|
||||
sk_sp<SkSurface> mBackbuffer;
|
||||
renderthread::VulkanManager& mVkManager;
|
||||
renderthread::VulkanSurface* mVkSurface = nullptr;
|
||||
};
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "CanvasContext.h"
|
||||
#include "EglManager.h"
|
||||
#include "RenderProxy.h"
|
||||
#include "VulkanManager.h"
|
||||
|
||||
#include <gui/DisplayEventReceiver.h>
|
||||
#include <gui/ISurfaceComposer.h>
|
||||
@@ -157,7 +158,8 @@ RenderThread::RenderThread() : Thread(true)
|
||||
, mFrameCallbackTaskPending(false)
|
||||
, mFrameCallbackTask(nullptr)
|
||||
, mRenderState(nullptr)
|
||||
, mEglManager(nullptr) {
|
||||
, mEglManager(nullptr)
|
||||
, mVkManager(nullptr) {
|
||||
Properties::load();
|
||||
mFrameCallbackTask = new DispatchFrameCallbacks(this);
|
||||
mLooper = new Looper(false);
|
||||
@@ -191,6 +193,7 @@ void RenderThread::initThreadLocals() {
|
||||
mEglManager = new EglManager(*this);
|
||||
mRenderState = new RenderState(*this);
|
||||
mJankTracker = new JankTracker(mDisplayInfo);
|
||||
mVkManager = new VulkanManager(*this);
|
||||
}
|
||||
|
||||
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
|
||||
|
||||
@@ -46,6 +46,7 @@ class CanvasContext;
|
||||
class DispatchFrameCallbacks;
|
||||
class EglManager;
|
||||
class RenderProxy;
|
||||
class VulkanManager;
|
||||
|
||||
class TaskQueue {
|
||||
public:
|
||||
@@ -98,6 +99,8 @@ public:
|
||||
GrContext* getGrContext() const { return mGrContext.get(); }
|
||||
void setGrContext(GrContext* cxt) { mGrContext.reset(cxt); }
|
||||
|
||||
VulkanManager& vulkanManager() { return *mVkManager; }
|
||||
|
||||
protected:
|
||||
virtual bool threadLoop() override;
|
||||
|
||||
@@ -150,6 +153,7 @@ private:
|
||||
JankTracker* mJankTracker = nullptr;
|
||||
|
||||
sk_sp<GrContext> mGrContext;
|
||||
VulkanManager* mVkManager;
|
||||
};
|
||||
|
||||
} /* namespace renderthread */
|
||||
|
||||
675
libs/hwui/renderthread/VulkanManager.cpp
Normal file
675
libs/hwui/renderthread/VulkanManager.cpp
Normal file
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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 "VulkanManager.h"
|
||||
|
||||
#include "DeviceInfo.h"
|
||||
#include "RenderThread.h"
|
||||
|
||||
#include <GrContext.h>
|
||||
#include <GrTypes.h>
|
||||
#include <vk/GrVkTypes.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace renderthread {
|
||||
|
||||
#define GET_PROC(F) m ## F = (PFN_vk ## F) vkGetInstanceProcAddr(instance, "vk" #F)
|
||||
#define GET_DEV_PROC(F) m ## F = (PFN_vk ## F) vkGetDeviceProcAddr(device, "vk" #F)
|
||||
|
||||
VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {
|
||||
}
|
||||
|
||||
void VulkanManager::destroy() {
|
||||
if (!hasVkContext()) return;
|
||||
|
||||
if (VK_NULL_HANDLE != mCommandPool) {
|
||||
mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr);
|
||||
mCommandPool = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanManager::initialize() {
|
||||
if (hasVkContext()) { return; }
|
||||
|
||||
auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; };
|
||||
|
||||
mBackendContext.reset(GrVkBackendContext::Create(&mPresentQueueIndex, canPresent));
|
||||
|
||||
// Get all the addresses of needed vulkan functions
|
||||
VkInstance instance = mBackendContext->fInstance;
|
||||
VkDevice device = mBackendContext->fDevice;
|
||||
GET_PROC(CreateAndroidSurfaceKHR);
|
||||
GET_PROC(DestroySurfaceKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
|
||||
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);
|
||||
GET_DEV_PROC(FreeCommandBuffers);
|
||||
GET_DEV_PROC(ResetCommandBuffer);
|
||||
GET_DEV_PROC(BeginCommandBuffer);
|
||||
GET_DEV_PROC(EndCommandBuffer);
|
||||
GET_DEV_PROC(CmdPipelineBarrier);
|
||||
GET_DEV_PROC(GetDeviceQueue);
|
||||
GET_DEV_PROC(QueueSubmit);
|
||||
GET_DEV_PROC(QueueWaitIdle);
|
||||
GET_DEV_PROC(DeviceWaitIdle);
|
||||
GET_DEV_PROC(CreateSemaphore);
|
||||
GET_DEV_PROC(DestroySemaphore);
|
||||
GET_DEV_PROC(CreateFence);
|
||||
GET_DEV_PROC(DestroyFence);
|
||||
GET_DEV_PROC(WaitForFences);
|
||||
GET_DEV_PROC(ResetFences);
|
||||
|
||||
// create the command pool for the command buffers
|
||||
if (VK_NULL_HANDLE == mCommandPool) {
|
||||
VkCommandPoolCreateInfo commandPoolInfo;
|
||||
memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
|
||||
commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
// this needs to be on the render queue
|
||||
commandPoolInfo.queueFamilyIndex = mBackendContext->fGraphicsQueueIndex;
|
||||
commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||||
SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice,
|
||||
&commandPoolInfo, nullptr, &mCommandPool);
|
||||
SkASSERT(VK_SUCCESS == res);
|
||||
}
|
||||
|
||||
mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
|
||||
|
||||
mRenderThread.setGrContext(GrContext::Create(kVulkan_GrBackend,
|
||||
(GrBackendContext) mBackendContext.get()));
|
||||
DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
++surface->mCurrentBackbufferIndex;
|
||||
if (surface->mCurrentBackbufferIndex > surface->mImageCount) {
|
||||
surface->mCurrentBackbufferIndex = 0;
|
||||
}
|
||||
|
||||
VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
|
||||
surface->mCurrentBackbufferIndex;
|
||||
|
||||
// Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely
|
||||
// reuse its commands buffers.
|
||||
VkResult res = mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences,
|
||||
true, UINT64_MAX);
|
||||
if (res != VK_SUCCESS) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return backbuffer;
|
||||
}
|
||||
|
||||
|
||||
SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) {
|
||||
VulkanSurface::BackbufferInfo* backbuffer = getAvailableBackbuffer(surface);
|
||||
SkASSERT(backbuffer);
|
||||
|
||||
VkResult res;
|
||||
|
||||
res = mResetFences(mBackendContext->fDevice, 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(mBackendContext->fDevice, 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) {
|
||||
// tear swapchain down and try again
|
||||
if (!createSwapchain(surface)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// acquire the image
|
||||
res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
|
||||
backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex);
|
||||
|
||||
if (VK_SUCCESS != res) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// set up layout transfer from initial to color attachment
|
||||
VkImageLayout layout = surface->mImageLayouts[backbuffer->mImageIndex];
|
||||
SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
|
||||
VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
VkAccessFlags srcAccessMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
|
||||
0 : VK_ACCESS_MEMORY_READ_BIT;
|
||||
VkAccessFlags dstAccessMask = 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
|
||||
mBackendContext->fGraphicsQueueIndex, // 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(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
|
||||
|
||||
// We need to notify Skia that we changed the layout of the wrapped VkImage
|
||||
GrVkImageInfo* imageInfo;
|
||||
sk_sp<SkSurface> skSurface = surface->mSurfaces[backbuffer->mImageIndex];
|
||||
skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
|
||||
SkSurface::kFlushRead_BackendHandleAccess);
|
||||
imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
|
||||
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(mBackendContext->fDevice, 2, surface->mBackbuffers[i].mUsageFences, true,
|
||||
UINT64_MAX);
|
||||
surface->mBackbuffers[i].mImageIndex = -1;
|
||||
mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore,
|
||||
nullptr);
|
||||
mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore,
|
||||
nullptr);
|
||||
mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2,
|
||||
surface->mBackbuffers[i].mTransitionCmdBuffers);
|
||||
mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
|
||||
mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] surface->mBackbuffers;
|
||||
surface->mBackbuffers = nullptr;
|
||||
delete[] surface->mSurfaces;
|
||||
surface->mSurfaces = nullptr;
|
||||
delete[] surface->mImageLayouts;
|
||||
surface->mImageLayouts = nullptr;
|
||||
delete[] surface->mImages;
|
||||
surface->mImages = nullptr;
|
||||
}
|
||||
|
||||
void VulkanManager::destroySurface(VulkanSurface* surface) {
|
||||
// Make sure all submit commands have finished before starting to destroy objects.
|
||||
if (VK_NULL_HANDLE != mPresentQueue) {
|
||||
mQueueWaitIdle(mPresentQueue);
|
||||
}
|
||||
mDeviceWaitIdle(mBackendContext->fDevice);
|
||||
|
||||
destroyBuffers(surface);
|
||||
|
||||
if (VK_NULL_HANDLE != surface->mSwapchain) {
|
||||
mDestroySwapchainKHR(mBackendContext->fDevice, surface->mSwapchain, nullptr);
|
||||
surface->mSwapchain = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (VK_NULL_HANDLE != surface->mVkSurface) {
|
||||
mDestroySurfaceKHR(mBackendContext->fInstance, surface->mVkSurface, nullptr);
|
||||
surface->mVkSurface = VK_NULL_HANDLE;
|
||||
}
|
||||
delete surface;
|
||||
}
|
||||
|
||||
void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) {
|
||||
mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
|
||||
nullptr);
|
||||
SkASSERT(surface->mImageCount);
|
||||
surface->mImages = new VkImage[surface->mImageCount];
|
||||
mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain,
|
||||
&surface->mImageCount, surface->mImages);
|
||||
|
||||
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
|
||||
|
||||
bool wantSRGB = VK_FORMAT_R8G8B8A8_SRGB == format;
|
||||
GrPixelConfig config = wantSRGB ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
|
||||
|
||||
// set up initial image layouts and create surfaces
|
||||
surface->mImageLayouts = new VkImageLayout[surface->mImageCount];
|
||||
surface->mSurfaces = new sk_sp<SkSurface>[surface->mImageCount];
|
||||
for (uint32_t i = 0; i < surface->mImageCount; ++i) {
|
||||
GrBackendRenderTargetDesc desc;
|
||||
GrVkImageInfo info;
|
||||
info.fImage = surface->mImages[i];
|
||||
info.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
|
||||
info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.fFormat = format;
|
||||
info.fLevelCount = 1;
|
||||
|
||||
desc.fWidth = extent.width;
|
||||
desc.fHeight = extent.height;
|
||||
desc.fConfig = config;
|
||||
desc.fOrigin = kTopLeft_GrSurfaceOrigin;
|
||||
desc.fSampleCnt = 0;
|
||||
desc.fStencilBits = 0;
|
||||
desc.fRenderTargetHandle = (GrBackendObject) &info;
|
||||
|
||||
surface->mSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
|
||||
desc, &props);
|
||||
surface->mImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
}
|
||||
|
||||
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(mBackendContext->fDevice, &semaphoreInfo, nullptr,
|
||||
&surface->mBackbuffers[i].mAcquireSemaphore);
|
||||
SkDEBUGCODE(res = ) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
|
||||
&surface->mBackbuffers[i].mRenderSemaphore);
|
||||
SkDEBUGCODE(res = ) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo,
|
||||
surface->mBackbuffers[i].mTransitionCmdBuffers);
|
||||
SkDEBUGCODE(res = ) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
|
||||
&surface->mBackbuffers[i].mUsageFences[0]);
|
||||
SkDEBUGCODE(res = ) mCreateFence(mBackendContext->fDevice, &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(mBackendContext->fPhysicalDevice,
|
||||
surface->mVkSurface, &caps);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t surfaceFormatCount;
|
||||
res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
|
||||
&surfaceFormatCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
|
||||
VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
|
||||
res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
|
||||
&surfaceFormatCount, surfaceFormats);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t presentModeCount;
|
||||
res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
|
||||
surface->mVkSurface, &presentModeCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
|
||||
VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
|
||||
res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
|
||||
surface->mVkSurface, &presentModeCount, presentModes);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
uint32_t imageCount = caps.minImageCount + 2;
|
||||
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.supportedTransforms & caps.currentTransform);
|
||||
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;
|
||||
|
||||
// Pick our surface format. For now, just make sure it matches our sRGB request:
|
||||
VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
|
||||
VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
|
||||
|
||||
bool wantSRGB = false;
|
||||
#ifdef ANDROID_ENABLE_LINEAR_BLENDING
|
||||
wantSRGB = true;
|
||||
#endif
|
||||
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 (VK_FORMAT_UNDEFINED == surfaceFormat) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If mailbox mode is available, use it, as it is the lowest-latency non-
|
||||
// tearing mode. If not, fall back to FIFO which is always available.
|
||||
VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
for (uint32_t i = 0; i < presentModeCount; ++i) {
|
||||
// use mailbox
|
||||
if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
|
||||
mode = presentModes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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 = extent;
|
||||
swapchainCreateInfo.imageArrayLayers = 1;
|
||||
swapchainCreateInfo.imageUsage = usageFlags;
|
||||
|
||||
uint32_t queueFamilies[] = { mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex };
|
||||
if (mBackendContext->fGraphicsQueueIndex != 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 = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
swapchainCreateInfo.compositeAlpha = composite_alpha;
|
||||
swapchainCreateInfo.presentMode = mode;
|
||||
swapchainCreateInfo.clipped = true;
|
||||
swapchainCreateInfo.oldSwapchain = surface->mSwapchain;
|
||||
|
||||
res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr,
|
||||
&surface->mSwapchain);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// destroy the old swapchain
|
||||
if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
|
||||
mDeviceWaitIdle(mBackendContext->fDevice);
|
||||
|
||||
destroyBuffers(surface);
|
||||
|
||||
mDestroySwapchainKHR(mBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
|
||||
}
|
||||
|
||||
createBuffers(surface, surfaceFormat, extent);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) {
|
||||
initialize();
|
||||
|
||||
if (!window) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VulkanSurface* surface = new VulkanSurface();
|
||||
|
||||
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(mBackendContext->fInstance, &surfaceCreateInfo,
|
||||
nullptr, &surface->mVkSurface);
|
||||
if (VK_SUCCESS != res) {
|
||||
delete surface;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkDEBUGCODE(
|
||||
VkBool32 supported;
|
||||
res = mGetPhysicalDeviceSurfaceSupportKHR(mBackendContext->fPhysicalDevice,
|
||||
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 layoutToPipelineStageFlags(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 ||
|
||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout ||
|
||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout ||
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
|
||||
return VK_PIPELINE_STAGE_ALL_GRAPHICS_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) {
|
||||
VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
|
||||
surface->mCurrentBackbufferIndex;
|
||||
GrVkImageInfo* imageInfo;
|
||||
SkSurface* skSurface = surface->mSurfaces[backbuffer->mImageIndex].get();
|
||||
skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
|
||||
SkSurface::kFlushRead_BackendHandleAccess);
|
||||
// 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 = layoutToPipelineStageFlags(layout);
|
||||
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
VkAccessFlags srcAccessMask = layoutToSrcAccessMask(layout);
|
||||
VkAccessFlags dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
|
||||
VkImageMemoryBarrier imageMemoryBarrier = {
|
||||
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
|
||||
NULL, // pNext
|
||||
srcAccessMask, // outputMask
|
||||
dstAccessMask, // inputMask
|
||||
layout, // oldLayout
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout
|
||||
mBackendContext->fGraphicsQueueIndex, // 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->mImageLayouts[backbuffer->mImageIndex] = 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(mBackendContext->fQueue, 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();
|
||||
}
|
||||
|
||||
} /* namespace renderthread */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
171
libs/hwui/renderthread/VulkanManager.h
Normal file
171
libs/hwui/renderthread/VulkanManager.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef VULKANMANAGER_H
|
||||
#define VULKANMANAGER_H
|
||||
|
||||
#include <SkSurface.h>
|
||||
#include <vk/GrVkBackendContext.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace renderthread {
|
||||
|
||||
class RenderThread;
|
||||
|
||||
class VulkanSurface {
|
||||
public:
|
||||
VulkanSurface() {}
|
||||
|
||||
sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
sk_sp<SkSurface> mBackbuffer;
|
||||
|
||||
VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
|
||||
VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
|
||||
|
||||
BackbufferInfo* mBackbuffers;
|
||||
uint32_t mCurrentBackbufferIndex;
|
||||
|
||||
uint32_t mImageCount;
|
||||
VkImage* mImages;
|
||||
VkImageLayout* mImageLayouts;
|
||||
sk_sp<SkSurface>* mSurfaces;
|
||||
};
|
||||
|
||||
// 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.
|
||||
class VulkanManager {
|
||||
public:
|
||||
// Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must
|
||||
// be call once before use of the VulkanManager. Multiple calls after the first will simiply
|
||||
// return.
|
||||
void initialize();
|
||||
|
||||
// Quick check to see if the VulkanManager has been initialized.
|
||||
bool hasVkContext() { return mBackendContext.get() != nullptr; }
|
||||
|
||||
// Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
|
||||
// VulkanSurface object which is returned.
|
||||
VulkanSurface* createSurface(ANativeWindow* window);
|
||||
|
||||
// Destroy the VulkanSurface and all associated vulkan objects.
|
||||
void destroySurface(VulkanSurface* surface);
|
||||
|
||||
// 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; }
|
||||
|
||||
// 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);
|
||||
|
||||
private:
|
||||
friend class RenderThread;
|
||||
|
||||
explicit VulkanManager(RenderThread& thread);
|
||||
~VulkanManager() { destroy(); }
|
||||
|
||||
void destroyBuffers(VulkanSurface* surface);
|
||||
|
||||
bool createSwapchain(VulkanSurface* surface);
|
||||
void createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent);
|
||||
|
||||
VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface);
|
||||
|
||||
// simple wrapper class that exists only to initialize a pointer to NULL
|
||||
template <typename FNPTR_TYPE> class VkPtr {
|
||||
public:
|
||||
VkPtr() : fPtr(NULL) {}
|
||||
VkPtr operator=(FNPTR_TYPE ptr) { fPtr = ptr; return *this; }
|
||||
operator FNPTR_TYPE() const { return fPtr; }
|
||||
private:
|
||||
FNPTR_TYPE fPtr;
|
||||
};
|
||||
|
||||
// WSI interface functions
|
||||
VkPtr<PFN_vkCreateAndroidSurfaceKHR> mCreateAndroidSurfaceKHR;
|
||||
VkPtr<PFN_vkDestroySurfaceKHR> mDestroySurfaceKHR;
|
||||
VkPtr<PFN_vkGetPhysicalDeviceSurfaceSupportKHR> mGetPhysicalDeviceSurfaceSupportKHR;
|
||||
VkPtr<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR> mGetPhysicalDeviceSurfaceCapabilitiesKHR;
|
||||
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;
|
||||
|
||||
// Additional vulkan functions
|
||||
VkPtr<PFN_vkCreateCommandPool> mCreateCommandPool;
|
||||
VkPtr<PFN_vkDestroyCommandPool> mDestroyCommandPool;
|
||||
VkPtr<PFN_vkAllocateCommandBuffers> mAllocateCommandBuffers;
|
||||
VkPtr<PFN_vkFreeCommandBuffers> mFreeCommandBuffers;
|
||||
VkPtr<PFN_vkResetCommandBuffer> mResetCommandBuffer;
|
||||
VkPtr<PFN_vkBeginCommandBuffer> mBeginCommandBuffer;
|
||||
VkPtr<PFN_vkEndCommandBuffer> mEndCommandBuffer;
|
||||
VkPtr<PFN_vkCmdPipelineBarrier> mCmdPipelineBarrier;
|
||||
|
||||
VkPtr<PFN_vkGetDeviceQueue> mGetDeviceQueue;
|
||||
VkPtr<PFN_vkQueueSubmit> mQueueSubmit;
|
||||
VkPtr<PFN_vkQueueWaitIdle> mQueueWaitIdle;
|
||||
VkPtr<PFN_vkDeviceWaitIdle> mDeviceWaitIdle;
|
||||
|
||||
VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore;
|
||||
VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore;
|
||||
VkPtr<PFN_vkCreateFence> mCreateFence;
|
||||
VkPtr<PFN_vkDestroyFence> mDestroyFence;
|
||||
VkPtr<PFN_vkWaitForFences> mWaitForFences;
|
||||
VkPtr<PFN_vkResetFences> mResetFences;
|
||||
|
||||
RenderThread& mRenderThread;
|
||||
|
||||
sk_sp<const GrVkBackendContext> mBackendContext;
|
||||
uint32_t mPresentQueueIndex;
|
||||
VkQueue mPresentQueue = VK_NULL_HANDLE;
|
||||
VkCommandPool mCommandPool = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} /* namespace renderthread */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
#endif /* VULKANMANAGER_H */
|
||||
|
||||
Reference in New Issue
Block a user