swapchain.cpp revision 0f2ac2e52d06ed171f7b888870c1c3c42b167d1a
1/*
2 * Copyright 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <algorithm>
18
19#include <log/log.h>
20#include <gui/BufferQueue.h>
21#include <sync/sync.h>
22#include <utils/StrongPointer.h>
23
24#include "driver.h"
25
26// TODO(jessehall): Currently we don't have a good error code for when a native
27// window operation fails. Just returning INITIALIZATION_FAILED for now. Later
28// versions (post SDK 0.9) of the API/extension have a better error code.
29// When updating to that version, audit all error returns.
30namespace vulkan {
31namespace driver {
32
33namespace {
34
35const VkSurfaceTransformFlagsKHR kSupportedTransforms =
36    VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR |
37    VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR |
38    VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR |
39    VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
40    // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform.
41    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR |
42    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
43    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR |
44    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR |
45    VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR;
46
47VkSurfaceTransformFlagBitsKHR TranslateNativeToVulkanTransform(int native) {
48    // Native and Vulkan transforms are isomorphic, but are represented
49    // differently. Vulkan transforms are built up of an optional horizontal
50    // mirror, followed by a clockwise 0/90/180/270-degree rotation. Native
51    // transforms are built up from a horizontal flip, vertical flip, and
52    // 90-degree rotation, all optional but always in that order.
53
54    // TODO(jessehall): For now, only support pure rotations, not
55    // flip or flip-and-rotate, until I have more time to test them and build
56    // sample code. As far as I know we never actually use anything besides
57    // pure rotations anyway.
58
59    switch (native) {
60        case 0:  // 0x0
61            return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
62        // case NATIVE_WINDOW_TRANSFORM_FLIP_H:  // 0x1
63        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR;
64        // case NATIVE_WINDOW_TRANSFORM_FLIP_V:  // 0x2
65        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR;
66        case NATIVE_WINDOW_TRANSFORM_ROT_180:  // FLIP_H | FLIP_V
67            return VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR;
68        case NATIVE_WINDOW_TRANSFORM_ROT_90:  // 0x4
69            return VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR;
70        // case NATIVE_WINDOW_TRANSFORM_FLIP_H | NATIVE_WINDOW_TRANSFORM_ROT_90:
71        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR;
72        // case NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_ROT_90:
73        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR;
74        case NATIVE_WINDOW_TRANSFORM_ROT_270:  // FLIP_H | FLIP_V | ROT_90
75            return VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
76        case NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY:
77        default:
78            return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
79    }
80}
81
82int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR transform) {
83    switch (transform) {
84        case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
85            return NATIVE_WINDOW_TRANSFORM_ROT_270;
86        case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
87            return NATIVE_WINDOW_TRANSFORM_ROT_180;
88        case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
89            return NATIVE_WINDOW_TRANSFORM_ROT_90;
90        // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform.
91        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
92        //     return NATIVE_WINDOW_TRANSFORM_FLIP_H;
93        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
94        //     return NATIVE_WINDOW_TRANSFORM_FLIP_H |
95        //            NATIVE_WINDOW_TRANSFORM_ROT_90;
96        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
97        //     return NATIVE_WINDOW_TRANSFORM_FLIP_V;
98        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
99        //     return NATIVE_WINDOW_TRANSFORM_FLIP_V |
100        //            NATIVE_WINDOW_TRANSFORM_ROT_90;
101        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
102        case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
103        default:
104            return 0;
105    }
106}
107
108// ----------------------------------------------------------------------------
109
110struct Surface {
111    android::sp<ANativeWindow> window;
112    VkSwapchainKHR swapchain_handle;
113};
114
115VkSurfaceKHR HandleFromSurface(Surface* surface) {
116    return VkSurfaceKHR(reinterpret_cast<uint64_t>(surface));
117}
118
119Surface* SurfaceFromHandle(VkSurfaceKHR handle) {
120    return reinterpret_cast<Surface*>(handle);
121}
122
123struct Swapchain {
124    Swapchain(Surface& surface_, uint32_t num_images_)
125        : surface(surface_),
126          num_images(num_images_),
127          frame_timestamps_enabled(false) {}
128
129    Surface& surface;
130    uint32_t num_images;
131    bool frame_timestamps_enabled;
132
133    struct Image {
134        Image() : image(VK_NULL_HANDLE), dequeue_fence(-1), dequeued(false) {}
135        VkImage image;
136        android::sp<ANativeWindowBuffer> buffer;
137        // The fence is only valid when the buffer is dequeued, and should be
138        // -1 any other time. When valid, we own the fd, and must ensure it is
139        // closed: either by closing it explicitly when queueing the buffer,
140        // or by passing ownership e.g. to ANativeWindow::cancelBuffer().
141        int dequeue_fence;
142        bool dequeued;
143    } images[android::BufferQueue::NUM_BUFFER_SLOTS];
144};
145
146VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
147    return VkSwapchainKHR(reinterpret_cast<uint64_t>(swapchain));
148}
149
150Swapchain* SwapchainFromHandle(VkSwapchainKHR handle) {
151    return reinterpret_cast<Swapchain*>(handle);
152}
153
154void ReleaseSwapchainImage(VkDevice device,
155                           ANativeWindow* window,
156                           int release_fence,
157                           Swapchain::Image& image) {
158    ALOG_ASSERT(release_fence == -1 || image.dequeued,
159                "ReleaseSwapchainImage: can't provide a release fence for "
160                "non-dequeued images");
161
162    if (image.dequeued) {
163        if (release_fence >= 0) {
164            // We get here from vkQueuePresentKHR. The application is
165            // responsible for creating an execution dependency chain from
166            // vkAcquireNextImage (dequeue_fence) to vkQueuePresentKHR
167            // (release_fence), so we can drop the dequeue_fence here.
168            if (image.dequeue_fence >= 0)
169                close(image.dequeue_fence);
170        } else {
171            // We get here during swapchain destruction, or various serious
172            // error cases e.g. when we can't create the release_fence during
173            // vkQueuePresentKHR. In non-error cases, the dequeue_fence should
174            // have already signalled, since the swapchain images are supposed
175            // to be idle before the swapchain is destroyed. In error cases,
176            // there may be rendering in flight to the image, but since we
177            // weren't able to create a release_fence, waiting for the
178            // dequeue_fence is about the best we can do.
179            release_fence = image.dequeue_fence;
180        }
181        image.dequeue_fence = -1;
182
183        if (window) {
184            window->cancelBuffer(window, image.buffer.get(), release_fence);
185        } else {
186            if (release_fence >= 0) {
187                sync_wait(release_fence, -1 /* forever */);
188                close(release_fence);
189            }
190        }
191
192        image.dequeued = false;
193    }
194
195    if (image.image) {
196        GetData(device).driver.DestroyImage(device, image.image, nullptr);
197        image.image = VK_NULL_HANDLE;
198    }
199
200    image.buffer.clear();
201}
202
203void OrphanSwapchain(VkDevice device, Swapchain* swapchain) {
204    if (swapchain->surface.swapchain_handle != HandleFromSwapchain(swapchain))
205        return;
206    for (uint32_t i = 0; i < swapchain->num_images; i++) {
207        if (!swapchain->images[i].dequeued)
208            ReleaseSwapchainImage(device, nullptr, -1, swapchain->images[i]);
209    }
210    swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
211}
212
213}  // anonymous namespace
214
215VKAPI_ATTR
216VkResult CreateAndroidSurfaceKHR(
217    VkInstance instance,
218    const VkAndroidSurfaceCreateInfoKHR* pCreateInfo,
219    const VkAllocationCallbacks* allocator,
220    VkSurfaceKHR* out_surface) {
221    if (!allocator)
222        allocator = &GetData(instance).allocator;
223    void* mem = allocator->pfnAllocation(allocator->pUserData, sizeof(Surface),
224                                         alignof(Surface),
225                                         VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
226    if (!mem)
227        return VK_ERROR_OUT_OF_HOST_MEMORY;
228    Surface* surface = new (mem) Surface;
229
230    surface->window = pCreateInfo->window;
231    surface->swapchain_handle = VK_NULL_HANDLE;
232
233    // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
234    int err =
235        native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL);
236    if (err != 0) {
237        // TODO(jessehall): Improve error reporting. Can we enumerate possible
238        // errors and translate them to valid Vulkan result codes?
239        ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
240              err);
241        surface->~Surface();
242        allocator->pfnFree(allocator->pUserData, surface);
243        return VK_ERROR_INITIALIZATION_FAILED;
244    }
245
246    *out_surface = HandleFromSurface(surface);
247    return VK_SUCCESS;
248}
249
250VKAPI_ATTR
251void DestroySurfaceKHR(VkInstance instance,
252                       VkSurfaceKHR surface_handle,
253                       const VkAllocationCallbacks* allocator) {
254    Surface* surface = SurfaceFromHandle(surface_handle);
255    if (!surface)
256        return;
257    native_window_api_disconnect(surface->window.get(), NATIVE_WINDOW_API_EGL);
258    ALOGV_IF(surface->swapchain_handle != VK_NULL_HANDLE,
259             "destroyed VkSurfaceKHR 0x%" PRIx64
260             " has active VkSwapchainKHR 0x%" PRIx64,
261             reinterpret_cast<uint64_t>(surface_handle),
262             reinterpret_cast<uint64_t>(surface->swapchain_handle));
263    surface->~Surface();
264    if (!allocator)
265        allocator = &GetData(instance).allocator;
266    allocator->pfnFree(allocator->pUserData, surface);
267}
268
269VKAPI_ATTR
270VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/,
271                                            uint32_t /*queue_family*/,
272                                            VkSurfaceKHR /*surface*/,
273                                            VkBool32* supported) {
274    *supported = VK_TRUE;
275    return VK_SUCCESS;
276}
277
278VKAPI_ATTR
279VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(
280    VkPhysicalDevice /*pdev*/,
281    VkSurfaceKHR surface,
282    VkSurfaceCapabilitiesKHR* capabilities) {
283    int err;
284    ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
285
286    int width, height;
287    err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
288    if (err != 0) {
289        ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
290              strerror(-err), err);
291        return VK_ERROR_INITIALIZATION_FAILED;
292    }
293    err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
294    if (err != 0) {
295        ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
296              strerror(-err), err);
297        return VK_ERROR_INITIALIZATION_FAILED;
298    }
299
300    int transform_hint;
301    err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
302    if (err != 0) {
303        ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
304              strerror(-err), err);
305        return VK_ERROR_INITIALIZATION_FAILED;
306    }
307
308    // TODO(jessehall): Figure out what the min/max values should be.
309    capabilities->minImageCount = 2;
310    capabilities->maxImageCount = 3;
311
312    capabilities->currentExtent =
313        VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
314
315    // TODO(jessehall): Figure out what the max extent should be. Maximum
316    // texture dimension maybe?
317    capabilities->minImageExtent = VkExtent2D{1, 1};
318    capabilities->maxImageExtent = VkExtent2D{4096, 4096};
319
320    capabilities->maxImageArrayLayers = 1;
321
322    capabilities->supportedTransforms = kSupportedTransforms;
323    capabilities->currentTransform =
324        TranslateNativeToVulkanTransform(transform_hint);
325
326    // On Android, window composition is a WindowManager property, not something
327    // associated with the bufferqueue. It can't be changed from here.
328    capabilities->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
329
330    // TODO(jessehall): I think these are right, but haven't thought hard about
331    // it. Do we need to query the driver for support of any of these?
332    // Currently not included:
333    // - VK_IMAGE_USAGE_DEPTH_STENCIL_BIT: definitely not
334    // - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT: definitely not
335    capabilities->supportedUsageFlags =
336        VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
337        VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
338        VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
339        VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
340
341    return VK_SUCCESS;
342}
343
344VKAPI_ATTR
345VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice /*pdev*/,
346                                            VkSurfaceKHR /*surface*/,
347                                            uint32_t* count,
348                                            VkSurfaceFormatKHR* formats) {
349    // TODO(jessehall): Fill out the set of supported formats. Longer term, add
350    // a new gralloc method to query whether a (format, usage) pair is
351    // supported, and check that for each gralloc format that corresponds to a
352    // Vulkan format. Shorter term, just add a few more formats to the ones
353    // hardcoded below.
354
355    const VkSurfaceFormatKHR kFormats[] = {
356        {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
357        {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
358        {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
359    };
360    const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
361
362    VkResult result = VK_SUCCESS;
363    if (formats) {
364        if (*count < kNumFormats)
365            result = VK_INCOMPLETE;
366        *count = std::min(*count, kNumFormats);
367        std::copy(kFormats, kFormats + *count, formats);
368    } else {
369        *count = kNumFormats;
370    }
371    return result;
372}
373
374VKAPI_ATTR
375VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice /*pdev*/,
376                                                 VkSurfaceKHR /*surface*/,
377                                                 uint32_t* count,
378                                                 VkPresentModeKHR* modes) {
379    const VkPresentModeKHR kModes[] = {
380        VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
381    };
382    const uint32_t kNumModes = sizeof(kModes) / sizeof(kModes[0]);
383
384    VkResult result = VK_SUCCESS;
385    if (modes) {
386        if (*count < kNumModes)
387            result = VK_INCOMPLETE;
388        *count = std::min(*count, kNumModes);
389        std::copy(kModes, kModes + *count, modes);
390    } else {
391        *count = kNumModes;
392    }
393    return result;
394}
395
396VKAPI_ATTR
397VkResult CreateSwapchainKHR(VkDevice device,
398                            const VkSwapchainCreateInfoKHR* create_info,
399                            const VkAllocationCallbacks* allocator,
400                            VkSwapchainKHR* swapchain_handle) {
401    int err;
402    VkResult result = VK_SUCCESS;
403
404    ALOGV("vkCreateSwapchainKHR: surface=0x%" PRIx64
405          " minImageCount=%u imageFormat=%u imageColorSpace=%u"
406          " imageExtent=%ux%u imageUsage=%#x preTransform=%u presentMode=%u"
407          " oldSwapchain=0x%" PRIx64,
408          reinterpret_cast<uint64_t>(create_info->surface),
409          create_info->minImageCount, create_info->imageFormat,
410          create_info->imageColorSpace, create_info->imageExtent.width,
411          create_info->imageExtent.height, create_info->imageUsage,
412          create_info->preTransform, create_info->presentMode,
413          reinterpret_cast<uint64_t>(create_info->oldSwapchain));
414
415    if (!allocator)
416        allocator = &GetData(device).allocator;
417
418    ALOGV_IF(create_info->imageArrayLayers != 1,
419             "swapchain imageArrayLayers=%u not supported",
420             create_info->imageArrayLayers);
421    ALOGV_IF(create_info->imageColorSpace != VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
422             "swapchain imageColorSpace=%u not supported",
423             create_info->imageColorSpace);
424    ALOGV_IF((create_info->preTransform & ~kSupportedTransforms) != 0,
425             "swapchain preTransform=%#x not supported",
426             create_info->preTransform);
427    ALOGV_IF(!(create_info->presentMode == VK_PRESENT_MODE_FIFO_KHR ||
428               create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR),
429             "swapchain presentMode=%u not supported",
430             create_info->presentMode);
431
432    Surface& surface = *SurfaceFromHandle(create_info->surface);
433
434    if (surface.swapchain_handle != create_info->oldSwapchain) {
435        ALOGV("Can't create a swapchain for VkSurfaceKHR 0x%" PRIx64
436              " because it already has active swapchain 0x%" PRIx64
437              " but VkSwapchainCreateInfo::oldSwapchain=0x%" PRIx64,
438              reinterpret_cast<uint64_t>(create_info->surface),
439              reinterpret_cast<uint64_t>(surface.swapchain_handle),
440              reinterpret_cast<uint64_t>(create_info->oldSwapchain));
441        return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
442    }
443    if (create_info->oldSwapchain != VK_NULL_HANDLE)
444        OrphanSwapchain(device, SwapchainFromHandle(create_info->oldSwapchain));
445
446    // -- Reset the native window --
447    // The native window might have been used previously, and had its properties
448    // changed from defaults. That will affect the answer we get for queries
449    // like MIN_UNDEQUED_BUFFERS. Reset to a known/default state before we
450    // attempt such queries.
451
452    // The native window only allows dequeueing all buffers before any have
453    // been queued, since after that point at least one is assumed to be in
454    // non-FREE state at any given time. Disconnecting and re-connecting
455    // orphans the previous buffers, getting us back to the state where we can
456    // dequeue all buffers.
457    err = native_window_api_disconnect(surface.window.get(),
458                                       NATIVE_WINDOW_API_EGL);
459    ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
460             strerror(-err), err);
461    err =
462        native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
463    ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
464             strerror(-err), err);
465
466    err = native_window_set_buffer_count(surface.window.get(), 0);
467    if (err != 0) {
468        ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
469              strerror(-err), err);
470        return VK_ERROR_INITIALIZATION_FAILED;
471    }
472
473    err = surface.window->setSwapInterval(surface.window.get(), 1);
474    if (err != 0) {
475        // TODO(jessehall): Improve error reporting. Can we enumerate possible
476        // errors and translate them to valid Vulkan result codes?
477        ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
478              strerror(-err), err);
479        return VK_ERROR_INITIALIZATION_FAILED;
480    }
481
482    // -- Configure the native window --
483
484    const auto& dispatch = GetData(device).driver;
485
486    int native_format = HAL_PIXEL_FORMAT_RGBA_8888;
487    switch (create_info->imageFormat) {
488        case VK_FORMAT_R8G8B8A8_UNORM:
489        case VK_FORMAT_R8G8B8A8_SRGB:
490            native_format = HAL_PIXEL_FORMAT_RGBA_8888;
491            break;
492        case VK_FORMAT_R5G6B5_UNORM_PACK16:
493            native_format = HAL_PIXEL_FORMAT_RGB_565;
494            break;
495        default:
496            ALOGV("unsupported swapchain format %d", create_info->imageFormat);
497            break;
498    }
499    err = native_window_set_buffers_format(surface.window.get(), native_format);
500    if (err != 0) {
501        // TODO(jessehall): Improve error reporting. Can we enumerate possible
502        // errors and translate them to valid Vulkan result codes?
503        ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
504              native_format, strerror(-err), err);
505        return VK_ERROR_INITIALIZATION_FAILED;
506    }
507    err = native_window_set_buffers_data_space(surface.window.get(),
508                                               HAL_DATASPACE_SRGB_LINEAR);
509    if (err != 0) {
510        // TODO(jessehall): Improve error reporting. Can we enumerate possible
511        // errors and translate them to valid Vulkan result codes?
512        ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
513              HAL_DATASPACE_SRGB_LINEAR, strerror(-err), err);
514        return VK_ERROR_INITIALIZATION_FAILED;
515    }
516
517    err = native_window_set_buffers_dimensions(
518        surface.window.get(), static_cast<int>(create_info->imageExtent.width),
519        static_cast<int>(create_info->imageExtent.height));
520    if (err != 0) {
521        // TODO(jessehall): Improve error reporting. Can we enumerate possible
522        // errors and translate them to valid Vulkan result codes?
523        ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
524              create_info->imageExtent.width, create_info->imageExtent.height,
525              strerror(-err), err);
526        return VK_ERROR_INITIALIZATION_FAILED;
527    }
528
529    // VkSwapchainCreateInfo::preTransform indicates the transformation the app
530    // applied during rendering. native_window_set_transform() expects the
531    // inverse: the transform the app is requesting that the compositor perform
532    // during composition. With native windows, pre-transform works by rendering
533    // with the same transform the compositor is applying (as in Vulkan), but
534    // then requesting the inverse transform, so that when the compositor does
535    // it's job the two transforms cancel each other out and the compositor ends
536    // up applying an identity transform to the app's buffer.
537    err = native_window_set_buffers_transform(
538        surface.window.get(),
539        InvertTransformToNative(create_info->preTransform));
540    if (err != 0) {
541        // TODO(jessehall): Improve error reporting. Can we enumerate possible
542        // errors and translate them to valid Vulkan result codes?
543        ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
544              InvertTransformToNative(create_info->preTransform),
545              strerror(-err), err);
546        return VK_ERROR_INITIALIZATION_FAILED;
547    }
548
549    err = native_window_set_scaling_mode(
550        surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
551    if (err != 0) {
552        // TODO(jessehall): Improve error reporting. Can we enumerate possible
553        // errors and translate them to valid Vulkan result codes?
554        ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
555              strerror(-err), err);
556        return VK_ERROR_INITIALIZATION_FAILED;
557    }
558
559    int query_value;
560    err = surface.window->query(surface.window.get(),
561                                NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
562                                &query_value);
563    if (err != 0 || query_value < 0) {
564        // TODO(jessehall): Improve error reporting. Can we enumerate possible
565        // errors and translate them to valid Vulkan result codes?
566        ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
567              query_value);
568        return VK_ERROR_INITIALIZATION_FAILED;
569    }
570    uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
571    // The MIN_UNDEQUEUED_BUFFERS query doesn't know whether we'll be using
572    // async mode or not, and assumes not. But in async mode, the BufferQueue
573    // requires an extra undequeued buffer.
574    // See BufferQueueCore::getMinUndequeuedBufferCountLocked().
575    if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
576        min_undequeued_buffers += 1;
577
578    uint32_t num_images =
579        (create_info->minImageCount - 1) + min_undequeued_buffers;
580    err = native_window_set_buffer_count(surface.window.get(), num_images);
581    if (err != 0) {
582        // TODO(jessehall): Improve error reporting. Can we enumerate possible
583        // errors and translate them to valid Vulkan result codes?
584        ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
585              strerror(-err), err);
586        return VK_ERROR_INITIALIZATION_FAILED;
587    }
588
589    VkSwapchainImageUsageFlagsANDROID swapchain_image_usage = 0;
590    int gralloc_usage = 0;
591    if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
592        result = dispatch.GetSwapchainGrallocUsage2ANDROID(
593            device, create_info->imageFormat, create_info->imageUsage,
594            swapchain_image_usage, &gralloc_usage);
595        if (result != VK_SUCCESS) {
596            ALOGE("vkGetSwapchainGrallocUsage2ANDROID failed: %d", result);
597            return VK_ERROR_INITIALIZATION_FAILED;
598        }
599    } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
600        result = dispatch.GetSwapchainGrallocUsageANDROID(
601            device, create_info->imageFormat, create_info->imageUsage,
602            &gralloc_usage);
603        if (result != VK_SUCCESS) {
604            ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
605            return VK_ERROR_INITIALIZATION_FAILED;
606        }
607    } else {
608        gralloc_usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
609    }
610    err = native_window_set_usage(surface.window.get(), gralloc_usage);
611    if (err != 0) {
612        // TODO(jessehall): Improve error reporting. Can we enumerate possible
613        // errors and translate them to valid Vulkan result codes?
614        ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
615        return VK_ERROR_INITIALIZATION_FAILED;
616    }
617
618    int swap_interval =
619        create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
620    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
621    if (err != 0) {
622        // TODO(jessehall): Improve error reporting. Can we enumerate possible
623        // errors and translate them to valid Vulkan result codes?
624        ALOGE("native_window->setSwapInterval(%d) failed: %s (%d)",
625              swap_interval, strerror(-err), err);
626        return VK_ERROR_INITIALIZATION_FAILED;
627    }
628
629    // -- Allocate our Swapchain object --
630    // After this point, we must deallocate the swapchain on error.
631
632    void* mem = allocator->pfnAllocation(allocator->pUserData,
633                                         sizeof(Swapchain), alignof(Swapchain),
634                                         VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
635    if (!mem)
636        return VK_ERROR_OUT_OF_HOST_MEMORY;
637    Swapchain* swapchain = new (mem) Swapchain(surface, num_images);
638
639    // -- Dequeue all buffers and create a VkImage for each --
640    // Any failures during or after this must cancel the dequeued buffers.
641
642    VkSwapchainImageCreateInfoANDROID swapchain_image_create = {
643#pragma clang diagnostic push
644#pragma clang diagnostic ignored "-Wold-style-cast"
645        .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID,
646#pragma clang diagnostic pop
647        .pNext = nullptr,
648        .usage = swapchain_image_usage,
649    };
650    VkNativeBufferANDROID image_native_buffer = {
651#pragma clang diagnostic push
652#pragma clang diagnostic ignored "-Wold-style-cast"
653        .sType = VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID,
654#pragma clang diagnostic pop
655        .pNext = &swapchain_image_create,
656    };
657    VkImageCreateInfo image_create = {
658        .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
659        .pNext = &image_native_buffer,
660        .imageType = VK_IMAGE_TYPE_2D,
661        .format = create_info->imageFormat,
662        .extent = {0, 0, 1},
663        .mipLevels = 1,
664        .arrayLayers = 1,
665        .samples = VK_SAMPLE_COUNT_1_BIT,
666        .tiling = VK_IMAGE_TILING_OPTIMAL,
667        .usage = create_info->imageUsage,
668        .flags = 0,
669        .sharingMode = create_info->imageSharingMode,
670        .queueFamilyIndexCount = create_info->queueFamilyIndexCount,
671        .pQueueFamilyIndices = create_info->pQueueFamilyIndices,
672    };
673
674    for (uint32_t i = 0; i < num_images; i++) {
675        Swapchain::Image& img = swapchain->images[i];
676
677        ANativeWindowBuffer* buffer;
678        err = surface.window->dequeueBuffer(surface.window.get(), &buffer,
679                                            &img.dequeue_fence);
680        if (err != 0) {
681            // TODO(jessehall): Improve error reporting. Can we enumerate
682            // possible errors and translate them to valid Vulkan result codes?
683            ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
684            result = VK_ERROR_INITIALIZATION_FAILED;
685            break;
686        }
687        img.buffer = buffer;
688        img.dequeued = true;
689
690        image_create.extent =
691            VkExtent3D{static_cast<uint32_t>(img.buffer->width),
692                       static_cast<uint32_t>(img.buffer->height),
693                       1};
694        image_native_buffer.handle = img.buffer->handle;
695        image_native_buffer.stride = img.buffer->stride;
696        image_native_buffer.format = img.buffer->format;
697        image_native_buffer.usage = img.buffer->usage;
698
699        result =
700            dispatch.CreateImage(device, &image_create, nullptr, &img.image);
701        if (result != VK_SUCCESS) {
702            ALOGD("vkCreateImage w/ native buffer failed: %u", result);
703            break;
704        }
705    }
706
707    // -- Cancel all buffers, returning them to the queue --
708    // If an error occurred before, also destroy the VkImage and release the
709    // buffer reference. Otherwise, we retain a strong reference to the buffer.
710    //
711    // TODO(jessehall): The error path here is the same as DestroySwapchain,
712    // but not the non-error path. Should refactor/unify.
713    for (uint32_t i = 0; i < num_images; i++) {
714        Swapchain::Image& img = swapchain->images[i];
715        if (img.dequeued) {
716            surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
717                                         img.dequeue_fence);
718            img.dequeue_fence = -1;
719            img.dequeued = false;
720        }
721        if (result != VK_SUCCESS) {
722            if (img.image)
723                dispatch.DestroyImage(device, img.image, nullptr);
724        }
725    }
726
727    if (result != VK_SUCCESS) {
728        swapchain->~Swapchain();
729        allocator->pfnFree(allocator->pUserData, swapchain);
730        return result;
731    }
732
733    surface.swapchain_handle = HandleFromSwapchain(swapchain);
734    *swapchain_handle = surface.swapchain_handle;
735    return VK_SUCCESS;
736}
737
738VKAPI_ATTR
739void DestroySwapchainKHR(VkDevice device,
740                         VkSwapchainKHR swapchain_handle,
741                         const VkAllocationCallbacks* allocator) {
742    const auto& dispatch = GetData(device).driver;
743    Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
744    if (!swapchain)
745        return;
746    bool active = swapchain->surface.swapchain_handle == swapchain_handle;
747    ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
748
749    if (swapchain->frame_timestamps_enabled) {
750        native_window_enable_frame_timestamps(window, false);
751    }
752    for (uint32_t i = 0; i < swapchain->num_images; i++)
753        ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
754    if (active)
755        swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
756    if (!allocator)
757        allocator = &GetData(device).allocator;
758    swapchain->~Swapchain();
759    allocator->pfnFree(allocator->pUserData, swapchain);
760}
761
762VKAPI_ATTR
763VkResult GetSwapchainImagesKHR(VkDevice,
764                               VkSwapchainKHR swapchain_handle,
765                               uint32_t* count,
766                               VkImage* images) {
767    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
768    ALOGW_IF(swapchain.surface.swapchain_handle != swapchain_handle,
769             "getting images for non-active swapchain 0x%" PRIx64
770             "; only dequeued image handles are valid",
771             reinterpret_cast<uint64_t>(swapchain_handle));
772    VkResult result = VK_SUCCESS;
773    if (images) {
774        uint32_t n = swapchain.num_images;
775        if (*count < swapchain.num_images) {
776            n = *count;
777            result = VK_INCOMPLETE;
778        }
779        for (uint32_t i = 0; i < n; i++)
780            images[i] = swapchain.images[i].image;
781        *count = n;
782    } else {
783        *count = swapchain.num_images;
784    }
785    return result;
786}
787
788VKAPI_ATTR
789VkResult AcquireNextImageKHR(VkDevice device,
790                             VkSwapchainKHR swapchain_handle,
791                             uint64_t timeout,
792                             VkSemaphore semaphore,
793                             VkFence vk_fence,
794                             uint32_t* image_index) {
795    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
796    ANativeWindow* window = swapchain.surface.window.get();
797    VkResult result;
798    int err;
799
800    if (swapchain.surface.swapchain_handle != swapchain_handle)
801        return VK_ERROR_OUT_OF_DATE_KHR;
802
803    ALOGW_IF(
804        timeout != UINT64_MAX,
805        "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
806
807    ANativeWindowBuffer* buffer;
808    int fence_fd;
809    err = window->dequeueBuffer(window, &buffer, &fence_fd);
810    if (err != 0) {
811        // TODO(jessehall): Improve error reporting. Can we enumerate possible
812        // errors and translate them to valid Vulkan result codes?
813        ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
814        return VK_ERROR_INITIALIZATION_FAILED;
815    }
816
817    uint32_t idx;
818    for (idx = 0; idx < swapchain.num_images; idx++) {
819        if (swapchain.images[idx].buffer.get() == buffer) {
820            swapchain.images[idx].dequeued = true;
821            swapchain.images[idx].dequeue_fence = fence_fd;
822            break;
823        }
824    }
825    if (idx == swapchain.num_images) {
826        ALOGE("dequeueBuffer returned unrecognized buffer");
827        window->cancelBuffer(window, buffer, fence_fd);
828        return VK_ERROR_OUT_OF_DATE_KHR;
829    }
830
831    int fence_clone = -1;
832    if (fence_fd != -1) {
833        fence_clone = dup(fence_fd);
834        if (fence_clone == -1) {
835            ALOGE("dup(fence) failed, stalling until signalled: %s (%d)",
836                  strerror(errno), errno);
837            sync_wait(fence_fd, -1 /* forever */);
838        }
839    }
840
841    result = GetData(device).driver.AcquireImageANDROID(
842        device, swapchain.images[idx].image, fence_clone, semaphore, vk_fence);
843    if (result != VK_SUCCESS) {
844        // NOTE: we're relying on AcquireImageANDROID to close fence_clone,
845        // even if the call fails. We could close it ourselves on failure, but
846        // that would create a race condition if the driver closes it on a
847        // failure path: some other thread might create an fd with the same
848        // number between the time the driver closes it and the time we close
849        // it. We must assume one of: the driver *always* closes it even on
850        // failure, or *never* closes it on failure.
851        window->cancelBuffer(window, buffer, fence_fd);
852        swapchain.images[idx].dequeued = false;
853        swapchain.images[idx].dequeue_fence = -1;
854        return result;
855    }
856
857    *image_index = idx;
858    return VK_SUCCESS;
859}
860
861static VkResult WorstPresentResult(VkResult a, VkResult b) {
862    // See the error ranking for vkQueuePresentKHR at the end of section 29.6
863    // (in spec version 1.0.14).
864    static const VkResult kWorstToBest[] = {
865        VK_ERROR_DEVICE_LOST,
866        VK_ERROR_SURFACE_LOST_KHR,
867        VK_ERROR_OUT_OF_DATE_KHR,
868        VK_ERROR_OUT_OF_DEVICE_MEMORY,
869        VK_ERROR_OUT_OF_HOST_MEMORY,
870        VK_SUBOPTIMAL_KHR,
871    };
872    for (auto result : kWorstToBest) {
873        if (a == result || b == result)
874            return result;
875    }
876    ALOG_ASSERT(a == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", a);
877    ALOG_ASSERT(b == VK_SUCCESS, "invalid vkQueuePresentKHR result %d", b);
878    return a != VK_SUCCESS ? a : b;
879}
880
881VKAPI_ATTR
882VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) {
883    ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
884             "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d",
885             present_info->sType);
886
887    VkDevice device = GetData(queue).driver_device;
888    const auto& dispatch = GetData(queue).driver;
889    VkResult final_result = VK_SUCCESS;
890
891    // Look at the pNext chain for supported extension structs:
892    const VkPresentRegionsKHR* present_regions = nullptr;
893    const VkPresentTimesInfoGOOGLE* present_times = nullptr;
894    const VkPresentRegionsKHR* next =
895        reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext);
896    while (next) {
897        switch (next->sType) {
898            case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:
899                present_regions = next;
900                break;
901            case VK_STRUCTURE_TYPE_PRESENT_TIMES_GOOGLE:
902                present_times =
903                    reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next);
904                break;
905            default:
906                ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x",
907                      next->sType);
908                break;
909        }
910        next = reinterpret_cast<const VkPresentRegionsKHR*>(next->pNext);
911    }
912    ALOGV_IF(
913        present_regions &&
914            present_regions->swapchainCount != present_info->swapchainCount,
915        "VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount");
916    ALOGV_IF(present_times &&
917                 present_times->swapchainCount != present_info->swapchainCount,
918             "VkPresentTimesInfoGOOGLE::swapchainCount != "
919             "VkPresentInfo::swapchainCount");
920    const VkPresentRegionKHR* regions =
921        (present_regions) ? present_regions->pRegions : nullptr;
922    const VkPresentTimeGOOGLE* times =
923        (present_times) ? present_times->pTimes : nullptr;
924    const VkAllocationCallbacks* allocator = &GetData(device).allocator;
925    android_native_rect_t* rects = nullptr;
926    uint32_t nrects = 0;
927
928    for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
929        Swapchain& swapchain =
930            *SwapchainFromHandle(present_info->pSwapchains[sc]);
931        uint32_t image_idx = present_info->pImageIndices[sc];
932        Swapchain::Image& img = swapchain.images[image_idx];
933        const VkPresentRegionKHR* region = (regions) ? &regions[sc] : nullptr;
934        const VkPresentTimeGOOGLE* time = (times) ? &times[sc] : nullptr;
935        VkResult swapchain_result = VK_SUCCESS;
936        VkResult result;
937        int err;
938
939        int fence = -1;
940        result = dispatch.QueueSignalReleaseImageANDROID(
941            queue, present_info->waitSemaphoreCount,
942            present_info->pWaitSemaphores, img.image, &fence);
943        if (result != VK_SUCCESS) {
944            ALOGE("QueueSignalReleaseImageANDROID failed: %d", result);
945            swapchain_result = result;
946        }
947
948        if (swapchain.surface.swapchain_handle ==
949            present_info->pSwapchains[sc]) {
950            ANativeWindow* window = swapchain.surface.window.get();
951            if (swapchain_result == VK_SUCCESS) {
952                if (region) {
953                    // Process the incremental-present hint for this swapchain:
954                    uint32_t rcount = region->rectangleCount;
955                    if (rcount > nrects) {
956                        android_native_rect_t* new_rects =
957                            static_cast<android_native_rect_t*>(
958                                allocator->pfnReallocation(
959                                    allocator->pUserData, rects,
960                                    sizeof(android_native_rect_t) * rcount,
961                                    alignof(android_native_rect_t),
962                                    VK_SYSTEM_ALLOCATION_SCOPE_COMMAND));
963                        if (new_rects) {
964                            rects = new_rects;
965                            nrects = rcount;
966                        } else {
967                            rcount = 0;  // Ignore the hint for this swapchain
968                        }
969                    }
970                    for (uint32_t r = 0; r < rcount; ++r) {
971                        if (region->pRectangles[r].layer > 0) {
972                            ALOGV(
973                                "vkQueuePresentKHR ignoring invalid layer "
974                                "(%u); using layer 0 instead",
975                                region->pRectangles[r].layer);
976                        }
977                        int x = region->pRectangles[r].offset.x;
978                        int y = region->pRectangles[r].offset.y;
979                        int width = static_cast<int>(
980                            region->pRectangles[r].extent.width);
981                        int height = static_cast<int>(
982                            region->pRectangles[r].extent.height);
983                        android_native_rect_t* cur_rect = &rects[r];
984                        cur_rect->left = x;
985                        cur_rect->top = y + height;
986                        cur_rect->right = x + width;
987                        cur_rect->bottom = y;
988                    }
989                    native_window_set_surface_damage(window, rects, rcount);
990                }
991                if (time) {
992                    if (!swapchain.frame_timestamps_enabled) {
993                        native_window_enable_frame_timestamps(window, true);
994                        swapchain.frame_timestamps_enabled = true;
995                    }
996                    // TODO(ianelliott): need to store the presentID (and
997                    // desiredPresentTime), so it can be later correlated to
998                    // this present.  Probably modify the following function
999                    // (and below) to plumb a path to store it in FrameEvents
1000                    // code, on the producer side.
1001                    native_window_set_buffers_timestamp(
1002                        window, static_cast<int64_t>(time->desiredPresentTime));
1003                }
1004                err = window->queueBuffer(window, img.buffer.get(), fence);
1005                // queueBuffer always closes fence, even on error
1006                if (err != 0) {
1007                    // TODO(jessehall): What now? We should probably cancel the
1008                    // buffer, I guess?
1009                    ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
1010                    swapchain_result = WorstPresentResult(
1011                        swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
1012                }
1013                if (img.dequeue_fence >= 0) {
1014                    close(img.dequeue_fence);
1015                    img.dequeue_fence = -1;
1016                }
1017                img.dequeued = false;
1018            }
1019            if (swapchain_result != VK_SUCCESS) {
1020                ReleaseSwapchainImage(device, window, fence, img);
1021                OrphanSwapchain(device, &swapchain);
1022            }
1023        } else {
1024            ReleaseSwapchainImage(device, nullptr, fence, img);
1025            swapchain_result = VK_ERROR_OUT_OF_DATE_KHR;
1026        }
1027
1028        if (present_info->pResults)
1029            present_info->pResults[sc] = swapchain_result;
1030
1031        if (swapchain_result != final_result)
1032            final_result = WorstPresentResult(final_result, swapchain_result);
1033    }
1034    if (rects) {
1035        allocator->pfnFree(allocator->pUserData, rects);
1036    }
1037
1038    return final_result;
1039}
1040
1041VKAPI_ATTR
1042VkResult GetRefreshCycleDurationGOOGLE(
1043    VkDevice,
1044    VkSwapchainKHR,
1045    VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
1046    VkResult result = VK_SUCCESS;
1047
1048    // TODO(ianelliott): FULLY IMPLEMENT THIS FUNCTION!!!
1049    pDisplayTimingProperties->minRefreshDuration = 16666666666;
1050    pDisplayTimingProperties->maxRefreshDuration = 16666666666;
1051
1052    return result;
1053}
1054
1055VKAPI_ATTR
1056VkResult GetPastPresentationTimingGOOGLE(
1057    VkDevice,
1058    VkSwapchainKHR swapchain_handle,
1059    uint32_t* count,
1060    VkPastPresentationTimingGOOGLE* timings) {
1061    Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
1062    ANativeWindow* window = swapchain.surface.window.get();
1063    VkResult result = VK_SUCCESS;
1064
1065    if (!swapchain.frame_timestamps_enabled) {
1066        native_window_enable_frame_timestamps(window, true);
1067        swapchain.frame_timestamps_enabled = true;
1068    }
1069
1070    // TODO(ianelliott): FULLY IMPLEMENT THIS FUNCTION!!!
1071    if (timings) {
1072        *count = 0;
1073    } else {
1074        *count = 0;
1075    }
1076
1077    return result;
1078}
1079
1080VKAPI_ATTR
1081VkResult GetSwapchainStatusKHR(
1082    VkDevice,
1083    VkSwapchainKHR) {
1084    VkResult result = VK_SUCCESS;
1085
1086    // TODO(chrisforbes): Implement this function properly
1087
1088    return result;
1089}
1090
1091}  // namespace driver
1092}  // namespace vulkan
1093