欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android?drawFunctor?原理及應(yīng)用詳情

 更新時間:2022年08月12日 08:57:34   作者:支付寶體驗科技  
這篇文章主要介紹了Android?drawFunctor原理及應(yīng)用詳情,drawFunctor是Android提供的一種在RenderThread渲染流程中插入執(zhí)行代碼機制,更多相關(guān)內(nèi)容需要的小伙伴可以參考一下

一. 背景

螞蟻 NativeCanvas 項目 Android 平臺中使用了基于 TextureView 環(huán)境實現(xiàn) GL 渲染的技術(shù)方案,而 TextureView 需使用與 Activity Window 獨立的 GraphicBuffer,RenderThread 在上屏 TextureView 內(nèi)容時需要將 GraphicBuffer 封裝為 EGLImage 上傳為紋理再渲染,內(nèi)存占用較高。為降低內(nèi)存占用,經(jīng)仔細調(diào)研 Android 源碼,發(fā)現(xiàn)其中存在一種稱為 drawFunctor 的技術(shù),用來將 WebView 合成后的內(nèi)容同步到 Activity Window 內(nèi)上屏。經(jīng)過一番探索成功實現(xiàn)了基于 drawFunctor 實現(xiàn) GL 注入 RenderThread 的功能,本文將介紹這是如何實現(xiàn)的。

二. drawFunctor 原理介紹

drawFunctor 是 Android 提供的一種在 RenderThread 渲染流程中插入執(zhí)行代碼機制,Android 框架是通過以下三步來實現(xiàn)這個機制的:

  • 在 UI 線程 View 繪制流程 onDraw 方法中,通過 RecordingCanvas.invoke 接口,將 functor 插入 DisplayList 中
  • 在 RenderThread 渲染 frame 時執(zhí)行 DisplayList,判斷如果是 functor 類型的 op,則保存當(dāng)前部分 gl 狀態(tài)
  • 在 RenderThread 中真正執(zhí)行 functor 邏輯,執(zhí)行完成后恢復(fù) gl 狀態(tài)并繼續(xù)

目前只能通過 View.OnDraw 來注入 functor,因此對于非 attached 的 view 是無法實現(xiàn)注入的。Functor 對具體要執(zhí)行的代碼并未限制,理論上可以插入任何代碼的,比如插入一些統(tǒng)計、性能檢測之類代碼。系統(tǒng)為了 functor 不影響當(dāng)前 gl context,執(zhí)行 functor 前后進行了基本的狀態(tài)保存和恢復(fù)工作。

另外,如果 View 設(shè)置了使用 HardwareLayer, 則 RenderThread 會單獨渲染此 View,具體做法是為 Layer 生成一塊 FBO,View 的內(nèi)容渲染到此 FBO 上,然后再將 FBO 以 View 在 hierachy 上的變換繪制 Activity Window Buffer 上。 對 drawFunctor 影響的是, 會切換到 View 對應(yīng)的 FBO 下執(zhí)行 functor, 即 functor 執(zhí)行的結(jié)果是寫入到 FBO 而不是 Window Buffer。

三. 利用 drawFunctor 注入 GL 渲染

根據(jù)上文介紹,通過 drawFunctor 可以在 RenderThread 中注入任何代碼,那么也一定可以注入 OpenGL API 來進行渲染。我們知道 OpenGL API 需要執(zhí)行 EGL Context 上,所以就有兩種策略:一種是利用 RenderThread 默認的 EGL Context 環(huán)境,一種是創(chuàng)建與 RenderThread EGL Context share 的 EGL Context。本文重點介紹第一種,第二種方法大同小異。

Android Functor 定義

首先找到 Android 源碼中 Functor 的頭文件定義并引入項目:

namespace android {
    class Functor {
    public:
    Functor() {}
    virtual ~Functor() {}
    virtual int operator()(int /*what*/, void * /*data*/) { return 0; }
    };
}

RenderThread 執(zhí)行 Functor 時將調(diào)用 operator()方法,what 表示 functor 的操作類型,常見的有同步和繪制, 而 data 是 RenderThread 執(zhí)行 functor 時傳入的參數(shù),根據(jù)源碼發(fā)現(xiàn)是 data 是 android::uirenderer::DrawGlInfo 類型指針,包含當(dāng)前裁剪區(qū)域、變換矩陣、dirty 區(qū)域等等。

DrawGlInfo 頭文件定義如下:

