Loading vulkan/libvulkan/swapchain.cpp +181 −172 Original line number Diff line number Diff line Loading @@ -243,6 +243,11 @@ enum { MAX_TIMING_INFOS = 10 }; // syncronous requests to Surface Flinger): enum { MIN_NUM_FRAMES_AGO = 5 }; bool IsSharedPresentMode(VkPresentModeKHR mode) { return mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR || mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR; } struct Swapchain { Swapchain(Surface& surface_, uint32_t num_images_, Loading @@ -254,9 +259,7 @@ struct Swapchain { pre_transform(pre_transform_), frame_timestamps_enabled(false), acquire_next_image_timeout(-1), shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR || present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) { shared(IsSharedPresentMode(present_mode)) { ANativeWindow* window = surface.window.get(); native_window_get_refresh_cycle_duration( window, Loading Loading @@ -1796,127 +1799,33 @@ static VkResult WorstPresentResult(VkResult a, VkResult b) { return a != VK_SUCCESS ? a : b; } VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { ATRACE_CALL(); ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d", present_info->sType); VkDevice device = GetData(queue).driver_device; const auto& dispatch = GetData(queue).driver; VkResult final_result = VK_SUCCESS; // Look at the pNext chain for supported extension structs: const VkPresentRegionsKHR* present_regions = nullptr; const VkPresentTimesInfoGOOGLE* present_times = nullptr; const VkPresentRegionsKHR* next = reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext); while (next) { switch (next->sType) { case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR: present_regions = next; break; case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE: present_times = reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next); break; default: ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x", next->sType); break; // KHR_incremental_present aspect of QueuePresentKHR static void SetSwapchainSurfaceDamage(ANativeWindow *window, const VkPresentRegionKHR *pRegion) { std::vector<android_native_rect_t> rects(pRegion->rectangleCount); for (auto i = 0u; i < pRegion->rectangleCount; i++) { auto const& rect = pRegion->pRectangles[i]; if (rect.layer > 0) { ALOGV("vkQueuePresentKHR ignoring invalid layer (%u); using layer 0 instead", rect.layer); } next = reinterpret_cast<const VkPresentRegionsKHR*>(next->pNext); } ALOGV_IF( present_regions && present_regions->swapchainCount != present_info->swapchainCount, "VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount"); ALOGV_IF(present_times && present_times->swapchainCount != present_info->swapchainCount, "VkPresentTimesInfoGOOGLE::swapchainCount != " "VkPresentInfo::swapchainCount"); const VkPresentRegionKHR* regions = (present_regions) ? present_regions->pRegions : nullptr; const VkPresentTimeGOOGLE* times = (present_times) ? present_times->pTimes : nullptr; const VkAllocationCallbacks* allocator = &GetData(device).allocator; android_native_rect_t* rects = nullptr; uint32_t nrects = 0; for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) { Swapchain& swapchain = *SwapchainFromHandle(present_info->pSwapchains[sc]); uint32_t image_idx = present_info->pImageIndices[sc]; Swapchain::Image& img = swapchain.images[image_idx]; const VkPresentRegionKHR* region = (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr; const VkPresentTimeGOOGLE* time = (times) ? ×[sc] : nullptr; VkResult swapchain_result = VK_SUCCESS; VkResult result; int err; int fence = -1; result = dispatch.QueueSignalReleaseImageANDROID( queue, present_info->waitSemaphoreCount, present_info->pWaitSemaphores, img.image, &fence); if (result != VK_SUCCESS) { ALOGE("QueueSignalReleaseImageANDROID failed: %d", result); swapchain_result = result; rects[i].left = rect.offset.x; rects[i].bottom = rect.offset.y; rects[i].right = rect.offset.x + rect.extent.width; rects[i].top = rect.offset.y + rect.extent.height; } native_window_set_surface_damage(window, rects.data(), rects.size()); } if (img.release_fence >= 0) close(img.release_fence); img.release_fence = fence < 0 ? -1 : dup(fence); if (swapchain.surface.swapchain_handle == present_info->pSwapchains[sc]) { // GOOGLE_display_timing aspect of QueuePresentKHR static void SetSwapchainFrameTimestamp(Swapchain &swapchain, const VkPresentTimeGOOGLE *pTime) { ANativeWindow *window = swapchain.surface.window.get(); if (swapchain_result == VK_SUCCESS) { if (region) { // Process the incremental-present hint for this swapchain: uint32_t rcount = region->rectangleCount; if (rcount > nrects) { android_native_rect_t* new_rects = static_cast<android_native_rect_t*>( allocator->pfnReallocation( allocator->pUserData, rects, sizeof(android_native_rect_t) * rcount, alignof(android_native_rect_t), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND)); if (new_rects) { rects = new_rects; nrects = rcount; } else { rcount = 0; // Ignore the hint for this swapchain } } for (uint32_t r = 0; r < rcount; ++r) { if (region->pRectangles[r].layer > 0) { ALOGV( "vkQueuePresentKHR ignoring invalid layer " "(%u); using layer 0 instead", region->pRectangles[r].layer); } int x = region->pRectangles[r].offset.x; int y = region->pRectangles[r].offset.y; int width = static_cast<int>( region->pRectangles[r].extent.width); int height = static_cast<int>( region->pRectangles[r].extent.height); android_native_rect_t* cur_rect = &rects[r]; cur_rect->left = x; cur_rect->top = y + height; cur_rect->right = x + width; cur_rect->bottom = y; } native_window_set_surface_damage(window, rects, rcount); } if (time) { // We don't know whether the app will actually use GOOGLE_display_timing // with a particular swapchain until QueuePresent; enable it on the BQ // now if needed if (!swapchain.frame_timestamps_enabled) { ALOGV( "Calling " "native_window_enable_frame_timestamps(true)"); ALOGV("Calling native_window_enable_frame_timestamps(true)"); native_window_enable_frame_timestamps(window, true); swapchain.frame_timestamps_enabled = true; } Loading @@ -1924,7 +1833,7 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { // Record the nativeFrameId so it can be later correlated to // this present. uint64_t nativeFrameId = 0; err = native_window_get_next_frame_id( int err = native_window_get_next_frame_id( window, &nativeFrameId); if (err != android::OK) { ALOGE("Failed to get next native frame ID."); Loading @@ -1932,20 +1841,63 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { // Add a new timing record with the user's presentID and // the nativeFrameId. swapchain.timing.emplace_back(time, nativeFrameId); while (swapchain.timing.size() > MAX_TIMING_INFOS) { swapchain.timing.erase(swapchain.timing.begin()); swapchain.timing.emplace_back(pTime, nativeFrameId); if (swapchain.timing.size() > MAX_TIMING_INFOS) { swapchain.timing.erase( swapchain.timing.begin(), swapchain.timing.begin() + swapchain.timing.size() - MAX_TIMING_INFOS); } if (time->desiredPresentTime) { // Set the desiredPresentTime: if (pTime->desiredPresentTime) { ALOGV( "Calling " "native_window_set_buffers_timestamp(%" PRId64 ")", time->desiredPresentTime); "Calling native_window_set_buffers_timestamp(%" PRId64 ")", pTime->desiredPresentTime); native_window_set_buffers_timestamp( window, static_cast<int64_t>(time->desiredPresentTime)); static_cast<int64_t>(pTime->desiredPresentTime)); } } static VkResult PresentOneSwapchain( VkQueue queue, Swapchain& swapchain, uint32_t imageIndex, const VkPresentRegionKHR *pRegion, const VkPresentTimeGOOGLE *pTime, uint32_t waitSemaphoreCount, const VkSemaphore *pWaitSemaphores) { VkDevice device = GetData(queue).driver_device; const auto& dispatch = GetData(queue).driver; Swapchain::Image& img = swapchain.images[imageIndex]; VkResult swapchain_result = VK_SUCCESS; VkResult result; int err; // XXX: long standing issue: QueueSignalReleaseImageANDROID consumes the // wait semaphores, so this doesn't actually work for the multiple swapchain // case. int fence = -1; result = dispatch.QueueSignalReleaseImageANDROID( queue, waitSemaphoreCount, pWaitSemaphores, img.image, &fence); if (result != VK_SUCCESS) { ALOGE("QueueSignalReleaseImageANDROID failed: %d", result); swapchain_result = result; } if (img.release_fence >= 0) close(img.release_fence); img.release_fence = fence < 0 ? -1 : dup(fence); if (swapchain.surface.swapchain_handle == HandleFromSwapchain(&swapchain)) { ANativeWindow* window = swapchain.surface.window.get(); if (swapchain_result == VK_SUCCESS) { if (pRegion) { SetSwapchainSurfaceDamage(window, pRegion); } if (pTime) { SetSwapchainFrameTimestamp(swapchain, pTime); } err = window->queueBuffer(window, img.buffer.get(), fence); Loading Loading @@ -2011,15 +1963,72 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { swapchain_result = VK_ERROR_OUT_OF_DATE_KHR; } return swapchain_result; } VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { ATRACE_CALL(); ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d", present_info->sType); VkResult final_result = VK_SUCCESS; // Look at the pNext chain for supported extension structs: const VkPresentRegionsKHR* present_regions = nullptr; const VkPresentTimesInfoGOOGLE* present_times = nullptr; const VkPresentRegionsKHR* next = reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext); while (next) { switch (next->sType) { case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR: present_regions = next; break; case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE: present_times = reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next); break; default: ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x", next->sType); break; } next = reinterpret_cast<const VkPresentRegionsKHR*>(next->pNext); } ALOGV_IF( present_regions && present_regions->swapchainCount != present_info->swapchainCount, "VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount"); ALOGV_IF(present_times && present_times->swapchainCount != present_info->swapchainCount, "VkPresentTimesInfoGOOGLE::swapchainCount != " "VkPresentInfo::swapchainCount"); const VkPresentRegionKHR* regions = (present_regions) ? present_regions->pRegions : nullptr; const VkPresentTimeGOOGLE* times = (present_times) ? present_times->pTimes : nullptr; for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) { Swapchain& swapchain = *SwapchainFromHandle(present_info->pSwapchains[sc]); VkResult swapchain_result = PresentOneSwapchain( queue, swapchain, present_info->pImageIndices[sc], (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr, times ? ×[sc] : nullptr, present_info->waitSemaphoreCount, present_info->pWaitSemaphores); if (present_info->pResults) present_info->pResults[sc] = swapchain_result; if (swapchain_result != final_result) final_result = WorstPresentResult(final_result, swapchain_result); } if (rects) { allocator->pfnFree(allocator->pUserData, rects); } return final_result; } Loading Loading
vulkan/libvulkan/swapchain.cpp +181 −172 Original line number Diff line number Diff line Loading @@ -243,6 +243,11 @@ enum { MAX_TIMING_INFOS = 10 }; // syncronous requests to Surface Flinger): enum { MIN_NUM_FRAMES_AGO = 5 }; bool IsSharedPresentMode(VkPresentModeKHR mode) { return mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR || mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR; } struct Swapchain { Swapchain(Surface& surface_, uint32_t num_images_, Loading @@ -254,9 +259,7 @@ struct Swapchain { pre_transform(pre_transform_), frame_timestamps_enabled(false), acquire_next_image_timeout(-1), shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR || present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) { shared(IsSharedPresentMode(present_mode)) { ANativeWindow* window = surface.window.get(); native_window_get_refresh_cycle_duration( window, Loading Loading @@ -1796,127 +1799,33 @@ static VkResult WorstPresentResult(VkResult a, VkResult b) { return a != VK_SUCCESS ? a : b; } VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { ATRACE_CALL(); ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d", present_info->sType); VkDevice device = GetData(queue).driver_device; const auto& dispatch = GetData(queue).driver; VkResult final_result = VK_SUCCESS; // Look at the pNext chain for supported extension structs: const VkPresentRegionsKHR* present_regions = nullptr; const VkPresentTimesInfoGOOGLE* present_times = nullptr; const VkPresentRegionsKHR* next = reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext); while (next) { switch (next->sType) { case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR: present_regions = next; break; case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE: present_times = reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next); break; default: ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x", next->sType); break; // KHR_incremental_present aspect of QueuePresentKHR static void SetSwapchainSurfaceDamage(ANativeWindow *window, const VkPresentRegionKHR *pRegion) { std::vector<android_native_rect_t> rects(pRegion->rectangleCount); for (auto i = 0u; i < pRegion->rectangleCount; i++) { auto const& rect = pRegion->pRectangles[i]; if (rect.layer > 0) { ALOGV("vkQueuePresentKHR ignoring invalid layer (%u); using layer 0 instead", rect.layer); } next = reinterpret_cast<const VkPresentRegionsKHR*>(next->pNext); } ALOGV_IF( present_regions && present_regions->swapchainCount != present_info->swapchainCount, "VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount"); ALOGV_IF(present_times && present_times->swapchainCount != present_info->swapchainCount, "VkPresentTimesInfoGOOGLE::swapchainCount != " "VkPresentInfo::swapchainCount"); const VkPresentRegionKHR* regions = (present_regions) ? present_regions->pRegions : nullptr; const VkPresentTimeGOOGLE* times = (present_times) ? present_times->pTimes : nullptr; const VkAllocationCallbacks* allocator = &GetData(device).allocator; android_native_rect_t* rects = nullptr; uint32_t nrects = 0; for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) { Swapchain& swapchain = *SwapchainFromHandle(present_info->pSwapchains[sc]); uint32_t image_idx = present_info->pImageIndices[sc]; Swapchain::Image& img = swapchain.images[image_idx]; const VkPresentRegionKHR* region = (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr; const VkPresentTimeGOOGLE* time = (times) ? ×[sc] : nullptr; VkResult swapchain_result = VK_SUCCESS; VkResult result; int err; int fence = -1; result = dispatch.QueueSignalReleaseImageANDROID( queue, present_info->waitSemaphoreCount, present_info->pWaitSemaphores, img.image, &fence); if (result != VK_SUCCESS) { ALOGE("QueueSignalReleaseImageANDROID failed: %d", result); swapchain_result = result; rects[i].left = rect.offset.x; rects[i].bottom = rect.offset.y; rects[i].right = rect.offset.x + rect.extent.width; rects[i].top = rect.offset.y + rect.extent.height; } native_window_set_surface_damage(window, rects.data(), rects.size()); } if (img.release_fence >= 0) close(img.release_fence); img.release_fence = fence < 0 ? -1 : dup(fence); if (swapchain.surface.swapchain_handle == present_info->pSwapchains[sc]) { // GOOGLE_display_timing aspect of QueuePresentKHR static void SetSwapchainFrameTimestamp(Swapchain &swapchain, const VkPresentTimeGOOGLE *pTime) { ANativeWindow *window = swapchain.surface.window.get(); if (swapchain_result == VK_SUCCESS) { if (region) { // Process the incremental-present hint for this swapchain: uint32_t rcount = region->rectangleCount; if (rcount > nrects) { android_native_rect_t* new_rects = static_cast<android_native_rect_t*>( allocator->pfnReallocation( allocator->pUserData, rects, sizeof(android_native_rect_t) * rcount, alignof(android_native_rect_t), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND)); if (new_rects) { rects = new_rects; nrects = rcount; } else { rcount = 0; // Ignore the hint for this swapchain } } for (uint32_t r = 0; r < rcount; ++r) { if (region->pRectangles[r].layer > 0) { ALOGV( "vkQueuePresentKHR ignoring invalid layer " "(%u); using layer 0 instead", region->pRectangles[r].layer); } int x = region->pRectangles[r].offset.x; int y = region->pRectangles[r].offset.y; int width = static_cast<int>( region->pRectangles[r].extent.width); int height = static_cast<int>( region->pRectangles[r].extent.height); android_native_rect_t* cur_rect = &rects[r]; cur_rect->left = x; cur_rect->top = y + height; cur_rect->right = x + width; cur_rect->bottom = y; } native_window_set_surface_damage(window, rects, rcount); } if (time) { // We don't know whether the app will actually use GOOGLE_display_timing // with a particular swapchain until QueuePresent; enable it on the BQ // now if needed if (!swapchain.frame_timestamps_enabled) { ALOGV( "Calling " "native_window_enable_frame_timestamps(true)"); ALOGV("Calling native_window_enable_frame_timestamps(true)"); native_window_enable_frame_timestamps(window, true); swapchain.frame_timestamps_enabled = true; } Loading @@ -1924,7 +1833,7 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { // Record the nativeFrameId so it can be later correlated to // this present. uint64_t nativeFrameId = 0; err = native_window_get_next_frame_id( int err = native_window_get_next_frame_id( window, &nativeFrameId); if (err != android::OK) { ALOGE("Failed to get next native frame ID."); Loading @@ -1932,20 +1841,63 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { // Add a new timing record with the user's presentID and // the nativeFrameId. swapchain.timing.emplace_back(time, nativeFrameId); while (swapchain.timing.size() > MAX_TIMING_INFOS) { swapchain.timing.erase(swapchain.timing.begin()); swapchain.timing.emplace_back(pTime, nativeFrameId); if (swapchain.timing.size() > MAX_TIMING_INFOS) { swapchain.timing.erase( swapchain.timing.begin(), swapchain.timing.begin() + swapchain.timing.size() - MAX_TIMING_INFOS); } if (time->desiredPresentTime) { // Set the desiredPresentTime: if (pTime->desiredPresentTime) { ALOGV( "Calling " "native_window_set_buffers_timestamp(%" PRId64 ")", time->desiredPresentTime); "Calling native_window_set_buffers_timestamp(%" PRId64 ")", pTime->desiredPresentTime); native_window_set_buffers_timestamp( window, static_cast<int64_t>(time->desiredPresentTime)); static_cast<int64_t>(pTime->desiredPresentTime)); } } static VkResult PresentOneSwapchain( VkQueue queue, Swapchain& swapchain, uint32_t imageIndex, const VkPresentRegionKHR *pRegion, const VkPresentTimeGOOGLE *pTime, uint32_t waitSemaphoreCount, const VkSemaphore *pWaitSemaphores) { VkDevice device = GetData(queue).driver_device; const auto& dispatch = GetData(queue).driver; Swapchain::Image& img = swapchain.images[imageIndex]; VkResult swapchain_result = VK_SUCCESS; VkResult result; int err; // XXX: long standing issue: QueueSignalReleaseImageANDROID consumes the // wait semaphores, so this doesn't actually work for the multiple swapchain // case. int fence = -1; result = dispatch.QueueSignalReleaseImageANDROID( queue, waitSemaphoreCount, pWaitSemaphores, img.image, &fence); if (result != VK_SUCCESS) { ALOGE("QueueSignalReleaseImageANDROID failed: %d", result); swapchain_result = result; } if (img.release_fence >= 0) close(img.release_fence); img.release_fence = fence < 0 ? -1 : dup(fence); if (swapchain.surface.swapchain_handle == HandleFromSwapchain(&swapchain)) { ANativeWindow* window = swapchain.surface.window.get(); if (swapchain_result == VK_SUCCESS) { if (pRegion) { SetSwapchainSurfaceDamage(window, pRegion); } if (pTime) { SetSwapchainFrameTimestamp(swapchain, pTime); } err = window->queueBuffer(window, img.buffer.get(), fence); Loading Loading @@ -2011,15 +1963,72 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { swapchain_result = VK_ERROR_OUT_OF_DATE_KHR; } return swapchain_result; } VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { ATRACE_CALL(); ALOGV_IF(present_info->sType != VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, "vkQueuePresentKHR: invalid VkPresentInfoKHR structure type %d", present_info->sType); VkResult final_result = VK_SUCCESS; // Look at the pNext chain for supported extension structs: const VkPresentRegionsKHR* present_regions = nullptr; const VkPresentTimesInfoGOOGLE* present_times = nullptr; const VkPresentRegionsKHR* next = reinterpret_cast<const VkPresentRegionsKHR*>(present_info->pNext); while (next) { switch (next->sType) { case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR: present_regions = next; break; case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE: present_times = reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next); break; default: ALOGV("QueuePresentKHR ignoring unrecognized pNext->sType = %x", next->sType); break; } next = reinterpret_cast<const VkPresentRegionsKHR*>(next->pNext); } ALOGV_IF( present_regions && present_regions->swapchainCount != present_info->swapchainCount, "VkPresentRegions::swapchainCount != VkPresentInfo::swapchainCount"); ALOGV_IF(present_times && present_times->swapchainCount != present_info->swapchainCount, "VkPresentTimesInfoGOOGLE::swapchainCount != " "VkPresentInfo::swapchainCount"); const VkPresentRegionKHR* regions = (present_regions) ? present_regions->pRegions : nullptr; const VkPresentTimeGOOGLE* times = (present_times) ? present_times->pTimes : nullptr; for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) { Swapchain& swapchain = *SwapchainFromHandle(present_info->pSwapchains[sc]); VkResult swapchain_result = PresentOneSwapchain( queue, swapchain, present_info->pImageIndices[sc], (regions && !swapchain.mailbox_mode) ? ®ions[sc] : nullptr, times ? ×[sc] : nullptr, present_info->waitSemaphoreCount, present_info->pWaitSemaphores); if (present_info->pResults) present_info->pResults[sc] = swapchain_result; if (swapchain_result != final_result) final_result = WorstPresentResult(final_result, swapchain_result); } if (rects) { allocator->pfnFree(allocator->pUserData, rects); } return final_result; } Loading