RenderScript

[譯]原文鏈接

RenderScript是運(yùn)行在Android系統(tǒng)上的一個(gè)高性能密集計(jì)算框架。RenderScript主要面向并行數(shù)據(jù)的計(jì)算,當(dāng)然也可以使串行數(shù)據(jù)計(jì)算的負(fù)載受益。RenderScript運(yùn)行時(shí)會(huì)在一臺(tái)設(shè)備所有可用處理器上并行工作绩鸣,比如多核的CPU,GPU或者DSP,這使得你可以專注于算法虏冻,而不是任務(wù)調(diào)度或者負(fù)載平衡槽棍。RenderScript在應(yīng)用程序執(zhí)行圖像處理墓怀,計(jì)算攝影或者計(jì)算機(jī)視覺(jué)等方面都非常有用汽纠。

為了開(kāi)始RenderScript的學(xué)習(xí),這有兩個(gè)很重要的概念是你需要了解的:

  • 高性能的計(jì)算內(nèi)核是通過(guò)衍生自C99的語(yǔ)言編寫(xiě)的傀履。
  • 有專用的Java API用于管理RenderScript的資源生命周期以及控制內(nèi)核的執(zhí)行虱朵。

編寫(xiě)一個(gè)RenderScript內(nèi)核

一個(gè)RenderScript內(nèi)核通常是駐留在<project_root>/src/目錄下的一個(gè).rs文件。每個(gè).rs文件被稱之為一個(gè)腳本钓账。每個(gè)腳本文件包含了它自己的一套內(nèi)核碴犬,函數(shù)和變量。一個(gè)腳本文件可以包含以下內(nèi)容:

  • 編譯聲明(#pragma version(1))梆暮,聲明腳本文件所使用的內(nèi)核語(yǔ)言版本服协,目前1是唯一的有效值。

  • 編譯聲明(#pragma rs java_package_name(com.example.app))啦粹,聲明腳本文件關(guān)聯(lián)Java類的包名偿荷。請(qǐng)注意,你的.rs文件必須是應(yīng)用程序包的一部分卖陵,而不是一個(gè)library項(xiàng)目遭顶。

  • 一些可調(diào)用函數(shù)±崮瑁可調(diào)用函數(shù)是一個(gè)單線程的RenderScript函數(shù)棒旗,你可以通過(guò)參數(shù)從java代碼進(jìn)行調(diào)用。這些往往對(duì)于初始設(shè)置或者更大處理管道的串行計(jì)算非常有用撩荣。

  • 一些全局變量铣揉。全局腳本變量其實(shí)等同于C語(yǔ)言變量。你可以通過(guò)java代碼訪問(wèn)這些全局腳本變量餐曹,這些變量往往是作為參數(shù)傳遞到RenderScript內(nèi)核逛拱。

  • 一些計(jì)算內(nèi)核。一個(gè)內(nèi)核是通過(guò)并行函數(shù)去處理每一個(gè)Allocation中的Element台猴。
    一個(gè)簡(jiǎn)單的內(nèi)核可能看起來(lái)就像下面這樣:

      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;
      }
    

在很多方面朽合,這等同于一個(gè)標(biāo)準(zhǔn)的C函數(shù)。第一個(gè)顯著的特點(diǎn)是_attribute_((kernel))適用于函數(shù)原型饱狂。它表示該函數(shù)是一個(gè)RenderScript內(nèi)核而不是一個(gè)可調(diào)用的功能曹步。下一個(gè)特點(diǎn)是in參數(shù)和它的類型。在RenderScript內(nèi)核里休讳,這是一個(gè)特別的參數(shù)讲婚,它會(huì)根據(jù)輸入的Allocation自動(dòng)填充,傳遞給內(nèi)核啟動(dòng)俊柔。內(nèi)核會(huì)執(zhí)行每一個(gè)Allocation中的Element筹麸,因此默認(rèn)情況下活合,內(nèi)核會(huì)在整個(gè)Allocation中運(yùn)行。第三個(gè)顯著的特點(diǎn)是內(nèi)核的返回類型物赶。從內(nèi)核返回的值會(huì)自動(dòng)寫(xiě)入到輸出Allocation的適當(dāng)位置白指。RenderScript會(huì)在運(yùn)行時(shí)進(jìn)行檢查,確保Allocation中輸入輸出的Element類型是和內(nèi)核中的原型相匹配的块差。如果它們不匹配侵续,將會(huì)拋出異常。
一個(gè)內(nèi)核可能有一個(gè)輸入的Allocation憨闰,一個(gè)輸出的Allocation,或者兩個(gè)都有需五。一個(gè)內(nèi)核可能不會(huì)有超過(guò)一個(gè)的輸入或輸出Allocation鹉动。如果需要超過(guò)一個(gè)的輸入和輸出,那這些對(duì)象需要被綁定到rs_allocation的全局腳本宏邮,通過(guò)rsGetElementAt_type()或者rsSetElementAt_type()進(jìn)行內(nèi)核訪問(wèn)或函數(shù)調(diào)用泽示。
一個(gè)內(nèi)核可以通過(guò)使用xy蜜氨,z參數(shù)來(lái)訪問(wèn)當(dāng)前執(zhí)行的坐標(biāo)械筛。這些參數(shù)是可選的,但是坐標(biāo)參數(shù)的類型必須是uint32_t飒炎。

  • 一個(gè)可選的init()函數(shù)埋哟。init()函數(shù)是一個(gè)特殊的功能函數(shù),它在腳本實(shí)例化完成后運(yùn)行郎汪。這使得一些計(jì)算可以在腳本創(chuàng)建后自動(dòng)運(yùn)行赤赊。
  • 一些靜態(tài)全局變量和函數(shù)。一個(gè)靜態(tài)全局變量相當(dāng)于全局變量只不過(guò)不能通過(guò)java代碼進(jìn)行設(shè)置煞赢。一個(gè)靜態(tài)函數(shù)相當(dāng)于一個(gè)標(biāo)準(zhǔn)的C函數(shù)抛计,能夠被內(nèi)核或其他腳本中的功能函數(shù)進(jìn)行調(diào)用,但是不暴露給Java API照筑。如果一個(gè)腳本變量或者函數(shù)不需要被Java代碼調(diào)用吹截,那強(qiáng)烈建議將其聲明為靜態(tài)的(static)。

設(shè)置浮點(diǎn)數(shù)精度

你可以通過(guò)腳本控制浮點(diǎn)數(shù)的精度級(jí)別凝危。如果不需要完整的IEEE 754-2008標(biāo)準(zhǔn)(默認(rèn)情況下使用)波俄,這非常有用。下面的這些聲明可以為浮點(diǎn)數(shù)精度設(shè)置不同的級(jí)別:

  • #pragma rs_fp_full(沒(méi)有指定會(huì)默認(rèn)聲明):針對(duì)需要浮點(diǎn)數(shù)精度為IEEE 754-2008標(biāo)準(zhǔn)的應(yīng)用媒抠。
  • #pragma rs_fp_relaxed :針對(duì)不需要嚴(yán)格遵循IEEE 754-2008標(biāo)準(zhǔn)弟断,并且可以接受精度要求不高的應(yīng)用。This mode enables flush-to-zero for denorms and round-towards-zero.
  • #pragma rs_fp_imprecise : 針對(duì)沒(méi)有嚴(yán)格精度要求的應(yīng)用趴生。這個(gè)模式允許在rs_fp_relaxed模式中的以下內(nèi)容:
    • -0.0的操作結(jié)果可以通過(guò)返回+0.0代替阀趴。
    • INF和NAN的操作是不確定的昏翰。
      大多數(shù)應(yīng)用可以使用rs_fp_relaxed,沒(méi)有任何的副作用刘急。這可能在某些結(jié)構(gòu)上非常有利棚菊,由于只需要適配寬松的進(jìn)度而帶來(lái)一些優(yōu)化(例如SIMD的CPU指令)。

訪問(wèn)RenderScript API接口

當(dāng)開(kāi)發(fā)一款應(yīng)用使用到RenderScript叔汁,你可以通過(guò)兩種方式來(lái)訪問(wèn)它的API:

我們強(qiáng)烈建議使用Support Library APIs來(lái)訪問(wèn)RenderScript的接口码邻,因?yàn)樗峁┝烁鼜V泛的設(shè)備兼容性。如果需要為特定版本的Android開(kāi)發(fā)應(yīng)用另假,可以使用android.renderscript像屋。