namespace android {
namespace uirenderer {
/**
* Structure used by OpenGLRenderer::callDrawGLFunction() to pass and
* receive data from OpenGL functors.
*/
struct DrawGlInfo {
// Input: current clip rect
int clipLeft;
int clipTop;
int clipRight;
int clipBottom;
// Input: current width/height of destination surface
int width;
int height;
// Input: is the render target an FBO
bool isLayer;
// Input: current transform matrix, in OpenGL format
float transform[16];
// Input: Color space.
// const SkColorSpace* color_space_ptr;
const void* color_space_ptr;
// Output: dirty region to redraw
float dirtyLeft;
float dirtyTop;
float dirtyRight;
float dirtyBottom;
/**
* Values used as the "what" parameter of the functor.
*/
enum Mode {
// Indicates that the functor is called to perform a draw
kModeDraw,
// Indicates the the functor is called only to perform
// processing and that no draw should be attempted
kModeProcess,
// Same as kModeProcess, however there is no GL context because it was
// lost or destroyed
kModeProcessNoContext,
// Invoked every time the UI thread pushes over a frame to the render thread
// *and the owning view has a dirty display list*. This is a signal to sync
// any data that needs to be shared between the UI thread and the render thread.
// During this time the UI thread is blocked.
kModeSync
};
/**
* Values used by OpenGL functors to tell the framework
* what to do next.
*/
enum Status {
// The functor is done
kStatusDone = 0x0,
// DisplayList actually issued GL drawing commands.
// This is used to signal the HardwareRenderer that the
// buffers should be flipped - otherwise, there were no
// changes to the buffer, so no need to flip. Some hardware
// has issues with stale buffer contents when no GL
// commands are issued.
kStatusDrew = 0x4
};
}; // struct DrawGlInfo
} // namespace uirenderer
} // namespace android

Functor 設(shè)計

operator()調(diào)用時傳入的 what 參數(shù)為 Mode 枚舉, 對于注入 GL 的場景只需處理 kModeDraw 即可,c++ 側(cè)類設(shè)計如下:

// MyFunctor定義
namespace android {
class MyFunctor : Functor {
public:
MyFunctor();
virtual ~MyFunctor() {}
virtual void onExec(int what,
android::uirenderer::DrawGlInfo* info);
virtual std::string getFunctorName() = 0;
int operator()(int /*what*/, void * /*data*/) override;
private:

};

}
// MyFunctor實現(xiàn)
int MyFunctor::operator() (int what, void *data) {
if (what == android::uirenderer::DrawGlInfo::Mode::kModeDraw) {
auto info = (android::uirenderer::DrawGlInfo*)data;
onExec(what, info);
}
return android::uirenderer::DrawGlInfo::Status::kStatusDone;

}
void MyFunctor::onExec(int what, android::uirenderer::DrawGlInfo* info) {
// 渲染實現(xiàn)

}

因為 functor 是 Java 層調(diào)度的,而真正實現(xiàn)是在 c++ 的,因此需要設(shè)計 java 側(cè)類并做 JNI 橋接:

// java MyFunctor定義
class MyFunctor {
private long nativeHandle;
public MyFunctor() {
nativeHandle = createNativeHandle();

}
public long getNativeHandle() {
return nativeHanlde;

}
private native long createNativeHandle();

}
// jni 方法:
extern "C" JNIEXPORT jlong JNICALL
Java_com_test_MyFunctor_createNativeHandle(JNIEnv *env, jobject thiz) {
auto p = new MyFunctor();
return (jlong)p;
}

在 View.onDraw () 中調(diào)度 functor

框架在 java Canvas 類上提供了 API,可以在 onDraw () 時將 functor 記錄到 Canvas 的 DisplayList 中。不過由于版本迭代的原因 API 在各版本上稍有不同,經(jīng)總結(jié)可采用如下代碼調(diào)用,兼容各版本區(qū)別:

public class FunctorView extends View {
...
private static Method sDrawGLFunction;
private MyFunctor myFunctor = new MyFunctor();
@Override
public void onDraw(Canvas cvs) {
super.onDraw(cvs);
getDrawFunctorMethodIfNot();
invokeFunctor(cvs, myFunctor);
}
private void invokeFunctor(Canvas canvas, MyFunctor functor) {
if (functor.getNativeHandle() != 0 && sDrawGLFunction != null) {
try {
sDrawGLFunction.invoke(canvas, functor.getNativeHandle());
} catch (Throwable t) {
// log
}
}
}
public synchronized static Method getDrawFunctorMethodIfNot() {

if (sDrawGLFunction != null) {
return sDrawGLFunction;

}
hasReflect = true;
String className;
String methodName;
Class<?> paramClass = long.class;
try {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
className = "android.graphics.RecordingCanvas";
methodName = "callDrawGLFunction2";
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
className = "android.view.DisplayListCanvas";
methodName = "callDrawGLFunction2";
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
className = "android.view.HardwareCanvas";
methodName = "callDrawGLFunction";
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) {
className = "android.view.HardwareCanvas";
methodName = "callDrawGLFunction2";
} else {
className = "android.view.HardwareCanvas";
methodName = "callDrawGLFunction";
paramClass = int.class;
}
Class<?> canvasClazz = Class.forName(className);
sDrawGLFunction = SystemApiReflector.getInstance().
getDeclaredMethod(SystemApiReflector.KEY_GL_FUNCTOR, canvasClazz,
methodName, paramClass);
} catch (Throwable t) {
// 異常
}
if (sDrawGLFunction != null) {
sDrawGLFunction.setAccessible(true);
} else {
// (異常)
}
return sDrawGLFunction;
}
}

