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