Android高效計算——RenderScript(一)

来源:http://www.cnblogs.com/willhua/archive/2016/08/17/5778415.html
-Advertisement-
Play Games

高效計算——RenderScript RenderScript是安卓平臺上很受谷歌推薦的一個高效計算平臺,它能夠自動把計算任務分配到各個可用的計算核心上,包括CPU,GPU以及DSP等,提供十分高效的並行計算能力。可能是由於應用開發時的需求不夠,關於RenderScript的相關文章很少,剛好我在工 ...


高效計算——RenderScript

RenderScript是安卓平臺上很受谷歌推薦的一個高效計算平臺,它能夠自動把計算任務分配到各個可用的計算核心上,包括CPU,GPU以及DSP等,提供十分高效的並行計算能力。可能是由於應用開發時的需求不夠,關於RenderScript的相關文章很少,剛好我在工作中應用到此平臺,做了一些筆記,因此決定整理成博文分享給大家。內容主要來源於官方文檔、StackOverflow以及自己的理解,如有錯誤,請大家指正。本篇主要介紹RenderScript的基本概念。

1 RenderScript簡介

RenderScript是安卓提供的一個高效計算平臺。它顯著的特點在於:

  1. 能夠自動利用各種核心,包括CPU,GPU以及DSP等,來進行並行計算,能大大提高在圖片處理、數學模型等領域提供高效的計算能力;
  2. 不需要針對不同的核心平臺而編寫不同的代碼,因為RenderScript是在設備上進行運行時編譯的。

使用了RenderScript的應用與一般的安卓應用在代碼編寫上與並沒有太大區別。使用了RenderScript的應用依然像傳統應用一樣運行在VM中,但是你需要給你的應用編寫你所需要的RenderScript代碼,且這部分代碼運行在native層。

RenderScript採用從屬控制架構:底層RenderScript被運行在虛擬機中的上層安卓系統所控制。安卓VM負責所有記憶體管理並把它分配給RenderScript的記憶體綁定到RenderScript運行時,所以RenderScript代碼能夠訪問這些記憶體。安卓框架對RenderScript進行非同步調用,每個調用都放在消息隊列中,並且會被儘快處理。

 

RenderScript工作流程需要經歷三層:

  1. RenderScript運行時API:提供進行運算的API
  2. 反射層:相當於NDK中的JNI膠水代碼,它是一些由安卓編譯工具自動生成的類,對你寫的RenderScript代碼進行包裝,使得安卓層能夠和RenderScript進行交互
  3. 安卓框架:通過調用反射層來訪問RenderScript運行時

 

RenderScript的主要優點:

  1. 可移植性:對於不同架構,不同的處理器都不需要考慮代碼的差異化,因為都是運行時在設備上進行編譯的;
  2. 高性能:提供充分利用所有核心的無縫的並行化計算
  3. 易用性:簡化編碼,不需要像JNI一樣寫膠水代碼

缺點:

  1. 開發複雜:需要去學習新的api
  2. 調試可見性:因為RenderScript可能運行在除了主cpu之外的處理器上,所以調試困難 

2  使用RenderScript

    使用RenderScript需要對編譯或者開發環境進行一定的配置。

    使用RenderScript主要分為兩個步驟:編寫.rs文件以及在Android framework中使用RenderScript,下麵分別介紹。

2.1 環境配置

  • RenderScript的API可以有兩種來源方式:

對於Android 3.0 (API level 11)及以上的可以在android.renderscript包中獲取

通過android.support.v8.renderscript包獲取,可以支持API level 8及以上的平臺,官方強烈建議使用此支持包的方式來獲取API

  • 編譯環境要求:

Android SDK Tools revision 需要22.2及以上

Android SDK Build-tools revision 需要18.1.0及以上

  • 在project.properties文件中寫入如下屬性:
renderscript.target=18

renderscript.support.mode=true

或者在AS中的build.gradle的defaultConfig中添加

renderscriptTargetApi 18

renderscriptSupportModeEnabled true

