帧同步依赖技术理解:安卓图形系统及Vsync 您所在的位置:网站首页 mhl荣耀6 帧同步依赖技术理解:安卓图形系统及Vsync

帧同步依赖技术理解:安卓图形系统及Vsync

#帧同步依赖技术理解:安卓图形系统及Vsync| 来源: 网络整理| 查看: 265

Android图形系统架构 QueueBuffer Display

Vulkan/GL -> BufferQueue -> SurfaceFlinger -> Display

https://source.android.com/docs/core/graphics?hl=zh-cn

https://source.android.com/static/docs/core/graphics/images/graphics-pipeline.png?hl=zh-cn

Android匿名共享内存(Ashmem)原理

https://www.jianshu.com/p/d9bc9c668ba6

通过Ashmem,APP进程同SurfaceFlinger共用一块内存,如此,就不需要进行数据拷贝,APP端绘制完毕,通知SurfaceFlinger端合成,再输出到硬件进行显示即可。

安卓窗口系统 Surface ANativeWindow GraphicBuffer关键对象// 设置Producer最多可以申请多少个Buffer,默认是3个; // 如果配置了属性ro.sf.disable_triple_buffer为true,那就只能用2个。 property_get("ro.sf.disable_triple_buffer", value, "1"); Buffer GraphicBuffer BufferQueue {createBufferQueue(c, p)} SurfaceFlinger SurfaceControl | \|/ Surface Layer{mTextureName} Window BufferQueue |继承 | |/ ANativeWindow {setSwapInterval queueBuffer dequeueBuffer} BufferLayer{genTextures, producer, consumer} BufferQueueCore BufferQueueProducer BufferQueueConsumer ColorLayer ANativeWindow ANativeWindowBuffer BufferQueue与多App

https://www.jianshu.com/p/f808813880b0

mSharedBufferMode是一种特殊的模式,是上层应用请求的,专门给特殊的应用使用的,主要是VR应用。因为VR应用要求低延时,BufferQueue采用的交换用的Buffer多了,延迟增加。为了降低延迟,设计了这个共享buffer的模式,Producer和Consumer共用一个Buffer。应用绘制完成后,直接给到Consumer进行显示。

Buffer SHARED状态是一个特殊的状态,SHARED的Buffer并不参与前面所说的状态迁移。它说明Buffer被用与共享Buffer模式。除了FREE状态,它可以是其他的任何状态。它可以被多次dequeued, queued, 或者 acquired。这中共享Buffer的模式,主要用于VR等低延迟要求的场合。

GraphicBuffer和Gralloc分析

https://www.jianshu.com/p/dd0b38832346

Buffer

Buffer getRenderEngine().setCurrentSurface(mSurface); setViewportAndProjection(); return success; }

创建了前面所说的FramebufferSurface,这就是FBTarget。

makeCurrent主要做了两件事:其一,设置RenderEngine的Surface,这个Surface封装了前面BufferQueue中创建的Producer,以及对应的NativeWindow;其二,设置Display的Viewport和Projection

Display 交换Buffer

https://www.jianshu.com/p/cf4455021fd5

void DisplayDevice::swapBuffers(HWComposer& hwc) const { if (hwc.hasClientComposition(mHwcDisplayId)) { mSurface.swapBuffers(); } status_t result = mDisplaySurface->advanceFrame(); } void Surface::swapBuffers() const { eglSwapBuffers(mEGLDisplay, mEGLSurface); } // eglSwapBuffers 将交换GPU处理的Buffer,处理完的Buffer, // 也就是包含Layer合成数据后的Buffer将被queue到BufferQueue中。 EGLBoolean egl_window_surface_v2_t::swapBuffers() { previousBuffer = buffer; nativeWindow->queueBuffer(nativeWindow, buffer, -1); buffer = 0; // dequeue a new buffer int fenceFd = -1; if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, &fenceFd) == NO_ERROR) { sp fence(new Fence(fenceFd)); if (fence->wait(Fence::TIMEOUT_NEVER)) { nativeWindow->cancelBuffer(nativeWindow, buffer, fenceFd); return setError(EGL_BAD_ALLOC, EGL_FALSE); } } else { return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE); } return EGL_TRUE; }