注意上述代碼反射系統(tǒng)內(nèi)部 API,Android 10 之后做了 Hidden API 保護,直接反射會失敗,此部分可網(wǎng)上搜索解決方案,此處不展開。

四. 實踐中遇到的問題

GL 狀態(tài)保存&恢復(fù)

Android RenderThread 在執(zhí)行 drawFunctor 前會保存部分 GL 狀態(tài),如下源碼:

// Android 9.0 code
// 保存狀態(tài)
void RenderState::interruptForFunctorInvoke() {
mCaches->setProgram(nullptr);
mCaches->textureState().resetActiveTexture();
meshState().unbindMeshBuffer();

meshState().unbindIndicesBuffer();
meshState().resetVertexPointers();
meshState().disableTexCoordsVertexArray();
debugOverdraw(false, false);
// TODO: We need a way to know whether the functor is sRGB aware (b/32072673)
if (mCaches->extensions().hasLinearBlending() &&
mCaches->extensions().hasSRGBWriteControl()) {
glDisable(GL_FRAMEBUFFER_SRGB_EXT);
}
}
// 恢復(fù)狀態(tài)
void RenderState::resumeFromFunctorInvoke() {
if (mCaches->extensions().hasLinearBlending() &&
mCaches->extensions().hasSRGBWriteControl()) {
glEnable(GL_FRAMEBUFFER_SRGB_EXT);

}
glViewport(0, 0, mViewportWidth, mViewportHeight);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
debugOverdraw(false, false);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
scissor().invalidate();
blend().invalidate();
mCaches->textureState().activateTexture(0);
mCaches->textureState().resetBoundTextures();
}

可以看出并沒有保存所有 GL 狀態(tài),可以增加保存和恢復(fù)所有其他 GL 狀態(tài)的邏輯,也可以針對實際 functor 中改變的狀態(tài)進行保存和恢復(fù);特別注意 functor 執(zhí)行時的 GL 狀態(tài)是非初始狀態(tài),例如 stencil、blend 等都可能被系統(tǒng) RenderThread 修改,因此很多狀態(tài)需要重置到默認。

View變換處理

當(dāng)承載 functor 的 View 外部套 ScrollView、ViewPager,或者 View 執(zhí)行動畫時,渲染結(jié)果異?;蛘卟徽_。例如水平滾動條中 View 使用 functor 渲染,內(nèi)容不會隨著滾動條移動調(diào)整位置。進一步研究源碼 Android 發(fā)現(xiàn),此類問題原因都是 Android 在渲染 View 時加入了變換,變換采用標(biāo)準 4x4 變換列矩陣描述,其值可以從 DrawGlInfo::transform 字段中獲取, 因此渲染時需要處理 transform,例如將 transform 作為模型變換矩陣傳入 shader。

ContextLost

Android framework 在 trimMemory 時在 RenderThread 中會銷毀當(dāng)前 GL Context 并創(chuàng)建一個新 Context, 這樣會導(dǎo)致 functor 的 program、shader、紋理等 GL 資源都不可用,再去渲染的話可能會導(dǎo)致閃退、渲染異常等問題,因此這種情況必須處理。

首先,需要響應(yīng) lowMemory 事件,可以通過監(jiān)聽 Application 的 trimMemory 回調(diào)實現(xiàn):

activity.getApplicationContext().registerComponentCallbacks(
new ComponentCallbacks2() {
@Override
public void onTrimMemory(int level) {
if (level == 15) {
// 觸發(fā)functor重建
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
}
@Override
public void onLowMemory() {
}
});

然后,保存 & 恢復(fù) functor 的 GL 資源和執(zhí)行狀態(tài),例如 shader、program、fbo 等需要重新初始化,紋理、buffer、uniform 數(shù)據(jù)需要重新上傳。注意由于無法事前知道 onTrimMemory 發(fā)生,上一幀內(nèi)容是無法恢復(fù)的,當(dāng)然知道完整的狀態(tài)是可以重新渲染出來的。

鑒于存在無法提前感知的 ContextLost 情況,建議采用基于 commandbuffer 的模式來實現(xiàn) functor 渲染邏輯。

五. 效果

我們用一個 OpenGL 渲染的簡單 case (分辨率1080x1920),對使用 TextureView 渲染和使用 drawFunctor 渲染的方式進行了比較,

結(jié)果如下:

Simple Case內(nèi)存CPU 占用
基于 TextureView100 M ( Graphics 38 M )6%
基于 GLFunctor84 M ( Graphics 26 M )4%

從上述結(jié)果可得出結(jié)論,使用 drawFunctor 方式在內(nèi)存、CPU 占用上具有優(yōu)勢, 可應(yīng)用于局部頁面的互動渲染、視頻渲染等場景。

到此這篇關(guān)于Android drawFunctor 原理及應(yīng)用詳情的文章就介紹到這了,更多相關(guān)Android drawFunctor 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論