註意:target的值應該為11及以上,但推薦使用18.如果在Manifest中配置的minSDK的值與target的值不相同,那麼在編譯的時候,將使用target的值替代Mainfest中的minSDK值。

 

2.2 編寫RenderScript文件

RenderScript代碼放在.rs或者.rsh文件中,在RenderScript代碼中包含計算邏輯以及聲明所有必須的變數和指針,通常一個.rs文件包含如下幾個部分:

  • 編譯聲明:#pragma rs java_package_name(package.name),比如#pragma rs java_package_name (com.willhua.RenderScript),用來聲明本rs所在的java包。註意:.rs文件只能在應用程式包中,而不能在library項目中。
  • 編譯聲明:#pragma version(1).聲明RenderScript版本,現在都是1
  • 主工作函數root().它會被RenderScript層的reForEach函數調用,實現多處理器對root工作的並行處理。Root函數必須返回void以及接受如下參數

1.分配給RenderScript的輸入輸出地址的指針。在Android3.2以及更低版本中,輸入輸出的指針都需要,在Android4.0及以後的版本中,給出其中一個或者兩個都可以

  •     2.下麵兩個參數是可選的,但是只要用了其中一個就必須兩個都提供

        a)   指向用戶數據的指針。該數據會在RenderScript的計算中用到。該數據可以指向原始類型或者複雜結構類型

        b)   用戶數據的大小

從官方文檔來看,老版本的文檔中有介紹root,而新版本的則用kernel替代。官方在弱化root函數的概念,而是推薦使用kernel概念。本質上來說,root僅僅是一個寫法形式上特殊的kernel而已。

  • 可選init()函數。可以用來做任何初始化工作,比如初始化變數。它將會在每次RenderScript啟動的時候,在其他任何代碼之前執行一次
  • 一些invokable函數。這些函數都是單線程函數(kernel函數的工作則是並行工作的),你可以給這些函數傳遞任意數量的參數。這些函數將會在反射層中生成對應的版本,可以從Android framework中調用。這些函數一般用來做一些初始化工作或者當做計算任務中的一個串列計算單元任務。註意:invokable函數不能是static的。
  • 一些計算內核(compute kernel)。計算內核是並行執行的,它將並行處理輸入Allocation中的每一個Element。一個簡單的compute kernel如下:
uchar4 __attribute__((kernel)) invert(uchar4 in, uint32_t x, uint32_t y) {
    uchar4 out = in;
    out.r = 255 - in.r;
    out.g = 255 - in.g;
    out.b = 255 - in.b;
    return out;
}

    compute kernel基本與一個C函數一樣,但是有如下特征:

      a)   __attribute__((kernel))標誌。該標誌表示該函數是一個RenderScript kernel函數,而不是一個invokable函數

      b)   in參數及其類型。在RenderScript kernel中,這個參數將會基於傳給kernel的輸入Allocation而自動賦值,且預設情況下,對於Allocation中每一個Element都將會執行一遍kernel函數

      c)   返回值及其類型。每次kernel函數執行的返回值將會自動寫入到輸出Allocation的正確位置。RenderScript將會對輸入輸出Allocation進行檢查,如果他們與kernel函數聲明不匹配則將拋出異常。

每個kernel都應該有一個輸入Allocation或者一個輸出Allocation或者二者都有,但不能有兩個及以上的輸入或者輸出Allocation。如果需要在kernel中訪問多個輸入或者輸出,則需要聲明rs_allocation全局變數來擔任多餘一個的輸入或者輸出角色,然後再kernel函數或者invokable函數中通過rsGetElementAt_type()或者rsSetElementAt_type()來訪問或者設置相應的Allocation,其中type為對應Allocation的Element類型對應的數據類型,比如uchar4。