mDisplaySurface的advanceFrame方法,虚显用的VirtualDisplaySurface,非虚显用的FramebufferSurface。advanceFrame获取 FBTarget 的数据,我们看非虚显:

status_t FramebufferSurface::advanceFrame() { uint32_t slot = 0; sp buf; sp acquireFence(Fence::NO_FENCE); android_dataspace_t dataspace = HAL_DATASPACE_UNKNOWN; status_t result = nextBuffer(slot, buf, acquireFence, dataspace); mDataSpace = dataspace; return result; } status_t FramebufferSurface::nextBuffer(uint32_t& outSlot, sp& outBuffer, sp& outFence, android_dataspace_t& outDataspace) { Mutex::Autolock lock(mMutex); BufferItem item; status_t err = acquireBufferLocked(&item, 0); ... ... if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT && item.mSlot != mCurrentBufferSlot) { mHasPendingRelease = true; mPreviousBufferSlot = mCurrentBufferSlot; mPreviousBuffer = mCurrentBuffer; } mCurrentBufferSlot = item.mSlot; mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer; mCurrentFence = item.mFence; outFence = item.mFence; mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, &outSlot, &outBuffer); outDataspace = item.mDataSpace; status_t result = mHwc.setClientTarget(mDisplayType, outSlot, outFence, outBuffer, outDataspace); ... ... } status_t HWComposer::setClientTarget(int32_t displayId, uint32_t slot, const sp& acquireFence, const sp& target, android_dataspace_t dataspace) { ... ... auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; auto error = hwcDisplay->setClientTarget(slot, target, acquireFence, dataspace); return NO_ERROR; }

nextBuffer函数中:

获取一个Buffer 如果是Client合成,swapBuffer时,将调用queueBuffer,queue到FrameBufferSurface的BufferQueue中。这里的acquireBufferLocked 将从BufferQueue中获取一个Buffer。替换Buffer 当前Buffer的序号mCurrentBufferSlot,当前Buffer mCurrentBuffer,对应的Fence mCurrentFence;如果新获取到的Buffer不一样,释放旧的。Buffer都被cache到mHwcBufferCache中。将 FBTarget 设置给HWC 关键代码mHwc.setClientTarget。

FBTarget 也是通过Command Buffer的方式传到HWC中的。

提交Framebuffer

需要Client合成的,已经合成完了,合成后的结果FBTarget已传给HWC。需要Device合成的数据之前也提交给HWC了。但是数据还没有最终合成显示出来。postFramebuffer 函数就是告诉HWC开始做最后的合成了。

void SurfaceFlinger::postFramebuffer() { auto& displayDevice = mDisplays[displayId]; const auto hwcId = displayDevice->getHwcDisplayId(); getBE().mHwc->presentAndGetReleaseFences(hwcId); displayDevice->onSwapBuffersCompleted(); displayDevice->makeCurrent(); auto hwcLayer = layer->getHwcLayer(hwcId); sp releaseFence = getBE().mHwc->getLayerReleaseFence(hwcId, hwcLayer); layer->onLayerDisplayed(releaseFence); } status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) { auto error = mHwcDevice->flushCommands(); auto error = hwcDisplay->present(&displayData.lastPresentFence); error = hwcDisplay->getReleaseFences(&releaseFences); } Error Composer::presentDisplay(Display display, int* outPresentFence) { mWriter.selectDisplay(display); mWriter.presentDisplay(); Error error = execute(); mReader.takePresentFence(display, outPresentFence); return Error::NONE; }

present操纵也是先写到Command Buffer中。最后调的execute。

RenderEngineeglCreatePbufferSurface(display, dummyConfig, attribs);创建Surface FBTarget,各Display独自拥有

在RenderEngine创建时,初始化了EGLDisplaym,EGLConfig,EGLContext。这些都是所有Display共用的,但是Surface每个Display的是自己的。

