blob: df6b9ed2cdcbade9080f34551651a03aa16fb253 [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
Derek Sollenbergera19b71a2019-02-15 16:36:30 -050019#include <SkSurface.h>
John Reck0fa0cbc2019-04-05 16:57:46 -070020#include <algorithm>
Derek Sollenbergera19b71a2019-02-15 16:36:30 -050021
22#include "VulkanManager.h"
Derek Sollenbergera19b71a2019-02-15 16:36:30 -050023#include "utils/Color.h"
John Reck0fa0cbc2019-04-05 16:57:46 -070024#include "utils/TraceUtils.h"
Derek Sollenbergera19b71a2019-02-15 16:36:30 -050025
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.
John Reck0fa0cbc2019-04-05 16:57:46 -070034 return transform == 0 || transform == NATIVE_WINDOW_TRANSFORM_ROT_90 ||
35 transform == NATIVE_WINDOW_TRANSFORM_ROT_180 ||
36 transform == NATIVE_WINDOW_TRANSFORM_ROT_270;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -050037}
38
39static 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
52static 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
67static 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
86void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize,
John Reck0fa0cbc2019-04-05 16:57:46 -070087 const SkISize& maxSize) {
Derek Sollenbergera19b71a2019-02-15 16:36:30 -050088 SkISize& windowSize = windowInfo->size;
89
90 // clamp width & height to handle currentExtent of -1 and protect us from broken hints
John Reck0fa0cbc2019-04-05 16:57:46 -070091 if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width() ||
92 windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) {
Derek Sollenbergera19b71a2019-02-15 16:36:30 -050093 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()));
John Reck0fa0cbc2019-04-05 16:57:46 -070095 ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]", windowSize.width(),
96 windowSize.height(), width, height);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -050097 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
108static 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
143class VkSurfaceAutoDeleter {
144public:
145 VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface,
146 PFN_vkDestroySurfaceKHR destroySurfaceKHR)
John Reck0fa0cbc2019-04-05 16:57:46 -0700147 : mInstance(instance), mSurface(surface), mDestroySurfaceKHR(destroySurfaceKHR) {}
148 ~VkSurfaceAutoDeleter() { destroy(); }
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500149
150 void destroy() {
151 if (mSurface != VK_NULL_HANDLE) {
152 mDestroySurfaceKHR(mInstance, mSurface, nullptr);
153 mSurface = VK_NULL_HANDLE;
154 }
155 }
156
157private:
158 VkInstance mInstance;
159 VkSurfaceKHR mSurface;
160 PFN_vkDestroySurfaceKHR mDestroySurfaceKHR;
161};
162
163VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
John Reck0fa0cbc2019-04-05 16:57:46 -0700164 SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
165 GrContext* grContext, const VulkanManager& vkManager,
166 uint32_t extraBuffers) {
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500167 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(
John Reck0fa0cbc2019-04-05 16:57:46 -0700186 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););
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500191
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
John Reckac513c22019-03-28 16:57:38 -0700221 int query_value;
222 int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
223 if (err != 0 || query_value < 0) {
John Reck0fa0cbc2019-04-05 16:57:46 -0700224 ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
John Reckac513c22019-03-28 16:57:38 -0700225 return nullptr;
226 }
227 auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
228
John Reck0fa0cbc2019-04-05 16:57:46 -0700229 windowInfo.bufferCount = min_undequeued_buffers +
230 std::max(sTargetBufferCount + extraBuffers, caps.minImageCount);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500231 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 |
John Reck0fa0cbc2019-04-05 16:57:46 -0700239 VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500240 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
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400263 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;
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400271
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400272 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;
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400280
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400281 VkAndroidHardwareBufferUsageANDROID hwbUsage;
282 hwbUsage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
283 hwbUsage.pNext = nullptr;
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400284
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400285 VkImageFormatProperties2 imgFormProps;
286 imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
287 imgFormProps.pNext = &hwbUsage;
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400288
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400289 res = vkManager.mGetPhysicalDeviceImageFormatProperties2(vkManager.mPhysicalDevice,
290 &imageFormatInfo, &imgFormProps);
291 if (VK_SUCCESS != res) {
292 ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2");
Derek Sollenberger31c1b822019-03-19 13:55:10 -0400293 return nullptr;
294 }
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500295
Derek Sollenberger4670126b2019-04-08 15:45:20 -0400296 uint64_t consumerUsage;
297 native_window_get_consumer_usage(window, &consumerUsage);
298 windowInfo.windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage;
299
300 if (vkManager.isQualcomm()) {
301 windowInfo.windowUsageFlags =
302 windowInfo.windowUsageFlags | AHARDWAREBUFFER_USAGE_VENDOR_0;
303 }
304
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500305 /*
306 * Now we attempt to modify the window!
307 */
308 if (!UpdateWindow(window, windowInfo)) {
309 return nullptr;
310 }
311
312 return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext);
313}
314
315bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) {
316 ATRACE_CALL();
317
318 if (!ResetNativeWindow(window)) {
319 return false;
320 }
321
322 // -- Configure the native window --
323 int err = native_window_set_buffers_format(window, windowInfo.pixelFormat);
324 if (err != 0) {
325 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)",
326 windowInfo.pixelFormat, strerror(-err), err);
327 return false;
328 }
329
330 err = native_window_set_buffers_data_space(window, windowInfo.dataspace);
331 if (err != 0) {
332 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) "
John Reck0fa0cbc2019-04-05 16:57:46 -0700333 "failed: %s (%d)",
334 windowInfo.dataspace, strerror(-err), err);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500335 return false;
336 }
337
338 const SkISize& size = windowInfo.actualSize;
339 err = native_window_set_buffers_dimensions(window, size.width(), size.height());
340 if (err != 0) {
341 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) "
John Reck0fa0cbc2019-04-05 16:57:46 -0700342 "failed: %s (%d)",
343 size.width(), size.height(), strerror(-err), err);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500344 return false;
345 }
346
347 // native_window_set_buffers_transform() expects the transform the app is requesting that
348 // the compositor perform during composition. With native windows, pre-transform works by
349 // rendering with the same transform the compositor is applying (as in Vulkan), but
350 // then requesting the inverse transform, so that when the compositor does
351 // it's job the two transforms cancel each other out and the compositor ends
352 // up applying an identity transform to the app's buffer.
353 err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform));
354 if (err != 0) {
355 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) "
John Reck0fa0cbc2019-04-05 16:57:46 -0700356 "failed: %s (%d)",
357 windowInfo.transform, strerror(-err), err);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500358 return false;
359 }
360
361 // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than
362 // HWUI's expectation
363 err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
364 if (err != 0) {
365 ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) "
John Reck0fa0cbc2019-04-05 16:57:46 -0700366 "failed: %s (%d)",
367 strerror(-err), err);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500368 return false;
369 }
370
John Reckac513c22019-03-28 16:57:38 -0700371 err = native_window_set_buffer_count(window, windowInfo.bufferCount);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500372 if (err != 0) {
John Reckac513c22019-03-28 16:57:38 -0700373 ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500374 windowInfo.bufferCount, strerror(-err), err);
375 return false;
376 }
377
378 err = native_window_set_usage(window, windowInfo.windowUsageFlags);
379 if (err != 0) {
380 ALOGE("VulkanSurface::UpdateWindow() native_window_set_usage failed: %s (%d)",
381 strerror(-err), err);
382 return false;
383 }
384
385 return err == 0;
386}
387
388VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
John Reck0fa0cbc2019-04-05 16:57:46 -0700389 SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext)
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500390 : mNativeWindow(window)
391 , mWindowInfo(windowInfo)
392 , mGrContext(grContext)
393 , mMinWindowSize(minWindowSize)
John Reck0fa0cbc2019-04-05 16:57:46 -0700394 , mMaxWindowSize(maxWindowSize) {}
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500395
396VulkanSurface::~VulkanSurface() {
397 releaseBuffers();
398
399 // release the native window to be available for use by other clients
400 int err = native_window_api_disconnect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL);
401 ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", strerror(-err), err);
402}
403
404void VulkanSurface::releaseBuffers() {
John Reckac513c22019-03-28 16:57:38 -0700405 for (uint32_t i = 0; i < mWindowInfo.bufferCount; i++) {
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500406 VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
407
408 if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
409 int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(),
410 bufferInfo.dequeue_fence);
411 if (err != 0) {
412 ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err);
413 }
414 bufferInfo.dequeued = false;
415
416 if (bufferInfo.dequeue_fence >= 0) {
417 close(bufferInfo.dequeue_fence);
418 bufferInfo.dequeue_fence = -1;
419 }
420 }
421
422 LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued);
423 LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence != -1);
424
425 bufferInfo.skSurface.reset();
426 bufferInfo.buffer.clear();
427 bufferInfo.hasValidContents = false;
428 bufferInfo.lastPresentedCount = 0;
429 }
430}
431
432VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400433 // Set the mCurrentBufferInfo to invalid in case of error and only reset it to the correct
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500434 // value at the end of the function if everything dequeued correctly.
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400435 mCurrentBufferInfo = nullptr;
436
John Reck0fa0cbc2019-04-05 16:57:46 -0700437 // check if the native window has been resized or rotated and update accordingly
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500438 SkISize newSize = SkISize::MakeEmpty();
439 int transformHint = 0;
440 mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth);
441 mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight);
442 mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
443 if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
444 WindowInfo newWindowInfo = mWindowInfo;
445 newWindowInfo.size = newSize;
446 newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0;
447 ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
448
449 int err = 0;
450 if (newWindowInfo.actualSize != mWindowInfo.actualSize) {
451 // reset the native buffers and update the window
452 err = native_window_set_buffers_dimensions(mNativeWindow.get(),
453 newWindowInfo.actualSize.width(),
454 newWindowInfo.actualSize.height());
455 if (err != 0) {
456 ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
John Reck0fa0cbc2019-04-05 16:57:46 -0700457 newWindowInfo.actualSize.width(), newWindowInfo.actualSize.height(),
458 strerror(-err), err);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500459 return nullptr;
460 }
461 // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
462 // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
463 releaseBuffers();
464 // TODO should we ask the nativewindow to allocate buffers?
465 }
466
467 if (newWindowInfo.transform != mWindowInfo.transform) {
468 err = native_window_set_buffers_transform(mNativeWindow.get(),
John Reck0fa0cbc2019-04-05 16:57:46 -0700469 InvertTransform(newWindowInfo.transform));
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500470 if (err != 0) {
471 ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
472 newWindowInfo.transform, strerror(-err), err);
473 newWindowInfo.transform = mWindowInfo.transform;
474 ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
475 }
476 }
477
478 mWindowInfo = newWindowInfo;
479 }
480
481 ANativeWindowBuffer* buffer;
482 int fence_fd;
483 int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd);
484 if (err != 0) {
485 ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
486 return nullptr;
487 }
488
489 uint32_t idx;
490 for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
491 if (mNativeBuffers[idx].buffer.get() == buffer) {
492 mNativeBuffers[idx].dequeued = true;
493 mNativeBuffers[idx].dequeue_fence = fence_fd;
494 break;
495 } else if (mNativeBuffers[idx].buffer.get() == nullptr) {
496 // increasing the number of buffers we have allocated
497 mNativeBuffers[idx].buffer = buffer;
498 mNativeBuffers[idx].dequeued = true;
499 mNativeBuffers[idx].dequeue_fence = fence_fd;
500 break;
501 }
502 }
503 if (idx == mWindowInfo.bufferCount) {
504 ALOGE("dequeueBuffer returned unrecognized buffer");
505 mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
506 return nullptr;
507 }
508
509 VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx];
510
511 if (bufferInfo->skSurface.get() == nullptr) {
John Reck0fa0cbc2019-04-05 16:57:46 -0700512 bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer(
513 mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
514 kTopLeft_GrSurfaceOrigin, DataSpaceToColorSpace(mWindowInfo.dataspace), nullptr);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500515 if (bufferInfo->skSurface.get() == nullptr) {
516 ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
517 mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
518 return nullptr;
519 }
520 }
521
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400522 mCurrentBufferInfo = bufferInfo;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500523 return bufferInfo;
524}
525
526bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) {
527 if (!dirtyRect.isEmpty()) {
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500528
John Reck321d8e52019-04-12 13:06:11 -0700529 // native_window_set_surface_damage takes a rectangle in prerotated space
530 // with a bottom-left origin. That is, top > bottom.
531 // The dirtyRect is also in prerotated space, so we just need to switch it to
532 // a bottom-left origin space.
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500533
John Reck321d8e52019-04-12 13:06:11 -0700534 SkIRect irect;
535 dirtyRect.roundOut(&irect);
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500536 android_native_rect_t aRect;
John Reck321d8e52019-04-12 13:06:11 -0700537 aRect.left = irect.left();
538 aRect.top = logicalHeight() - irect.top();
539 aRect.right = irect.right();
540 aRect.bottom = logicalHeight() - irect.bottom();
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500541
542 int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1);
543 ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err);
544 }
545
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400546 LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
547 VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500548 int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence;
549 int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd);
550
551 currentBuffer.dequeued = false;
552 // queueBuffer always closes fence, even on error
553 if (err != 0) {
554 ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
555 mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(),
556 currentBuffer.dequeue_fence);
557 } else {
558 currentBuffer.hasValidContents = true;
559 currentBuffer.lastPresentedCount = mPresentCount;
560 mPresentCount++;
561 }
562
563 if (currentBuffer.dequeue_fence >= 0) {
564 close(currentBuffer.dequeue_fence);
565 currentBuffer.dequeue_fence = -1;
566 }
567
568 return err == 0;
569}
570
571int VulkanSurface::getCurrentBuffersAge() {
Stan Ilievbc5f06b2019-03-26 15:14:34 -0400572 LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
573 VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
Derek Sollenbergera19b71a2019-02-15 16:36:30 -0500574 return currentBuffer.hasValidContents ? (mPresentCount - currentBuffer.lastPresentedCount) : 0;
575}
576
577} /* namespace renderthread */
578} /* namespace uirenderer */
579} /* namespace android */