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