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