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)"

相关推荐
lxysbly2 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首2 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节
消失的旧时光-19435 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed
Jinkxs5 小时前
Gradle - 与Groovy/Kotlin DSL对比 构建脚本语言选择指南
android·开发语言·kotlin
&有梦想的咸鱼&5 小时前
Kotlin委托机制的底层实现深度解析(74)
android·开发语言·kotlin
LDORntKQH5 小时前
基于深度强化学习的混合动力汽车能量管理策略 1.利用DQN算法控制电池和发动机发电机组的功率分配 2
android
冬奇Lab5 小时前
Android 15 ServiceManager与Binder服务注册深度解析
android·源码·源码阅读
2501_916008897 小时前
深入解析iOS机审4.3原理与混淆实战方法
android·java·开发语言·ios·小程序·uni-app·iphone
独行soc8 小时前
2026年渗透测试面试题总结-20(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
常利兵8 小时前
2026年,Android开发已死?不,它正迎来黄金时代!
android