在kernel中,可以通過可選的xyz參數來獲取當前Element在整個Allocation中的坐標值,比如上面的invert中就通過xy來獲取了xy坐標值。註意xyz的參數名不能設置為其他名稱,且類型必須為uint32_t。

  • 任何要在RenderScript中用到的變數,指針以及結構。這些聲明也可以在.rsh文件中
  • 所需要的script變數。就和C中的全局變數一樣,這些全局變數一般用來傳遞參數給計算kernel。
  • 一些靜態變數以及函數。靜態的變數與普通全局變數的區別在於:靜態變數不會在映射在反射層,也即無法從Android framework中調用;靜態函數就是一個標準的C函數,但是不會映射到反射層,也無法從Android framework中調用,但是可以在RenderScript中的kernel或者invokable中調用。如果有變數或者函數需要在RenderScript中使用但是不需要在Java中使用,強烈推薦設置為static的。
  • 可選的精度控制配置,主要有三個等級:

a)   #pragma rs_fp_full:預設的等級。表示的完全遵守IEEE 754-2008 standard的精度要求

b)   #pragma rs_fp_relaxed:不嚴格的IEEE 754-2008 standard的精度要求

c)   #pragma rs_fp_imprecise:比relaxed更低的精度要求

對於大部分應用來說,使用relaxed精度要求都可以滿足要求而無任何副作用 

example.rs :

#pragma version(1)
#pragma rs java_package_name(com.willhua.rgbtoyuv)
#pragma rs_fp_relaxed

typedef struct Point_T{
    int x;
    int y;
}Point;

//script variable
uint32_t inW;
uint32_t inH;
uint32_t inCount;
rs_allocation outYUV;
struct Point point;

