Home | History | Annotate | Download | only in renderthread
      1 /*
      2  * Copyright (C) 2019 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 "VulkanSurface.h"
     18 
     19 #include <SkSurface.h>
     20 #include <algorithm>
     21 
     22 #include "VulkanManager.h"
     23 #include "utils/Color.h"
     24 #include "utils/TraceUtils.h"
     25 
     26 namespace android {
     27 namespace uirenderer {
     28 namespace renderthread {
     29 
     30 static bool IsTransformSupported(int transform) {
     31     // For now, only support pure rotations, not flip or flip-and-rotate, until we have
     32     // more time to test them and build sample code. As far as I know we never actually
     33     // use anything besides pure rotations anyway.
     34     return transform == 0 || transform == NATIVE_WINDOW_TRANSFORM_ROT_90 ||
     35            transform == NATIVE_WINDOW_TRANSFORM_ROT_180 ||
     36            transform == NATIVE_WINDOW_TRANSFORM_ROT_270;
     37 }
     38 
     39 static int InvertTransform(int transform) {
     40     switch (transform) {
     41         case NATIVE_WINDOW_TRANSFORM_ROT_90:
     42             return NATIVE_WINDOW_TRANSFORM_ROT_270;
     43         case NATIVE_WINDOW_TRANSFORM_ROT_180:
     44             return NATIVE_WINDOW_TRANSFORM_ROT_180;
     45         case NATIVE_WINDOW_TRANSFORM_ROT_270:
     46             return NATIVE_WINDOW_TRANSFORM_ROT_90;
     47         default:
     48             return 0;
     49     }
     50 }
     51 
     52 static int ConvertVkTransformToNative(VkSurfaceTransformFlagsKHR transform) {
     53     switch (transform) {
     54         case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
     55             return NATIVE_WINDOW_TRANSFORM_ROT_270;
     56         case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
     57             return NATIVE_WINDOW_TRANSFORM_ROT_180;
     58         case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
     59             return NATIVE_WINDOW_TRANSFORM_ROT_90;
     60         case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
     61         case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
     62         default:
     63             return 0;
     64     }
     65 }
     66 
     67 static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) {
     68     const int width = windowSize.width();
     69     const int height = windowSize.height();
     70 
     71     switch (transform) {
     72         case 0:
     73             return SkMatrix::I();
     74         case NATIVE_WINDOW_TRANSFORM_ROT_90:
     75             return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
     76         case NATIVE_WINDOW_TRANSFORM_ROT_180:
     77             return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
     78         case NATIVE_WINDOW_TRANSFORM_ROT_270:
     79             return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
     80         default:
     81             LOG_ALWAYS_FATAL("Unsupported Window Transform (%d)", transform);
     82     }
     83     return SkMatrix::I();
     84 }
     85 
     86 void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize,
     87                                                   const SkISize& maxSize) {
     88     SkISize& windowSize = windowInfo->size;
     89 
     90     // clamp width & height to handle currentExtent of -1 and  protect us from broken hints
     91     if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width() ||
     92         windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) {
     93         int width = std::min(maxSize.width(), std::max(minSize.width(), windowSize.width()));
     94         int height = std::min(maxSize.height(), std::max(minSize.height(), windowSize.height()));
     95         ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]", windowSize.width(),
     96               windowSize.height(), width, height);
     97         windowSize.set(width, height);
     98     }
     99 
    100     windowInfo->actualSize = windowSize;
    101     if (windowInfo->transform & HAL_TRANSFORM_ROT_90) {
    102         windowInfo->actualSize.set(windowSize.height(), windowSize.width());
    103     }
    104 
    105     windowInfo->preTransform = GetPreTransformMatrix(windowInfo->size, windowInfo->transform);
    106 }
    107 
    108 static bool ResetNativeWindow(ANativeWindow* window) {
    109     // -- Reset the native window --
    110     // The native window might have been used previously, and had its properties
    111     // changed from defaults. That will affect the answer we get for queries
    112     // like MIN_UNDEQUEUED_BUFFERS. Reset to a known/default state before we
    113     // attempt such queries.
    114 
    115     int err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
    116     if (err != 0) {
    117         ALOGW("native_window_api_connect failed: %s (%d)", strerror(-err), err);
    118         return false;
    119     }
    120 
    121     // this will match what we do on GL so pick that here.
    122     err = window->setSwapInterval(window, 1);
    123     if (err != 0) {
    124         ALOGW("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err);
    125         return false;
    126     }
    127 
    128     err = native_window_set_shared_buffer_mode(window, false);
    129     if (err != 0) {
    130         ALOGW("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err);
    131         return false;
    132     }
    133 
    134     err = native_window_set_auto_refresh(window, false);
    135     if (err != 0) {
    136         ALOGW("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err);
    137         return false;
    138     }
    139 
    140     return true;
    141 }
    142 
    143 class VkSurfaceAutoDeleter {
    144 public:
    145     VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface,
    146                          PFN_vkDestroySurfaceKHR destroySurfaceKHR)
    147             : mInstance(instance), mSurface(surface), mDestroySurfaceKHR(destroySurfaceKHR) {}
    148     ~VkSurfaceAutoDeleter() { destroy(); }
    149 
    150     void destroy() {
    151         if (mSurface != VK_NULL_HANDLE) {
    152             mDestroySurfaceKHR(mInstance, mSurface, nullptr);
    153             mSurface = VK_NULL_HANDLE;
    154         }
    155     }
    156 
    157 private:
    158     VkInstance mInstance;
    159     VkSurfaceKHR mSurface;
    160     PFN_vkDestroySurfaceKHR mDestroySurfaceKHR;
    161 };
    162 
    163 VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
    164                                      SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
    165                                      GrContext* grContext, const VulkanManager& vkManager,
    166                                      uint32_t extraBuffers) {
    167     VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
    168     memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
    169     surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
    170     surfaceCreateInfo.pNext = nullptr;
    171     surfaceCreateInfo.flags = 0;
    172     surfaceCreateInfo.window = window;
    173 
    174     VkSurfaceKHR vkSurface = VK_NULL_HANDLE;
    175     VkResult res = vkManager.mCreateAndroidSurfaceKHR(vkManager.mInstance, &surfaceCreateInfo,
    176                                                       nullptr, &vkSurface);
    177     if (VK_SUCCESS != res) {
    178         ALOGE("VulkanSurface::Create() vkCreateAndroidSurfaceKHR failed (%d)", res);
    179         return nullptr;
    180     }
    181 
    182     VkSurfaceAutoDeleter vkSurfaceDeleter(vkManager.mInstance, vkSurface,
    183                                           vkManager.mDestroySurfaceKHR);
    184 
    185     SkDEBUGCODE(VkBool32 supported; res = vkManager.mGetPhysicalDeviceSurfaceSupportKHR(
    186                                             vkManager.mPhysicalDevice, vkManager.mPresentQueueIndex,
    187                                             vkSurface, &supported);
    188                 // All physical devices and queue families on Android must be capable of
    189                 // presentation with any native window.
    190                 SkASSERT(VK_SUCCESS == res && supported););
    191 
    192     // check for capabilities
    193     VkSurfaceCapabilitiesKHR caps;
    194     res = vkManager.mGetPhysicalDeviceSurfaceCapabilitiesKHR(vkManager.mPhysicalDevice, vkSurface,
    195                                                              &caps);
    196     if (VK_SUCCESS != res) {
    197         ALOGE("VulkanSurface::Create() vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed (%d)", res);
    198         return nullptr;
    199     }
    200 
    201     LOG_ALWAYS_FATAL_IF(0 == (caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR));
    202 
    203     /*
    204      * We must destroy the VK Surface before attempting to update the window as doing so after
    205      * will cause the native window to be modified in unexpected ways.
    206      */
    207     vkSurfaceDeleter.destroy();
    208 
    209     /*
    210      * Populate Window Info struct
    211      */
    212     WindowInfo windowInfo;
    213 
    214     windowInfo.transform = ConvertVkTransformToNative(caps.supportedTransforms);
    215     windowInfo.size = SkISize::Make(caps.currentExtent.width, caps.currentExtent.height);
    216 
    217     const SkISize minSize = SkISize::Make(caps.minImageExtent.width, caps.minImageExtent.height);
    218     const SkISize maxSize = SkISize::Make(caps.maxImageExtent.width, caps.maxImageExtent.height);
    219     ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize);
    220 
    221     int query_value;
    222     int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
    223     if (err != 0 || query_value < 0) {
    224         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
    225         return nullptr;
    226     }
    227     auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
    228 
    229     windowInfo.bufferCount = min_undequeued_buffers +
    230                              std::max(sTargetBufferCount + extraBuffers, caps.minImageCount);
    231     if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) {
    232         // Application must settle for fewer images than desired:
    233         windowInfo.bufferCount = caps.maxImageCount;
    234     }
    235 
    236     // Currently Skia requires the images to be color attachments and support all transfer
    237     // operations.
    238     VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
    239                                    VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
    240                                    VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    241     LOG_ALWAYS_FATAL_IF((caps.supportedUsageFlags & usageFlags) != usageFlags);
    242 
    243     windowInfo.dataspace = HAL_DATASPACE_V0_SRGB;
    244     if (colorMode == ColorMode::WideColorGamut) {
    245         skcms_Matrix3x3 surfaceGamut;
    246         LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&surfaceGamut),
    247                             "Could not get gamut matrix from color space");
    248         if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) {
    249             windowInfo.dataspace = HAL_DATASPACE_V0_SCRGB;
    250         } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) {
    251             windowInfo.dataspace = HAL_DATASPACE_DISPLAY_P3;
    252         } else {
    253             LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
    254         }
    255     }
    256 
    257     windowInfo.pixelFormat = ColorTypeToPixelFormat(colorType);
    258     VkFormat vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM;
    259     if (windowInfo.pixelFormat == PIXEL_FORMAT_RGBA_FP16) {
    260         vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
    261     }
    262 
    263     LOG_ALWAYS_FATAL_IF(nullptr == vkManager.mGetPhysicalDeviceImageFormatProperties2,
    264                         "vkGetPhysicalDeviceImageFormatProperties2 is missing");
    265     VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo;
    266     externalImageFormatInfo.sType =
    267             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
    268     externalImageFormatInfo.pNext = nullptr;
    269     externalImageFormatInfo.handleType =
    270             VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
    271 
    272     VkPhysicalDeviceImageFormatInfo2 imageFormatInfo;
    273     imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
    274     imageFormatInfo.pNext = &externalImageFormatInfo;
    275     imageFormatInfo.format = vkPixelFormat;
    276     imageFormatInfo.type = VK_IMAGE_TYPE_2D;
    277     imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
    278     imageFormatInfo.usage = usageFlags;
    279     imageFormatInfo.flags = 0;
    280 
    281     VkAndroidHardwareBufferUsageANDROID hwbUsage;
    282     hwbUsage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
    283     hwbUsage.pNext = nullptr;
    284 
    285     VkImageFormatProperties2 imgFormProps;
    286     imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
    287     imgFormProps.pNext = &hwbUsage;
    288 
    289     res = vkManager.mGetPhysicalDeviceImageFormatProperties2(vkManager.mPhysicalDevice,
    290                                                              &imageFormatInfo, &imgFormProps);
    291     if (VK_SUCCESS != res) {
    292         ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2");
    293         return nullptr;
    294     }
    295 
    296     uint64_t consumerUsage;
    297     native_window_get_consumer_usage(window, &consumerUsage);
    298     windowInfo.windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage;
    299 
    300     /*
    301      * Now we attempt to modify the window!
    302      */
    303     if (!UpdateWindow(window, windowInfo)) {
    304         return nullptr;
    305     }
    306 
    307     return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext);
    308 }
    309 
    310 bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) {
    311     ATRACE_CALL();
    312 
    313     if (!ResetNativeWindow(window)) {
    314         return false;
    315     }
    316 
    317     // -- Configure the native window --
    318     int err = native_window_set_buffers_format(window, windowInfo.pixelFormat);
    319     if (err != 0) {
    320         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)",
    321               windowInfo.pixelFormat, strerror(-err), err);
    322         return false;
    323     }
    324 
    325     err = native_window_set_buffers_data_space(window, windowInfo.dataspace);
    326     if (err != 0) {
    327         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) "
    328               "failed: %s (%d)",
    329               windowInfo.dataspace, strerror(-err), err);
    330         return false;
    331     }
    332 
    333     const SkISize& size = windowInfo.actualSize;
    334     err = native_window_set_buffers_dimensions(window, size.width(), size.height());
    335     if (err != 0) {
    336         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) "
    337               "failed: %s (%d)",
    338               size.width(), size.height(), strerror(-err), err);
    339         return false;
    340     }
    341 
    342     // native_window_set_buffers_transform() expects the transform the app is requesting that
    343     // the compositor perform during composition. With native windows, pre-transform works by
    344     // rendering with the same transform the compositor is applying (as in Vulkan), but
    345     // then requesting the inverse transform, so that when the compositor does
    346     // it's job the two transforms cancel each other out and the compositor ends
    347     // up applying an identity transform to the app's buffer.
    348     err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform));
    349     if (err != 0) {
    350         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) "
    351               "failed: %s (%d)",
    352               windowInfo.transform, strerror(-err), err);
    353         return false;
    354     }
    355 
    356     // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than
    357     // HWUI's expectation
    358     err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
    359     if (err != 0) {
    360         ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) "
    361               "failed: %s (%d)",
    362               strerror(-err), err);
    363         return false;
    364     }
    365 
    366     err = native_window_set_buffer_count(window, windowInfo.bufferCount);
    367     if (err != 0) {
    368         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
    369               windowInfo.bufferCount, strerror(-err), err);
    370         return false;
    371     }
    372 
    373     err = native_window_set_usage(window, windowInfo.windowUsageFlags);
    374     if (err != 0) {
    375         ALOGE("VulkanSurface::UpdateWindow() native_window_set_usage failed: %s (%d)",
    376               strerror(-err), err);
    377         return false;
    378     }
    379 
    380     return err == 0;
    381 }
    382 
    383 VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
    384                              SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext)
    385         : mNativeWindow(window)
    386         , mWindowInfo(windowInfo)
    387         , mGrContext(grContext)
    388         , mMinWindowSize(minWindowSize)
    389         , mMaxWindowSize(maxWindowSize) {}
    390 
    391 VulkanSurface::~VulkanSurface() {
    392     releaseBuffers();
    393 
    394     // release the native window to be available for use by other clients
    395     int err = native_window_api_disconnect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL);
    396     ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", strerror(-err), err);
    397 }
    398 
    399 void VulkanSurface::releaseBuffers() {
    400     for (uint32_t i = 0; i < mWindowInfo.bufferCount; i++) {
    401         VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
    402 
    403         if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
    404             int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(),
    405                                                   bufferInfo.dequeue_fence);
    406             if (err != 0) {
    407                 ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err);
    408             }
    409             bufferInfo.dequeued = false;
    410 
    411             if (bufferInfo.dequeue_fence >= 0) {
    412                 close(bufferInfo.dequeue_fence);
    413                 bufferInfo.dequeue_fence = -1;
    414             }
    415         }
    416 
    417         LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued);
    418         LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence != -1);
    419 
    420         bufferInfo.skSurface.reset();
    421         bufferInfo.buffer.clear();
    422         bufferInfo.hasValidContents = false;
    423         bufferInfo.lastPresentedCount = 0;
    424     }
    425 }
    426 
    427 VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
    428     // Set the mCurrentBufferInfo to invalid in case of error and only reset it to the correct
    429     // value at the end of the function if everything dequeued correctly.
    430     mCurrentBufferInfo = nullptr;
    431 
    432     // check if the native window has been resized or rotated and update accordingly
    433     SkISize newSize = SkISize::MakeEmpty();
    434     int transformHint = 0;
    435     mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth);
    436     mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight);
    437     mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
    438     if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
    439         WindowInfo newWindowInfo = mWindowInfo;
    440         newWindowInfo.size = newSize;
    441         newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0;
    442         ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
    443 
    444         int err = 0;
    445         if (newWindowInfo.actualSize != mWindowInfo.actualSize) {
    446             // reset the native buffers and update the window
    447             err = native_window_set_buffers_dimensions(mNativeWindow.get(),
    448                                                        newWindowInfo.actualSize.width(),
    449                                                        newWindowInfo.actualSize.height());
    450             if (err != 0) {
    451                 ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
    452                       newWindowInfo.actualSize.width(), newWindowInfo.actualSize.height(),
    453                       strerror(-err), err);
    454                 return nullptr;
    455             }
    456             // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
    457             // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
    458             releaseBuffers();
    459             // TODO should we ask the nativewindow to allocate buffers?
    460         }
    461 
    462         if (newWindowInfo.transform != mWindowInfo.transform) {
    463             err = native_window_set_buffers_transform(mNativeWindow.get(),
    464                                                       InvertTransform(newWindowInfo.transform));
    465             if (err != 0) {
    466                 ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
    467                       newWindowInfo.transform, strerror(-err), err);
    468                 newWindowInfo.transform = mWindowInfo.transform;
    469                 ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
    470             }
    471         }
    472 
    473         mWindowInfo = newWindowInfo;
    474     }
    475 
    476     ANativeWindowBuffer* buffer;
    477     int fence_fd;
    478     int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd);
    479     if (err != 0) {
    480         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
    481         return nullptr;
    482     }
    483 
    484     uint32_t idx;
    485     for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
    486         if (mNativeBuffers[idx].buffer.get() == buffer) {
    487             mNativeBuffers[idx].dequeued = true;
    488             mNativeBuffers[idx].dequeue_fence = fence_fd;
    489             break;
    490         } else if (mNativeBuffers[idx].buffer.get() == nullptr) {
    491             // increasing the number of buffers we have allocated
    492             mNativeBuffers[idx].buffer = buffer;
    493             mNativeBuffers[idx].dequeued = true;
    494             mNativeBuffers[idx].dequeue_fence = fence_fd;
    495             break;
    496         }
    497     }
    498     if (idx == mWindowInfo.bufferCount) {
    499         ALOGE("dequeueBuffer returned unrecognized buffer");
    500         mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
    501         return nullptr;
    502     }
    503 
    504     VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx];
    505 
    506     if (bufferInfo->skSurface.get() == nullptr) {
    507         bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer(
    508                 mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
    509                 kTopLeft_GrSurfaceOrigin, DataSpaceToColorSpace(mWindowInfo.dataspace), nullptr);
    510         if (bufferInfo->skSurface.get() == nullptr) {
    511             ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
    512             mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
    513             return nullptr;
    514         }
    515     }
    516 
    517     mCurrentBufferInfo = bufferInfo;
    518     return bufferInfo;
    519 }
    520 
    521 bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) {
    522     if (!dirtyRect.isEmpty()) {
    523 
    524         // native_window_set_surface_damage takes a rectangle in prerotated space
    525         // with a bottom-left origin. That is, top > bottom.
    526         // The dirtyRect is also in prerotated space, so we just need to switch it to
    527         // a bottom-left origin space.
    528 
    529         SkIRect irect;
    530         dirtyRect.roundOut(&irect);
    531         android_native_rect_t aRect;
    532         aRect.left = irect.left();
    533         aRect.top = logicalHeight() - irect.top();
    534         aRect.right = irect.right();
    535         aRect.bottom = logicalHeight() - irect.bottom();
    536 
    537         int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1);
    538         ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err);
    539     }
    540 
    541     LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
    542     VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
    543     int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence;
    544     int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd);
    545 
    546     currentBuffer.dequeued = false;
    547     // queueBuffer always closes fence, even on error
    548     if (err != 0) {
    549         ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
    550         mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(),
    551                                     currentBuffer.dequeue_fence);
    552     } else {
    553         currentBuffer.hasValidContents = true;
    554         currentBuffer.lastPresentedCount = mPresentCount;
    555         mPresentCount++;
    556     }
    557 
    558     if (currentBuffer.dequeue_fence >= 0) {
    559         close(currentBuffer.dequeue_fence);
    560         currentBuffer.dequeue_fence = -1;
    561     }
    562 
    563     return err == 0;
    564 }
    565 
    566 int VulkanSurface::getCurrentBuffersAge() {
    567     LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
    568     VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
    569     return currentBuffer.hasValidContents ? (mPresentCount - currentBuffer.lastPresentedCount) : 0;
    570 }
    571 
    572 } /* namespace renderthread */
    573 } /* namespace uirenderer */
    574 } /* namespace android */
    575