blob: 36f540c47973847f92295e4a59f3a972443485ca [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
John Reckac513c22019-03-28 16:57:38 -0700225 int query_value;
226 int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
227 if (err != 0 || query_value < 0) {
228 ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
229 query_value);
230 return nullptr;
231 }
232 auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
233
234 windowInfo.bufferCount = min_undequeued_buffers
235 + std::max(VulkanSurface::sTargetBufferCount, caps.minImageCount);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500236 if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) {
237 // Application must settle for fewer images than desired:
238 windowInfo.bufferCount = caps.maxImageCount;
239 }
240
241 // Currently Skia requires the images to be color attachments and support all transfer
242 // operations.
243 VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
244 VK_IMAGE_USAGE_SAMPLED_BIT |
245 VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
246 VK_IMAGE_USAGE_TRANSFER_DST_BIT;
247 LOG_ALWAYS_FATAL_IF((caps.supportedUsageFlags & usageFlags) != usageFlags);
248
249 windowInfo.dataspace = HAL_DATASPACE_V0_SRGB;
250 if (colorMode == ColorMode::WideColorGamut) {
251 skcms_Matrix3x3 surfaceGamut;
252 LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&surfaceGamut),
253 "Could not get gamut matrix from color space");
254 if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) {
255 windowInfo.dataspace = HAL_DATASPACE_V0_SCRGB;
256 } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) {
257 windowInfo.dataspace = HAL_DATASPACE_DISPLAY_P3;
258 } else {
259 LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
260 }
261 }
262
263 windowInfo.pixelFormat = ColorTypeToPixelFormat(colorType);
264 VkFormat vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM;
265 if (windowInfo.pixelFormat == PIXEL_FORMAT_RGBA_FP16) {
266 vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
267 }
268
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400269 if (nullptr != vkManager.mGetPhysicalDeviceImageFormatProperties2) {
270 VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo;
271 externalImageFormatInfo.sType =
272 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
273 externalImageFormatInfo.pNext = nullptr;
274 externalImageFormatInfo.handleType =
275 VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
276
277 VkPhysicalDeviceImageFormatInfo2 imageFormatInfo;
278 imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
279 imageFormatInfo.pNext = &externalImageFormatInfo;
280 imageFormatInfo.format = vkPixelFormat;
281 imageFormatInfo.type = VK_IMAGE_TYPE_2D;
282 imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
283 imageFormatInfo.usage = usageFlags;
284 imageFormatInfo.flags = 0;
285
286 VkAndroidHardwareBufferUsageANDROID hwbUsage;
287 hwbUsage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
288 hwbUsage.pNext = nullptr;
289
290 VkImageFormatProperties2 imgFormProps;
291 imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
292 imgFormProps.pNext = &hwbUsage;
293
294 res = vkManager.mGetPhysicalDeviceImageFormatProperties2(vkManager.mPhysicalDevice,
295 &imageFormatInfo, &imgFormProps);
296 if (VK_SUCCESS != res) {
297 ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2");
298 return nullptr;
299 }
300
301 windowInfo.windowUsageFlags = hwbUsage.androidHardwareBufferUsage;
Greg Daniel2173f182019-04-01 09:29:44 -0400302 if (vkManager.isQualcomm()) {
303 windowInfo.windowUsageFlags =
304 windowInfo.windowUsageFlags | AHARDWAREBUFFER_USAGE_VENDOR_0;
305 }
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400306
307 } else {
308 ALOGE("VulkanSurface::Create() vkmGetPhysicalDeviceImageFormatProperties2 is missing");
309 return nullptr;
310 }
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500311
312 /*
313 * Now we attempt to modify the window!
314 */
315 if (!UpdateWindow(window, windowInfo)) {
316 return nullptr;
317 }
318
319 return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext);
320}
321
322bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) {
323 ATRACE_CALL();
324
325 if (!ResetNativeWindow(window)) {
326 return false;
327 }
328
329 // -- Configure the native window --
330 int err = native_window_set_buffers_format(window, windowInfo.pixelFormat);
331 if (err != 0) {
332 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)",
333 windowInfo.pixelFormat, strerror(-err), err);
334 return false;
335 }
336
337 err = native_window_set_buffers_data_space(window, windowInfo.dataspace);
338 if (err != 0) {
339 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) "
340 "failed: %s (%d)", windowInfo.dataspace, strerror(-err), err);
341 return false;
342 }
343
344 const SkISize& size = windowInfo.actualSize;
345 err = native_window_set_buffers_dimensions(window, size.width(), size.height());
346 if (err != 0) {
347 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) "
348 "failed: %s (%d)", size.width(), size.height(), strerror(-err), err);
349 return false;
350 }
351
352 // native_window_set_buffers_transform() expects the transform the app is requesting that
353 // the compositor perform during composition. With native windows, pre-transform works by
354 // rendering with the same transform the compositor is applying (as in Vulkan), but
355 // then requesting the inverse transform, so that when the compositor does
356 // it's job the two transforms cancel each other out and the compositor ends
357 // up applying an identity transform to the app's buffer.
358 err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform));
359 if (err != 0) {
360 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) "
361 "failed: %s (%d)", windowInfo.transform, strerror(-err), err);
362 return false;
363 }
364
365 // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than
366 // HWUI's expectation
367 err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
368 if (err != 0) {
369 ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) "
370 "failed: %s (%d)", strerror(-err), err);
371 return false;
372 }
373
John Reckac513c22019-03-28 16:57:38 -0700374 err = native_window_set_buffer_count(window, windowInfo.bufferCount);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500375 if (err != 0) {
John Reckac513c22019-03-28 16:57:38 -0700376 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500377 windowInfo.bufferCount, strerror(-err), err);
378 return false;
379 }
380
381 err = native_window_set_usage(window, windowInfo.windowUsageFlags);
382 if (err != 0) {
383 ALOGE("VulkanSurface::UpdateWindow() native_window_set_usage failed: %s (%d)",
384 strerror(-err), err);
385 return false;
386 }
387
388 return err == 0;
389}
390
391VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
392 SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext)
393 : mNativeWindow(window)
394 , mWindowInfo(windowInfo)
395 , mGrContext(grContext)
396 , mMinWindowSize(minWindowSize)
397 , mMaxWindowSize(maxWindowSize) { }
398
399VulkanSurface::~VulkanSurface() {
400 releaseBuffers();
401
402 // release the native window to be available for use by other clients
403 int err = native_window_api_disconnect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL);
404 ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", strerror(-err), err);
405}
406
407void VulkanSurface::releaseBuffers() {
John Reckac513c22019-03-28 16:57:38 -0700408 for (uint32_t i = 0; i < mWindowInfo.bufferCount; i++) {
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500409 VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
410
411 if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
412 int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(),
413 bufferInfo.dequeue_fence);
414 if (err != 0) {
415 ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err);
416 }
417 bufferInfo.dequeued = false;
418
419 if (bufferInfo.dequeue_fence >= 0) {
420 close(bufferInfo.dequeue_fence);
421 bufferInfo.dequeue_fence = -1;
422 }
423 }
424
425 LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued);
426 LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence != -1);
427
428 bufferInfo.skSurface.reset();
429 bufferInfo.buffer.clear();
430 bufferInfo.hasValidContents = false;
431 bufferInfo.lastPresentedCount = 0;
432 }
433}
434
435VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400436 // Set the mCurrentBufferInfo to invalid in case of error and only reset it to the correct
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500437 // value at the end of the function if everything dequeued correctly.
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400438 mCurrentBufferInfo = nullptr;
439
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500440
441 //check if the native window has been resized or rotated and update accordingly
442 SkISize newSize = SkISize::MakeEmpty();
443 int transformHint = 0;
444 mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth);
445 mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight);
446 mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
447 if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
448 WindowInfo newWindowInfo = mWindowInfo;
449 newWindowInfo.size = newSize;
450 newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0;
451 ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
452
453 int err = 0;
454 if (newWindowInfo.actualSize != mWindowInfo.actualSize) {
455 // reset the native buffers and update the window
456 err = native_window_set_buffers_dimensions(mNativeWindow.get(),
457 newWindowInfo.actualSize.width(),
458 newWindowInfo.actualSize.height());
459 if (err != 0) {
460 ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
461 newWindowInfo.actualSize.width(),
462 newWindowInfo.actualSize.height(), strerror(-err), err);
463 return nullptr;
464 }
465 // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
466 // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
467 releaseBuffers();
468 // TODO should we ask the nativewindow to allocate buffers?
469 }
470
471 if (newWindowInfo.transform != mWindowInfo.transform) {
472 err = native_window_set_buffers_transform(mNativeWindow.get(),
473 InvertTransform(newWindowInfo.transform));
474 if (err != 0) {
475 ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
476 newWindowInfo.transform, strerror(-err), err);
477 newWindowInfo.transform = mWindowInfo.transform;
478 ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
479 }
480 }
481
482 mWindowInfo = newWindowInfo;
483 }
484
485 ANativeWindowBuffer* buffer;
486 int fence_fd;
487 int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd);
488 if (err != 0) {
489 ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
490 return nullptr;
491 }
492
493 uint32_t idx;
494 for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
495 if (mNativeBuffers[idx].buffer.get() == buffer) {
496 mNativeBuffers[idx].dequeued = true;
497 mNativeBuffers[idx].dequeue_fence = fence_fd;
498 break;
499 } else if (mNativeBuffers[idx].buffer.get() == nullptr) {
500 // increasing the number of buffers we have allocated
501 mNativeBuffers[idx].buffer = buffer;
502 mNativeBuffers[idx].dequeued = true;
503 mNativeBuffers[idx].dequeue_fence = fence_fd;
504 break;
505 }
506 }
507 if (idx == mWindowInfo.bufferCount) {
508 ALOGE("dequeueBuffer returned unrecognized buffer");
509 mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
510 return nullptr;
511 }
512
513 VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx];
514
515 if (bufferInfo->skSurface.get() == nullptr) {
516 bufferInfo->skSurface =
517 SkSurface::MakeFromAHardwareBuffer(mGrContext,
518 ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
519 kTopLeft_GrSurfaceOrigin, DataSpaceToColorSpace(mWindowInfo.dataspace),
520 nullptr);
521 if (bufferInfo->skSurface.get() == nullptr) {
522 ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
523 mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
524 return nullptr;
525 }
526 }
527
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400528 mCurrentBufferInfo = bufferInfo;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500529 return bufferInfo;
530}
531
532bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) {
533 if (!dirtyRect.isEmpty()) {
534 SkRect transformedRect;
535 mWindowInfo.preTransform.mapRect(&transformedRect, dirtyRect);
536
537 SkIRect transformedIRect;
538 transformedRect.roundOut(&transformedIRect);
539 transformedIRect.intersect(0, 0, mWindowInfo.size.fWidth, mWindowInfo.size.fHeight);
540
541 // map to bottom-left coordinate system
542 android_native_rect_t aRect;
543 aRect.left = transformedIRect.x();
544 aRect.top = mWindowInfo.size.fHeight - (transformedIRect.y() + transformedIRect.height());
545 aRect.right = aRect.left + transformedIRect.width();
546 aRect.bottom = aRect.top - transformedIRect.height();
547
548 int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1);
549 ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err);
550 }
551
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400552 LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
553 VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500554 int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence;
555 int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd);
556
557 currentBuffer.dequeued = false;
558 // queueBuffer always closes fence, even on error
559 if (err != 0) {
560 ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
561 mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(),
562 currentBuffer.dequeue_fence);
563 } else {
564 currentBuffer.hasValidContents = true;
565 currentBuffer.lastPresentedCount = mPresentCount;
566 mPresentCount++;
567 }
568
569 if (currentBuffer.dequeue_fence >= 0) {
570 close(currentBuffer.dequeue_fence);
571 currentBuffer.dequeue_fence = -1;
572 }
573
574 return err == 0;
575}
576
577int VulkanSurface::getCurrentBuffersAge() {
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400578 LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
579 VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500580 return currentBuffer.hasValidContents ? (mPresentCount - currentBuffer.lastPresentedCount) : 0;
581}
582
583} /* namespace renderthread */
584} /* namespace uirenderer */
585} /* namespace android */