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