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