//root
void root(const uchar4 *in, uint32_t x, uint32_t y){
    struct myStruct my;
    my.x = 0;
    struct myStruct my2 = my;
    int u = my.x;
    uchar R,G,B;
    int Y,U,V;
    R = (*in).r;
    G = (*in).g;
    B = (*in).b;
    Y = ( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16;
    uint32_t yIndex = y * inW + x;
    Y = ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
    rsSetElementAt_uchar(outYUV, ((uchar)Y), yIndex);
    if((x & 1) == 0 && (y & 1) == 0) {
        U = ( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128;
        V = ( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128;
        uint32_t index = (y >> 1) * inW + x + inCount;
        U = ((U < 0) ? 0 : ((U > 255) ? 255 : U));
        V = ((V < 0) ? 0 : ((V > 255) ? 255 : V));
        rsSetElementAt_uchar(outYUV, ((uchar)U), index + 1);
        rsSetElementAt_uchar(outYUV, ((uchar)V), index );
    }
}

//compute kernel
__attribute__((kernel)) invert(uchar4 in, uint32_t x, uint32_t y) {
    uchar4 out = in;
    out.r = 255 - in.r;
    out.g = 255 - in.g;
    out.b = 255 - in.b;
    return out;
}
//invokable function
void setInPara(uint32_t w, uint32_t h){
    inW = w;
    inH = h;
    inCount = w * h;
}

//init
void init(){
}
View Code

 

2.3 Android framework層調用RenderScript

雖然各個應用使用RenderScript細節各不相同,但大體有著這樣的模式:

  1. 初始化RenderScript context。通過RenderScript.create函數可以創建相應的context,有了該context才可以進行RenderScript的其他動作,並通過該context可以控制其他RenderScript對象的生命周期。因為context的創建需要在不同的硬體設備上創建資源,所以可能會比較耗時。因此,最好不要讓context的創建在響應速度要求比較高的時間點上。一般來說,一個應用應該只有一個context。
  2. 至少需要創建一個Allocation。用的比較多的方法是createTyped(RenderScript, Type) 或createFromBitmap(RenderScript, Bitmap)。
  3. 創建需要的scripts。可以分為兩種,一種就是通過自定義.rs文件,然後在反射層自動生成的ScriptC子類,名字為ScriptC_rsfilename;還有一種就是系統定義的一些scripts,比如高斯模糊等,他們都是ScriptIntrinsic的子類。
  4. 給Allocation填充數據。使用Allocation的copy系類方法
  5. 設置必要的script變數。在ScriptC_rsfilename會給script變數生成相應的set方法,比如int型名為num的script變數則會在反射層生成set_num(int)方法。
  6. 啟動計算。.rs中定義的kernel函數都會在反射層的ScriptC_rsfilename類中生成forEach_kernalname方法。該方法的執行是非同步的,且會在RenderScript中按照kernel調用順序來執行。前面提到過,forEach_kernalname方法預設對對應Allocation中所有的Element執行計算,但是可以在forEach_kernalname參數的最後傳入一個Scrpit.LunchOptions參數來指定Allocation中需要被計算的子集。.rs中定義的invokable函數則會在反射層生成invoke_functionname的方法,也可以按需調用
  7. 從Allocation中得到數據。通過Allocation的copyTo系類方法,可以把Allocation中的數據放到Java數據中。這些copyTo方法與forEach方法保持同步,forEach的計算完成之後就會自動啟動copy過程。目前只支持基本類型的copyTo,暫不支持比如自定義的struct等數據。
  8. 釋放資源。通過RenderScript.destory方法來釋放相關資源。 

2.4 RenderScript工作流程

最開始就提到,RenderScript是一個主從架構,底層的RenderScript被上層的Android framework所控制。其工作流程也正是如此。從在Android framework創建RenderScript的context開始,然後給RenderScript層分配、綁定相關記憶體,對script變數進行初始化,然後調用forEach函數通知啟動RenderScript計算。RenderScript將會自動把它的計算任務分配到各個可用的核心上來完成計算任務(現在還只能支持CPU,以後將會支持到GPU以及DSP,且代碼不需要變動)。RenderScript計算完成以後將會自動把計算結果放到相應的Allocation記憶體,然後在Android framework層再從Allocation中copy出數據,最後Android framework層命令RenderScript釋放資源,流程介紹。下圖展示了RenderScript的工作流程:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 為了保證臨界資源的安全性和可靠性,線程不得不使用鎖,同一時間只允許一個或幾個線程訪問變數。常用的鎖有互斥量,讀寫鎖,條件變數 一、互斥量 互斥量是用pthread_mutex_t數據類型表示的,在使用之前,必須對其進行初始化,可以把它設置為PTHREAD_MUTEX_INITIALIZER(只適於靜 ...
  • 一 、安裝java jdk配置環境變數 1、 卸載原有openjdk yum -y remove java-1.7.0-openjdk* yum -y remove tzdata-java.noarch 2、 下載新的jdk 並解壓 [root@localhost java]# curl -O ht ...
  • 很多kali愛好者想把kali linux作為系統使用,但是有些win下的程式有時候也需要用到,此時需要虛擬機。 kali系統在安裝虛擬機的時候也會遇到一大堆坑,接下來是我的爬坑過程。 一波三折。 環境 :kali linux 2.0 32位 軟體 :virtualbox 虛擬機 0x01 bash ...
  • typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; // 未使用,總為0 DWORD TimeDateStamp; // 文件創建時間戳 WORD MajorVersion; // 未使用,總為0 WORD MinorVer... ...
  • Touch ID是iPhone5S後加入的一項新的功能,也就是大家熟知的指紋識別技術。大家用得最多的可能是手機的解屏操作,不用在和以前一樣輸入手機的四位數密碼進行驗證。一方面不用擔心密碼被別人看到,另一方面也方便了自己的操作。iOS8後蘋果開放了Touch ID的API給開發者,這也給我們的app帶 ...
  • 如果你對iOS逆向工程有所瞭解,那麼你對Tweak並不陌生。那麼由Tweak我們又會引出Theos, 那麼什麼是Theos呢,簡單一句話,Theos是一個越獄開發工具包,Theos是越獄開發工具的首先,因為其最大的特點就是簡單。大道至簡,Theos的下載安裝、編譯發佈都比較簡單,越獄開發中另一個常用 ...
  • 一,效果圖。 二,工程圖。 三,代碼。 AppDelegate.m ...
  • 調用相機拍照獲取圖片: 跳轉到到拍照界面: Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //下麵這句指定調用相機拍照後的照片存儲的路徑 mSzImageFileName = Long.toString(System ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...