Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4cd01fb2 authored by Chris Forbes's avatar Chris Forbes
Browse files

Break QueuePresentKHR into more manageable pieces

Instead of QueuePresentKHR doing everything, let's decompose to:

QueuePresentKHR
 -> PresentOneSwapchain     (considers just one swapchain in the list)
     -> SetSwapchainSurfaceDamage      (does KHR_incremental_present)
     -> SetSwapchainFrameTimestamp     (does GOOGLE_display_timing)

As part of the decomposition, stop trying to be so clever about reusing
allocations for KHR_incremental_present; it's not worth threading it
through the list of swapchains. Also just use a vector; it's not worth
using the app-provided allocator from the device scope.

Note some issues with the multi-swapchain case in general. We should
also refine the fd handling to be leak-free by construction; handling
raw fds is asking for trouble.

Bug: b/255376900

Change-Id: Iaa66b7b1bfd2be2a1fda578fcb8141607be1e360
parent 9a319744
Loading
Loading
Loading
Loading
+181 −172
Original line number Diff line number Diff line
@@ -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_,
@@ -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,
@@ -1783,127 +1786,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) ? &regions[sc] : nullptr;
        const VkPresentTimeGOOGLE* time = (times) ? &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;
    }
@@ -1911,7 +1820,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.");
@@ -1919,20 +1828,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);
@@ -1998,15 +1950,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) ? &regions[sc] : nullptr,
            times ? &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;
}