您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)Android硬件加速的原理是什么,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
前言
硬件加速,直觀上說就是依賴 GPU 實(shí)現(xiàn)圖形繪制加速,同軟硬件加速的區(qū)別主要是圖形的繪制究竟是 GPU 來處理還是 CPU,如果是GPU,就認(rèn)為是硬件加速繪制,反之,軟件繪制。在 Android 中也是如此,不過相對(duì)于普通的軟件繪制,硬件加速還做了其他方面優(yōu)化,不僅僅限定在繪制方面,繪制之前,在如何構(gòu)建繪制區(qū)域上,硬件加速也做出了很大優(yōu)化,因此硬件加速特性可以從下面兩部分來分析:
1、前期策略:如何構(gòu)建需要繪制的區(qū)域
2、后期繪制:?jiǎn)为?dú)渲染線程,依賴 GPU 進(jìn)行繪制
無論是軟件繪制還是硬件加速,繪制內(nèi)存的分配都是類似的,都是需要請(qǐng)求 SurfaceFlinger 服務(wù)分配一塊內(nèi)存,只不過硬件加速有可能從FrameBuffer 硬件緩沖區(qū)直接分配內(nèi)存(SurfaceFlinger 一直這么干的),兩者的繪制都是在APP端,繪制完成之后同樣需要通知SurfaceFlinger 進(jìn)行合成,在這個(gè)流程上沒有任何區(qū)別,真正的區(qū)別在于在 APP 端如何完成UI數(shù)據(jù)繪制,本文就直觀的了解下兩者的區(qū)別,會(huì)涉及部分源碼,但不求甚解。
軟硬件加速的分歧點(diǎn)
大概從 Android 4.+開始,默認(rèn)情況下都是支持跟開啟了硬件加速的,也存在手機(jī)支持硬件加速,但是部分API不支持硬件加速的情況,如果使用了這些API,就需要主關(guān)閉硬件加速,或者在 View 層,或者在Activity 層,比如 Canvas 的 clipPath等。但是,View 的繪制是軟件加速實(shí)現(xiàn)的還是硬件加速實(shí)現(xiàn)的,一般在開發(fā)的時(shí)候并不可見,那圖形繪制的時(shí)候,軟硬件的分歧點(diǎn)究竟在哪呢?舉個(gè)例子,有個(gè) View 需要重繪,一般會(huì)調(diào)用 View 的 invalidate,觸發(fā)重繪,跟著這條線走,去查一下分歧點(diǎn)。
從上面的調(diào)用流程可以看出,視圖重繪最后會(huì)進(jìn)入ViewRootImpl的draw,這里有個(gè)判斷點(diǎn)是軟硬件加速的分歧點(diǎn),簡(jiǎn)化后如下
ViewRootImpl.java
private void draw(boolean fullRedrawNeeded) { ... if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { <!--關(guān)鍵點(diǎn)1 是否開啟硬件加速--> if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { ... dirty.setEmpty(); mBlockResizeBuffer = false; <!--關(guān)鍵點(diǎn)2 硬件加速繪制--> mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this); } else { ... <!--關(guān)鍵點(diǎn)3 軟件繪制--> if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) { return; } ...
關(guān)鍵點(diǎn)1是啟用硬件加速的條件,必須支持硬件并且開啟了硬件加速才可以,滿足,就利用 HardwareRenderer.draw,否則 drawSoftware(軟件繪制)。簡(jiǎn)答看一下這個(gè)條件,默認(rèn)情況下,該條件是成立的,因?yàn)?.+之后的手機(jī)一般都支持硬件加速,而且在添加窗口的時(shí)候,ViewRootImpl 會(huì) enableHardwareAcceleration 開啟硬件加速,new HardwareRenderer,并初始化硬件加速環(huán)境。
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) { <!--根據(jù)配置,獲取硬件加速的開關(guān)--> // Try to enable hardware acceleration if requested final boolean hardwareAccelerated = (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; if (hardwareAccelerated) { ... <!--新建硬件加速圖形渲染器--> mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent); if (mAttachInfo.mHardwareRenderer != null) { mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString()); mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; } ...
其實(shí)到這里軟件繪制跟硬件加速的分歧點(diǎn)已經(jīng)找到了,就是ViewRootImpl在draw 的時(shí)候,如果需要硬件加速就利用 HardwareRenderer 進(jìn)行 draw,否則走軟件繪制流程,drawSoftware其實(shí)很簡(jiǎn)單,利用 Surface.lockCanvas,向 SurfaceFlinger 申請(qǐng)一塊匿名共享內(nèi)存內(nèi)存分配,同時(shí)獲取一個(gè)普通的 SkiaCanvas,用于調(diào)用Skia 庫,進(jìn)行圖形繪制,
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty) { final Canvas canvas; try { <!--關(guān)鍵點(diǎn)1 --> canvas = mSurface.lockCanvas(dirty); .. <!--關(guān)鍵點(diǎn)2 繪制--> mView.draw(canvas); .. 關(guān)鍵點(diǎn)3 通知SurfaceFlinger進(jìn)行圖層合成 surface.unlockCanvasAndPost(canvas); } ... return true; }
drawSoftware 工作完全由 CPU 來完成,不會(huì)牽扯到 GPU 的操作,下面重點(diǎn)看下 HardwareRenderer 所進(jìn)行的硬件加速繪制。
HardwareRenderer 硬件加速繪制模型
開頭說過,硬件加速繪制包括兩個(gè)階段:構(gòu)建階段+繪制階段,所謂構(gòu)建就是遞歸遍歷所有視圖,將需要的操作緩存下來,之后再交給單獨(dú)的Render 線程利用 OpenGL 渲染。在 Android 硬件加速框架中,View視圖被抽象成 RenderNode 節(jié)點(diǎn),View 中的繪制都會(huì)被抽象成一個(gè)個(gè)DrawOp(DisplayListOp),比如 View 中 drawLine,構(gòu)建中就會(huì)被抽象成一個(gè) DrawLintOp,drawBitmap 操作會(huì)被抽象成DrawBitmapOp,每個(gè)子 View 的繪制被抽象成DrawRenderNodeOp,每個(gè) DrawOp 有對(duì)應(yīng)的 OpenGL 繪制命令,同時(shí)內(nèi)部也握著繪圖所需要的數(shù)據(jù)。如下所示:
繪圖Op抽象
如此以來,每個(gè) View 不僅僅握有自己 DrawOp List,同時(shí)還拿著子View的繪制入口,如此遞歸,便能夠統(tǒng)計(jì)到所有的繪制Op,很多分析都稱為 Display List,源碼中也是這么來命名類的,不過這里其實(shí)更像是一個(gè)樹,而不僅僅是List,示意如下:
硬件加速.jpg
構(gòu)建完成后,就可以將這個(gè)繪圖Op樹交給Render線程進(jìn)行繪制,這里是同軟件繪制很不同的地方,軟件繪制時(shí),View一般都在主線程中完成繪制,而硬件加速,除非特殊要求,一般都是在單獨(dú)線程中完成繪制,如此以來就分擔(dān)了主線程很多壓力,提高了UI線程的響應(yīng)速度。
硬件加速模型.jpg
知道整個(gè)模型后,就代碼來簡(jiǎn)單了解下實(shí)現(xiàn)流程,先看下遞歸構(gòu)建RenderNode 樹及 DrawOp 集。
利用HardwareRenderer構(gòu)建DrawOp集
HardwareRenderer 是整個(gè)硬件加速繪制的入口,實(shí)現(xiàn)是一個(gè)ThreadedRenderer 對(duì)象,從名字能看出,ThreadedRenderer應(yīng)該跟一個(gè)Render線程息息相關(guān),不過ThreadedRenderer是在UI線程中創(chuàng)建的,那么與UI線程也必定相關(guān),其主要作用:
1、在UI線程中完成DrawOp集構(gòu)建
2、負(fù)責(zé)跟渲染線程通信
可見ThreadedRenderer的作用是很重要的,簡(jiǎn)單看一下實(shí)現(xiàn):
ThreadedRenderer(Context context, boolean translucent) { ... <!--新建native node--> long rootNodePtr = nCreateRootRenderNode(); mRootNode = RenderNode.adopt(rootNodePtr); mRootNode.setClipToBounds(false); <!--新建NativeProxy--> mNativeProxy = nCreateProxy(translucent, rootNodePtr); ProcessInitializer.sInstance.init(context, mNativeProxy); loadSystemProperties(); }
從上面代碼看出,ThreadedRenderer中有一個(gè)RootNode用來標(biāo)識(shí)整個(gè)DrawOp樹的根節(jié)點(diǎn),有個(gè)這個(gè)根節(jié)點(diǎn)就可以訪問所有的繪制Op,同時(shí)還有個(gè)RenderProxy對(duì)象,這個(gè)對(duì)象就是用來跟渲染線程進(jìn)行通信的句柄,看一下其構(gòu)造函數(shù):
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) : mRenderThread(RenderThread::getInstance()) , mContext(nullptr) { SETUP_TASK(createContext); args->translucent = translucent; args->rootRenderNode = rootRenderNode; args->thread = &mRenderThread; args->contextFactory = contextFactory; mContext = (CanvasContext*) postAndWait(task); mDrawFrameTask.setContext(&mRenderThread, mContext); }
從RenderThread::getInstance()可以看出,RenderThread是一個(gè)單例線程,也就是說,每個(gè)進(jìn)程最多只有一個(gè)硬件渲染線程,這樣就不會(huì)存在多線程并發(fā)訪問沖突問題,到這里其實(shí)環(huán)境硬件渲染環(huán)境已經(jīng)搭建好好了。下面就接著看ThreadedRenderer的draw函數(shù),如何構(gòu)建渲染Op樹:
@Override void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) { attachInfo.mIgnoreDirtyState = true; final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer; choreographer.mFrameInfo.markDrawStart(); <!--關(guān)鍵點(diǎn)1:構(gòu)建View的DrawOp樹--> updateRootDisplayList(view, callbacks); <!--關(guān)鍵點(diǎn)2:通知RenderThread線程繪制--> int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length); ... }
只關(guān)心關(guān)鍵點(diǎn)1 updateRootDisplayList,構(gòu)建RootDisplayList,其實(shí)就是構(gòu)建View的DrawOp樹,updateRootDisplayList會(huì)進(jìn)而調(diào)用根View的updateDisplayListIfDirty,讓其遞歸子View的updateDisplayListIfDirty,從而完成DrawOp樹的創(chuàng)建,簡(jiǎn)述一下流程:
private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) { <!--更新--> updateViewTreeDisplayList(view); if (mRootNodeNeedsUpdate || !mRootNode.isValid()) { <!--獲取DisplayListCanvas--> DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight); try { <!--利用canvas緩存Op--> final int saveCount = canvas.save(); canvas.translate(mInsetLeft, mInsetTop); callbacks.onHardwarePreDraw(canvas); canvas.insertReorderBarrier(); canvas.drawRenderNode(view.updateDisplayListIfDirty()); canvas.insertInorderBarrier(); callbacks.onHardwarePostDraw(canvas); canvas.restoreToCount(saveCount); mRootNodeNeedsUpdate = false; } finally { <!--將所有Op填充到RootRenderNode--> mRootNode.end(canvas); } } }
利用View的RenderNode獲取一個(gè)DisplayListCanvas
利用DisplayListCanvas構(gòu)建并緩存所有的DrawOp
將DisplayListCanvas緩存的DrawOp填充到RenderNode
將根View的緩存DrawOp設(shè)置到RootRenderNode中,完成構(gòu)建
繪制流程
簡(jiǎn)單看一下View遞歸構(gòu)建DrawOp,并將自己填充到
@NonNull public RenderNode updateDisplayListIfDirty() { final RenderNode renderNode = mRenderNode; ... // start 獲取一個(gè) DisplayListCanvas 用于繪制 硬件加速 final DisplayListCanvas canvas = renderNode.start(width, height); try { // 是否是textureView final HardwareLayer layer = getHardwareLayer(); if (layer != null && layer.isValid()) { canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint); } else if (layerType == LAYER_TYPE_SOFTWARE) { // 是否強(qiáng)制軟件繪制 buildDrawingCache(true); Bitmap cache = getDrawingCache(true); if (cache != null) { canvas.drawBitmap(cache, 0, 0, mLayerPaint); } } else { // 如果僅僅是ViewGroup,并且自身不用繪制,直接遞歸子View if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { dispatchDraw(canvas); } else { <!--調(diào)用自己draw,如果是ViewGroup會(huì)遞歸子View--> draw(canvas); } } } finally { <!--緩存構(gòu)建Op--> renderNode.end(canvas); setDisplayListProperties(renderNode); } } return renderNode; }
TextureView跟強(qiáng)制軟件繪制的View比較特殊,有額外的處理,這里不關(guān)心,直接看普通的draw,假如在View onDraw中,有個(gè)drawLine,這里就會(huì)調(diào)用DisplayListCanvas的drawLine函數(shù),DisplayListCanvas及RenderNode類圖大概如下
硬件加速類圖
DisplayListCanvas的drawLine 函數(shù)最終會(huì)進(jìn)入DisplayListCanvas.cpp的drawLine,
void DisplayListCanvas::drawLines(const float* points, int count, const SkPaint& paint) { points = refBuffer<float>(points, count); addDrawOp(new (alloc()) DrawLinesOp(points, count, refPaint(&paint))); }
可以看到,這里構(gòu)建了一個(gè)DrawLinesOp,并添加到DisplayListCanvas的緩存列表中去,如此遞歸便可以完成DrawOp樹的構(gòu)建,在構(gòu)建后利用RenderNode的end函數(shù),將DisplayListCanvas中的數(shù)據(jù)緩存到RenderNode中去:
public void end(DisplayListCanvas canvas) { canvas.onPostDraw(); long renderNodeData = canvas.finishRecording(); <!--將DrawOp緩存到RenderNode中去--> nSetDisplayListData(mNativeRenderNode, renderNodeData); // canvas 回收掉] canvas.recycle(); mValid = true; }
如此,便完成了DrawOp樹的構(gòu)建,之后,利用RenderProxy向RenderThread發(fā)送消息,請(qǐng)求OpenGL線程進(jìn)行渲染。
RenderThread渲染UI到Graphic Buffer
DrawOp樹構(gòu)建完畢后,UI線程利用RenderProxy向RenderThread線程發(fā)送一個(gè)DrawFrameTask任務(wù)請(qǐng)求,RenderThread被喚醒,開始渲染,大致流程如下:
首先進(jìn)行DrawOp的合并
接著繪制特殊的Layer
最后繪制其余所有的DrawOpList
調(diào)用swapBuffers將前面已經(jīng)繪制好的圖形緩沖區(qū)提交給Surface Flinger合成和顯示。
不過再這之前先復(fù)習(xí)一下繪制內(nèi)存的由來,畢竟之前DrawOp樹的構(gòu)建只是在普通的用戶內(nèi)存中,而部分?jǐn)?shù)據(jù)對(duì)于SurfaceFlinger都是不可見的,之后又繪制到共享內(nèi)存中的數(shù)據(jù)才會(huì)被SurfaceFlinger合成,之前分析過軟件繪制的UI是來自匿名共享內(nèi)存,那么硬件加速的共享內(nèi)存來自何處呢?到這里可能要倒回去看看 ViewRootImlp
private void performTraversals() { ... if (mAttachInfo.mHardwareRenderer != null) { try { hwInitialized = mAttachInfo.mHardwareRenderer.initialize( mSurface); if (hwInitialized && (host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) { mSurface.allocateBuffers(); } } catch (OutOfResourcesException e) { handleOutOfResourcesException(e); return; } } .... /** * Allocate buffers ahead of time to avoid allocation delays during rendering * @hide */ public void allocateBuffers() { synchronized (mLock) { checkNotReleasedLocked(); nativeAllocateBuffers(mNativeObject); } }
可以看出,對(duì)于硬件加速的場(chǎng)景,內(nèi)存分配的時(shí)機(jī)會(huì)稍微提前,而不是像軟件繪制事,由Surface的lockCanvas發(fā)起,主要目的是:避免在渲染的時(shí)候再申請(qǐng),一是避免分配失敗,浪費(fèi)了CPU之前的準(zhǔn)備工作,二是也可以將渲染線程個(gè)工作簡(jiǎn)化,在分析Android窗口管理分析(4):Android View繪制內(nèi)存的分配、傳遞、使用的時(shí)候分析過,在分配成功后,如果有必要,會(huì)進(jìn)行一次UI數(shù)據(jù)拷貝,這是局部繪制的根基,也是保證DrawOp可以部分執(zhí)行的基礎(chǔ),到這里內(nèi)存也分配完畢。不過,還是會(huì)存在另一個(gè)問題,一個(gè)APP進(jìn)程,同一時(shí)刻會(huì)有過個(gè)Surface繪圖界面,但是渲染線程只有一個(gè),那么究竟渲染那個(gè)呢?這個(gè)時(shí)候就需要將Surface與渲染線程(上下文)綁定。
static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject jsurface) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); sp<ANativeWindow> window = android_view_Surface_getNativeWindow(env, jsurface); return proxy->initialize(window); }
首先通過android_view_Surface_getNativeWindowSurface獲取Surface,在Native層,Surface對(duì)應(yīng)一個(gè)ANativeWindow,接著,通過RenderProxy類的成員函數(shù)initialize將前面獲得的ANativeWindow綁定到RenderThread
bool RenderProxy::initialize(const sp<ANativeWindow>& window) { SETUP_TASK(initialize); args->context = mContext; args->window = window.get(); return (bool) postAndWait(task); }
仍舊是向渲染線程發(fā)送消息,讓其綁定當(dāng)前Window,其實(shí)就是調(diào)用CanvasContext的initialize函數(shù),讓繪圖上下文綁定繪圖內(nèi)存:
bool CanvasContext::initialize(ANativeWindow* window) { setSurface(window); if (mCanvas) return false; mCanvas = new OpenGLRenderer(mRenderThread.renderState()); mCanvas->initProperties(); return true; }
CanvasContext通過setSurface將當(dāng)前要渲染的Surface綁定到到RenderThread中,大概流程是通過eglApi獲得一個(gè)EGLSurface,EGLSurface封裝了一個(gè)繪圖表面,進(jìn)而,通過eglApi將EGLSurface設(shè)定為當(dāng)前渲染窗口,并將繪圖內(nèi)存等信息進(jìn)行同步,之后通過RenderThread繪制的時(shí)候才能知道是在哪個(gè)窗口上進(jìn)行繪制。這里主要是跟OpenGL庫對(duì)接,所有的操作最終都會(huì)歸結(jié)到eglApi抽象接口中去。假如,這里不是Android,是普通的Java平臺(tái),同樣需要相似的操作,進(jìn)行封裝處理,并綁定當(dāng)前EGLSurface才能進(jìn)行渲染,因?yàn)镺penGL是一套規(guī)范,想要使用,就必須按照這套規(guī)范走。之后,再創(chuàng)建一個(gè)OpenGLRenderer對(duì)象,后面執(zhí)行OpenGL相關(guān)操作的時(shí)候,其實(shí)就是通過OpenGLRenderer來進(jìn)行的。
綁定流程
上面的流程走完,有序DrawOp樹已經(jīng)構(gòu)建好、內(nèi)存也已分配好、環(huán)境及場(chǎng)景也綁定成功,剩下的就是繪制了,不過之前說過,真正調(diào)用OpenGL繪制之前還有一些合并操作,這是Android硬件加速做的優(yōu)化,回過頭繼續(xù)走draw流程,其實(shí)就是走OpenGLRenderer的drawRenderNode進(jìn)行遞歸處理:
void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) { ... <!--構(gòu)建deferredList--> DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw); DeferStateStruct deferStruct(deferredList, *this, replayFlags); <!--合并及分組--> renderNode->defer(deferStruct, 0); <!--繪制layer--> flushLayers(); startFrame(); <!--繪制 DrawOp樹--> deferredList.flush(*this, dirty); ... }
硬件加速渲染流程
先看下renderNode->defer(deferStruct, 0),合并操作,DrawOp樹并不是直接被繪制的,而是首先通過DeferredDisplayList進(jìn)行一個(gè)合并優(yōu)化,這個(gè)是Android硬件加速中采用的一種優(yōu)化手段,不僅可以減少不必要的繪制,還可以將相似的繪制集中處理,提高繪制速度。
void RenderNode::defer(DeferStateStruct& deferStruct, const int level) { DeferOperationHandler handler(deferStruct, level); issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); }
RenderNode::defer其實(shí)內(nèi)含遞歸操作,比如,如果當(dāng)前RenderNode代表DecorView,它就會(huì)遞歸所有的子View進(jìn)行合并優(yōu)化處理,簡(jiǎn)述一下合并及優(yōu)化的流程及算法,其實(shí)主要就是根據(jù)DrawOp樹構(gòu)建DeferedDisplayList,defer本來就有延遲的意思,對(duì)于DrawOp的合并有兩個(gè)必要條件,
1:兩個(gè)DrawOp的類型必須相同,這個(gè)類型在合并的時(shí)候被抽象為Batch ID,取值主要有以下幾種
enum OpBatchId { kOpBatch_None = 0, // Don't batch kOpBatch_Bitmap, kOpBatch_Patch, kOpBatch_AlphaVertices, kOpBatch_Vertices, kOpBatch_AlphaMaskTexture, kOpBatch_Text, kOpBatch_ColorText, kOpBatch_Count, // Add other batch ids before this };
2:DrawOp的Merge ID必須相同,Merge ID沒有太多限制,由每個(gè)DrawOp自定決定,不過好像只有DrawPatchOp、DrawBitmapOp、DrawTextOp比較特殊,其余的似乎不需要考慮合并問題,即時(shí)是以上三種,合并的條件也很苛刻
在合并過程中,DrawOp被分為兩種:需要合的與不需要合并的,并分別緩存在不同的列表中,無法合并的按照類型分別存放在Batch* mBatchLookup[kOpBatch_Count]中,可以合并的按照類型及MergeID存儲(chǔ)到TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count]中,示意圖如下:
DrawOp合并操作.jpg
合并之后,DeferredDisplayList Vector<Batch> mBatches包含全部整合后的繪制命令,之后渲染即可,需要注意的是這里的合并并不是多個(gè)變一個(gè),只是做了一個(gè)集合,主要是方便使用各資源紋理等,比如繪制文字的時(shí)候,需要根據(jù)文字的紋理進(jìn)行渲染,而這個(gè)時(shí)候就需要查詢文字的紋理坐標(biāo)系,合并到一起方便統(tǒng)一處理,一次渲染,減少資源加載的浪費(fèi),當(dāng)然對(duì)于理解硬件加速的整體流程,這個(gè)合并操作可以完全無視,甚至可以直觀認(rèn)為,構(gòu)建完之后,就可以直接渲染,它的主要特點(diǎn)是在另一個(gè)Render線程使用OpenGL進(jìn)行繪制,這個(gè)是它最重要的特點(diǎn)*。而mBatches中所有的DrawOp都會(huì)通過OpenGL被繪制到GraphicBuffer中,最后通過swapBuffers通知
以上就是Android硬件加速的原理是什么,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。