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 <GrDirectContext.h>
20 #include <SkSurface.h>
21 #include <algorithm>
22 
23 #include <gui/TraceUtils.h>
24 #include "VulkanManager.h"
25 #include "utils/Color.h"
26 
27 namespace android {
28 namespace uirenderer {
29 namespace renderthread {
30 
InvertTransform(int transform)31 static int InvertTransform(int transform) {
32     switch (transform) {
33         case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
34             return ANATIVEWINDOW_TRANSFORM_ROTATE_270;
35         case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
36             return ANATIVEWINDOW_TRANSFORM_ROTATE_180;
37         case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
38             return ANATIVEWINDOW_TRANSFORM_ROTATE_90;
39         default:
40             return 0;
41     }
42 }
43 
GetPreTransformMatrix(SkISize windowSize,int transform)44 static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) {
45     const int width = windowSize.width();
46     const int height = windowSize.height();
47 
48     switch (transform) {
49         case 0:
50             return SkMatrix::I();
51         case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
52             return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
53         case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
54             return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
55         case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
56             return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
57         default:
58             LOG_ALWAYS_FATAL("Unsupported Window Transform (%d)", transform);
59     }
60     return SkMatrix::I();
61 }
62 
ConnectAndSetWindowDefaults(ANativeWindow * window)63 static bool ConnectAndSetWindowDefaults(ANativeWindow* window) {
64     ATRACE_CALL();
65 
66     int err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
67     if (err != 0) {
68         ALOGE("native_window_api_connect failed: %s (%d)", strerror(-err), err);
69         return false;
70     }
71 
72     // this will match what we do on GL so pick that here.
73     err = window->setSwapInterval(window, 1);
74     if (err != 0) {
75         ALOGE("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err);
76         return false;
77     }
78 
79     err = native_window_set_shared_buffer_mode(window, false);
80     if (err != 0) {
81         ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err);
82         return false;
83     }
84 
85     err = native_window_set_auto_refresh(window, false);
86     if (err != 0) {
87         ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err);
88         return false;
89     }
90 
91     err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
92     if (err != 0) {
93         ALOGE("native_window_set_scaling_mode(NATIVE_WINDOW_SCALING_MODE_FREEZE) failed: %s (%d)",
94               strerror(-err), err);
95         return false;
96     }
97 
98     // Let consumer drive the size of the buffers.
99     err = native_window_set_buffers_dimensions(window, 0, 0);
100     if (err != 0) {
101         ALOGE("native_window_set_buffers_dimensions(0,0) failed: %s (%d)", strerror(-err), err);
102         return false;
103     }
104 
105     // Enable auto prerotation, so when buffer size is driven by the consumer
106     // and the transform hint specifies a 90 or 270 degree rotation, the width
107     // and height used for buffer pre-allocation and dequeueBuffer will be
108     // additionally swapped.
109     err = native_window_set_auto_prerotation(window, true);
110     if (err != 0) {
111         ALOGE("VulkanSurface::UpdateWindow() native_window_set_auto_prerotation failed: %s (%d)",
112               strerror(-err), err);
113         return false;
114     }
115 
116     return true;
117 }
118 
Create(ANativeWindow * window,ColorMode colorMode,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,GrDirectContext * grContext,const VulkanManager & vkManager,uint32_t extraBuffers)119 VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
120                                      SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
121                                      GrDirectContext* grContext, const VulkanManager& vkManager,
122                                      uint32_t extraBuffers) {
123     // Connect and set native window to default configurations.
124     if (!ConnectAndSetWindowDefaults(window)) {
125         return nullptr;
126     }
127 
128     // Initialize WindowInfo struct.
129     WindowInfo windowInfo;
130     if (!InitializeWindowInfoStruct(window, colorMode, colorType, colorSpace, vkManager,
131                                     extraBuffers, &windowInfo)) {
132         return nullptr;
133     }
134 
135     // Now we attempt to modify the window.
136     if (!UpdateWindow(window, windowInfo)) {
137         return nullptr;
138     }
139 
140     return new VulkanSurface(window, windowInfo, grContext);
141 }
142 
InitializeWindowInfoStruct(ANativeWindow * window,ColorMode colorMode,SkColorType colorType,sk_sp<SkColorSpace> colorSpace,const VulkanManager & vkManager,uint32_t extraBuffers,WindowInfo * outWindowInfo)143 bool VulkanSurface::InitializeWindowInfoStruct(ANativeWindow* window, ColorMode colorMode,
144                                                SkColorType colorType,
145                                                sk_sp<SkColorSpace> colorSpace,
146                                                const VulkanManager& vkManager,
147                                                uint32_t extraBuffers, WindowInfo* outWindowInfo) {
148     ATRACE_CALL();
149 
150     int width, height;
151     int err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
152     if (err != 0 || width < 0) {
153         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, width);
154         return false;
155     }
156     err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
157     if (err != 0 || height < 0) {
158         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, height);
159         return false;
160     }
161     outWindowInfo->size = SkISize::Make(width, height);
162 
163     int query_value;
164     err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &query_value);
165     if (err != 0 || query_value < 0) {
166         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
167         return false;
168     }
169     outWindowInfo->transform = query_value;
170 
171     outWindowInfo->actualSize = outWindowInfo->size;
172     if (outWindowInfo->transform & ANATIVEWINDOW_TRANSFORM_ROTATE_90) {
173         outWindowInfo->actualSize.set(outWindowInfo->size.height(), outWindowInfo->size.width());
174     }
175 
176     outWindowInfo->preTransform =
177             GetPreTransformMatrix(outWindowInfo->size, outWindowInfo->transform);
178 
179     err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
180     if (err != 0 || query_value < 0) {
181         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
182         return false;
183     }
184     outWindowInfo->bufferCount =
185             static_cast<uint32_t>(query_value) + sTargetBufferCount + extraBuffers;
186 
187     err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
188     if (err != 0 || query_value < 0) {
189         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
190         return false;
191     }
192     if (outWindowInfo->bufferCount > static_cast<uint32_t>(query_value)) {
193         // Application must settle for fewer images than desired:
194         outWindowInfo->bufferCount = static_cast<uint32_t>(query_value);
195     }
196 
197     outWindowInfo->bufferFormat = ColorTypeToBufferFormat(colorType);
198     outWindowInfo->colorspace = colorSpace;
199     outWindowInfo->dataspace = ColorSpaceToADataSpace(colorSpace.get(), colorType);
200     LOG_ALWAYS_FATAL_IF(outWindowInfo->dataspace == HAL_DATASPACE_UNKNOWN,
201                         "Unsupported colorspace");
202 
203     VkFormat vkPixelFormat;
204     switch (colorType) {
205         case kRGBA_8888_SkColorType:
206             vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM;
207             break;
208         case kRGBA_F16_SkColorType:
209             vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
210             break;
211         case kRGBA_1010102_SkColorType:
212             vkPixelFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
213             break;
214         default:
215             LOG_ALWAYS_FATAL("Unsupported colorType: %d", (int)colorType);
216     }
217 
218     LOG_ALWAYS_FATAL_IF(nullptr == vkManager.mGetPhysicalDeviceImageFormatProperties2,
219                         "vkGetPhysicalDeviceImageFormatProperties2 is missing");
220     VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo;
221     externalImageFormatInfo.sType =
222             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO;
223     externalImageFormatInfo.pNext = nullptr;
224     externalImageFormatInfo.handleType =
225             VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
226 
227     VkPhysicalDeviceImageFormatInfo2 imageFormatInfo;
228     imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
229     imageFormatInfo.pNext = &externalImageFormatInfo;
230     imageFormatInfo.format = vkPixelFormat;
231     imageFormatInfo.type = VK_IMAGE_TYPE_2D;
232     imageFormatInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
233     // Currently Skia requires the images to be color attachments and support all transfer
234     // operations.
235     imageFormatInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
236                             VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
237     imageFormatInfo.flags = 0;
238 
239     VkAndroidHardwareBufferUsageANDROID hwbUsage;
240     hwbUsage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID;
241     hwbUsage.pNext = nullptr;
242 
243     VkImageFormatProperties2 imgFormProps;
244     imgFormProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2;
245     imgFormProps.pNext = &hwbUsage;
246 
247     VkResult res = vkManager.mGetPhysicalDeviceImageFormatProperties2(
248             vkManager.mPhysicalDevice, &imageFormatInfo, &imgFormProps);
249     if (VK_SUCCESS != res) {
250         ALOGE("Failed to query GetPhysicalDeviceImageFormatProperties2");
251         return false;
252     }
253 
254     uint64_t consumerUsage;
255     err = native_window_get_consumer_usage(window, &consumerUsage);
256     if (err != 0) {
257         ALOGE("native_window_get_consumer_usage failed: %s (%d)", strerror(-err), err);
258         return false;
259     }
260     outWindowInfo->windowUsageFlags = consumerUsage | hwbUsage.androidHardwareBufferUsage;
261 
262     return true;
263 }
264 
UpdateWindow(ANativeWindow * window,const WindowInfo & windowInfo)265 bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) {
266     ATRACE_CALL();
267 
268     int err = native_window_set_buffers_format(window, windowInfo.bufferFormat);
269     if (err != 0) {
270         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)",
271               windowInfo.bufferFormat, strerror(-err), err);
272         return false;
273     }
274 
275     err = native_window_set_buffers_data_space(window, windowInfo.dataspace);
276     if (err != 0) {
277         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) "
278               "failed: %s (%d)",
279               windowInfo.dataspace, strerror(-err), err);
280         return false;
281     }
282 
283     // native_window_set_buffers_transform() expects the transform the app is requesting that
284     // the compositor perform during composition. With native windows, pre-transform works by
285     // rendering with the same transform the compositor is applying (as in Vulkan), but
286     // then requesting the inverse transform, so that when the compositor does
287     // it's job the two transforms cancel each other out and the compositor ends
288     // up applying an identity transform to the app's buffer.
289     err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform));
290     if (err != 0) {
291         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) "
292               "failed: %s (%d)",
293               windowInfo.transform, strerror(-err), err);
294         return false;
295     }
296 
297     err = native_window_set_buffer_count(window, windowInfo.bufferCount);
298     if (err != 0) {
299         ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%zu) failed: %s (%d)",
300               windowInfo.bufferCount, strerror(-err), err);
301         return false;
302     }
303 
304     err = native_window_set_usage(window, windowInfo.windowUsageFlags);
305     if (err != 0) {
306         ALOGE("VulkanSurface::UpdateWindow() native_window_set_usage failed: %s (%d)",
307               strerror(-err), err);
308         return false;
309     }
310 
311     return true;
312 }
313 
VulkanSurface(ANativeWindow * window,const WindowInfo & windowInfo,GrDirectContext * grContext)314 VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
315                              GrDirectContext* grContext)
316         : mNativeWindow(window), mWindowInfo(windowInfo), mGrContext(grContext) {}
317 
~VulkanSurface()318 VulkanSurface::~VulkanSurface() {
319     releaseBuffers();
320 
321     // release the native window to be available for use by other clients
322     int err = native_window_api_disconnect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL);
323     ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", strerror(-err), err);
324 }
325 
releaseBuffers()326 void VulkanSurface::releaseBuffers() {
327     for (uint32_t i = 0; i < mWindowInfo.bufferCount; i++) {
328         VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
329 
330         if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
331             int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(),
332                                                   bufferInfo.dequeue_fence.release());
333             if (err != 0) {
334                 ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err);
335             }
336             bufferInfo.dequeued = false;
337             bufferInfo.dequeue_fence.reset();
338         }
339 
340         LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued);
341         LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence.ok());
342 
343         bufferInfo.skSurface.reset();
344         bufferInfo.buffer.clear();
345         bufferInfo.hasValidContents = false;
346         bufferInfo.lastPresentedCount = 0;
347     }
348 }
349 
dequeueNativeBuffer()350 VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
351     // Set the mCurrentBufferInfo to invalid in case of error and only reset it to the correct
352     // value at the end of the function if everything dequeued correctly.
353     mCurrentBufferInfo = nullptr;
354 
355     // Query the transform hint synced from the initial Surface connect or last queueBuffer. The
356     // auto prerotation on the buffer is based on the same transform hint in use by the producer.
357     int transformHint = 0;
358     int err =
359             mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
360 
361     // Since auto pre-rotation is enabled, dequeueBuffer to get the consumer driven buffer size
362     // from ANativeWindowBuffer.
363     ANativeWindowBuffer* buffer;
364     base::unique_fd fence_fd;
365     {
366         int rawFd = -1;
367         err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &rawFd);
368         fence_fd.reset(rawFd);
369     }
370     if (err != 0) {
371         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
372         return nullptr;
373     }
374 
375     SkISize actualSize = SkISize::Make(buffer->width, buffer->height);
376     if (actualSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
377         if (actualSize != mWindowInfo.actualSize) {
378             // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
379             // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
380             mWindowInfo.actualSize = actualSize;
381             releaseBuffers();
382         }
383 
384         if (transformHint != mWindowInfo.transform) {
385             err = native_window_set_buffers_transform(mNativeWindow.get(),
386                                                       InvertTransform(transformHint));
387             if (err != 0) {
388                 ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", transformHint,
389                       strerror(-err), err);
390                 mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd.release());
391                 return nullptr;
392             }
393             mWindowInfo.transform = transformHint;
394         }
395 
396         mWindowInfo.size = actualSize;
397         if (mWindowInfo.transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
398             mWindowInfo.size.set(actualSize.height(), actualSize.width());
399         }
400 
401         mWindowInfo.preTransform = GetPreTransformMatrix(mWindowInfo.size, mWindowInfo.transform);
402     }
403 
404     uint32_t idx;
405     for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
406         if (mNativeBuffers[idx].buffer.get() == buffer) {
407             mNativeBuffers[idx].dequeued = true;
408             mNativeBuffers[idx].dequeue_fence = std::move(fence_fd);
409             break;
410         } else if (mNativeBuffers[idx].buffer.get() == nullptr) {
411             // increasing the number of buffers we have allocated
412             mNativeBuffers[idx].buffer = buffer;
413             mNativeBuffers[idx].dequeued = true;
414             mNativeBuffers[idx].dequeue_fence = std::move(fence_fd);
415             break;
416         }
417     }
418     if (idx == mWindowInfo.bufferCount) {
419         ALOGE("dequeueBuffer returned unrecognized buffer");
420         mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd.release());
421         return nullptr;
422     }
423 
424     VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx];
425 
426     if (bufferInfo->skSurface.get() == nullptr) {
427         bufferInfo->skSurface = SkSurface::MakeFromAHardwareBuffer(
428                 mGrContext, ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
429                 kTopLeft_GrSurfaceOrigin, mWindowInfo.colorspace, nullptr);
430         if (bufferInfo->skSurface.get() == nullptr) {
431             ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
432             mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer,
433                                         mNativeBuffers[idx].dequeue_fence.release());
434             mNativeBuffers[idx].dequeued = false;
435             return nullptr;
436         }
437     }
438 
439     mCurrentBufferInfo = bufferInfo;
440     return bufferInfo;
441 }
442 
presentCurrentBuffer(const SkRect & dirtyRect,int semaphoreFd)443 bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) {
444     if (!dirtyRect.isEmpty()) {
445 
446         // native_window_set_surface_damage takes a rectangle in prerotated space
447         // with a bottom-left origin. That is, top > bottom.
448         // The dirtyRect is also in prerotated space, so we just need to switch it to
449         // a bottom-left origin space.
450 
451         SkIRect irect;
452         dirtyRect.roundOut(&irect);
453         android_native_rect_t aRect;
454         aRect.left = irect.left();
455         aRect.top = logicalHeight() - irect.top();
456         aRect.right = irect.right();
457         aRect.bottom = logicalHeight() - irect.bottom();
458 
459         int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1);
460         ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err);
461     }
462 
463     LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
464     VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
465     // queueBuffer always closes fence, even on error
466     int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence.release();
467     int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd);
468 
469     currentBuffer.dequeued = false;
470     if (err != 0) {
471         ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
472         // cancelBuffer takes ownership of the fence
473         mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(),
474                                     currentBuffer.dequeue_fence.release());
475     } else {
476         currentBuffer.hasValidContents = true;
477         currentBuffer.lastPresentedCount = mPresentCount;
478         mPresentCount++;
479     }
480 
481     currentBuffer.dequeue_fence.reset();
482 
483     return err == 0;
484 }
485 
getCurrentBuffersAge()486 int VulkanSurface::getCurrentBuffersAge() {
487     LOG_ALWAYS_FATAL_IF(!mCurrentBufferInfo);
488     VulkanSurface::NativeBufferInfo& currentBuffer = *mCurrentBufferInfo;
489     return currentBuffer.hasValidContents ? (mPresentCount - currentBuffer.lastPresentedCount) : 0;
490 }
491 
492 } /* namespace renderthread */
493 } /* namespace uirenderer */
494 } /* namespace android */
495