1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "VulkanManager.h"
18
19#include "DeviceInfo.h"
20#include "Properties.h"
21#include "RenderThread.h"
22#include "renderstate/RenderState.h"
23#include "utils/FatVector.h"
24
25#include <GrContext.h>
26#include <GrTypes.h>
27#include <vk/GrVkTypes.h>
28
29namespace android {
30namespace uirenderer {
31namespace renderthread {
32
33#define GET_PROC(F) m ## F = (PFN_vk ## F) vkGetInstanceProcAddr(instance, "vk" #F)
34#define GET_DEV_PROC(F) m ## F = (PFN_vk ## F) vkGetDeviceProcAddr(device, "vk" #F)
35
36VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {
37}
38
39void VulkanManager::destroy() {
40    if (!hasVkContext()) return;
41
42    mRenderThread.renderState().onVkContextDestroyed();
43    mRenderThread.setGrContext(nullptr);
44
45    if (VK_NULL_HANDLE != mCommandPool) {
46        mDestroyCommandPool(mBackendContext->fDevice, mCommandPool, nullptr);
47        mCommandPool = VK_NULL_HANDLE;
48    }
49    mBackendContext.reset();
50}
51
52void VulkanManager::initialize() {
53    if (hasVkContext()) { return; }
54
55    auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; };
56
57    mBackendContext.reset(GrVkBackendContext::Create(&mPresentQueueIndex, canPresent));
58
59    // Get all the addresses of needed vulkan functions
60    VkInstance instance = mBackendContext->fInstance;
61    VkDevice device = mBackendContext->fDevice;
62    GET_PROC(CreateAndroidSurfaceKHR);
63    GET_PROC(DestroySurfaceKHR);
64    GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
65    GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
66    GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
67    GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
68    GET_DEV_PROC(CreateSwapchainKHR);
69    GET_DEV_PROC(DestroySwapchainKHR);
70    GET_DEV_PROC(GetSwapchainImagesKHR);
71    GET_DEV_PROC(AcquireNextImageKHR);
72    GET_DEV_PROC(QueuePresentKHR);
73    GET_DEV_PROC(CreateCommandPool);
74    GET_DEV_PROC(DestroyCommandPool);
75    GET_DEV_PROC(AllocateCommandBuffers);
76    GET_DEV_PROC(FreeCommandBuffers);
77    GET_DEV_PROC(ResetCommandBuffer);
78    GET_DEV_PROC(BeginCommandBuffer);
79    GET_DEV_PROC(EndCommandBuffer);
80    GET_DEV_PROC(CmdPipelineBarrier);
81    GET_DEV_PROC(GetDeviceQueue);
82    GET_DEV_PROC(QueueSubmit);
83    GET_DEV_PROC(QueueWaitIdle);
84    GET_DEV_PROC(DeviceWaitIdle);
85    GET_DEV_PROC(CreateSemaphore);
86    GET_DEV_PROC(DestroySemaphore);
87    GET_DEV_PROC(CreateFence);
88    GET_DEV_PROC(DestroyFence);
89    GET_DEV_PROC(WaitForFences);
90    GET_DEV_PROC(ResetFences);
91
92    // create the command pool for the command buffers
93    if (VK_NULL_HANDLE == mCommandPool) {
94        VkCommandPoolCreateInfo commandPoolInfo;
95        memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
96        commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
97        // this needs to be on the render queue
98        commandPoolInfo.queueFamilyIndex = mBackendContext->fGraphicsQueueIndex;
99        commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
100        SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice,
101                &commandPoolInfo, nullptr, &mCommandPool);
102        SkASSERT(VK_SUCCESS == res);
103    }
104
105    mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
106
107    mRenderThread.setGrContext(GrContext::Create(kVulkan_GrBackend,
108            (GrBackendContext) mBackendContext.get()));
109    DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
110
111    if (Properties::enablePartialUpdates && Properties::useBufferAge) {
112        mSwapBehavior = SwapBehavior::BufferAge;
113    }
114
115    mRenderThread.renderState().onVkContextCreated();
116}
117
118// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
119// previous uses have finished before returning.
120VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) {
121    SkASSERT(surface->mBackbuffers);
122
123    ++surface->mCurrentBackbufferIndex;
124    if (surface->mCurrentBackbufferIndex > surface->mImageCount) {
125        surface->mCurrentBackbufferIndex = 0;
126    }
127
128    VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
129            surface->mCurrentBackbufferIndex;
130
131    // Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely
132    // reuse its commands buffers.
133    VkResult res = mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences,
134            true, UINT64_MAX);
135    if (res != VK_SUCCESS) {
136        return nullptr;
137    }
138
139    return backbuffer;
140}
141
142
143SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) {
144    VulkanSurface::BackbufferInfo* backbuffer = getAvailableBackbuffer(surface);
145    SkASSERT(backbuffer);
146
147    VkResult res;
148
149    res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
150    SkASSERT(VK_SUCCESS == res);
151
152    // The acquire will signal the attached mAcquireSemaphore. We use this to know the image has
153    // finished presenting and that it is safe to begin sending new commands to the returned image.
154    res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
155            backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex);
156
157    if (VK_ERROR_SURFACE_LOST_KHR == res) {
158        // need to figure out how to create a new vkSurface without the platformData*
159        // maybe use attach somehow? but need a Window
160        return nullptr;
161    }
162    if (VK_ERROR_OUT_OF_DATE_KHR == res) {
163        // tear swapchain down and try again
164        if (!createSwapchain(surface)) {
165            return nullptr;
166        }
167        backbuffer = getAvailableBackbuffer(surface);
168        res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences);
169        SkASSERT(VK_SUCCESS == res);
170
171        // acquire the image
172        res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX,
173                backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex);
174
175        if (VK_SUCCESS != res) {
176            return nullptr;
177        }
178    }
179
180    // set up layout transfer from initial to color attachment
181    VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout;
182    SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
183    VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
184                                        VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
185                                        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
186    VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
187    VkAccessFlags srcAccessMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
188                                  0 : VK_ACCESS_MEMORY_READ_BIT;
189    VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
190
191    VkImageMemoryBarrier imageMemoryBarrier = {
192        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,    // sType
193        NULL,                                      // pNext
194        srcAccessMask,                             // outputMask
195        dstAccessMask,                             // inputMask
196        layout,                                    // oldLayout
197        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,  // newLayout
198        mPresentQueueIndex,                        // srcQueueFamilyIndex
199        mBackendContext->fGraphicsQueueIndex,      // dstQueueFamilyIndex
200        surface->mImages[backbuffer->mImageIndex], // image
201        { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }  // subresourceRange
202    };
203    mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[0], 0);
204
205    VkCommandBufferBeginInfo info;
206    memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
207    info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
208    info.flags = 0;
209    mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[0], &info);
210
211    mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[0], srcStageMask, dstStageMask, 0,
212            0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
213
214    mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[0]);
215
216    VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
217    // insert the layout transfer into the queue and wait on the acquire
218    VkSubmitInfo submitInfo;
219    memset(&submitInfo, 0, sizeof(VkSubmitInfo));
220    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
221    submitInfo.waitSemaphoreCount = 1;
222    // Wait to make sure aquire semaphore set above has signaled.
223    submitInfo.pWaitSemaphores = &backbuffer->mAcquireSemaphore;
224    submitInfo.pWaitDstStageMask = &waitDstStageFlags;
225    submitInfo.commandBufferCount = 1;
226    submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[0];
227    submitInfo.signalSemaphoreCount = 0;
228
229    // Attach first fence to submission here so we can track when the command buffer finishes.
230    mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
231
232    // We need to notify Skia that we changed the layout of the wrapped VkImage
233    GrVkImageInfo* imageInfo;
234    sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
235    skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
236            SkSurface::kFlushRead_BackendHandleAccess);
237    imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
238
239    surface->mBackbuffer = std::move(skSurface);
240    return surface->mBackbuffer.get();
241}
242
243void VulkanManager::destroyBuffers(VulkanSurface* surface) {
244    if (surface->mBackbuffers) {
245        for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
246            mWaitForFences(mBackendContext->fDevice, 2, surface->mBackbuffers[i].mUsageFences, true,
247                    UINT64_MAX);
248            surface->mBackbuffers[i].mImageIndex = -1;
249            mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore,
250                    nullptr);
251            mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore,
252                    nullptr);
253            mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2,
254                    surface->mBackbuffers[i].mTransitionCmdBuffers);
255            mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
256            mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
257        }
258    }
259
260    delete[] surface->mBackbuffers;
261    surface->mBackbuffers = nullptr;
262    delete[] surface->mImageInfos;
263    surface->mImageInfos = nullptr;
264    delete[] surface->mImages;
265    surface->mImages = nullptr;
266}
267
268void VulkanManager::destroySurface(VulkanSurface* surface) {
269    // Make sure all submit commands have finished before starting to destroy objects.
270    if (VK_NULL_HANDLE != mPresentQueue) {
271        mQueueWaitIdle(mPresentQueue);
272    }
273    mDeviceWaitIdle(mBackendContext->fDevice);
274
275    destroyBuffers(surface);
276
277    if (VK_NULL_HANDLE != surface->mSwapchain) {
278        mDestroySwapchainKHR(mBackendContext->fDevice, surface->mSwapchain, nullptr);
279        surface->mSwapchain = VK_NULL_HANDLE;
280    }
281
282    if (VK_NULL_HANDLE != surface->mVkSurface) {
283        mDestroySurfaceKHR(mBackendContext->fInstance, surface->mVkSurface, nullptr);
284        surface->mVkSurface = VK_NULL_HANDLE;
285    }
286    delete surface;
287}
288
289void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) {
290    mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount,
291            nullptr);
292    SkASSERT(surface->mImageCount);
293    surface->mImages = new VkImage[surface->mImageCount];
294    mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain,
295            &surface->mImageCount, surface->mImages);
296
297    SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
298
299    bool wantSRGB = VK_FORMAT_R8G8B8A8_SRGB == format;
300    GrPixelConfig config = wantSRGB ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
301
302    // set up initial image layouts and create surfaces
303    surface->mImageInfos = new VulkanSurface::ImageInfo[surface->mImageCount];
304    for (uint32_t i = 0; i < surface->mImageCount; ++i) {
305        GrBackendRenderTargetDesc desc;
306        GrVkImageInfo info;
307        info.fImage = surface->mImages[i];
308        info.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
309        info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
310        info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
311        info.fFormat = format;
312        info.fLevelCount = 1;
313
314        desc.fWidth = extent.width;
315        desc.fHeight = extent.height;
316        desc.fConfig = config;
317        desc.fOrigin = kTopLeft_GrSurfaceOrigin;
318        desc.fSampleCnt = 0;
319        desc.fStencilBits = 0;
320        desc.fRenderTargetHandle = (GrBackendObject) &info;
321
322        VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
323        imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
324                desc, &props);
325    }
326
327    SkASSERT(mCommandPool != VK_NULL_HANDLE);
328
329    // set up the backbuffers
330    VkSemaphoreCreateInfo semaphoreInfo;
331    memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
332    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
333    semaphoreInfo.pNext = nullptr;
334    semaphoreInfo.flags = 0;
335    VkCommandBufferAllocateInfo commandBuffersInfo;
336    memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo));
337    commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
338    commandBuffersInfo.pNext = nullptr;
339    commandBuffersInfo.commandPool = mCommandPool;
340    commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
341    commandBuffersInfo.commandBufferCount = 2;
342    VkFenceCreateInfo fenceInfo;
343    memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
344    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
345    fenceInfo.pNext = nullptr;
346    fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
347
348    // we create one additional backbuffer structure here, because we want to
349    // give the command buffers they contain a chance to finish before we cycle back
350    surface->mBackbuffers = new VulkanSurface::BackbufferInfo[surface->mImageCount + 1];
351    for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
352        SkDEBUGCODE(VkResult res);
353        surface->mBackbuffers[i].mImageIndex = -1;
354        SkDEBUGCODE(res = ) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
355                &surface->mBackbuffers[i].mAcquireSemaphore);
356        SkDEBUGCODE(res = ) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr,
357                &surface->mBackbuffers[i].mRenderSemaphore);
358        SkDEBUGCODE(res = ) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo,
359                surface->mBackbuffers[i].mTransitionCmdBuffers);
360        SkDEBUGCODE(res = ) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
361                &surface->mBackbuffers[i].mUsageFences[0]);
362        SkDEBUGCODE(res = ) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr,
363                &surface->mBackbuffers[i].mUsageFences[1]);
364        SkASSERT(VK_SUCCESS == res);
365    }
366    surface->mCurrentBackbufferIndex = surface->mImageCount;
367}
368
369bool VulkanManager::createSwapchain(VulkanSurface* surface) {
370    // check for capabilities
371    VkSurfaceCapabilitiesKHR caps;
372    VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mBackendContext->fPhysicalDevice,
373            surface->mVkSurface, &caps);
374    if (VK_SUCCESS != res) {
375        return false;
376    }
377
378    uint32_t surfaceFormatCount;
379    res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
380            &surfaceFormatCount, nullptr);
381    if (VK_SUCCESS != res) {
382        return false;
383    }
384
385    FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount);
386    res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface,
387            &surfaceFormatCount, surfaceFormats.data());
388    if (VK_SUCCESS != res) {
389        return false;
390    }
391
392    uint32_t presentModeCount;
393    res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
394            surface->mVkSurface, &presentModeCount, nullptr);
395    if (VK_SUCCESS != res) {
396        return false;
397    }
398
399    FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount);
400    res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice,
401            surface->mVkSurface, &presentModeCount, presentModes.data());
402    if (VK_SUCCESS != res) {
403        return false;
404    }
405
406    VkExtent2D extent = caps.currentExtent;
407    // clamp width; to handle currentExtent of -1 and  protect us from broken hints
408    if (extent.width < caps.minImageExtent.width) {
409        extent.width = caps.minImageExtent.width;
410    }
411    SkASSERT(extent.width <= caps.maxImageExtent.width);
412    // clamp height
413    if (extent.height < caps.minImageExtent.height) {
414        extent.height = caps.minImageExtent.height;
415    }
416    SkASSERT(extent.height <= caps.maxImageExtent.height);
417
418    uint32_t imageCount = caps.minImageCount + 2;
419    if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
420        // Application must settle for fewer images than desired:
421        imageCount = caps.maxImageCount;
422    }
423
424    // Currently Skia requires the images to be color attchments and support all transfer
425    // operations.
426    VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
427                                   VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
428                                   VK_IMAGE_USAGE_TRANSFER_DST_BIT;
429    SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
430    SkASSERT(caps.supportedTransforms & caps.currentTransform);
431    SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
432                                             VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
433    VkCompositeAlphaFlagBitsKHR composite_alpha =
434        (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ?
435                                        VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR :
436                                        VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
437
438    // Pick our surface format. For now, just make sure it matches our sRGB request:
439    VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
440    VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
441
442    bool wantSRGB = false;
443#ifdef ANDROID_ENABLE_LINEAR_BLENDING
444    wantSRGB = true;
445#endif
446    for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
447        // We are assuming we can get either R8G8B8A8_UNORM or R8G8B8A8_SRGB
448        VkFormat desiredFormat = wantSRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
449        if (desiredFormat == surfaceFormats[i].format) {
450            surfaceFormat = surfaceFormats[i].format;
451            colorSpace = surfaceFormats[i].colorSpace;
452        }
453    }
454
455    if (VK_FORMAT_UNDEFINED == surfaceFormat) {
456        return false;
457    }
458
459    // If mailbox mode is available, use it, as it is the lowest-latency non-
460    // tearing mode. If not, fall back to FIFO which is always available.
461    VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
462    for (uint32_t i = 0; i < presentModeCount; ++i) {
463        // use mailbox
464        if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
465            mode = presentModes[i];
466            break;
467        }
468    }
469
470    VkSwapchainCreateInfoKHR swapchainCreateInfo;
471    memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
472    swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
473    swapchainCreateInfo.surface = surface->mVkSurface;
474    swapchainCreateInfo.minImageCount = imageCount;
475    swapchainCreateInfo.imageFormat = surfaceFormat;
476    swapchainCreateInfo.imageColorSpace = colorSpace;
477    swapchainCreateInfo.imageExtent = extent;
478    swapchainCreateInfo.imageArrayLayers = 1;
479    swapchainCreateInfo.imageUsage = usageFlags;
480
481    uint32_t queueFamilies[] = { mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex };
482    if (mBackendContext->fGraphicsQueueIndex != mPresentQueueIndex) {
483        swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
484        swapchainCreateInfo.queueFamilyIndexCount = 2;
485        swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
486    } else {
487        swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
488        swapchainCreateInfo.queueFamilyIndexCount = 0;
489        swapchainCreateInfo.pQueueFamilyIndices = nullptr;
490    }
491
492    swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
493    swapchainCreateInfo.compositeAlpha = composite_alpha;
494    swapchainCreateInfo.presentMode = mode;
495    swapchainCreateInfo.clipped = true;
496    swapchainCreateInfo.oldSwapchain = surface->mSwapchain;
497
498    res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr,
499            &surface->mSwapchain);
500    if (VK_SUCCESS != res) {
501        return false;
502    }
503
504    // destroy the old swapchain
505    if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
506        mDeviceWaitIdle(mBackendContext->fDevice);
507
508        destroyBuffers(surface);
509
510        mDestroySwapchainKHR(mBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
511    }
512
513    createBuffers(surface, surfaceFormat, extent);
514
515    return true;
516}
517
518
519VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) {
520    initialize();
521
522    if (!window) {
523        return nullptr;
524    }
525
526    VulkanSurface* surface = new VulkanSurface();
527
528    VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
529    memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
530    surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
531    surfaceCreateInfo.pNext = nullptr;
532    surfaceCreateInfo.flags = 0;
533    surfaceCreateInfo.window = window;
534
535    VkResult res = mCreateAndroidSurfaceKHR(mBackendContext->fInstance, &surfaceCreateInfo,
536            nullptr, &surface->mVkSurface);
537    if (VK_SUCCESS != res) {
538        delete surface;
539        return nullptr;
540    }
541
542SkDEBUGCODE(
543    VkBool32 supported;
544    res = mGetPhysicalDeviceSurfaceSupportKHR(mBackendContext->fPhysicalDevice,
545            mPresentQueueIndex, surface->mVkSurface, &supported);
546    // All physical devices and queue families on Android must be capable of presentation with any
547    // native window.
548    SkASSERT(VK_SUCCESS == res && supported);
549);
550
551    if (!createSwapchain(surface)) {
552        destroySurface(surface);
553        return nullptr;
554    }
555
556    return surface;
557}
558
559// Helper to know which src stage flags we need to set when transitioning to the present layout
560static VkPipelineStageFlags layoutToPipelineStageFlags(const VkImageLayout layout) {
561    if (VK_IMAGE_LAYOUT_GENERAL == layout) {
562        return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
563    } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout ||
564               VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
565        return VK_PIPELINE_STAGE_TRANSFER_BIT;
566    } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout ||
567               VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout ||
568               VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout ||
569               VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
570        return VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
571    } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
572        return VK_PIPELINE_STAGE_HOST_BIT;
573    }
574
575    SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout);
576    return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
577}
578
579// Helper to know which src access mask we need to set when transitioning to the present layout
580static VkAccessFlags layoutToSrcAccessMask(const VkImageLayout layout) {
581    VkAccessFlags flags = 0;
582    if (VK_IMAGE_LAYOUT_GENERAL == layout) {
583        flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
584                VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
585                VK_ACCESS_TRANSFER_WRITE_BIT |
586                VK_ACCESS_TRANSFER_READ_BIT |
587                VK_ACCESS_SHADER_READ_BIT |
588                VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_HOST_READ_BIT;
589    } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
590        flags = VK_ACCESS_HOST_WRITE_BIT;
591    } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
592        flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
593    } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout) {
594        flags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
595    } else if (VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
596        flags = VK_ACCESS_TRANSFER_WRITE_BIT;
597    } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout) {
598        flags = VK_ACCESS_TRANSFER_READ_BIT;
599    } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
600        flags = VK_ACCESS_SHADER_READ_BIT;
601    }
602    return flags;
603}
604
605void VulkanManager::swapBuffers(VulkanSurface* surface) {
606    if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
607        ATRACE_NAME("Finishing GPU work");
608        mDeviceWaitIdle(mBackendContext->fDevice);
609    }
610
611    VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
612            surface->mCurrentBackbufferIndex;
613    GrVkImageInfo* imageInfo;
614    SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get();
615    skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
616            SkSurface::kFlushRead_BackendHandleAccess);
617    // Check to make sure we never change the actually wrapped image
618    SkASSERT(imageInfo->fImage == surface->mImages[backbuffer->mImageIndex]);
619
620    // We need to transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR and make sure that all
621    // previous work is complete for before presenting. So we first add the necessary barrier here.
622    VkImageLayout layout = imageInfo->fImageLayout;
623    VkPipelineStageFlags srcStageMask = layoutToPipelineStageFlags(layout);
624    VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
625    VkAccessFlags srcAccessMask = layoutToSrcAccessMask(layout);
626    VkAccessFlags dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
627
628    VkImageMemoryBarrier imageMemoryBarrier = {
629        VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,    // sType
630        NULL,                                      // pNext
631        srcAccessMask,                             // outputMask
632        dstAccessMask,                             // inputMask
633        layout,                                    // oldLayout
634        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,           // newLayout
635        mBackendContext->fGraphicsQueueIndex,      // srcQueueFamilyIndex
636        mPresentQueueIndex,                        // dstQueueFamilyIndex
637        surface->mImages[backbuffer->mImageIndex], // image
638        { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }  // subresourceRange
639    };
640
641    mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[1], 0);
642    VkCommandBufferBeginInfo info;
643    memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
644    info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
645    info.flags = 0;
646    mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[1], &info);
647    mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[1], srcStageMask, dstStageMask, 0,
648            0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
649    mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]);
650
651    surface->mImageInfos[backbuffer->mImageIndex].mImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
652
653    // insert the layout transfer into the queue and wait on the acquire
654    VkSubmitInfo submitInfo;
655    memset(&submitInfo, 0, sizeof(VkSubmitInfo));
656    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
657    submitInfo.waitSemaphoreCount = 0;
658    submitInfo.pWaitDstStageMask = 0;
659    submitInfo.commandBufferCount = 1;
660    submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[1];
661    submitInfo.signalSemaphoreCount = 1;
662    // When this command buffer finishes we will signal this semaphore so that we know it is now
663    // safe to present the image to the screen.
664    submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore;
665
666    // Attach second fence to submission here so we can track when the command buffer finishes.
667    mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
668
669    // Submit present operation to present queue. We use a semaphore here to make sure all rendering
670    // to the image is complete and that the layout has been change to present on the graphics
671    // queue.
672    const VkPresentInfoKHR presentInfo =
673    {
674        VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType
675        NULL, // pNext
676        1, // waitSemaphoreCount
677        &backbuffer->mRenderSemaphore, // pWaitSemaphores
678        1, // swapchainCount
679        &surface->mSwapchain, // pSwapchains
680        &backbuffer->mImageIndex, // pImageIndices
681        NULL // pResults
682    };
683
684    mQueuePresentKHR(mPresentQueue, &presentInfo);
685
686    surface->mBackbuffer.reset();
687    surface->mImageInfos[backbuffer->mImageIndex].mLastUsed = surface->mCurrentTime;
688    surface->mImageInfos[backbuffer->mImageIndex].mInvalid = false;
689    surface->mCurrentTime++;
690}
691
692int VulkanManager::getAge(VulkanSurface* surface) {
693    VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
694            surface->mCurrentBackbufferIndex;
695    if (mSwapBehavior == SwapBehavior::Discard
696            || surface->mImageInfos[backbuffer->mImageIndex].mInvalid) {
697        return 0;
698    }
699    uint16_t lastUsed = surface->mImageInfos[backbuffer->mImageIndex].mLastUsed;
700    return surface->mCurrentTime - lastUsed;
701}
702
703} /* namespace renderthread */
704} /* namespace uirenderer */
705} /* namespace android */
706