在DisplayDevice创建时,创建对应的Surface

DisplayDevice::DisplayDevice( ... ... mSurface{flinger->getRenderEngine()}, ... ... { // clang-format on Surface* surface; mNativeWindow = surface = new Surface(producer, false); ANativeWindow* const window = mNativeWindow.get(); ... ... mSurface.setCritical(mType == DisplayDevice::DISPLAY_PRIMARY); mSurface.setAsync(mType >= DisplayDevice::DISPLAY_VIRTUAL); mSurface.setNativeWindow(window); mDisplayWidth = mSurface.queryWidth(); mDisplayHeight = mSurface.queryHeight(); ... ... if (useTripleFramebuffer) { surface->allocateBuffers(); } }eglCreateWindowSurface Surface和DisplayDevice的BufferQueue建立联系

注意mSurface.setNativeWindow,通过ANativeWindow,Surface就和DisplayDevice的BufferQueue建立了联系。

void Surface::setNativeWindow(ANativeWindow* window) { if (mEGLSurface != EGL_NO_SURFACE) { eglDestroySurface(mEGLDisplay, mEGLSurface); mEGLSurface = EGL_NO_SURFACE; } mWindow = window; if (mWindow) { mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mWindow, nullptr); } }

创建的EGLSurface mEGLSurface和nativewindow mWindow 关联。这个GPU就可以通过nativewindow,从BufferQueue中dequeue Buffer进行渲染,swapBuffer时,也queue到Bufferqueu中。这里的ANativeWindow,本质就是FBTarget。

Layer的合成

合成时,每个Display的每个Layer都合成到Display对应的Surface上。主要是在Layer的draw方法中完成。

GraphicBuffer GLeglImageOES eglCreateImageKHR() glEGLImageTargetTexture2DOES() //Layer数据就送到了GPU进行处理 BufferLayer::drawWithOpenGL() engine.drawMesh(getBE().mMesh); glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());Surface和窗口系统

https://www.jianshu.com/p/8e7a9a0b5726

在显示子系统中,Surface是一个很重要的类,通过Surface我们就能获取到Buffer,我们就能够和Android Native窗口系统建立连接。

Native应用建立

ANativeWindow

HWC2 概述

HWC2是 SurfaceFlinger 用来与专门的窗口合成硬件进行通信。

显示屏Display

Android支持多给屏幕,多个屏幕可以显示一样的内容,也可以显示不一样的内容。我们通常的把他们称为主显,外显和虚显。主显就是内置的,默认的手机屏幕;外显就是HDMI,MHL等连接的屏幕;虚显,就是虚拟显示器,通过WFD连接的屏幕,或者是类似屏幕一样的Buffer消费者。

系统可以具有多个显示设备,并且在正常系统操作期间可以添加或删除显示设备。该添加/删除可以应 HWC 设备的热插拔请求,或者应客户端的请求进行,这允许创建虚拟显示设备,其内容会渲染到离屏缓冲区(而不是物理显示设备)。

HWC中,SurfaceFlinger中创建的Layer,在合成开始时,将被指定到每个Display上,此后合成过程中,每个Display合成指定给自己的Layer。

SurfaceFlinger服务中,Display的描述,用的DisplayDevice。每添加一个显示屏,都会创建一个DisplayDevice。

DRM

https://www.jianshu.com/p/2d6754c631de

DRM原为Linux下的图形渲染架构(Direct Rendering Manager), 是device-independent内核级别驱动,内核提供直接访问硬件的权限, 原本是设计提供给PC使用来支持复杂的图形设备,后来也用于嵌入式系统上。而我们所属的的DRM,还包括KMS。

目前高通,HWInterface分为两种实现,一种直接基于FB驱动实现,另外一种也是目前在用的,基于DRM驱动架构实现。

DRM

https://mp.weixin.qq.com/s?__biz=Mzg2OTc0ODAzMw==&mid=2247510491&idx=1&sn=b39a784c1457507e57aeb6da67fd9229

传统linux显示设备驱动开发时,通常使用FB驱动架构,随着显卡性能升级:显示覆盖(菜单层级)、GPU加速、硬件光标,传统FB架构无法很好支持,此外,对于多应用的访问冲突也无法很好控制。在这样的背景下,DRM应用而生。

DRM是linux内核中负责与显卡交互的管理架构,用户空间很方便的利用DRM提供的API,实现3D渲染、视频解码和GPU计算等工作。

安卓显示屏Display

https://www.jianshu.com/p/824a9ddf68b9

显示屏Display是合成的另一个重要单元。系统可以具有多个显示设备,并且在正常系统操作期间可以添加或删除显示设备。

FrameBuffer驱动,通过initWithFb初始化

HWC驱动,通过initWithHwc初始化

Vsync 基本概念Vsync UI Thread RenderThread流程

https://www.androidperformance.com/2019/11/06/Android-Systrace-MainThread-And-RenderThread/#/%E6%AD%A3%E6%96%87

主线程处于 Sleep 状态,等待 Vsync 信号

Vsync 信号到来,主线程被唤醒,Choreographer 回调 FrameDisplayEventReceiver.onVsync 开始一帧的绘制处理 App 这一帧的 Input 事件(如果有的话)处理 App 这一帧的 Animation 事件(如果有的话)处理 App 这一帧的 Traversal 事件(如果有的话)主线程与渲染线程同步渲染数据,同步结束后,主线程结束一帧的绘制,可以继续处理下一个 Message(如果有的话,IdleHandler 如果不为空,这时候也会触发处理),或者进入 Sleep 状态等待下一个 Vsync渲染线程首先需要从 BufferQueue 里面取一个 Buffer(dequeueBuffer) , 进行数据处理之后,调用 OpenGL 相关的函数,真正地进行渲染操作,然后将这个渲染好的 Buffer 还给 BufferQueue (queueBuffer) , SurfaceFlinger 在 Vsync-SF 到了之后,将所有准备好的 Buffer 取出进行合成(这个流程在讲 SurfaceFlinger 的时候会提到)王者荣耀 surfaceview直接与surfaceflinger交互

swapbuffer到BufferQueue,SF接受Vsync信号,从BufferQueue取Buffer合成,然后呈现。

https://www.androidperformance.com/2019/11/06/Android-Systrace-MainThread-And-RenderThread/#/%E6%B8%B8%E6%88%8F%E7%9A%84%E4%B8%BB%E7%BA%BF%E7%A8%8B%E4%B8%8E%E6%B8%B2%E6%9F%93%E7%BA%BF%E7%A8%8B

垂直同步

https://blog.csdn.net/hfy8971613/article/details/108041504

VBI Vertical Blanking Interval 扫描完一个屏幕后,设备需要重新回到第一行,以进入下一次循环,中间的时间空隙,称之为VBI。

VSync 垂直同步 Vertical Synchronization,利用VBI期间出现的vertical sync pulse(垂直同步脉冲)来保证双缓冲在最佳时间点才进行交换。

//添加同步屏障,屏蔽同步消息,保证VSync到来立即执行绘制 mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //移除同步屏障 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); // VSync事件接收器mDisplayEventReceiver // USE_VSYNC 4.1以上默认是true,表示 具备接受VSync的能力,这个接受能力就是FrameDisplayEventReceiver mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource): null; // 申请和接受VSync private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); } // 申请VSYNC中断信号,会回调onVsync方法 nativeScheduleVsync(mReceiverPtr); // 接收到VSync脉冲时 回调 void onVsync(long timestampNanos, long physicalDisplayId, int frame) { } void doFrame(long frameTimeNanos, int frame) { // 计算掉帧数 }Android Vsync详解

https://androidperformance.com/2019/12/01/Android-Systrace-Vsync/#/Vsync-Offset

目前大部分厂商都没有配置这个 Offset,所以 App 和 SurfaceFlinger 是同时收到 Vsync 信号的.

如果 App 的 Buffer 渲染结束,Swap 到 BufferQueue 中 ,就触发 SurfaceFlinger 去做合成,那岂不是省了一些时间(0-16.6ms )?

答案是可行的,这也就引入了 Offset 机制,在这种情况下,App 先收到 Vsync 信号,进行一帧的渲染工作,然后过了 Offset 时间后,SurfaceFlinger 才收到 Vsync 信号开始合成,这时候如果 App 的 Buffer 已经 Ready 了,那么 SurfaceFlinger 这一次合成就可以包含 App 这一帧,用户也会早一点看到。

HW_Vsync

这里需要说明的是,不是每次申请 Vsync 都会由硬件产生 Vsync,只有此次请求 vsync 的时间距离上次合成时间大于 500ms,才会通知 hwc,请求 HW_VSYNC。DispSync 拿到 6 个 VSYNC 后就会计算出 SW_VSYNC,只要收到的 Present Fence 没有超过误差,硬件 VSYNC 就会关掉,不然会继续接收硬件 VSYNC 计算 SW_VSYNC 的值,直到误差小于 threshold.

Android垂直同步信号

https://blog.csdn.net/houliang120/article/details/50908098

https://www.cnblogs.com/blogs-of-lxl/p/11443693.html

捕捉垂直同步信号

// HWComposer(产生VSync)--->SurfaceFlinger.onVSyncReceived // --->DispSync.updateModelLocked-->DispSyncThread.updateModel // --->回调所有注册的监听器的onDispSyncEvent函数 mHwc = new HWComposer(this,*static_cast(this)); // HWComposer.cpp cb_context* mCBContext = new cb_context(); mCBContext->procs.vsync = &hook_vsync; //硬件产生中断,通过hook_vsync函数进行同步信号的通知 // don't need a vsync thread if we have a hardware composer 存在硬件同步信号发生器,就不需要软件模拟线程 needVSyncThread = false; if (needVSyncThread) { //设备不支持硬件HWC,用软件模拟同步信号,这里不讨论,整个的流程还是一样的 // we don't have VSYNC support, we need to fake it mVSyncThread = new VSyncThread(*this); } void HWComposer::vsync(int disp, int64_t timestamp) { //HWC以及VSyncThread软件模拟,最终都是通过mEventHandler.onVSyncReceived函数将同步信号发出去的 mEventHandler.onVSyncReceived(disp, timestamp); }Android Vsync 信号机制和 UI 刷新流程

https://www.51cto.com/article/694573.html

当我们通过setText改变TextView内容后,UI界面不会立刻改变,APP端会先向VSYNC服务请求,等到下一次VSYNC信号触发后,APP端的UI才真的开始刷新。

ViewRootImp {

3 scheduleTraversal 准备重绘,插入同步栅栏

5 收到消息,删除同步栅栏,重绘UI

}

OpenGL同步化

https://www.khronos.org/opengl/wiki/Synchronization

命令状态(Command state)

渲染命令状态:未发布、已发布但未完成和完成。

应用程序 -> 驱动程序(内部命令缓冲区) -> GPU硬件(GPU command queue)

如果已将命令提供给OpenGL驱动程序,但驱动程序尚未将命令提供给硬件实际执行,则该命令未发出。

显式同步(Explicit synchronization)

OpenGL 提供了两种简单的显式同步机制:glFinish和glFlush。

glFinish: 它不会返回,停止当前 CPU 线程,直到所有已发送的渲染命令都已完成。

glFlush: 告诉 OpenGL 坐在那里等待,暂停当前的 CPU 线程,直到所有命令都已添加到 GPU 的命令队列中。这不会像glFinish一样长,但它仍然很耗时。

同步对象(Sync Object)

https://www.khronos.org/opengl/wiki/Sync_Object

glClientWaitSync 阻止CPU

glWaitSysn 不阻止CPU

GLsync glFenceSync(GLenum condition, GLbitfield flags) // 创建一个栅栏,且将它添加到命令流中



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有