blob: be78b694f53a1e7079f508b14981caff6d8ceba5 [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 Sollenberger4670126b2019-04-08 15:45:20 -0400269 LOG_ALWAYS_FATAL_IF(nullptr == vkManager.mGetPhysicalDeviceImageFormatProperties2,
270 "vkGetPhysicalDeviceImageFormatProperties2 is missing");
271 VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo;
272 externalImageFormatInfo.sType =
273 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
274 externalImageFormatInfo.pNext = nullptr;
275 externalImageFormatInfo.handleType =
276 VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400277
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400278 VkPhysicalDeviceImageFormatInfo2 imageFormatInfo;
279 imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
280 imageFormatInfo.pNext = &externalImageFormatInfo;
281 imageFormatInfo.format = vkPixelFormat;
282 imageFormatInfo.type = VK_IMAGE_TYPE_2D;
283 imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
284 imageFormatInfo.usage = usageFlags;
285 imageFormatInfo.flags = 0;
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400286
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400287 VkAndroidHardwareBufferUsageANDROID hwbUsage;
288 hwbUsage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
289 hwbUsage.pNext = nullptr;
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400290
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400291 VkImageFormatProperties2 imgFormProps;
292 imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
293 imgFormProps.pNext = &hwbUsage;
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400294
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400295 res = vkManager.mGetPhysicalDeviceImageFormatProperties2(vkManager.mPhysicalDevice,
296 &imageFormatInfo, &imgFormProps);
297 if (VK_SUCCESS != res) {
298 ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2");
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400299 return nullptr;
300 }
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500301
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400302 uint64_t consumerUsage;
303 native_window_get_consumer_usage(window, &consumerUsage);
304 windowInfo.windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage;
305
306 if (vkManager.isQualcomm()) {
307 windowInfo.windowUsageFlags =
308 windowInfo.windowUsageFlags | AHARDWAREBUFFER_USAGE_VENDOR_0;
309 }
310
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500311 /*
312 * Now we attempt to modify the window!
313 */
314 if (!UpdateWindow(window, windowInfo)) {
315 return nullptr;
316 }
317
318 return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext);
319}
320
321bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) {
322 ATRACE_CALL();
323
324 if (!ResetNativeWindow(window)) {
325 return false;
326 }
327
328 // -- Configure the native window --
329 int err = native_window_set_buffers_format(window, windowInfo.pixelFormat);
330 if (err != 0) {
331 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)",
332 windowInfo.pixelFormat, strerror(-err), err);
333 return false;
334 }
335
336 err = native_window_set_buffers_data_space(window, windowInfo.dataspace);
337 if (err != 0) {
338 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) "
339 "failed: %s (%d)", windowInfo.dataspace, strerror(-err), err);
340 return false;
341 }
342
343 const SkISize& size = windowInfo.actualSize;
344 err = native_window_set_buffers_dimensions(window, size.width(), size.height());
345 if (err != 0) {
346 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) "
347 "failed: %s (%d)", size.width(), size.height(), strerror(-err), err);
348 return false;
349 }
350
351 // native_window_set_buffers_transform() expects the transform the app is requesting that
352 // the compositor perform during composition. With native windows, pre-transform works by
353 // rendering with the same transform the compositor is applying (as in Vulkan), but
354 // then requesting the inverse transform, so that when the compositor does
355 // it's job the two transforms cancel each other out and the compositor ends
356 // up applying an identity transform to the app's buffer.
357 err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform));
358 if (err != 0) {
359 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) "
360 "failed: %s (%d)", windowInfo.transform, strerror(-err), err);
361 return false;
362 }
363
364 // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than
365 // HWUI's expectation
366 err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
367 if (err != 0) {
368 ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) "
369 "failed: %s (%d)", strerror(-err), err);
370 return false;
371 }
372
John Reckac513c22019-03-28 16:57:38 -0700373 err = native_window_set_buffer_count(window, windowInfo.bufferCount);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500374 if (err != 0) {
John Reckac513c22019-03-28 16:57:38 -0700375 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500376 windowInfo.bufferCount, strerror(-err), err);
377 return false;
378 }
379
380 err = native_window_set_usage(window, windowInfo.windowUsageFlags);
381 if (err != 0) {
382 ALOGE("VulkanSurface::UpdateWindow() native_window_set_usage failed: %s (%d)",
383 strerror(-err), err);
384 return false;
385 }
386
387 return err == 0;
388}
389
390VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
391 SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext)
392 : mNativeWindow(window)
393 , mWindowInfo(windowInfo)
394 , mGrContext(grContext)
395 , mMinWindowSize(minWindowSize)
396 , mMaxWindowSize(maxWindowSize) { }
397
398VulkanSurface::~VulkanSurface() {
399 releaseBuffers();
400
401 // release the native window to be available for use by other clients
402 int err = native_window_api_disconnect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL);
403 ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", strerror(-err), err);
404}
405
406void VulkanSurface::releaseBuffers() {
John Reckac513c22019-03-28 16:57:38 -0700407 for (uint32_t i = 0; i < mWindowInfo.bufferCount; i++) {
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500408 VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
409
410 if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
411 int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(),
412 bufferInfo.dequeue_fence);
413 if (err != 0) {
414 ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err);
415 }
416 bufferInfo.dequeued = false;
417
418 if (bufferInfo.dequeue_fence >= 0) {
419 close(bufferInfo.dequeue_fence);
420 bufferInfo.dequeue_fence = -1;
421 }
422 }
423
424 LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued);
425 LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence != -1);
426
427 bufferInfo.skSurface.reset();
428 bufferInfo.buffer.clear();
429 bufferInfo.hasValidContents = false;
430 bufferInfo.lastPresentedCount = 0;
431 }
432}
433
434VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400435 // Set the mCurrentBufferInfo to invalid in case of error and only reset it to the correct
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500436 // value at the end of the function if everything dequeued correctly.
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400437 mCurrentBufferInfo = nullptr;
438
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500439
440 //check if the native window has been resized or rotated and update accordingly
441 SkISize newSize = SkISize::MakeEmpty();
442 int transformHint = 0;
443 mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth);
444 mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight);
445 mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
446 if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
447 WindowInfo newWindowInfo = mWindowInfo;
448 newWindowInfo.size = newSize;
449 newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0;
450 ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
451
452 int err = 0;
453 if (newWindowInfo.actualSize != mWindowInfo.actualSize) {
454 // reset the native buffers and update the window
455 err = native_window_set_buffers_dimensions(mNativeWindow.get(),
456 newWindowInfo.actualSize.width(),
457 newWindowInfo.actualSize.height());
458 if (err != 0) {
459 ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
460 newWindowInfo.actualSize.width(),
461 newWindowInfo.actualSize.height(), strerror(-err), err);
462 return nullptr;
463 }
464 // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
465 // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
466 releaseBuffers();
467 // TODO should we ask the nativewindow to allocate buffers?
468 }
469
470 if (newWindowInfo.transform != mWindowInfo.transform) {
471 err = native_window_set_buffers_transform(mNativeWindow.get(),
472 InvertTransform(newWindowInfo.transform));
473 if (err != 0) {
474 ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
475 newWindowInfo.transform, strerror(-err), err);
476 newWindowInfo.transform = mWindowInfo.transform;
477 ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
478 }
479 }
480
481 mWindowInfo = newWindowInfo;
482 }
483
484 ANativeWindowBuffer* buffer;
485 int fence_fd;
486 int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd);
487 if (err != 0) {
488 ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
489 return nullptr;
490 }
491
492 uint32_t idx;
493 for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
494 if (mNativeBuffers[idx].buffer.get() == buffer) {
495 mNativeBuffers[idx].dequeued = true;
496 mNativeBuffers[idx].dequeue_fence = fence_fd;
497 break;
498 } else if (mNativeBuffers[idx].buffer.get() == nullptr) {
499 // increasing the number of buffers we have allocated
500 mNativeBuffers[idx].buffer = buffer;
501 mNativeBuffers[idx].dequeued = true;
502 mNativeBuffers[idx].dequeue_fence = fence_fd;
503 break;
504 }
505 }
506 if (idx == mWindowInfo.bufferCount) {
507 ALOGE("dequeueBuffer returned unrecognized buffer");
508 mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
509 return nullptr;
510 }
511
512 VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx];
513
514 if (bufferInfo->skSurface.get() == nullptr) {
515 bufferInfo->skSurface =
516 SkSurface::MakeFromAHardwareBuffer(mGrContext,
517 ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
518 kTopLeft_GrSurfaceOrigin, DataSpaceToColorSpace(mWindowInfo.dataspace),
519 nullptr);
520 if (bufferInfo->skSurface.get() == nullptr) {
521 ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
522 mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
523 return nullptr;
524 }
525 }
526
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400527 mCurrentBufferInfo = bufferInfo;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500528 return bufferInfo;
529}
530
531bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) {
532 if (!dirtyRect.isEmpty()) {
533 SkRect transformedRect;
534 mWindowInfo.preTransform.mapRect(&transformedRect, dirtyRect);
535
536 SkIRect transformedIRect;
537 transformedRect.roundOut(&transformedIRect);
538 transformedIRect.intersect(0, 0, mWindowInfo.size.fWidth, mWindowInfo.size.fHeight);
539
540 // map to bottom-left coordinate system
541 android_native_rect_t aRect;
542 aRect.left = transformedIRect.x();
543 aRect.top = mWindowInfo.size.fHeight - (transformedIRect.y() + transformedIRect.height());
544 aRect.right = aRect.left + transformedIRect.width();
545 aRect.bottom = aRect.top - transformedIRect.height();
546
547 int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1);
548 ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err);
549 }
550
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400551 LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
552 VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500553 int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence;
554 int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd);
555
556 currentBuffer.dequeued = false;
557 // queueBuffer always closes fence, even on error
558 if (err != 0) {
559 ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
560 mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(),
561 currentBuffer.dequeue_fence);
562 } else {
563 currentBuffer.hasValidContents = true;
564 currentBuffer.lastPresentedCount = mPresentCount;
565 mPresentCount++;
566 }
567
568 if (currentBuffer.dequeue_fence >= 0) {
569 close(currentBuffer.dequeue_fence);
570 currentBuffer.dequeue_fence = -1;
571 }
572
573 return err == 0;
574}
575
576int VulkanSurface::getCurrentBuffersAge() {
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400577 LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
578 VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500579 return currentBuffer.hasValidContents ? (mPresentCount - currentBuffer.lastPresentedCount) : 0;
580}
581
582} /* namespace renderthread */
583} /* namespace uirenderer */
584} /* namespace android */