Android Vulkan 开启VK_GOOGLE_DISPLAY_TIMING 后,一个vsync 会释放两个imageBuffer现象分析

android vulkan 程序,当开启VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME 功能, 跑在android12 上, swapchain 用的是三缓冲,所有当sufaceflinger 一个vsync 来的时候, 图像缓冲中有app已经渲染好的两个的的图像, 同一个vsync 中会释放两个buffer(如上图), 一个是两图像中的前一个, 一个是上一帧显示完成的。

复制代码
 frameIndex ++;
    //设置id,和desiredPresentTime 
    VkPresentTimeGOOGLE times = {
            .presentID = frameIndex,
            .desiredPresentTime = os_realtime_get_ns()+10000000,  
    };

    VkPresentTimesInfoGOOGLE timings = {
            .sType = VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
            .swapchainCount = 1,
            .pTimes = &times,
    };

  VkPresentInfoKHR presentInfo{};
  presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;

  presentInfo.waitSemaphoreCount = 1;
  presentInfo.pWaitSemaphores = signalSemaphores;

  VkSwapchainKHR swapChains[] = {swapChain};
  presentInfo.swapchainCount = 1;
  presentInfo.pSwapchains = swapChains;
  presentInfo.pImageIndices = &imageIndex;
  presentInfo.pResults = nullptr;
  presentInfo.pNext = &timings;

  result = vkQueuePresentKHR(presentQueue, &presentInfo);
  if (result == VK_SUBOPTIMAL_KHR) {
    orientationChanged = true;
  } else if (result == VK_ERROR_OUT_OF_DATE_KHR) {
    recreateSwapChain();
  } else {
    assert(result == VK_SUCCESS);  // failed to present swap chain image!
  }
  currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;


  //获取vkGetPastPresentationTimingGOOGLE

    VkPastPresentationTimingGOOGLE tims[3];
    uint32_t count = 0;
    vkGetPastPresentationTimingGOOGLE( //
            device,                        //
            swapChain,             //
            &count,                            //
            nullptr);                             //
    LOGI("bacal_log vkGetPastPresentationTimingGOOGLE count=%d",count);

    if(count > 0) {
        VkPastPresentationTimingGOOGLE tims[3];

        vkGetPastPresentationTimingGOOGLE( //
                device,                        //
                swapChain,             //
                &count,                            //
                tims);                          //

        for (int i = 0; i < count; i++) {

            LOGI("image presentID=%d, desiredPresentTime=%lld, actualPresentTime=%lld, diffPresentTime=%lld", tims[i].presentID, tims[i].desiredPresentTime, tims[i].actualPresentTime, ((tims[i].earliestPresentTime-tims[i].desiredPresentTime)/1000000));

        }
    }

Vsync 来的时候, SF 遍历可以用来合成显示的buffer, 两个buffer都满足(transactionIsReadyToBeApplied),

有没有开TimingGOOGLE,影响到surfaceFlinger 显示buffer的策略(isAutoTimestamp 值不一样):

cpp 复制代码
bool SurfaceFlinger::transactionIsReadyToBeApplied(
        const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
        uid_t originUid, const Vector<ComposerState>& states,
        const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
                bufferLayersReadyToPresent) const {
    ATRACE_CALL();
    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();

    ......
    for (const ComposerState& state : states) {
        const layer_state_t& s = state.state;
        const bool acquireFenceChanged = (s.what & layer_state_t::eAcquireFenceChanged);
        if (acquireFenceChanged && s.acquireFence && !enableLatchUnsignaled &&
            s.acquireFence->getStatus() == Fence::Status::Unsignaled) {
            ATRACE_NAME("fence unsignaled");
            return false;
        }

        sp<Layer> layer = nullptr;
        if (s.surface) {
            layer = fromHandle(s.surface).promote();
        } else if (s.hasBufferChanges()) {
            ALOGW("Transaction with buffer, but no Layer?");
            continue;
        }
        if (!layer) {
            continue;
        }

        ATRACE_NAME(layer->getName().c_str());

        if (s.hasBufferChanges()) {
            //layer 中已经有buffer等待用于合成显示了
            //isAutoTimestamp = true, TimingGOOGLE 没有开,
            //isAutoTimestamp = false, TimingGOOGLE 打开了
            //TimingGOOGLE开了后,第二哥buffer和会认为符合用于显示
            const bool hasPendingBuffer =
                    bufferLayersReadyToPresent.find(s.surface) != bufferLayersReadyToPresent.end();
            if (layer->backpressureEnabled() && hasPendingBuffer && isAutoTimestamp) {
                ATRACE_NAME("hasPendingBuffer");
                return false;
            }
        }
    }
    return true;

TimingGOOGLE 功能是如何改变isAutoTimestamp 的值的?

cpp 复制代码
//swapchain.cpp  (vulkan framework)
VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info){

    //present_info 中带有VkPresentTimesInfoGOOGLE 信息

    const VkPresentTimesInfoGOOGLE* present_times = nullptr;

     present_times =
                    reinterpret_cast<const VkPresentTimesInfoGOOGLE*>(next);

     const VkPresentTimeGOOGLE* times =
        (present_times) ? present_times->pTimes : nullptr;

     if (time) {

                        native_window_set_buffers_timestamp(
                            window,
                            static_cast<int64_t>(time->desiredPresentTime));

     }

}


