swapchain.cpp revision 62c48c931f88ec44c41621afe988c34cab1fb41d
1/*
2 * Copyright 2015 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 <algorithm>
18
19#include <log/log.h>
20#include <gui/BufferQueue.h>
21#include <sync/sync.h>
22#include <utils/StrongPointer.h>
23#include <utils/SortedVector.h>
24
25#include "driver.h"
26
27// TODO(jessehall): Currently we don't have a good error code for when a native
28// window operation fails. Just returning INITIALIZATION_FAILED for now. Later
29// versions (post SDK 0.9) of the API/extension have a better error code.
30// When updating to that version, audit all error returns.
31namespace vulkan {
32namespace driver {
33
34namespace {
35
36const VkSurfaceTransformFlagsKHR kSupportedTransforms =
37    VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR |
38    VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR |
39    VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR |
40    VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
41    // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform.
42    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR |
43    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
44    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR |
45    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR |
46    VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR;
47
48VkSurfaceTransformFlagBitsKHR TranslateNativeToVulkanTransform(int native) {
49    // Native and Vulkan transforms are isomorphic, but are represented
50    // differently. Vulkan transforms are built up of an optional horizontal
51    // mirror, followed by a clockwise 0/90/180/270-degree rotation. Native
52    // transforms are built up from a horizontal flip, vertical flip, and
53    // 90-degree rotation, all optional but always in that order.
54
55    // TODO(jessehall): For now, only support pure rotations, not
56    // flip or flip-and-rotate, until I have more time to test them and build
57    // sample code. As far as I know we never actually use anything besides
58    // pure rotations anyway.
59
60    switch (native) {
61        case 0:  // 0x0
62            return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
63        // case NATIVE_WINDOW_TRANSFORM_FLIP_H:  // 0x1
64        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR;
65        // case NATIVE_WINDOW_TRANSFORM_FLIP_V:  // 0x2
66        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR;
67        case NATIVE_WINDOW_TRANSFORM_ROT_180:  // FLIP_H | FLIP_V
68            return VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR;
69        case NATIVE_WINDOW_TRANSFORM_ROT_90:  // 0x4
70            return VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR;
71        // case NATIVE_WINDOW_TRANSFORM_FLIP_H | NATIVE_WINDOW_TRANSFORM_ROT_90:
72        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR;
73        // case NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_ROT_90:
74        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR;
75        case NATIVE_WINDOW_TRANSFORM_ROT_270:  // FLIP_H | FLIP_V | ROT_90
76            return VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
77        case NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY:
78        default:
79            return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
80    }
81}
82
83int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR transform) {
84    switch (transform) {
85        case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
86            return NATIVE_WINDOW_TRANSFORM_ROT_270;
87        case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
88            return NATIVE_WINDOW_TRANSFORM_ROT_180;
89        case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
90            return NATIVE_WINDOW_TRANSFORM_ROT_90;
91        // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform.
92        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
93        //     return NATIVE_WINDOW_TRANSFORM_FLIP_H;
94        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
95        //     return NATIVE_WINDOW_TRANSFORM_FLIP_H |
96        //            NATIVE_WINDOW_TRANSFORM_ROT_90;
97        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
98        //     return NATIVE_WINDOW_TRANSFORM_FLIP_V;
99        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
100        //     return NATIVE_WINDOW_TRANSFORM_FLIP_V |
101        //            NATIVE_WINDOW_TRANSFORM_ROT_90;
102        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
103        case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
104        default:
105            return 0;
106    }
107}
108
109class TimingInfo {
110   public:
111    TimingInfo()
112        : vals_{0, 0, 0, 0, 0},
113          timestamp_desired_present_time_(0),
114          timestamp_actual_present_time_(0),
115          timestamp_render_complete_time_(0),
116          timestamp_composition_latch_time_(0) {}
117    TimingInfo(const VkPresentTimeGOOGLE* qp)
118        : vals_{qp->presentID, qp->desiredPresentTime, 0, 0, 0},
119          timestamp_desired_present_time_(0),
120          timestamp_actual_present_time_(0),
121          timestamp_render_complete_time_(0),
122          timestamp_composition_latch_time_(0) {}
123    bool ready() {
124        return (timestamp_desired_present_time_ &&
125                timestamp_actual_present_time_ &&
126                timestamp_render_complete_time_ &&
127                timestamp_composition_latch_time_);
128    }
129    void calculate(uint64_t rdur) {
130        vals_.actualPresentTime = timestamp_actual_present_time_;
131        uint64_t margin = (timestamp_composition_latch_time_ -
132                           timestamp_render_complete_time_);
133        // Calculate vals_.earliestPresentTime, and potentially adjust
134        // vals_.presentMargin.  The initial value of vals_.earliestPresentTime
135        // is vals_.actualPresentTime.  If we can subtract rdur (the duration
136        // of a refresh cycle) from vals_.earliestPresentTime (and also from
137        // vals_.presentMargin) and still leave a positive margin, then we can
138        // report to the application that it could have presented earlier than
139        // it did (per the extension specification).  If for some reason, we
140        // can do this subtraction repeatedly, we do, since
141        // vals_.earliestPresentTime really is supposed to be the "earliest".
142        uint64_t early_time = vals_.actualPresentTime;
143        while ((margin > rdur) &&
144               ((early_time - rdur) > timestamp_composition_latch_time_)) {
145            early_time -= rdur;
146            margin -= rdur;
147        }
148        vals_.earliestPresentTime = early_time;
149        vals_.presentMargin = margin;
150    }
151    void get_values(VkPastPresentationTimingGOOGLE* values) { *values = vals_; }
152
153   public:
154    VkPastPresentationTimingGOOGLE vals_;
155
156    uint64_t timestamp_desired_present_time_;
157    uint64_t timestamp_actual_present_time_;
158    uint64_t timestamp_render_complete_time_;
159    uint64_t timestamp_composition_latch_time_;
160};
161
162static inline int compare_type(const TimingInfo& lhs, const TimingInfo& rhs) {
163    // TODO(ianelliott): Change this from presentID to the frame ID once
164    // brianderson lands the appropriate patch:
165    if (lhs.vals_.presentID < rhs.vals_.presentID)
166        return -1;
167    if (lhs.vals_.presentID > rhs.vals_.presentID)
168        return 1;
169    return 0;
170}
171
172// ----------------------------------------------------------------------------
173
174struct Surface {
175    android::sp<ANativeWindow> window;
176    VkSwapchainKHR swapchain_handle;
177};
178
179VkSurfaceKHR HandleFromSurface(Surface* surface) {
180    return VkSurfaceKHR(reinterpret_cast<uint64_t>(surface));
181}
182
183Surface* SurfaceFromHandle(VkSurfaceKHR handle) {
184    return reinterpret_cast<Surface*>(handle);
185}
186
187// Maximum number of TimingInfo structs to keep per swapchain:
188enum { MAX_TIMING_INFOS = 10 };
189// Minimum number of frames to look for in the past (so we don't cause
190// syncronous requests to Surface Flinger):
191enum { MIN_NUM_FRAMES_AGO = 5 };
192
193struct Swapchain {
194    Swapchain(Surface& surface_, uint32_t num_images_)
195        : surface(surface_),
196          num_images(num_images_),
197          frame_timestamps_enabled(false) {
198        timing.clear();
199        ANativeWindow* window = surface.window.get();
200        int64_t min_rdur;
201        int64_t max_rdur;
202        native_window_get_refresh_cycle_period(
203            window,
204            &min_rdur,
205            &max_rdur);
206        min_refresh_duration = static_cast<uint64_t>(min_rdur);
207        max_refresh_duration = static_cast<uint64_t>(max_rdur);
208    }
209
210    Surface& surface;
211    uint32_t num_images;
212    bool frame_timestamps_enabled;
213    uint64_t min_refresh_duration;
214    uint64_t max_refresh_duration;
215
216    struct Image {
217        Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
218        VkImage image;
219        android::sp<ANativeWindowBuffer> buffer;
220        // The fence is only valid when the buffer is dequeued, and should be
221        // -1 any other time. When valid, we own the fd, and must ensure it is
222        // closed: either by closing it explicitly when queueing the buffer,
223        // or by passing ownership e.g. to ANativeWindow::cancelBuffer().
224        int dequeue_fence;
225        bool dequeued;
226    } images[android::BufferQueue::NUM_BUFFER_SLOTS];
227
228    android::SortedVector<TimingInfo> timing;
229};
230
231VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
232    return VkSwapchainKHR(reinterpret_cast<uint64_t>(swapchain));
233}
234
235Swapchain* SwapchainFromHandle(VkSwapchainKHR handle) {
236    return reinterpret_cast<Swapchain*>(handle);
237}
238
239void ReleaseSwapchainImage(VkDevice device,
240                           ANativeWindow* window,
241                           int release_fence,
242                           Swapchain::Image& image) {
243    ALOG_ASSERT(release_fence == -1 || image.dequeued,
244                "ReleaseSwapchainImage: can't provide a release fence for "
245                "non-dequeued images");
246
247    if (image.dequeued) {
248        if (release_fence >= 0) {
249            // We get here from vkQueuePresentKHR. The application is
250            // responsible for creating an execution dependency chain from
251            // vkAcquireNextImage (dequeue_fence) to vkQueuePresentKHR
252            // (release_fence), so we can drop the dequeue_fence here.
253            if (image.dequeue_fence >= 0)
254                close(image.dequeue_fence);
255        } else {
256            // We get here during swapchain destruction, or various serious
257            // error cases e.g. when we can't create the release_fence during
258            // vkQueuePresentKHR. In non-error cases, the dequeue_fence should
259            // have already signalled, since the swapchain images are supposed
260            // to be idle before the swapchain is destroyed. In error cases,
261            // there may be rendering in flight to the image, but since we
262            // weren't able to create a release_fence, waiting for the
263            // dequeue_fence is about the best we can do.
264            release_fence = image.dequeue_fence;
265        }
266        image.dequeue_fence = -1;
267
268        if (window) {
269            window->cancelBuffer(window, image.buffer.get(), release_fence);
270        } else {
271            if (release_fence >= 0) {
272                sync_wait(release_fence, -1 /* forever */);
273                close(release_fence);
274            }
275        }
276
277        image.dequeued = false;
278    }
279
280    if (image.image) {
281        GetData(device).driver.DestroyImage(device, image.image, nullptr);
282        image.image = VK_NULL_HANDLE;
283    }
284
285    image.buffer.clear();
286}
287
288void OrphanSwapchain(VkDevice device, Swapchain* swapchain) {
289    if (swapchain->surface.swapchain_handle != HandleFromSwapchain(swapchain))
290        return;
291    for (uint32_t i = 0; i < swapchain->num_images; i++) {
292        if (!swapchain->images[i].dequeued)
293            ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
294    }
295    swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
296    swapchain->timing.clear();
297}
298
299uint32_t get_num_ready_timings(Swapchain& swapchain) {
300    uint32_t num_ready = 0;
301    uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
302    uint32_t frames_ago = num_timings;
303    for (uint32_t i = 0; i < num_timings; i++) {
304        TimingInfo* ti = &swapchain.timing.editItemAt(i);
305        if (ti) {
306            if (ti->ready()) {
307                // This TimingInfo is ready to be reported to the user.  Add it
308                // to the num_ready.
309                num_ready++;
310            } else {
311                // This TimingInfo is not yet ready to be reported to the user,
312                // and so we should look for any available timestamps that
313                // might make it ready.
314                int64_t desired_present_time = 0;
315                int64_t render_complete_time = 0;
316                int64_t composition_latch_time = 0;
317                int64_t actual_present_time = 0;
318                for (uint32_t f = MIN_NUM_FRAMES_AGO; f < frames_ago; f++) {
319                    // Obtain timestamps:
320                    int ret = native_window_get_frame_timestamps(
321                        swapchain.surface.window.get(), f,
322                        &desired_present_time, &render_complete_time,
323                        &composition_latch_time,
324                        NULL,  //&first_composition_start_time,
325                        NULL,  //&last_composition_start_time,
326                        NULL,  //&composition_finish_time,
327                        // TODO(ianelliott): Maybe ask if this one is
328                        // supported, at startup time (since it may not be
329                        // supported):
330                        &actual_present_time,
331                        NULL,  //&display_retire_time,
332                        NULL,  //&dequeue_ready_time,
333                        NULL /*&reads_done_time*/);
334                    if (ret) {
335                        break;
336                    } else if (!ret) {
337                        // We obtained at least one valid timestamp.  See if it
338                        // is for the present represented by this TimingInfo:
339                        if (static_cast<uint64_t>(desired_present_time) ==
340                            ti->vals_.desiredPresentTime) {
341                            // Record the timestamp(s) we received, and then
342                            // see if this TimingInfo is ready to be reported
343                            // to the user:
344                            ti->timestamp_desired_present_time_ =
345                                static_cast<uint64_t>(desired_present_time);
346                            ti->timestamp_actual_present_time_ =
347                                static_cast<uint64_t>(actual_present_time);
348                            ti->timestamp_render_complete_time_ =
349                                static_cast<uint64_t>(render_complete_time);
350                            ti->timestamp_composition_latch_time_ =
351                                static_cast<uint64_t>(composition_latch_time);
352
353                            if (ti->ready()) {
354                                // The TimingInfo has received enough
355                                // timestamps, and should now use those
356                                // timestamps to calculate the info that should
357                                // be reported to the user:
358                                //
359                                ti->calculate(swapchain.min_refresh_duration);
360                                num_ready++;
361                            }
362                            break;
363                        }
364                    }
365                }
366            }
367        }
368    }
369    return num_ready;
370}
371
372// TODO(ianelliott): DEAL WITH RETURN VALUE (e.g. VK_INCOMPLETE)!!!
373void copy_ready_timings(Swapchain& swapchain,
374                        uint32_t* count,
375                        VkPastPresentationTimingGOOGLE* timings) {
376    uint32_t num_copied = 0;
377    uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
378    if (*count < num_timings) {
379        num_timings = *count;
380    }
381    for (uint32_t i = 0; i < num_timings; i++) {
382        TimingInfo* ti = &swapchain.timing.editItemAt(i);
383        if (ti && ti->ready()) {
384            ti->get_values(&timings[num_copied]);
385            num_copied++;
386            // We only report the values for a given present once, so remove
387            // them from swapchain.timing:
388            //
389            // TODO(ianelliott): SEE WHAT HAPPENS TO THE LOOP WHEN THE
390            // FOLLOWING IS DONE:
391            swapchain.timing.removeAt(i);
392            i--;
393            num_timings--;
394            if (*count == num_copied) {
395                break;
396            }
397        }
398    }
399    *count = num_copied;
400}
401
402}  // anonymous namespace
403
404VKAPI_ATTR
405VkResult CreateAndroidSurfaceKHR(
406    VkInstance instance,
407    const VkAndroidSurfaceCreateInfoKHR* pCreateInfo,
408    const VkAllocationCallbacks* allocator,
409    VkSurfaceKHR* out_surface) {
410    if (!allocator)
411        allocator = &GetData(instance).allocator;
412    void* mem = allocator->pfnAllocation(allocator->pUserData, sizeof(Surface),
413                                         alignof(Surface),
414                                         VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
415    if (!mem)
416        return VK_ERROR_OUT_OF_HOST_MEMORY;
417    Surface* surface = new (mem) Surface;
418
419    surface->window = pCreateInfo->window;
420    surface->swapchain_handle = VK_NULL_HANDLE;
421
422    // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
423    int err =
424        native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL);
425    if (err != 0) {
426        // TODO(jessehall): Improve error reporting. Can we enumerate possible
427        // errors and translate them to valid Vulkan result codes?
428        ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
429              err);
430        surface->~Surface();
431        allocator->pfnFree(allocator->pUserData, surface);
432        return VK_ERROR_INITIALIZATION_FAILED;
433    }
434
435    *out_surface = HandleFromSurface(surface);
436    return VK_SUCCESS;
437}
438
439VKAPI_ATTR
440void DestroySurfaceKHR(VkInstance instance,
441                       VkSurfaceKHR surface_handle,
442                       const VkAllocationCallbacks* allocator) {
443    Surface* surface = SurfaceFromHandle(surface_handle);
444    if (!surface)
445        return;
446    native_window_api_disconnect(surface->window.get(), NATIVE_WINDOW_API_EGL);
447    ALOGV_IF(surface->swapchain_handle != VK_NULL_HANDLE,
448             "destroyed VkSurfaceKHR 0x%" PRIx64
449             " has active VkSwapchainKHR 0x%" PRIx64,
450             reinterpret_cast<uint64_t>(surface_handle),
451             reinterpret_cast<uint64_t>(surface->swapchain_handle));
452    surface->~Surface();
453    if (!allocator)
454        allocator = &GetData(instance).allocator;
455    allocator->pfnFree(allocator->pUserData, surface);
456}
457
458VKAPI_ATTR
459VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/,
460                                            uint32_t /*queue_family*/,
461                                            VkSurfaceKHR /*surface*/,
462                                            VkBool32* supported) {
463    *supported = VK_TRUE;
464    return VK_SUCCESS;
465}
466
467VKAPI_ATTR
468VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(
469    VkPhysicalDevice /*pdev*/,
470    VkSurfaceKHR surface,
471    VkSurfaceCapabilitiesKHR* capabilities) {
472    int err;
473    ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
474
475    int width, height;
476    err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
477    if (err != 0) {
478        ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
479              strerror(-err), err);
480        return VK_ERROR_INITIALIZATION_FAILED;
481    }
482    err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
483    if (err != 0) {
484        ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
485              strerror(-err), err);
486        return VK_ERROR_INITIALIZATION_FAILED;
487    }
488
489    int transform_hint;
490    err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
491    if (err != 0) {
492        ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
493              strerror(-err), err);
494        return VK_ERROR_INITIALIZATION_FAILED;
495    }
496
497    // TODO(jessehall): Figure out what the min/max values should be.
498    capabilities->minImageCount = 2;
499    capabilities->maxImageCount = 3;
500
501    capabilities->currentExtent =
502        VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
503
504    // TODO(jessehall): Figure out what the max extent should be. Maximum
505    // texture dimension maybe?
506    capabilities->minImageExtent = VkExtent2D{1, 1};
507    capabilities->maxImageExtent = VkExtent2D{4096, 4096};
508
509    capabilities->maxImageArrayLayers = 1;
510
511    capabilities->supportedTransforms = kSupportedTransforms;
512    capabilities->currentTransform =
513        TranslateNativeToVulkanTransform(transform_hint);
514
515    // On Android, window composition is a WindowManager property, not something
516    // associated with the bufferqueue. It can't be changed from here.
517    capabilities->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
518
519    // TODO(jessehall): I think these are right, but haven't thought hard about
520    // it. Do we need to query the driver for support of any of these?
521    // Currently not included:
522    // - VK_IMAGE_USAGE_DEPTH_STENCIL_BIT: definitely not
523    // - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT: definitely not
524    capabilities->supportedUsageFlags =
525        VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
526        VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
527        VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
528        VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
529
530    return VK_SUCCESS;
531}
532
533VKAPI_ATTR
534VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice /*pdev*/,
535                                            VkSurfaceKHR /*surface*/,
536                                            uint32_t* count,
537                                            VkSurfaceFormatKHR* formats) {
538    // TODO(jessehall): Fill out the set of supported formats. Longer term, add
539    // a new gralloc method to query whether a (format, usage) pair is
540    // supported, and check that for each gralloc format that corresponds to a
541    // Vulkan format. Shorter term, just add a few more formats to the ones
542    // hardcoded below.
543
544    const VkSurfaceFormatKHR kFormats[] = {
545        {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
546        {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
547        {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
548    };
549    const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
550
551    VkResult result = VK_SUCCESS;
552    if (formats) {
553        if (*count < kNumFormats)
554            result = VK_INCOMPLETE;
555        *count = std::min(*count, kNumFormats);
556        std::copy(kFormats, kFormats + *count, formats);
557    } else {
558        *count = kNumFormats;
559    }
560    return result;
561}
562
563VKAPI_ATTR
564VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice /*pdev*/,
565                                                 VkSurfaceKHR /*surface*/,
566                                                 uint32_t* count,
567                                                 VkPresentModeKHR* modes) {
568    const VkPresentModeKHR kModes[] = {
569        VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
570        // TODO(chrisforbes): should only expose this if the driver can.
571        VK_PRESENT_MODE_FRONT_BUFFERED_DEMAND_REFRESH_KHR,
572        VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR,
573    };
574    const uint32_t kNumModes = sizeof(kModes) / sizeof(kModes[0]);
575
576    VkResult result = VK_SUCCESS;
577    if (modes) {
578        if (*count < kNumModes)
579            result = VK_INCOMPLETE;
580        *count = std::min(*count, kNumModes);
581        std::copy(kModes, kModes + *count, modes);
582    } else {
583        *count = kNumModes;
584    }
585    return result;
586}
587
588VKAPI_ATTR
589VkResult CreateSwapchainKHR(VkDevice device,
590                            const VkSwapchainCreateInfoKHR* create_info,
591                            const VkAllocationCallbacks* allocator,
592                            VkSwapchainKHR* swapchain_handle) {
593    int err;
594    VkResult result = VK_SUCCESS;
595
596    ALOGV("vkCreateSwapchainKHR: surface=0x%" PRIx64
597          " minImageCount=%u imageFormat=%u imageColorSpace=%u"
598          " imageExtent=%ux%u imageUsage=%#x preTransform=%u presentMode=%u"
599          " oldSwapchain=0x%" PRIx64,
600          reinterpret_cast<uint64_t>(create_info->surface),
601          create_info->minImageCount, create_info->imageFormat,
602          create_info->imageColorSpace, create_info->imageExtent.width,
603          create_info->imageExtent.height, create_info->imageUsage,
604          create_info->preTransform, create_info->presentMode,
605          reinterpret_cast<uint64_t>(create_info->oldSwapchain));
606
607    if (!allocator)
608        allocator = &GetData(device).allocator;
609
610    ALOGV_IF(create_info->imageArrayLayers != 1,
611             "swapchain imageArrayLayers=%u not supported",
612             create_info->imageArrayLayers);
613    ALOGV_IF(create_info->imageColorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
614             "swapchain imageColorSpace=%u not supported",
615             create_info->imageColorSpace);
616    ALOGV_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
617             "swapchain preTransform=%#x not supported",
618             create_info->preTransform);
619    ALOGV_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
620               create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ||
621               create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_DEMAND_REFRESH_KHR ||
622               create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR),
623             "swapchain presentMode=%u not supported",
624             create_info->presentMode);
625
626    Surface& surface = *SurfaceFromHandle(create_info->surface);
627
628    if (surface.swapchain_handle != create_info->oldSwapchain) {
629        ALOGV("Can't create a swapchain for VkSurfaceKHR 0x%" PRIx64
630              " because it already has active swapchain 0x%" PRIx64
631              " but VkSwapchainCreateInfo::oldSwapchain=0x%" PRIx64,
632              reinterpret_cast<uint64_t>(create_info->surface),
633              reinterpret_cast<uint64_t>(surface.swapchain_handle),
634              reinterpret_cast<uint64_t>(create_info->oldSwapchain));
635        return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
636    }
637    if (create_info->oldSwapchain != VK_NULL_HANDLE)
638        OrphanSwapchain(device, SwapchainFromHandle(create_info->oldSwapchain));
639
640    // -- Reset the native window --
641    // The native window might have been used previously, and had its properties
642    // changed from defaults. That will affect the answer we get for queries
643    // like MIN_UNDEQUED_BUFFERS. Reset to a known/default state before we
644    // attempt such queries.
645
646    // The native window only allows dequeueing all buffers before any have
647    // been queued, since after that point at least one is assumed to be in
648    // non-FREE state at any given time. Disconnecting and re-connecting
649    // orphans the previous buffers, getting us back to the state where we can
650    // dequeue all buffers.
651    err = native_window_api_disconnect(surface.window.get(),
652                                       NATIVE_WINDOW_API_EGL);
653    ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
654             strerror(-err), err);
655    err =
656        native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
657    ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
658             strerror(-err), err);
659
660    err = native_window_set_buffer_count(surface.window.get(), 0);
661    if (err != 0) {
662        ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
663              strerror(-err), err);
664        return VK_ERROR_INITIALIZATION_FAILED;
665    }
666
667    err = surface.window->setSwapInterval(surface.window.get(), 1);
668    if (err != 0) {
669        // TODO(jessehall): Improve error reporting. Can we enumerate possible
670        // errors and translate them to valid Vulkan result codes?
671        ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
672              strerror(-err), err);
673        return VK_ERROR_INITIALIZATION_FAILED;
674    }
675
676    err = native_window_set_shared_buffer_mode(surface.window.get(), false);
677    if (err != 0) {
678        ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)",
679              strerror(-err), err);
680        return VK_ERROR_INITIALIZATION_FAILED;
681    }
682
683    err = native_window_set_auto_refresh(surface.window.get(), false);
684    if (err != 0) {
685        ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)",
686              strerror(-err), err);
687        return VK_ERROR_INITIALIZATION_FAILED;
688    }
689
690    // -- Configure the native window --
691
692    const auto& dispatch = GetData(device).driver;
693
694    int native_format = HAL_PIXEL_FORMAT_RGBA_8888;
695    switch (create_info->imageFormat) {
696        case VK_FORMAT_R8G8B8A8_UNORM:
697        case VK_FORMAT_R8G8B8A8_SRGB:
698            native_format = HAL_PIXEL_FORMAT_RGBA_8888;
699            break;
700        case VK_FORMAT_R5G6B5_UNORM_PACK16:
701            native_format = HAL_PIXEL_FORMAT_RGB_565;
702            break;
703        default:
704            ALOGV("unsupported swapchain format %d", create_info->imageFormat);
705            break;
706    }
707    err = native_window_set_buffers_format(surface.window.get(), native_format);
708    if (err != 0) {
709        // TODO(jessehall): Improve error reporting. Can we enumerate possible
710        // errors and translate them to valid Vulkan result codes?
711        ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
712              native_format, strerror(-err), err);
713        return VK_ERROR_INITIALIZATION_FAILED;
714    }
715    err = native_window_set_buffers_data_space(surface.window.get(),
716                                               HAL_DATASPACE_SRGB_LINEAR);
717    if (err != 0) {
718        // TODO(jessehall): Improve error reporting. Can we enumerate possible
719        // errors and translate them to valid Vulkan result codes?
720        ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
721              HAL_DATASPACE_SRGB_LINEAR, strerror(-err), err);
722        return VK_ERROR_INITIALIZATION_FAILED;
723    }
724
725    err = native_window_set_buffers_dimensions(
726        surface.window.get(), static_cast<int>(create_info->imageExtent.width),
727        static_cast<int>(create_info->imageExtent.height));
728    if (err != 0) {
729        // TODO(jessehall): Improve error reporting. Can we enumerate possible
730        // errors and translate them to valid Vulkan result codes?
731        ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
732              create_info->imageExtent.width, create_info->imageExtent.height,
733              strerror(-err), err);
734        return VK_ERROR_INITIALIZATION_FAILED;
735    }
736
737    // VkSwapchainCreateInfo::preTransform indicates the transformation the app
738    // applied during rendering. native_window_set_transform() expects the
739    // inverse: the transform the app is requesting that the compositor perform
740    // during composition. With native windows, pre-transform works by rendering
741    // with the same transform the compositor is applying (as in Vulkan), but
742    // then requesting the inverse transform, so that when the compositor does
743    // it's job the two transforms cancel each other out and the compositor ends
744    // up applying an identity transform to the app's buffer.
745    err = native_window_set_buffers_transform(
746        surface.window.get(),
747        InvertTransformToNative(create_info->preTransform));
748    if (err != 0) {
749        // TODO(jessehall): Improve error reporting. Can we enumerate possible
750        // errors and translate them to valid Vulkan result codes?
751        ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
752              InvertTransformToNative(create_info->preTransform),
753              strerror(-err), err);
754        return VK_ERROR_INITIALIZATION_FAILED;
755    }
756
757    err = native_window_set_scaling_mode(
758        surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
759    if (err != 0) {
760        // TODO(jessehall): Improve error reporting. Can we enumerate possible
761        // errors and translate them to valid Vulkan result codes?
762        ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
763              strerror(-err), err);
764        return VK_ERROR_INITIALIZATION_FAILED;
765    }
766
767    int query_value;
768    err = surface.window->query(surface.window.get(),
769                                NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
770                                &query_value);
771    if (err != 0 || query_value < 0) {
772        // TODO(jessehall): Improve error reporting. Can we enumerate possible
773        // errors and translate them to valid Vulkan result codes?
774        ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
775              query_value);
776        return VK_ERROR_INITIALIZATION_FAILED;
777    }
778    uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
779    // The MIN_UNDEQUEUED_BUFFERS query doesn't know whether we'll be using
780    // async mode or not, and assumes not. But in async mode, the BufferQueue
781    // requires an extra undequeued buffer.
782    // See BufferQueueCore::getMinUndequeuedBufferCountLocked().
783    if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
784        min_undequeued_buffers += 1;
785
786    uint32_t num_images =
787        (create_info->minImageCount - 1) + min_undequeued_buffers;
788    err = native_window_set_buffer_count(surface.window.get(), num_images);
789    if (err != 0) {
790        // TODO(jessehall): Improve error reporting. Can we enumerate possible
791        // errors and translate them to valid Vulkan result codes?
792        ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
793              strerror(-err), err);
794        return VK_ERROR_INITIALIZATION_FAILED;
795    }
796
797    VkSwapchainImageUsageFlagsANDROID swapchain_image_usage = 0;
798    if (create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_DEMAND_REFRESH_KHR ||
799        create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR) {
800        swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_FRONT_BUFFER_BIT_ANDROID;
801
802        err = native_window_set_shared_buffer_mode(surface.window.get(), true);
803        if (err != 0) {
804            ALOGE("native_window_set_shared_buffer_mode failed: %s (%d)", strerror(-err), err);
805            return VK_ERROR_INITIALIZATION_FAILED;
806        }
807    }
808
809    if (create_info->presentMode == VK_PRESENT_MODE_FRONT_BUFFERED_CONTINUOUS_REFRESH_KHR) {
810        err = native_window_set_auto_refresh(surface.window.get(), true);
811        if (err != 0) {
812            ALOGE("native_window_set_auto_refresh failed: %s (%d)", strerror(-err), err);
813            return VK_ERROR_INITIALIZATION_FAILED;
814        }
815    }
816
817    int gralloc_usage = 0;
818    if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
819        result = dispatch.GetSwapchainGrallocUsage2ANDROID(
820            device, create_info->imageFormat, create_info->imageUsage,
821            swapchain_image_usage, &gralloc_usage);
822        if (result != VK_SUCCESS) {
823            ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
824            return VK_ERROR_INITIALIZATION_FAILED;
825        }
826    } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
827        result = dispatch.GetSwapchainGrallocUsageANDROID(
828            device, create_info->imageFormat, create_info->imageUsage,
829            &gralloc_usage);
830        if (result != VK_SUCCESS) {
831            ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
832            return VK_ERROR_INITIALIZATION_FAILED;
833        }
834    } else {
835        gralloc_usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
836    }
837    err = native_window_set_usage(surface.window.get(), gralloc_usage);
838    if (err != 0) {
839        // TODO(jessehall): Improve error reporting. Can we enumerate possible
840        // errors and translate them to valid Vulkan result codes?
841        ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
842        return VK_ERROR_INITIALIZATION_FAILED;
843    }
844
845    int swap_interval =
846        create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
847    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
848    if (err != 0) {
849        // TODO(jessehall): Improve error reporting. Can we enumerate possible
850        // errors and translate them to valid Vulkan result codes?
851        ALOGE("native_window->setSwapInterval(%d) failed: %s (%d)",
852              swap_interval, strerror(-err), err);
853        return VK_ERROR_INITIALIZATION_FAILED;
854    }
855
856    // -- Allocate our Swapchain object --
857    // After this point, we must deallocate the swapchain on error.
858
859    void* mem = allocator->pfnAllocation(allocator->pUserData,
860                                         sizeof(Swapchain), alignof(Swapchain),
861                                         VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
862    if (!mem)
863        return VK_ERROR_OUT_OF_HOST_MEMORY;
864    Swapchain* swapchain = new (mem) Swapchain(surface, num_images);
865
866    // -- Dequeue all buffers and create a VkImage for each --
867    // Any failures during or after this must cancel the dequeued buffers.
868
869    VkSwapchainImageCreateInfoANDROID swapchain_image_create = {
870#pragma clang diagnostic push
871#pragma clang diagnostic ignored "-Wold-style-cast"
872        .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID,
873#pragma clang diagnostic pop
874        .pNext = nullptr,
875        .usage = swapchain_image_usage,
876    };
877    VkNativeBufferANDROID image_native_buffer = {
878#pragma clang diagnostic push
879#pragma clang diagnostic ignored "-Wold-style-cast"
880        .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
881#pragma clang diagnostic pop
882        .pNext = &swapchain_image_create,
883    };
884    VkImageCreateInfo image_create = {
885        .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
886        .pNext = &image_native_buffer,
887        .imageType = VK_IMAGE_TYPE_2D,
888        .format = create_info->imageFormat,
889        .extent = {0, 0, 1},
890        .mipLevels = 1,
891        .arrayLayers = 1,
892        .samples = VK_SAMPLE_COUNT_1_BIT,
893        .tiling = VK_IMAGE_TILING_OPTIMAL,
894        .usage = create_info->imageUsage,
895        .flags = 0,
896        .sharingMode = create_info->imageSharingMode,
897        .queueFamilyIndexCount = create_info->queueFamilyIndexCount,
898        .pQueueFamilyIndices = create_info->pQueueFamilyIndices,
899    };
900
901    for (uint32_t i = 0; i < num_images; i++) {
902        Swapchain::Image& img = swapchain->images[i];
903
904        ANativeWindowBuffer* buffer;
905        err = surface.window->dequeueBuffer(surface.window.get(), &buffer,
906                                            &img.dequeue_fence);
907        if (err != 0) {
908            // TODO(jessehall): Improve error reporting. Can we enumerate
909            // possible errors and translate them to valid Vulkan result codes?
910            ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
911            result = VK_ERROR_INITIALIZATION_FAILED;
912            break;
913        }
914        img.buffer = buffer;
915        img.dequeued = true;
916
917        image_create.extent =
918            VkExtent3D{static_cast<uint32_t>(img.buffer->width),
919                       static_cast<uint32_t>(img.buffer->height),
920                       1};
921        image_native_buffer.handle = img.buffer->handle;
922        image_native_buffer.stride = img.buffer->stride;
923        image_native_buffer.format = img.buffer->format;
924        image_native_buffer.usage = img.buffer->usage;
925
926        result =
927            dispatch.CreateImage(device, &image_create, nullptr, &img.image);
928        if (result != VK_SUCCESS) {
929            ALOGD("vkCreateImage w/ native buffer failed: %u", result);
930            break;
931        }
932    }
933
934    // -- Cancel all buffers, returning them to the queue --
935    // If an error occurred before, also destroy the VkImage and release the
936    // buffer reference. Otherwise, we retain a strong reference to the buffer.
937    //
938    // TODO(jessehall): The error path here is the same as DestroySwapchain,
939    // but not the non-error path. Should refactor/unify.
940    for (uint32_t i = 0; i < num_images; i++) {
941        Swapchain::Image& img = swapchain->images[i];
942        if (img.dequeued) {
943            surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
944                                         img.dequeue_fence);
945            img.dequeue_fence = -1;
946            img.dequeued = false;
947        }
948        if (result != VK_SUCCESS) {
949            if (img.image)
950                dispatch.DestroyImage(device, img.image, nullptr);
951        }
952    }
953
954    if (result != VK_SUCCESS) {
955        swapchain->~Swapchain();
956        allocator->pfnFree(allocator->pUserData, swapchain);
957        return result;
958    }
959
960    surface.swapchain_handle = HandleFromSwapchain(swapchain);
961    *swapchain_handle = surface.swapchain_handle;
962    return VK_SUCCESS;
963}
964
965VKAPI_ATTR
966void DestroySwapchainKHR(VkDevice device,
967                         VkSwapchainKHR swapchain_handle,
968                         const VkAllocationCallbacks* allocator) {
969    const auto& dispatch = GetData(device).driver;
970    Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
971    if (!swapchain)
972        return;
973    bool active = swapchain->surface.swapchain_handle == swapchain_handle;
974    ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
975
976    if (swapchain->frame_timestamps_enabled) {
977        native_window_enable_frame_timestamps(window, false);
978    }
979    for (uint32_t i = 0; i < swapchain->num_images; i++)
980        ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
981    if (active)
982        swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
983    if (!allocator)
984        allocator = &GetData(device).allocator;
985    swapchain->~Swapchain();
986    allocator->pfnFree(allocator->pUserData, swapchain);
987}
988
989VKAPI_ATTR
990VkResult GetSwapchainImagesKHR(VkDevice,
991                               VkSwapchainKHR swapchain_handle,
992                               uint32_t* count,
993                               VkImage* images) {
994    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
995    ALOGW_IF(swapchain.surface.swapchain_handle != swapchain_handle,
996             "getting images for non-active swapchain 0x%" PRIx64
997             "; only dequeued image handles are valid",
998             reinterpret_cast<uint64_t>(swapchain_handle));
999    VkResult result = VK_SUCCESS;
1000    if (images) {
1001        uint32_t n = swapchain.num_images;
1002        if (*count < swapchain.num_images) {
1003            n = *count;
1004            result = VK_INCOMPLETE;
1005        }
1006        for (uint32_t i = 0; i < n; i++)
1007            images[i] = swapchain.images[i].image;
1008        *count = n;
1009    } else {
1010        *count = swapchain.num_images;
1011    }
1012    return result;
1013}
1014
1015VKAPI_ATTR
1016VkResult AcquireNextImageKHR(VkDevice device,
1017                             VkSwapchainKHR swapchain_handle,
1018                             uint64_t timeout,
1019                             VkSemaphore semaphore,
1020                             VkFence vk_fence,
1021                             uint32_t* image_index) {
1022    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
1023    ANativeWindow* window = swapchain.surface.window.get();
1024    VkResult result;
1025    int err;
1026
1027    if (swapchain.surface.swapchain_handle != swapchain_handle)
1028        return VK_ERROR_OUT_OF_DATE_KHR;
1029
1030    ALOGW_IF(
1031        timeout != UINT64_MAX,
1032        "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
1033
1034    ANativeWindowBuffer* buffer;
1035    int fence_fd;
1036    err = window->dequeueBuffer(window, &buffer, &fence_fd);
1037    if (err != 0) {
1038        // TODO(jessehall): Improve error reporting. Can we enumerate possible
1039        // errors and translate them to valid Vulkan result codes?
1040        ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
1041        return VK_ERROR_INITIALIZATION_FAILED;
1042    }
1043
1044    uint32_t idx;
1045    for (idx = 0; idx < swapchain.num_images; idx++) {
1046        if (swapchain.images[idx].buffer.get() == buffer) {
1047            swapchain.images[idx].dequeued = true;
1048            swapchain.images[idx].dequeue_fence = fence_fd;
1049            break;
1050        }
1051    }
1052    if (idx == swapchain.num_images) {
1053        ALOGE("dequeueBuffer returned unrecognized buffer");
1054        window->cancelBuffer(window, buffer, fence_fd);
1055        return VK_ERROR_OUT_OF_DATE_KHR;
1056    }
1057
1058    int fence_clone = -1;
1059    if (fence_fd != -1) {
1060        fence_clone = dup(fence_fd);
1061        if (fence_clone == -1) {
1062            ALOGE("dup(fence) failed, stalling until signalled: %s (%d)",
1063                  strerror(errno), errno);
1064            sync_wait(fence_fd, -1 /* forever */);
1065        }
1066    }
1067
1068    result = GetData(device).driver.AcquireImageANDROID(
1069        device, swapchain.images[idx].image, fence_clone, semaphore, vk_fence);
1070    if (result != VK_SUCCESS) {
1071        // NOTE: we're relying on AcquireImageANDROID to close fence_clone,
1072        // even if the call fails. We could close it ourselves on failure, but
1073        // that would create a race condition if the driver closes it on a
1074        // failure path: some other thread might create an fd with the same
1075        // number between the time the driver closes it and the time we close
1076        // it. We must assume one of: the driver *always* closes it even on
1077        // failure, or *never* closes it on failure.
1078        window->cancelBuffer(window, buffer, fence_fd);
1079        swapchain.images[idx].dequeued = false;
1080        swapchain.images[idx].dequeue_fence = -1;
1081        return result;
1082    }
1083
1084    *image_index = idx;
1085    return VK_SUCCESS;
1086}
1087
1088static VkResult WorstPresentResult(VkResult a, VkResult b) {
1089    // See the error ranking for vkQueuePresentKHR at the end of section 29.6
1090    // (in spec version 1.0.14).
1091    static const VkResult kWorstToBest[] = {
1092        VK_ERROR_DEVICE_LOST,
1093        VK_ERROR_SURFACE_LOST_KHR,
1094        VK_ERROR_OUT_OF_DATE_KHR,
1095        VK_ERROR_OUT_OF_DEVICE_MEMORY,
1096        VK_ERROR_OUT_OF_HOST_MEMORY,
1097        VK_SUBOPTIMAL_KHR,
1098    };
1099    for (auto result : kWorstToBest) {
1100        if (a == result || b == result)
1101            return result;
1102    }
1103    ALOG_ASSERT(a == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", a);
1104    ALOG_ASSERT(b == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", b);
1105    return a != VK_SUCCESS ? a : b;
1106}
1107
1108VKAPI_ATTR
1109VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
1110    ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
1111             "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
1112             present_info->sType);
1113
1114    VkDevice device = GetData(queue).driver_device;
1115    const auto& dispatch = GetData(queue).driver;
1116    VkResult final_result = VK_SUCCESS;
1117
1118    // Look at the pNext chain for supported extension structs:
1119    const VkPresentRegionsKHR* present_regions = nullptr;
1120    const VkPresentTimesInfoGOOGLE* present_times = nullptr;
1121    const VkPresentRegionsKHR* next =
1122        reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext);
1123    while (next) {
1124        switch (next->sType) {
1125            case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:
1126                present_regions = next;
1127                break;
1128            case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE:
1129                present_times =
1130                    reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next);
1131                break;
1132            default:
1133                ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x",
1134                      next->sType);
1135                break;
1136        }
1137        next = reinterpret_cast<const VkPresentRegionsKHR*>(next->pNext);
1138    }
1139    ALOGV_IF(
1140        present_regions &&
1141            present_regions->swapchainCount != present_info->swapchainCount,
1142        "VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount");
1143    ALOGV_IF(present_times &&
1144                 present_times->swapchainCount != present_info->swapchainCount,
1145             "VkPresentTimesInfoGOOGLE::swapchainCount != "
1146             "VkPresentInfo::swapchainCount");
1147    const VkPresentRegionKHR* regions =
1148        (present_regions) ? present_regions->pRegions : nullptr;
1149    const VkPresentTimeGOOGLE* times =
1150        (present_times) ? present_times->pTimes : nullptr;
1151    const VkAllocationCallbacks* allocator = &GetData(device).allocator;
1152    android_native_rect_t* rects = nullptr;
1153    uint32_t nrects = 0;
1154
1155    for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
1156        Swapchain& swapchain =
1157            *SwapchainFromHandle(present_info->pSwapchains[sc]);
1158        uint32_t image_idx = present_info->pImageIndices[sc];
1159        Swapchain::Image& img = swapchain.images[image_idx];
1160        const VkPresentRegionKHR* region = (regions) ? &regions[sc] : nullptr;
1161        const VkPresentTimeGOOGLE* time = (times) ? &times[sc] : nullptr;
1162        VkResult swapchain_result = VK_SUCCESS;
1163        VkResult result;
1164        int err;
1165
1166        int fence = -1;
1167        result = dispatch.QueueSignalReleaseImageANDROID(
1168            queue, present_info->waitSemaphoreCount,
1169            present_info->pWaitSemaphores, img.image, &fence);
1170        if (result != VK_SUCCESS) {
1171            ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
1172            swapchain_result = result;
1173        }
1174
1175        if (swapchain.surface.swapchain_handle ==
1176            present_info->pSwapchains[sc]) {
1177            ANativeWindow* window = swapchain.surface.window.get();
1178            if (swapchain_result == VK_SUCCESS) {
1179                if (region) {
1180                    // Process the incremental-present hint for this swapchain:
1181                    uint32_t rcount = region->rectangleCount;
1182                    if (rcount > nrects) {
1183                        android_native_rect_t* new_rects =
1184                            static_cast<android_native_rect_t*>(
1185                                allocator->pfnReallocation(
1186                                    allocator->pUserData, rects,
1187                                    sizeof(android_native_rect_t) * rcount,
1188                                    alignof(android_native_rect_t),
1189                                    VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
1190                        if (new_rects) {
1191                            rects = new_rects;
1192                            nrects = rcount;
1193                        } else {
1194                            rcount = 0;  // Ignore the hint for this swapchain
1195                        }
1196                    }
1197                    for (uint32_t r = 0; r < rcount; ++r) {
1198                        if (region->pRectangles[r].layer > 0) {
1199                            ALOGV(
1200                                "vkQueuePresentKHR ignoring invalid layer "
1201                                "(%u); using layer 0 instead",
1202                                region->pRectangles[r].layer);
1203                        }
1204                        int x = region->pRectangles[r].offset.x;
1205                        int y = region->pRectangles[r].offset.y;
1206                        int width = static_cast<int>(
1207                            region->pRectangles[r].extent.width);
1208                        int height = static_cast<int>(
1209                            region->pRectangles[r].extent.height);
1210                        android_native_rect_t* cur_rect = &rects[r];
1211                        cur_rect->left = x;
1212                        cur_rect->top = y + height;
1213                        cur_rect->right = x + width;
1214                        cur_rect->bottom = y;
1215                    }
1216                    native_window_set_surface_damage(window, rects, rcount);
1217                }
1218                if (time) {
1219                    if (!swapchain.frame_timestamps_enabled) {
1220                        ALOGV(
1221                            "Calling "
1222                            "native_window_enable_frame_timestamps(true)");
1223                        native_window_enable_frame_timestamps(window, true);
1224                        swapchain.frame_timestamps_enabled = true;
1225                    }
1226                    // Record this presentID and desiredPresentTime so it can
1227                    // be later correlated to this present.
1228                    TimingInfo timing_record(time);
1229                    swapchain.timing.add(timing_record);
1230                    uint32_t num_timings =
1231                        static_cast<uint32_t>(swapchain.timing.size());
1232                    if (num_timings > MAX_TIMING_INFOS) {
1233                        swapchain.timing.removeAt(0);
1234                    }
1235                    if (time->desiredPresentTime) {
1236                        // Set the desiredPresentTime:
1237                        ALOGV(
1238                            "Calling "
1239                            "native_window_set_buffers_timestamp(%" PRId64 ")",
1240                            time->desiredPresentTime);
1241                        native_window_set_buffers_timestamp(
1242                            window,
1243                            static_cast<int64_t>(time->desiredPresentTime));
1244                    }
1245                }
1246                err = window->queueBuffer(window, img.buffer.get(), fence);
1247                // queueBuffer always closes fence, even on error
1248                if (err != 0) {
1249                    // TODO(jessehall): What now? We should probably cancel the
1250                    // buffer, I guess?
1251                    ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
1252                    swapchain_result = WorstPresentResult(
1253                        swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
1254                }
1255                if (img.dequeue_fence >= 0) {
1256                    close(img.dequeue_fence);
1257                    img.dequeue_fence = -1;
1258                }
1259                img.dequeued = false;
1260            }
1261            if (swapchain_result != VK_SUCCESS) {
1262                ReleaseSwapchainImage(device, window, fence, img);
1263                OrphanSwapchain(device, &swapchain);
1264            }
1265        } else {
1266            ReleaseSwapchainImage(device, nullptr, fence, img);
1267            swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
1268        }
1269
1270        if (present_info->pResults)
1271            present_info->pResults[sc] = swapchain_result;
1272
1273        if (swapchain_result != final_result)
1274            final_result = WorstPresentResult(final_result, swapchain_result);
1275    }
1276    if (rects) {
1277        allocator->pfnFree(allocator->pUserData, rects);
1278    }
1279
1280    return final_result;
1281}
1282
1283VKAPI_ATTR
1284VkResult GetRefreshCycleDurationGOOGLE(
1285    VkDevice,
1286    VkSwapchainKHR swapchain_handle,
1287    VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
1288    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
1289    VkResult result = VK_SUCCESS;
1290
1291    pDisplayTimingProperties->minRefreshDuration = swapchain.min_refresh_duration;
1292    pDisplayTimingProperties->maxRefreshDuration = swapchain.max_refresh_duration;
1293
1294    return result;
1295}
1296
1297VKAPI_ATTR
1298VkResult GetPastPresentationTimingGOOGLE(
1299    VkDevice,
1300    VkSwapchainKHR swapchain_handle,
1301    uint32_t* count,
1302    VkPastPresentationTimingGOOGLE* timings) {
1303    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
1304    ANativeWindow* window = swapchain.surface.window.get();
1305    VkResult result = VK_SUCCESS;
1306
1307    if (!swapchain.frame_timestamps_enabled) {
1308        ALOGV("Calling native_window_enable_frame_timestamps(true)");
1309        native_window_enable_frame_timestamps(window, true);
1310        swapchain.frame_timestamps_enabled = true;
1311    }
1312
1313    if (timings) {
1314        // TODO(ianelliott): plumb return value (e.g. VK_INCOMPLETE)
1315        copy_ready_timings(swapchain, count, timings);
1316    } else {
1317        *count = get_num_ready_timings(swapchain);
1318    }
1319
1320    return result;
1321}
1322
1323VKAPI_ATTR
1324VkResult GetSwapchainStatusKHR(
1325    VkDevice,
1326    VkSwapchainKHR swapchain_handle) {
1327    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
1328    VkResult result = VK_SUCCESS;
1329
1330    if (swapchain.surface.swapchain_handle != swapchain_handle) {
1331        return VK_ERROR_OUT_OF_DATE_KHR;
1332    }
1333
1334    // TODO(chrisforbes): Implement this function properly
1335
1336    return result;
1337}
1338
1339}  // namespace driver
1340}  // namespace vulkan
1341