swapchain.cpp revision 061938022b3f5f37f7aaebf7ccc8ac20bf4dbf97
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// #define LOG_NDEBUG 0
18
19#include <algorithm>
20#include <memory>
21
22#include <gui/BufferQueue.h>
23#include <log/log.h>
24#include <sync/sync.h>
25
26#include "loader.h"
27
28using namespace vulkan;
29
30// TODO(jessehall): Currently we don't have a good error code for when a native
31// window operation fails. Just returning INITIALIZATION_FAILED for now. Later
32// versions (post SDK 0.9) of the API/extension have a better error code.
33// When updating to that version, audit all error returns.
34
35namespace {
36
37// ----------------------------------------------------------------------------
38// These functions/classes form an adaptor that allows objects to be refcounted
39// by both android::sp<> and std::shared_ptr<> simultaneously, and delegates
40// allocation of the shared_ptr<> control structure to VkAllocationCallbacks.
41// The
42// platform holds a reference to the ANativeWindow using its embedded reference
43// count, and the ANativeWindow implementation holds references to the
44// ANativeWindowBuffers using their embedded reference counts, so the
45// shared_ptr *must* cooperate with these and hold at least one reference to
46// the object using the embedded reference count.
47
48template <typename T>
49struct NativeBaseDeleter {
50    void operator()(T* obj) { obj->common.decRef(&obj->common); }
51};
52
53template <typename Host>
54struct AllocScope {};
55
56template <>
57struct AllocScope<VkInstance> {
58    static const VkSystemAllocationScope kScope =
59        VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE;
60};
61
62template <>
63struct AllocScope<VkDevice> {
64    static const VkSystemAllocationScope kScope =
65        VK_SYSTEM_ALLOCATION_SCOPE_DEVICE;
66};
67
68template <typename T, typename Host>
69class VulkanAllocator {
70   public:
71    typedef T value_type;
72
73    explicit VulkanAllocator(Host host) : host_(host) {}
74
75    template <typename U>
76    explicit VulkanAllocator(const VulkanAllocator<U, Host>& other)
77        : host_(other.host_) {}
78
79    T* allocate(size_t n) const {
80        return static_cast<T*>(AllocMem(host_, n * sizeof(T), alignof(T),
81                                        AllocScope<Host>::kScope));
82    }
83    void deallocate(T* p, size_t) const { return FreeMem(host_, p); }
84
85   private:
86    template <typename U, typename H>
87    friend class VulkanAllocator;
88    Host host_;
89};
90
91template <typename T, typename Host>
92std::shared_ptr<T> InitSharedPtr(Host host, T* obj) {
93    obj->common.incRef(&obj->common);
94    return std::shared_ptr<T>(obj, NativeBaseDeleter<T>(),
95                              VulkanAllocator<T, Host>(host));
96}
97
98// ----------------------------------------------------------------------------
99
100struct Surface {
101    std::shared_ptr<ANativeWindow> window;
102};
103
104VkSurfaceKHR HandleFromSurface(Surface* surface) {
105    return VkSurfaceKHR(reinterpret_cast<uint64_t>(surface));
106}
107
108Surface* SurfaceFromHandle(VkSurfaceKHR handle) {
109    return reinterpret_cast<Surface*>(handle);
110}
111
112struct Swapchain {
113    Swapchain(Surface& surface_, uint32_t num_images_)
114        : surface(surface_), num_images(num_images_) {}
115
116    Surface& surface;
117    uint32_t num_images;
118
119    struct Image {
120        Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
121        VkImage image;
122        std::shared_ptr<ANativeWindowBuffer> buffer;
123        // The fence is only valid when the buffer is dequeued, and should be
124        // -1 any other time. When valid, we own the fd, and must ensure it is
125        // closed: either by closing it explicitly when queueing the buffer,
126        // or by passing ownership e.g. to ANativeWindow::cancelBuffer().
127        int dequeue_fence;
128        bool dequeued;
129    } images[android::BufferQueue::NUM_BUFFER_SLOTS];
130};
131
132VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
133    return VkSwapchainKHR(reinterpret_cast<uint64_t>(swapchain));
134}
135
136Swapchain* SwapchainFromHandle(VkSwapchainKHR handle) {
137    return reinterpret_cast<Swapchain*>(handle);
138}
139
140}  // anonymous namespace
141
142namespace vulkan {
143
144VKAPI_ATTR
145VkResult CreateAndroidSurfaceKHR(VkInstance instance,
146                                 ANativeWindow* window,
147                                 const VkAllocationCallbacks* /*allocator*/,
148                                 VkSurfaceKHR* out_surface) {
149    void* mem = AllocMem(instance, sizeof(Surface), alignof(Surface),
150                         VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
151    if (!mem)
152        return VK_ERROR_OUT_OF_HOST_MEMORY;
153    Surface* surface = new (mem) Surface;
154
155    surface->window = InitSharedPtr(instance, window);
156
157    // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
158    int err =
159        native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL);
160    if (err != 0) {
161        // TODO(jessehall): Improve error reporting. Can we enumerate possible
162        // errors and translate them to valid Vulkan result codes?
163        ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
164              err);
165        surface->~Surface();
166        FreeMem(instance, surface);
167        return VK_ERROR_INITIALIZATION_FAILED;
168    }
169
170    *out_surface = HandleFromSurface(surface);
171    return VK_SUCCESS;
172}
173
174VKAPI_ATTR
175void DestroySurfaceKHR(VkInstance instance,
176                       VkSurfaceKHR surface_handle,
177                       const VkAllocationCallbacks* /*allocator*/) {
178    Surface* surface = SurfaceFromHandle(surface_handle);
179    if (!surface)
180        return;
181    native_window_api_disconnect(surface->window.get(), NATIVE_WINDOW_API_EGL);
182    surface->~Surface();
183    FreeMem(instance, surface);
184}
185
186VKAPI_ATTR
187VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/,
188                                            uint32_t /*queue_family*/,
189                                            VkSurfaceKHR /*surface*/,
190                                            VkBool32* supported) {
191    *supported = VK_TRUE;
192    return VK_SUCCESS;
193}
194
195VKAPI_ATTR
196VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(
197    VkPhysicalDevice /*pdev*/,
198    VkSurfaceKHR surface,
199    VkSurfaceCapabilitiesKHR* capabilities) {
200    int err;
201    ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
202
203    int width, height;
204    err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
205    if (err != 0) {
206        ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
207              strerror(-err), err);
208        return VK_ERROR_INITIALIZATION_FAILED;
209    }
210    err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
211    if (err != 0) {
212        ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
213              strerror(-err), err);
214        return VK_ERROR_INITIALIZATION_FAILED;
215    }
216
217    capabilities->currentExtent = VkExtent2D{width, height};
218
219    // TODO(jessehall): Figure out what the min/max values should be.
220    capabilities->minImageCount = 2;
221    capabilities->maxImageCount = 3;
222
223    // TODO(jessehall): Figure out what the max extent should be. Maximum
224    // texture dimension maybe?
225    capabilities->minImageExtent = VkExtent2D{1, 1};
226    capabilities->maxImageExtent = VkExtent2D{4096, 4096};
227
228    // TODO(jessehall): We can support all transforms, fix this once
229    // implemented.
230    capabilities->supportedTransforms = VK_SURFACE_TRANSFORM_NONE_BIT_KHR;
231
232    // TODO(jessehall): Implement based on NATIVE_WINDOW_TRANSFORM_HINT.
233    capabilities->currentTransform = VK_SURFACE_TRANSFORM_NONE_BIT_KHR;
234
235    capabilities->maxImageArrayLayers = 1;
236
237    // TODO(jessehall): I think these are right, but haven't thought hard about
238    // it. Do we need to query the driver for support of any of these?
239    // Currently not included:
240    // - VK_IMAGE_USAGE_GENERAL: maybe? does this imply cpu mappable?
241    // - VK_IMAGE_USAGE_DEPTH_STENCIL_BIT: definitely not
242    // - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT: definitely not
243    capabilities->supportedUsageFlags =
244        VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
245        VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
246        VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
247        VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
248
249    return VK_SUCCESS;
250}
251
252VKAPI_ATTR
253VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice /*pdev*/,
254                                            VkSurfaceKHR /*surface*/,
255                                            uint32_t* count,
256                                            VkSurfaceFormatKHR* formats) {
257    // TODO(jessehall): Fill out the set of supported formats. Longer term, add
258    // a new gralloc method to query whether a (format, usage) pair is
259    // supported, and check that for each gralloc format that corresponds to a
260    // Vulkan format. Shorter term, just add a few more formats to the ones
261    // hardcoded below.
262
263    const VkSurfaceFormatKHR kFormats[] = {
264        {VK_FORMAT_R8G8B8A8_UNORM, VK_COLORSPACE_SRGB_NONLINEAR_KHR},
265        {VK_FORMAT_R8G8B8A8_SRGB, VK_COLORSPACE_SRGB_NONLINEAR_KHR},
266    };
267    const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
268
269    VkResult result = VK_SUCCESS;
270    if (formats) {
271        if (*count < kNumFormats)
272            result = VK_INCOMPLETE;
273        std::copy(kFormats, kFormats + std::min(*count, kNumFormats), formats);
274    }
275    *count = kNumFormats;
276    return result;
277}
278
279VKAPI_ATTR
280VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice /*pdev*/,
281                                                 VkSurfaceKHR /*surface*/,
282                                                 uint32_t* count,
283                                                 VkPresentModeKHR* modes) {
284    const VkPresentModeKHR kModes[] = {
285        VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
286    };
287    const uint32_t kNumModes = sizeof(kModes) / sizeof(kModes[0]);
288
289    VkResult result = VK_SUCCESS;
290    if (modes) {
291        if (*count < kNumModes)
292            result = VK_INCOMPLETE;
293        std::copy(kModes, kModes + std::min(*count, kNumModes), modes);
294    }
295    *count = kNumModes;
296    return result;
297}
298
299VKAPI_ATTR
300VkResult CreateSwapchainKHR(VkDevice device,
301                            const VkSwapchainCreateInfoKHR* create_info,
302                            const VkAllocationCallbacks* /*allocator*/,
303                            VkSwapchainKHR* swapchain_handle) {
304    int err;
305    VkResult result = VK_SUCCESS;
306
307    ALOGV_IF(create_info->imageArraySize != 1,
308             "Swapchain imageArraySize (%u) != 1 not supported",
309             create_info->imageArraySize);
310
311    ALOGE_IF(create_info->imageFormat != VK_FORMAT_R8G8B8A8_UNORM,
312             "swapchain formats other than R8G8B8A8_UNORM not yet implemented");
313    ALOGE_IF(create_info->imageColorSpace != VK_COLORSPACE_SRGB_NONLINEAR_KHR,
314             "color spaces other than SRGB_NONLINEAR not yet implemented");
315    ALOGE_IF(create_info->oldSwapchain,
316             "swapchain re-creation not yet implemented");
317    ALOGE_IF(create_info->preTransform != VK_SURFACE_TRANSFORM_NONE_BIT_KHR,
318             "swapchain preTransform not yet implemented");
319    ALOGE_IF(create_info->presentMode != VK_PRESENT_MODE_FIFO_KHR,
320             "present modes other than FIFO are not yet implemented");
321
322    // -- Configure the native window --
323
324    Surface& surface = *SurfaceFromHandle(create_info->surface);
325    const DeviceVtbl& driver_vtbl = GetDriverVtbl(device);
326
327    err = native_window_set_buffers_dimensions(surface.window.get(),
328                                               create_info->imageExtent.width,
329                                               create_info->imageExtent.height);
330    if (err != 0) {
331        // TODO(jessehall): Improve error reporting. Can we enumerate possible
332        // errors and translate them to valid Vulkan result codes?
333        ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
334              create_info->imageExtent.width, create_info->imageExtent.height,
335              strerror(-err), err);
336        return VK_ERROR_INITIALIZATION_FAILED;
337    }
338
339    err = native_window_set_scaling_mode(
340        surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
341    if (err != 0) {
342        // TODO(jessehall): Improve error reporting. Can we enumerate possible
343        // errors and translate them to valid Vulkan result codes?
344        ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
345              strerror(-err), err);
346        return VK_ERROR_INITIALIZATION_FAILED;
347    }
348
349    uint32_t min_undequeued_buffers;
350    err = surface.window->query(
351        surface.window.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
352        reinterpret_cast<int*>(&min_undequeued_buffers));
353    if (err != 0) {
354        // TODO(jessehall): Improve error reporting. Can we enumerate possible
355        // errors and translate them to valid Vulkan result codes?
356        ALOGE("window->query failed: %s (%d)", strerror(-err), err);
357        return VK_ERROR_INITIALIZATION_FAILED;
358    }
359    uint32_t num_images =
360        (create_info->minImageCount - 1) + min_undequeued_buffers;
361    err = native_window_set_buffer_count(surface.window.get(), num_images);
362    if (err != 0) {
363        // TODO(jessehall): Improve error reporting. Can we enumerate possible
364        // errors and translate them to valid Vulkan result codes?
365        ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
366              err);
367        return VK_ERROR_INITIALIZATION_FAILED;
368    }
369
370    int gralloc_usage = 0;
371    // TODO(jessehall): Remove conditional once all drivers have been updated
372    if (driver_vtbl.GetSwapchainGrallocUsageANDROID) {
373        result = driver_vtbl.GetSwapchainGrallocUsageANDROID(
374            device, create_info->imageFormat, create_info->imageUsage,
375            &gralloc_usage);
376        if (result != VK_SUCCESS) {
377            ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
378            return VK_ERROR_INITIALIZATION_FAILED;
379        }
380    } else {
381        gralloc_usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
382    }
383    err = native_window_set_usage(surface.window.get(), gralloc_usage);
384    if (err != 0) {
385        // TODO(jessehall): Improve error reporting. Can we enumerate possible
386        // errors and translate them to valid Vulkan result codes?
387        ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
388        return VK_ERROR_INITIALIZATION_FAILED;
389    }
390
391    // -- Allocate our Swapchain object --
392    // After this point, we must deallocate the swapchain on error.
393
394    void* mem = AllocMem(device, sizeof(Swapchain), alignof(Swapchain),
395                         VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
396    if (!mem)
397        return VK_ERROR_OUT_OF_HOST_MEMORY;
398    Swapchain* swapchain = new (mem) Swapchain(surface, num_images);
399
400    // -- Dequeue all buffers and create a VkImage for each --
401    // Any failures during or after this must cancel the dequeued buffers.
402
403    VkNativeBufferANDROID image_native_buffer = {
404// TODO(jessehall): Figure out how to make extension headers not horrible.
405#pragma clang diagnostic push
406#pragma clang diagnostic ignored "-Wold-style-cast"
407        .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
408#pragma clang diagnostic pop
409        .pNext = nullptr,
410    };
411    VkImageCreateInfo image_create = {
412        .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
413        .pNext = &image_native_buffer,
414        .imageType = VK_IMAGE_TYPE_2D,
415        .format = VK_FORMAT_R8G8B8A8_UNORM,  // TODO(jessehall)
416        .extent = {0, 0, 1},
417        .mipLevels = 1,
418        .arrayLayers = 1,
419        .samples = VK_SAMPLE_COUNT_1_BIT,
420        .tiling = VK_IMAGE_TILING_OPTIMAL,
421        .usage = create_info->imageUsage,
422        .flags = 0,
423        .sharingMode = create_info->imageSharingMode,
424        .queueFamilyIndexCount = create_info->queueFamilyIndexCount,
425        .pQueueFamilyIndices = create_info->pQueueFamilyIndices,
426    };
427
428    for (uint32_t i = 0; i < num_images; i++) {
429        Swapchain::Image& img = swapchain->images[i];
430
431        ANativeWindowBuffer* buffer;
432        err = surface.window->dequeueBuffer(surface.window.get(), &buffer,
433                                            &img.dequeue_fence);
434        if (err != 0) {
435            // TODO(jessehall): Improve error reporting. Can we enumerate
436            // possible errors and translate them to valid Vulkan result codes?
437            ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
438            result = VK_ERROR_INITIALIZATION_FAILED;
439            break;
440        }
441        img.buffer = InitSharedPtr(device, buffer);
442        img.dequeued = true;
443
444        image_create.extent =
445            VkExtent3D{img.buffer->width, img.buffer->height, 1};
446        image_native_buffer.handle = img.buffer->handle;
447        image_native_buffer.stride = img.buffer->stride;
448        image_native_buffer.format = img.buffer->format;
449        image_native_buffer.usage = img.buffer->usage;
450
451        result =
452            driver_vtbl.CreateImage(device, &image_create, nullptr, &img.image);
453        if (result != VK_SUCCESS) {
454            ALOGD("vkCreateImage w/ native buffer failed: %u", result);
455            break;
456        }
457    }
458
459    // -- Cancel all buffers, returning them to the queue --
460    // If an error occurred before, also destroy the VkImage and release the
461    // buffer reference. Otherwise, we retain a strong reference to the buffer.
462    //
463    // TODO(jessehall): The error path here is the same as DestroySwapchain,
464    // but not the non-error path. Should refactor/unify.
465    for (uint32_t i = 0; i < num_images; i++) {
466        Swapchain::Image& img = swapchain->images[i];
467        if (img.dequeued) {
468            surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
469                                         img.dequeue_fence);
470            img.dequeue_fence = -1;
471            img.dequeued = false;
472        }
473        if (result != VK_SUCCESS) {
474            if (img.image)
475                driver_vtbl.DestroyImage(device, img.image, nullptr);
476        }
477    }
478
479    if (result != VK_SUCCESS) {
480        swapchain->~Swapchain();
481        FreeMem(device, swapchain);
482        return result;
483    }
484
485    *swapchain_handle = HandleFromSwapchain(swapchain);
486    return VK_SUCCESS;
487}
488
489VKAPI_ATTR
490void DestroySwapchainKHR(VkDevice device,
491                         VkSwapchainKHR swapchain_handle,
492                         const VkAllocationCallbacks* /*allocator*/) {
493    const DeviceVtbl& driver_vtbl = GetDriverVtbl(device);
494    Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
495    const std::shared_ptr<ANativeWindow>& window = swapchain->surface.window;
496
497    for (uint32_t i = 0; i < swapchain->num_images; i++) {
498        Swapchain::Image& img = swapchain->images[i];
499        if (img.dequeued) {
500            window->cancelBuffer(window.get(), img.buffer.get(),
501                                 img.dequeue_fence);
502            img.dequeue_fence = -1;
503            img.dequeued = false;
504        }
505        if (img.image) {
506            driver_vtbl.DestroyImage(device, img.image, nullptr);
507        }
508    }
509
510    swapchain->~Swapchain();
511    FreeMem(device, swapchain);
512}
513
514VKAPI_ATTR
515VkResult GetSwapchainImagesKHR(VkDevice,
516                               VkSwapchainKHR swapchain_handle,
517                               uint32_t* count,
518                               VkImage* images) {
519    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
520    VkResult result = VK_SUCCESS;
521    if (images) {
522        uint32_t n = swapchain.num_images;
523        if (*count < swapchain.num_images) {
524            n = *count;
525            result = VK_INCOMPLETE;
526        }
527        for (uint32_t i = 0; i < n; i++)
528            images[i] = swapchain.images[i].image;
529    }
530    *count = swapchain.num_images;
531    return result;
532}
533
534VKAPI_ATTR
535VkResult AcquireNextImageKHR(VkDevice device,
536                             VkSwapchainKHR swapchain_handle,
537                             uint64_t timeout,
538                             VkSemaphore semaphore,
539                             VkFence vk_fence,
540                             uint32_t* image_index) {
541    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
542    ANativeWindow* window = swapchain.surface.window.get();
543    VkResult result;
544    int err;
545
546    ALOGW_IF(
547        timeout != UINT64_MAX,
548        "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
549
550    ANativeWindowBuffer* buffer;
551    int fence_fd;
552    err = window->dequeueBuffer(window, &buffer, &fence_fd);
553    if (err != 0) {
554        // TODO(jessehall): Improve error reporting. Can we enumerate possible
555        // errors and translate them to valid Vulkan result codes?
556        ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
557        return VK_ERROR_INITIALIZATION_FAILED;
558    }
559
560    uint32_t idx;
561    for (idx = 0; idx < swapchain.num_images; idx++) {
562        if (swapchain.images[idx].buffer.get() == buffer) {
563            swapchain.images[idx].dequeued = true;
564            swapchain.images[idx].dequeue_fence = fence_fd;
565            break;
566        }
567    }
568    if (idx == swapchain.num_images) {
569        ALOGE("dequeueBuffer returned unrecognized buffer");
570        window->cancelBuffer(window, buffer, fence_fd);
571        return VK_ERROR_OUT_OF_DATE_KHR;
572    }
573
574    int fence_clone = -1;
575    if (fence_fd != -1) {
576        fence_clone = dup(fence_fd);
577        if (fence_clone == -1) {
578            ALOGE("dup(fence) failed, stalling until signalled: %s (%d)",
579                  strerror(errno), errno);
580            sync_wait(fence_fd, -1 /* forever */);
581        }
582    }
583
584    const DeviceVtbl& driver_vtbl = GetDriverVtbl(device);
585    if (driver_vtbl.AcquireImageANDROID) {
586        result =
587            driver_vtbl.AcquireImageANDROID(device, swapchain.images[idx].image,
588                                            fence_clone, semaphore, vk_fence);
589    } else {
590        ALOG_ASSERT(driver_vtbl.ImportNativeFenceANDROID,
591                    "Have neither vkAcquireImageANDROID nor "
592                    "vkImportNativeFenceANDROID");
593        result = driver_vtbl.ImportNativeFenceANDROID(device, semaphore,
594                                                      fence_clone);
595    }
596    if (result != VK_SUCCESS) {
597        // NOTE: we're relying on AcquireImageANDROID to close fence_clone,
598        // even if the call fails. We could close it ourselves on failure, but
599        // that would create a race condition if the driver closes it on a
600        // failure path: some other thread might create an fd with the same
601        // number between the time the driver closes it and the time we close
602        // it. We must assume one of: the driver *always* closes it even on
603        // failure, or *never* closes it on failure.
604        window->cancelBuffer(window, buffer, fence_fd);
605        swapchain.images[idx].dequeued = false;
606        swapchain.images[idx].dequeue_fence = -1;
607        return result;
608    }
609
610    *image_index = idx;
611    return VK_SUCCESS;
612}
613
614VKAPI_ATTR
615VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
616    ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
617             "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
618             present_info->sType);
619    ALOGV_IF(present_info->pNext, "VkPresentInfo::pNext != NULL");
620
621    const DeviceVtbl& driver_vtbl = GetDriverVtbl(queue);
622    VkResult final_result = VK_SUCCESS;
623    for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
624        Swapchain& swapchain =
625            *SwapchainFromHandle(present_info->pSwapchains[sc]);
626        ANativeWindow* window = swapchain.surface.window.get();
627        uint32_t image_idx = present_info->pImageIndices[sc];
628        Swapchain::Image& img = swapchain.images[image_idx];
629        VkResult result;
630        int err;
631
632        int fence = -1;
633        if (driver_vtbl.QueueSignalReleaseImageANDROID) {
634            result = driver_vtbl.QueueSignalReleaseImageANDROID(
635                queue, img.image, &fence);
636        } else {
637            ALOG_ASSERT(driver_vtbl.QueueSignalNativeFenceANDROID,
638                        "Have neither vkQueueSignalReleaseImageANDROID nor "
639                        "vkQueueSignalNativeFenceANDROID");
640            result = driver_vtbl.QueueSignalNativeFenceANDROID(queue, &fence);
641        }
642        if (result != VK_SUCCESS) {
643            ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
644            if (present_info->pResults)
645                present_info->pResults[sc] = result;
646            if (final_result == VK_SUCCESS)
647                final_result = result;
648            // TODO(jessehall): What happens to the buffer here? Does the app
649            // still own it or not, i.e. should we cancel the buffer? Hard to
650            // do correctly without synchronizing, though I guess we could wait
651            // for the queue to idle.
652            continue;
653        }
654
655        err = window->queueBuffer(window, img.buffer.get(), fence);
656        if (err != 0) {
657            // TODO(jessehall): What now? We should probably cancel the buffer,
658            // I guess?
659            ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
660            if (present_info->pResults)
661                present_info->pResults[sc] = result;
662            if (final_result == VK_SUCCESS)
663                final_result = VK_ERROR_INITIALIZATION_FAILED;
664            continue;
665        }
666
667        if (img.dequeue_fence != -1) {
668            close(img.dequeue_fence);
669            img.dequeue_fence = -1;
670        }
671        img.dequeued = false;
672
673        if (present_info->pResults)
674            present_info->pResults[sc] = VK_SUCCESS;
675    }
676
677    return final_result;
678}
679
680}  // namespace vulkan
681