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