static inline int native_window_set_buffers_timestamp(
        struct ANativeWindow* window,
        int64_t timestamp)
{
    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP,
            timestamp);
}

//Surface.cpp
//最终这个时间设置到了Surface中
int Surface::setBuffersTimestamp(int64_t timestamp)
{
    ALOGV("Surface::setBuffersTimestamp");
    Mutex::Autolock lock(mMutex);
    mTimestamp = timestamp;
    return NO_ERROR;
}

//buffer 加到缓冲区的时候, 会用到这个时间去改变isAutoTimestamp 的值
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {

    getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input);
}


void Surface::getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd,
        nsecs_t timestamp, IGraphicBufferProducer::QueueBufferInput* out) {
    bool isAutoTimestamp = false;  //默认是false

    //如果app有设置时间,就是vulkan中设置的desiredPresentTime
    if (timestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {
        timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
        isAutoTimestamp = true;  //被修改为true了
        ALOGV("Surface::queueBuffer making up timestamp: %.2f ms",
            timestamp / 1000000.0);
    }

    // Make sure the crop rectangle is entirely inside the buffer.
    Rect crop(Rect::EMPTY_RECT);
    mCrop.intersect(Rect(buffer->width, buffer->height), &crop);

    sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
    //isAutoTimestamp  值被传到了GraphicBuffer中, 后续的SF就会用到这个值

    IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
            static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
            mTransform ^ mStickyTransform, fence, mStickyTransform,
            mEnableFrameTimestamps);

这里是SF 判断了两个buffer 都符合条件可以用于合成,(但是显然,同一帧没有必要合成同一个窗口的两个buffer, 肯定需要丢掉一个)

所有在后面提交buffer的时候,释放了第一个buffer(在第二个setBuffer 中,释放了前一个buffer)

cpp 复制代码
//SurfaceFlinger.cpp

void SurfaceFlinger::flushTransactionQueues() {

     // Now apply all transactions.
        for (const auto& transaction : transactions) {
            applyTransactionState(transaction.frameTimelineInfo, transaction.states,
                                  transaction.displays, transaction.flags,
                                  transaction.inputWindowCommands, transaction.desiredPresentTime,
                                  transaction.isAutoTimestamp, transaction.buffer,
                                  transaction.postTime, transaction.permissions,
                                  transaction.hasListenerCallbacks, transaction.listenerCallbacks,
                                  transaction.originPid, transaction.originUid, transaction.id);
            if (transaction.transactionCommittedSignal) {
                mTransactionCommittedSignals.emplace_back(
                        std::move(transaction.transactionCommittedSignal));
            }
        }
    }
}

applyTransactionState();
setClientStateLocked();

bool BufferStateLayer::setBuffer


    
    if (mDrawingState.buffer) {
        mReleasePreviousBuffer = true;
        if (mDrawingState.buffer != mBufferInfo.mBuffer ||
            mDrawingState.frameNumber != mBufferInfo.mFrameNumber) {
            // If mDrawingState has a buffer, and we are about to update again
            // before swapping to drawing state, then the first buffer will be
            // dropped and we should decrement the pending buffer count and
            // call any release buffer callbacks if set.
            callReleaseBufferCallback(mDrawingState.releaseBufferListener,
                                      mDrawingState.buffer->getBuffer(), mDrawingState.frameNumber,
                                      mDrawingState.acquireFence, mTransformHint,
                                      mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(
                                              mOwnerUid));
            decrementPendingBufferCount();
            if (mDrawingState.bufferSurfaceFrameTX != nullptr &&
                mDrawingState.bufferSurfaceFrameTX->getPresentState() != PresentState::Presented) {
              addSurfaceFrameDroppedForBuffer(mDrawingState.bufferSurfaceFrameTX);
              mDrawingState.bufferSurfaceFrameTX.reset();
            }
        }
    }

第二个buffer的释放是在SF合成结束的时候(postComposition)

releasePendingBuffer 释放的不是 "当前帧(本次合成的帧)的 Buffer" ,而是该图层中 "已完成合成 / 呈现流程、不再被使用的旧 Buffer(通常是上一帧 / 更早的 Buffer)"

相关推荐
Jerry1 小时前
Compose 状态思维
android
k***45992 小时前
MySQL----case的用法
android·数据库·mysql
r***86983 小时前
Plugin ‘mysql_native_password‘ is not loaded`
android·数据库·mysql
v***59833 小时前
MySQL-mysql zip安装包配置教程
android·mysql·adb
不用89k4 小时前
Android无法区分USB摄像头是哪一个
android
ljt27249606615 小时前
Compose笔记(五十七)--snapshotFlow
android·笔记·android jetpack
花阴偷移5 小时前
kotlin语法(上)
android·java·开发语言·kotlin
Smart-佀5 小时前
Android初学必备:选Kotlin 还是Java ?
android·android studio·安卓
普通网友5 小时前
Android kotlin Jetpack mvvm 项目
android·开发语言·kotlin