• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <GrDirectContext.h>
20 #include <SkSurface.h>
21 #include <algorithm>
22 
23 #include <gui/TraceUtils.h>
24 #include "VulkanManager.h"
25 #include "utils/Color.h"
26 
27 #undef LOG_TAG
28 #define LOG_TAG "VulkanSurface"
29 
30 namespace android {
31 namespace uirenderer {
32 namespace renderthread {
33 
InvertTransform(int transform)34 static int InvertTransform(int transform) {
35     switch (transform) {
36         case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
37             return ANATIVEWINDOW_TRANSFORM_ROTATE_270;
38         case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
39             return ANATIVEWINDOW_TRANSFORM_ROTATE_180;
40         case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
41             return ANATIVEWINDOW_TRANSFORM_ROTATE_90;
42         default:
43             return 0;
44     }
45 }
46 
GetPreTransformMatrix(SkISize windowSize,int transform)47 static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) {
48     const int width = windowSize.width();
49     const int height = windowSize.height();
50 
51     switch (transform) {
52         case 0:
53             return SkMatrix::I();
54         case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
55             return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
56         case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
57             return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
58         case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
59             return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
60         default:
61             LOG_ALWAYS_FATAL("Unsupported Window Transform (%d)", transform);
62     }
63     return SkMatrix::I();
64 }
65 
GetPixelSnapMatrix(SkISize windowSize,int transform)66 static SkM44 GetPixelSnapMatrix(SkISize windowSize, int transform) {
67     // Small (~1/16th) nudge to ensure that pixel-aligned non-AA'd draws fill the
68     // desired fragment
69     static const SkScalar kOffset = 0.063f;
70     SkMatrix preRotation = GetPreTransformMatrix(windowSize, transform);
71     SkMatrix invert;
72     LOG_ALWAYS_FATAL_IF(!preRotation.invert(&invert));
73     return SkM44::Translate(kOffset, kOffset)
74             .postConcat(SkM44(preRotation))
75             .preConcat(SkM44(invert));
76 }
77 
ConnectAndSetWindowDefaults(ANativeWindow * window)78 static bool ConnectAndSetWindowDefaults(ANativeWindow* window) {
79     ATRACE_CALL();
80 
81     int err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
82     if (err != 0) {
83         ALOGE("native_window_api_connect failed: %s (%d)", strerror(-err), err);
84         return false;
85     }
86 
87     // this will match what we do on GL so pick that here.
88     err = window->setSwapInterval(window, 1);
89     if (err != 0) {
90         ALOGE("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err);
91         return false;
92     }
93 
94     err = native_window_set_shared_buffer_mode(window, false);
95     if (err != 0) {
96         ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err);
97         return false;
98     }
99 
100     err = native_window_set_auto_refresh(window, false);
101     if (err != 0) {
102         ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err);
103         return false;
104     }
105 
106     err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
107     if (err != 0) {
108         ALOGE("native_window_set_scaling_mode(NATIVE_WINDOW_SCALING_MODE_FREEZE) failed: %s (%d)",
109               strerror(-err), err);
110         return false;
111     }
112 
113     // Let consumer drive the size of the buffers.
114     err = native_window_set_buffers_dimensions(window, 0, 0);
115     if (err != 0) {
116         ALOGE("native_window_set_buffers_dimensions(0,0) failed: %s (%d)", strerror(-err), err);
117         return false;
118     }
119 
120     // Enable auto prerotation, so when buffer size is driven by the consumer
121     // and the transform hint specifies a 90 or 270 degree rotation, the width
122     // and height used for buffer pre-allocation and dequeueBuffer will be
123     // additionally swapped.
124     err = native_window_set_auto_prerotation(window, true);
125     if (err != 0) {
126         ALOGE("VulkanSurface::UpdateWindow() native_window_set_auto_prerotation failed: %s (%d)",
127               strerror(-err), err);
128         return false;
129     }
130 
131     return true;
132 }
133 
Create(ANativeWindow * window,ColorMode colorMode,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,GrDirectContext * grContext,const VulkanManager & vkManager,uint32_t extraBuffers)134 VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
135                                      SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
136                                      GrDirectContext* grContext, const VulkanManager& vkManager,
137                                      uint32_t extraBuffers) {
138     // Connect and set native window to default configurations.
139     if (!ConnectAndSetWindowDefaults(window)) {
140         return nullptr;
141     }
142 
143     // Initialize WindowInfo struct.
144     WindowInfo windowInfo;
145     if (!InitializeWindowInfoStruct(window, colorMode, colorType, colorSpace, vkManager,
146                                     extraBuffers, &windowInfo)) {
147         return nullptr;
148     }
149 
150     // Now we attempt to modify the window.
151     if (!UpdateWindow(window, windowInfo)) {
152         return nullptr;
153     }
154 
155     return new VulkanSurface(window, windowInfo, grContext);
156 }
157 
InitializeWindowInfoStruct(ANativeWindow * window,ColorMode colorMode,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,const VulkanManager & vkManager,uint32_t extraBuffers,WindowInfo * outWindowInfo)158 bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode,
159                                                SkColorType colorType,
160                                                sk_sp<SkColorSpace> colorSpace,
161                                                const VulkanManager& vkManager,
162                                                uint32_t extraBuffers, WindowInfo* outWindowInfo) {
163     ATRACE_CALL();
164 
165     int width, height;
166     int err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
167     if (err != 0 || width < 0) {
168         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, width);
169         return false;
170     }
171     err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
172     if (err != 0 || height < 0) {
173         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, height);
174         return false;
175     }
176     outWindowInfo->size = SkISize::Make(width, height);
177 
178     int query_value;
179     err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &query_value);
180     if (err != 0 || query_value < 0) {
181         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
182         return false;
183     }
184     outWindowInfo->transform = query_value;
185 
186     outWindowInfo->actualSize = outWindowInfo->size;
187     if (outWindowInfo->transform & ANATIVEWINDOW_TRANSFORM_ROTATE_90) {
188         outWindowInfo->actualSize.set(outWindowInfo->size.height(), outWindowInfo->size.width());
189     }
190 
191     outWindowInfo->preTransform =
192             GetPreTransformMatrix(outWindowInfo->size, outWindowInfo->transform);
193     outWindowInfo->pixelSnapMatrix =
194             GetPixelSnapMatrix(outWindowInfo->size, outWindowInfo->transform);
195 
196     err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
197     if (err != 0 || query_value < 0) {
198         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
199         return false;
200     }
201     outWindowInfo->bufferCount =
202             static_cast<uint32_t>(query_value) + sTargetBufferCount + extraBuffers;
203 
204     err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
205     if (err != 0 || query_value < 0) {
206         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
207         return false;
208     }
209     if (outWindowInfo->bufferCount > static_cast<uint32_t>(query_value)) {
210         // Application must settle for fewer images than desired:
211         outWindowInfo->bufferCount = static_cast<uint32_t>(query_value);
212     }
213 
214     outWindowInfo->bufferFormat = ColorTypeToBufferFormat(colorType);
215     outWindowInfo->colorspace = colorSpace;
216     outWindowInfo->dataspace = ColorSpaceToADataSpace(colorSpace.get(), colorType);
217     LOG_ALWAYS_FATAL_IF(
218             outWindowInfo->dataspace == HAL_DATASPACE_UNKNOWN && colorType != kAlpha_8_SkColorType,
219             "Unsupported colorspace");
220 
221     VkFormat vkPixelFormat;
222     switch (colorType) {
223         case kRGBA_8888_SkColorType:
224             vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM;
225             break;
226         case kRGBA_F16_SkColorType:
227             vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
228             break;
229         case kRGBA_1010102_SkColorType:
230             vkPixelFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
231             break;
232         case kAlpha_8_SkColorType:
233             vkPixelFormat = VK_FORMAT_R8_UNORM;
234             break;
235         default:
236             LOG_ALWAYS_FATAL("Unsupported colorType: %d", (int)colorType);
237     }
238 
239     LOG_ALWAYS_FATAL_IF(nullptr == vkManager.mGetPhysicalDeviceImageFormatProperties2,
240                         "vkGetPhysicalDeviceImageFormatProperties2 is missing");
241     VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo;
242     externalImageFormatInfo.sType =
243             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
244     externalImageFormatInfo.pNext = nullptr;
245     externalImageFormatInfo.handleType =
246             VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
247 
248     VkPhysicalDeviceImageFormatInfo2 imageFormatInfo;
249     imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
250     imageFormatInfo.pNext = &externalImageFormatInfo;
251     imageFormatInfo.format = vkPixelFormat;
252     imageFormatInfo.type = VK_IMAGE_TYPE_2D;
253     imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
254     // Currently Skia requires the images to be color attachments and support all transfer
255     // operations.
256     imageFormatInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
257                             VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
258     imageFormatInfo.flags = 0;
259 
260     VkAndroidHardwareBufferUsageANDROID hwbUsage;
261     hwbUsage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
262     hwbUsage.pNext = nullptr;
263 
264     VkImageFormatProperties2 imgFormProps;
265     imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
266     imgFormProps.pNext = &hwbUsage;
267 
268     VkResult res = vkManager.mGetPhysicalDeviceImageFormatProperties2(
269             vkManager.mPhysicalDevice, &imageFormatInfo, &imgFormProps);
270     if (VK_SUCCESS != res) {
271         ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2");
272         return false;
273     }
274 
275     uint64_t consumerUsage;
276     err = native_window_get_consumer_usage(window, &consumerUsage);
277     if (err != 0) {
278         ALOGE("native_window_get_consumer_usage failed: %s (%d)", strerror(-err), err);
279         return false;
280     }
281     outWindowInfo->windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage;
282 
283     return true;
284 }
285 
UpdateWindow(ANativeWindow * window,const WindowInfo & windowInfo)286 bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) {
287     ATRACE_CALL();
288 
289     int err = native_window_set_buffers_format(window, windowInfo.bufferFormat);
290     if (err != 0) {
291         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)",
292               windowInfo.bufferFormat, strerror(-err), err);
293         return false;
294     }
295 
296     err = native_window_set_buffers_data_space(window, windowInfo.dataspace);
297     if (err != 0) {
298         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) "
299               "failed: %s (%d)",
300               windowInfo.dataspace, strerror(-err), err);
301         return false;
302     }
303 
304     // native_window_set_buffers_transform() expects the transform the app is requesting that
305     // the compositor perform during composition. With native windows, pre-transform works by
306     // rendering with the same transform the compositor is applying (as in Vulkan), but
307     // then requesting the inverse transform, so that when the compositor does
308     // it's job the two transforms cancel each other out and the compositor ends
309     // up applying an identity transform to the app's buffer.
310     err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform));
311     if (err != 0) {
312         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) "
313               "failed: %s (%d)",
314               windowInfo.transform, strerror(-err), err);
315         return false;
316     }
317 
318     err = native_window_set_buffer_count(window, windowInfo.bufferCount);
319     if (err != 0) {
320         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
321               windowInfo.bufferCount, strerror(-err), err);
322         return false;
323     }
324 
325     err = native_window_set_usage(window, windowInfo.windowUsageFlags);
326     if (err != 0) {
327         ALOGE("VulkanSurface::UpdateWindow() native_window_set_usage failed: %s (%d)",
328               strerror(-err), err);
329         return false;
330     }
331 
332     return true;
333 }
334 
VulkanSurface(ANativeWindow * window,const WindowInfo & windowInfo,GrDirectContext * grContext)335 VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
336                              GrDirectContext* grContext)
337         : mNativeWindow(window), mWindowInfo(windowInfo), mGrContext(grContext) {}
338 
~VulkanSurface()339 VulkanSurface::~VulkanSurface() {
340     releaseBuffers();
341 
342     // release the native window to be available for use by other clients
343     int err = native_window_api_disconnect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL);
344     ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", strerror(-err), err);
345 }
346 
releaseBuffers()347 void VulkanSurface::releaseBuffers() {
348     for (uint32_t i = 0; i < mWindowInfo.bufferCount; i++) {
349         VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
350 
351         if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
352             int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(),
353                                                   bufferInfo.dequeue_fence.release());
354             if (err != 0) {
355                 ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err);
356             }
357             bufferInfo.dequeued = false;
358             bufferInfo.dequeue_fence.reset();
359         }
360 
361         LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued);
362         LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence.ok());
363 
364         bufferInfo.skSurface.reset();
365         bufferInfo.buffer.clear();
366         bufferInfo.hasValidContents = false;
367         bufferInfo.lastPresentedCount = 0;
368     }
369 }
370 
dequeueNativeBuffer()371 VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
372     // Set the mCurrentBufferInfo to invalid in case of error and only reset it to the correct
373     // value at the end of the function if everything dequeued correctly.
374     mCurrentBufferInfo = nullptr;
375 
376     // Query the transform hint synced from the initial Surface connect or last queueBuffer. The
377     // auto prerotation on the buffer is based on the same transform hint in use by the producer.
378     int transformHint = 0;
379     int err =
380             mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
381 
382     // Since auto pre-rotation is enabled, dequeueBuffer to get the consumer driven buffer size
383     // from ANativeWindowBuffer.
384     ANativeWindowBuffer* buffer;
385     base::unique_fd fence_fd;
386     {
387         int rawFd = -1;
388         err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &rawFd);
389         fence_fd.reset(rawFd);
390     }
391     if (err != 0) {
392         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
393         return nullptr;
394     }
395 
396     SkISize actualSize = SkISize::Make(buffer->width, buffer->height);
397     if (actualSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
398         if (actualSize != mWindowInfo.actualSize) {
399             // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
400             // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
401             mWindowInfo.actualSize = actualSize;
402             releaseBuffers();
403         }
404 
405         if (transformHint != mWindowInfo.transform) {
406             err = native_window_set_buffers_transform(mNativeWindow.get(),
407                                                       InvertTransform(transformHint));
408             if (err != 0) {
409                 ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", transformHint,
410                       strerror(-err), err);
411                 mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd.release());
412                 return nullptr;
413             }
414             mWindowInfo.transform = transformHint;
415         }
416 
417         mWindowInfo.size = actualSize;
418         if (mWindowInfo.transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
419             mWindowInfo.size.set(actualSize.height(), actualSize.width());
420         }
421 
422         mWindowInfo.preTransform = GetPreTransformMatrix(mWindowInfo.size, mWindowInfo.transform);
423         mWindowInfo.pixelSnapMatrix = GetPixelSnapMatrix(mWindowInfo.size, mWindowInfo.transform);
424     }
425 
426     uint32_t idx;
427     for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
428         if (mNativeBuffers[idx].buffer.get() == buffer) {
429             mNativeBuffers[idx].dequeued = true;
430             mNativeBuffers[idx].dequeue_fence = std::move(fence_fd);
431             break;
432         } else if (mNativeBuffers[idx].buffer.get() == nullptr) {
433             // increasing the number of buffers we have allocated
434             mNativeBuffers[idx].buffer = buffer;
435             mNativeBuffers[idx].dequeued = true;
436             mNativeBuffers[idx].dequeue_fence = std::move(fence_fd);
437             break;
438         }
439     }
440     if (idx == mWindowInfo.bufferCount) {
441         ALOGE("dequeueBuffer returned unrecognized buffer");
442         mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd.release());
443         return nullptr;
444     }
445 
446     VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx];
447 
448     if (bufferInfo->skSurface.get() == nullptr) {
449         bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer(
450                 mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
451                 kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr, /*from_window=*/true);
452         if (bufferInfo->skSurface.get() == nullptr) {
453             ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
454             mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer,
455                                         mNativeBuffers[idx].dequeue_fence.release());
456             mNativeBuffers[idx].dequeued = false;
457             return nullptr;
458         }
459     }
460 
461     mCurrentBufferInfo = bufferInfo;
462     return bufferInfo;
463 }
464 
presentCurrentBuffer(const SkRect & dirtyRect,int semaphoreFd)465 bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) {
466     if (!dirtyRect.isEmpty()) {
467 
468         // native_window_set_surface_damage takes a rectangle in prerotated space
469         // with a bottom-left origin. That is, top > bottom.
470         // The dirtyRect is also in prerotated space, so we just need to switch it to
471         // a bottom-left origin space.
472 
473         SkIRect irect;
474         dirtyRect.roundOut(&irect);
475         android_native_rect_t aRect;
476         aRect.left = irect.left();
477         aRect.top = logicalHeight() - irect.top();
478         aRect.right = irect.right();
479         aRect.bottom = logicalHeight() - irect.bottom();
480 
481         int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1);
482         ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err);
483     }
484 
485     LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
486     VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
487     // queueBuffer always closes fence, even on error
488     int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence.release();
489     int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd);
490 
491     currentBuffer.dequeued = false;
492     if (err != 0) {
493         ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
494         // cancelBuffer takes ownership of the fence
495         mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(),
496                                     currentBuffer.dequeue_fence.release());
497     } else {
498         currentBuffer.hasValidContents = true;
499         currentBuffer.lastPresentedCount = mPresentCount;
500         mPresentCount++;
501     }
502 
503     currentBuffer.dequeue_fence.reset();
504 
505     return err == 0;
506 }
507 
getCurrentBuffersAge()508 int VulkanSurface::getCurrentBuffersAge() {
509     LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
510     VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
511     return currentBuffer.hasValidContents ? (mPresentCount - currentBuffer.lastPresentedCount) : 0;
512 }
513 
514 } /* namespace renderthread */
515 } /* namespace uirenderer */
516 } /* namespace android */
517