使用RenderScript Support Library APIs

為了使用Support Library RenderScript的API,你必須確保你的開(kāi)發(fā)環(huán)境能夠訪問(wèn)到它們边篮。下面的Android SDK工具是使用這些API所需要的:

  • Android SDK Tools revision 22.2 or higher
  • Android SDK Build-tools revision 18.1.0 or higher

你可以在Android SDK Manager里面檢查并更新這些工具己莺。
使用Support Library RenderScript的API:

  1. 確保已經(jīng)安裝了所需要的Android SDK版本以及構(gòu)建工具。
  2. 更新Android構(gòu)建過(guò)程的配置戈轿,加入RenderScript的配置:
  • 打開(kāi)你的應(yīng)用模塊的build.gradle文件

  • 添加RenderScript的配置

      android {
        compileSdkVersion 23
        buildToolsVersion "23.0.3"
    
        defaultConfig {
            minSdkVersion 8
            targetSdkVersion 19
    
            renderscriptTargetApi 18
            renderscriptSupportModeEnabled true
    
        }
      }
    

上述構(gòu)建過(guò)程中的配置控制著不同的行為:

  • renderscriptTargetApi - 指定生成的字節(jié)碼版本凌受。我們建議你設(shè)置這個(gè)值為能夠提供你正在使用的所有功能的最低API級(jí)別,同時(shí)設(shè)置renderscriptSupportModeEnabled為true思杯。這個(gè)設(shè)置的有效值為11到最近發(fā)布的API級(jí)別值胜蛉。如果你的應(yīng)用程序配置文件里面指定的最小SDK版本設(shè)置為不同的值,那這個(gè)值將會(huì)被忽略智蝠,構(gòu)建文件中的目標(biāo)版本值用來(lái)設(shè)置最低SDK版本腾么。
  • renderscriptSupportModeEnabled - 如果運(yùn)行的設(shè)備不支持該目標(biāo)版本,那么可以指定生成的字節(jié)碼回落到一個(gè)兼容的版本杈湾。
  • buildToolsVersion - Android 構(gòu)建工具所使用的版本解虱。這個(gè)值應(yīng)該被設(shè)置為18.1.0或者更高。如果這個(gè)可選的配置沒(méi)有被指定漆撞,那么將會(huì)默認(rèn)使用安裝的最高版本的構(gòu)建工具殴泰。你應(yīng)該總是設(shè)置這個(gè)值,以確保不同開(kāi)發(fā)設(shè)備不同配置之間的一致性浮驳。
  1. 在你使用RenderScript的類文件里面悍汛,導(dǎo)入Support Library的類:

     import android.support.v8.renderscript.*;
    

