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) ? ®ions[sc] : nullptr; 934 const VkPresentTimeGOOGLE* time = (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