最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android?drawFunctor?原理及應用詳情

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

一. 背景

螞蟻 NativeCanvas 項目 Android 平臺中使用了基于 TextureView 環(huán)境實現(xiàn) GL 渲染的技術方案,而 TextureView 需使用與 Activity Window 獨立的 GraphicBuffer,RenderThread 在上屏 TextureView 內容時需要將 GraphicBuffer 封裝為 EGLImage 上傳為紋理再渲染,內存占用較高。為降低內存占用,經仔細調研 Android 源碼,發(fā)現(xiàn)其中存在一種稱為 drawFunctor 的技術,用來將 WebView 合成后的內容同步到 Activity Window 內上屏。經過一番探索成功實現(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,則保存當前部分 gl 狀態(tài)
  • 在 RenderThread 中真正執(zhí)行 functor 邏輯,執(zhí)行完成后恢復 gl 狀態(tài)并繼續(xù)

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

另外,如果 View 設置了使用 HardwareLayer, 則 RenderThread 會單獨渲染此 View,具體做法是為 Layer 生成一塊 FBO,View 的內容渲染到此 FBO 上,然后再將 FBO 以 View 在 hierachy 上的變換繪制 Activity Window Buffer 上。 對 drawFunctor 影響的是, 會切換到 View 對應的 FBO 下執(zhí)行 functor, 即 functor 執(zhí)行的結果是寫入到 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 時將調用 operator()方法,what 表示 functor 的操作類型,常見的有同步和繪制, 而 data 是 RenderThread 執(zhí)行 functor 時傳入的參數(shù),根據(jù)源碼發(fā)現(xiàn)是 data 是 android::uirenderer::DrawGlInfo 類型指針,包含當前裁剪區(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 設計

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

// 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 層調度的,而真正實現(xiàn)是在 c++ 的,因此需要設計 java 側類并做 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 () 中調度 functor

框架在 java Canvas 類上提供了 API,可以在 onDraw () 時將 functor 記錄到 Canvas 的 DisplayList 中。不過由于版本迭代的原因 API 在各版本上稍有不同,經總結可采用如下代碼調用,兼容各版本區(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)內部 API,Android 10 之后做了 Hidden API 保護,直接反射會失敗,此部分可網上搜索解決方案,此處不展開。

四. 實踐中遇到的問題

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

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

View變換處理

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

ContextLost

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

首先,需要響應 lowMemory 事件,可以通過監(jiān)聽 Application 的 trimMemory 回調實現(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() {
}
});

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

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

五. 效果

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

結果如下:

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

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

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

相關文章

最新評論

甘孜| 海宁市| 都昌县| 若羌县| 平湖市| 固原市| 思茅市| 德州市| 邵武市| 屯留县| 囊谦县| 永福县| 天台县| 平山县| 广安市| 名山县| 马鞍山市| 许昌市| 枣庄市| 邵东县| 黄骅市| 天全县| 辛集市| 康乐县| 阿拉善盟| 米泉市| 安吉县| 慈利县| 南昌县| 思茅市| 江华| 乌拉特中旗| 西宁市| 左云县| 射洪县| 屯门区| 寿宁县| 霞浦县| 南召县| 南城县| 东乡县|