通過(guò)Java代碼使用RenderScript

通過(guò)Java代碼來(lái)使用RenderScript依賴于位于android.renderscriptandroid.support.v8.renderscript包中的API類。大多數(shù)應(yīng)用都遵循基本的使用模式:

  1. 初始化一個(gè)RenderScript上下文至会。RenderScript上下文离咐,通過(guò)create(Context)方法進(jìn)行創(chuàng)建,確保RenderScript能過(guò)被使用,同時(shí)提供一個(gè)對(duì)象來(lái)控制所有后續(xù)的RenderScript對(duì)象的生存期宵蛀。你應(yīng)該考慮創(chuàng)建一個(gè)長(zhǎng)期運(yùn)行的context昆著,因?yàn)樗梢詣?chuàng)建不同的硬件資源。通常术陶,一個(gè)應(yīng)用程序只能持有一個(gè)RenderScript單例凑懂。

  2. 創(chuàng)建至少一個(gè)Allocation傳遞到腳本中。一個(gè)Allocation是一個(gè)RenderScript對(duì)象梧宫,提供固定數(shù)據(jù)的存儲(chǔ)接谨。腳本中的內(nèi)核,采取Allocation對(duì)象作為它們的輸入和輸出塘匣,同時(shí)Allocation對(duì)象可以在綁定全局腳本變量的時(shí)候脓豪,通過(guò)調(diào)用rsGetElementAt_type()和rsSetElementAt_type()方法在內(nèi)核中進(jìn)行訪問(wèn)。Allocation對(duì)象運(yùn)行數(shù)組從Java代碼中傳遞到RenderScript代碼馆铁,反之亦然跑揉。Allocation對(duì)象創(chuàng)建最典型的方式是使用createTyped(RenderScript, Type)或者createFromBitmap(RenderScript, Bitmap)

  3. 創(chuàng)建腳本是必要的埠巨。在使用RenderScript時(shí),提供兩種類型的腳本:

    • ScriptC:這些是寫(xiě)在上述RenderScript內(nèi)核中的用戶自定義腳本现拒。為了使Java代碼能夠更容易訪問(wèn)到腳本代碼辣垒,每個(gè)腳本都會(huì)通過(guò)RenderScript編譯器反射為一個(gè)具體的Java類。這些類的名字為ScriptC_filename印蔬。例如勋桶,你的內(nèi)核位于invert.rs,并且一個(gè)RenderScript上下文已經(jīng)位于mRS,Java代碼實(shí)例化腳本為:

        ScriptC_invert invert = new ScriptC_invert(mRenderScript);
      
    • ScriptIntrinsic:這些是內(nèi)置在RenderScript內(nèi)核的共同操作侥猬,比如高斯模糊例驹,圖像混合等等。想了解更多信息退唠,請(qǐng)參閱ScriptIntrinsic的子類鹃锈。

  4. 用數(shù)據(jù)填充Allocation對(duì)象。除了通過(guò)android.renderscript創(chuàng)建Allocation對(duì)象瞧预,一個(gè)Allocation對(duì)象會(huì)在第一次創(chuàng)建的時(shí)候被填充空數(shù)據(jù)屎债。為了填充一個(gè)Allocation對(duì)象,使用Allocation方法中的copy方法垢油。

  5. 設(shè)置所有需要的全局變量盆驹。全局變量可以使用同一ScriptC_filename類里面,名為set_globalname的方法進(jìn)行設(shè)置滩愁。例如躯喇,為了設(shè)置一個(gè)名為elementsint值,使用java方法set_elements(int)硝枉。RenderScript對(duì)象也可以在內(nèi)核里面進(jìn)行設(shè)置廉丽;例如倦微,名為lookuprs_allocation變量,可以通過(guò)方法set_lookup(Allocation)進(jìn)行設(shè)置雅倒。

  6. 啟動(dòng)相應(yīng)的內(nèi)核璃诀。啟動(dòng)指定內(nèi)核的方法,體現(xiàn)在相同的具有名為forEach_kernelname()方法的ScriptC_filename類蔑匣。這些啟動(dòng)是異步的劣欢,并會(huì)通過(guò)啟動(dòng)的順序進(jìn)行序列化。根據(jù)傳遞給內(nèi)核的不同參數(shù)裁良,這些方法將會(huì)采取一個(gè)或兩個(gè)Allocation對(duì)象凿将。默認(rèn)情況下,一個(gè)內(nèi)核會(huì)執(zhí)行整個(gè)輸入或者輸出Allocation;為了執(zhí)行Allocation的子集价脾,可以通過(guò)適當(dāng)?shù)?a target="_blank" rel="nofollow">Script.LaunchOptions作為最后一個(gè)參數(shù)傳遞給forEach方法牧抵。調(diào)用的函數(shù)可以通過(guò)反射ScriptC_filename內(nèi)中的invoke_functionname方法進(jìn)行調(diào)用。

  7. 復(fù)制Allocation對(duì)象的數(shù)據(jù)侨把。為了從Java代碼訪問(wèn)到Allocation對(duì)象的數(shù)據(jù)犀变,必須使用Allocationcopy方法將數(shù)據(jù)拷貝到Java的緩沖區(qū)中。

  8. 回收RenderScript上下文秋柄。RenderScript的上下文可以通過(guò)destroy()方法進(jìn)行回收获枝,或者通過(guò)垃圾回收機(jī)制進(jìn)行銷毀。這會(huì)導(dǎo)致任何想要繼續(xù)使用這個(gè)對(duì)象的地方拋出異常骇笔。

