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