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 
IsTransformSupported(int transform)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 
InvertTransform(int transform)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 
ConvertVkTransformToNative(VkSurfaceTransformFlagsKHR transform)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 
GetPreTransformMatrix(SkISize windowSize,int transform)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 
ComputeWindowSizeAndTransform(WindowInfo * windowInfo,const SkISize & minSize,const SkISize & maxSize)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 
ResetNativeWindow(ANativeWindow * window)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:
VkSurfaceAutoDeleter(VkInstance instance,VkSurfaceKHR surface,PFN_vkDestroySurfaceKHR destroySurfaceKHR)145     VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface,
146                          PFN_vkDestroySurfaceKHR destroySurfaceKHR)
147             : mInstance(instance), mSurface(surface), mDestroySurfaceKHR(destroySurfaceKHR) {}
~VkSurfaceAutoDeleter()148     ~VkSurfaceAutoDeleter() { destroy(); }
149 
destroy()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 
Create(ANativeWindow * window,ColorMode colorMode,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,GrContext * grContext,const VulkanManager & vkManager,uint32_t extraBuffers)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 
UpdateWindow(ANativeWindow * window,const WindowInfo & windowInfo)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 
VulkanSurface(ANativeWindow * window,const WindowInfo & windowInfo,SkISize minWindowSize,SkISize maxWindowSize,GrContext * grContext)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 
~VulkanSurface()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 
releaseBuffers()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 
dequeueNativeBuffer()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 
presentCurrentBuffer(const SkRect & dirtyRect,int semaphoreFd)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 
getCurrentBuffersAge()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