譯者注:以前沒(méi)了解過(guò)RenderScript省店,所以翻譯起來(lái)也分外吃力,很多地方可能存在一些理解上的誤差笨触,在學(xué)習(xí)過(guò)程中會(huì)繼續(xù)校正懦傍,如果大家在閱讀過(guò)程中遇到問(wèn)題,也請(qǐng)直接查看原文芦劣。附上幾個(gè)例子粗俱,以加深對(duì)RenderScript的理解:

使用RenderScript實(shí)現(xiàn)圖片高斯模糊效果

  1. build.gradle添加RenderScript配置

     apply plugin: 'com.android.application'
    
     android {
         compileSdkVersion 23
         buildToolsVersion "23.0.3"
    
         defaultConfig {
             minSdkVersion 14
             targetSdkVersion 23
             versionCode 1
             versionName "1.0"
    
             renderscriptTargetApi 19
             renderscriptSupportModeEnabled true
         }
         
         buildTypes {
             release {
             minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
    
  2. 自定義BlurBitmap類,實(shí)現(xiàn)高斯模糊方法

     public class BlurBitmap {
         private static final float BITMAP_SCALE = 1.0f;
         private static final float BLUR_RADIUS = 25;
    
         public static Bitmap blur(Context context, Bitmap image) {
             int width = Math.round(image.getWidth() * BITMAP_SCALE);
             int height = Math.round(image.getHeight() * BITMAP_SCALE);
    
             Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
             Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
    
             RenderScript rs = RenderScript.create(context);
             ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
             Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
             Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
             theIntrinsic.setRadius(BLUR_RADIUS);
             theIntrinsic.setInput(tmpIn);
             theIntrinsic.forEach(tmpOut);
             tmpOut.copyTo(outputBitmap);
    
             return outputBitmap;
         }
     }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末持寄,一起剝皮案震驚了整個(gè)濱河市源梭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稍味,老刑警劉巖废麻,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異模庐,居然都是意外死亡烛愧,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)怜姿,“玉大人慎冤,你說(shuō)我怎么就攤上這事〔茁” “怎么了蚁堤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)但狭。 經(jīng)常有香客問(wèn)我披诗,道長(zhǎng),這世上最難降的妖魔是什么立磁? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任呈队,我火速辦了婚禮,結(jié)果婚禮上唱歧,老公的妹妹穿的比我還像新娘宪摧。我一直安慰自己,他們只是感情好颅崩,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布几于。 她就那樣靜靜地躺著,像睡著了一般沿后。 火紅的嫁衣襯著肌膚如雪孩革。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天得运,我揣著相機(jī)與錄音,去河邊找鬼锅移。 笑死熔掺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的非剃。 我是一名探鬼主播置逻,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼备绽!你這毒婦竟也來(lái)了券坞?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤肺素,失蹤者是張志新(化名)和其女友劉穎恨锚,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體倍靡,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猴伶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片他挎。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筝尾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出办桨,到底是詐尸還是另有隱情筹淫,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布呢撞,位于F島的核電站损姜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏狸相。R本人自食惡果不足惜薛匪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脓鹃。 院中可真熱鬧逸尖,春花似錦、人聲如沸瘸右。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至垢袱,卻和暖如春匿沛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吃谣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留做裙,地道東北人岗憋。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像锚贱,于是被迫代替她去往敵國(guó)和親仔戈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容