第三部分:Android 應用程序接口指南---第五節:計算---第一章 RenderScript


第1章 RenderScript

RenderScript提供一個獨立於平台並在本地運行的計算引擎,用它來加速你需要大量計算能力的應用。RenderScript是一個運行與Android上計算密集型的高性能架構。RenderScript主要是面向數據的並行計算,雖然串行計算密集型負載也不錯。RenderScript運行時將在所有可用的處理器上並行工作,如多核CPU,GPU或DSP,讓你專注於算法而不是調度工作或負載均衡。RenderScript對於圖像處理,計算攝影或計算機視覺這樣的應用程序尤其有用。對於RenderScript,你先要明白兩個概念:

1.高性能計算內核由C99語言編寫

2.Java API用於管理RenderScript的資源壽命和控制核心的執行

1.1 寫一個RenderScript內核

一個RenderScript內核通常駐留在一個.rs文件中,它在<project_root>/src/目錄下,每一個rs文件被叫做一個腳本。每個腳本包含它自己的一套內核,函數和變量。一個腳本可以包含:

◆編譯指示聲明(#pragma version(1))用來聲明一個RenderScript內核語言的版本。目前,1是唯一的有效值

◆編譯指示聲明(#pragma rs java_package_name(com.example.app))用來聲明一個Java類的包名

◆一些調用函數。一個調用函數是一個單線程RenderScript函數,你能從你的java代碼中使用任意參數來調用。這些經常用於初始化設置或在一個巨大的處理管道內進行串行計算。

◆一些腳本的全局變量,一個腳本的全局變量等價於一個在C中的全局變量。你能從java代碼中訪問腳本全局變量,並且這些變量經常被用於參數傳遞到RenderScript內核中。

◆一些計算內核,一個內核是一個並行函數,執行配置中的每一個元素。

下面是一個簡單的例子:如代碼清單1-1所示:

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;

}

 

代碼清單1-1

在許多方面,這是一個標准的C函數。第一個顯著的特點是__attribute__((kernel))應用函數原型。這是指此函數是一個RenderScript內核而不是調用函數。接下來的特性是在參數及其類型。在RenderScript內核,這是一個特殊的參數,它自動填充基於輸入配置傳遞給內核啟動。默認情況下,內核跨整個配置運行,在配置中每個元素都帶一個內核執行體。第三個顯著的特性是內核的返回類型。在輸出配置中,返回的值從內核自動寫入到適當的位置。RenderScript運行時檢查以確保輸入和輸出元素配置匹配內核原型,如果它們不匹配,則拋出一個異常。

一個內核可以有一個輸入配置或一個輸出配置,或兩者都有。一個內核可能沒有一個以上的輸入或輸出配置。如果一個以上的輸入和輸入是必須的,那些對象將結合rs_allocation 腳本全局變量並通過rsGetElementAt_type()或rsSetElementAt_type()從一個內核或者調用函數訪問。一個內核可以使用X,Y,Z參數訪問當前執行的坐標。這些參數是可選的,但坐標的參數類型必須是uint32_t

一個可選的init()函數。一個init()函數是一種特殊類型的調用函數,當腳本首次實例化時,需要調用它。這使得一些計算自動發生在腳本創建時。

一些靜態腳本全局變量和函數。靜態腳本全局變量幾乎等價於腳本全局變量,只是靜態不允許從java代碼調用。靜態函數是一個標准的C函數,它能被任意內核或調用函數調用,而不用暴露在Java API。如果一個腳本全局變量或函數不需要從java代碼中調用,那么強烈建議聲明為靜態的。

設置浮點精度

你可以在一個腳本中控制所需的浮點精度,如果完整的IEEE 754-2008標准是有用的。那么以下編譯指示可以設置一個不同的浮點精度等級:

◆#pragma rs_fp_full (默認如果沒有什么被指定): 應用程序需要通過IEEE754-2008標准的浮點精度概述。

◆#pragma rs_fp_relaxed應用程序不需要嚴格的IEEE 754-2008標准並可以允許不精確,對於de-norms計算這種模式啟動了清零(flush-to-zero),並且向零舍入(round-towards-zero)。

◆#pragma rs_fp_imprecise 應用程序不具有嚴格的精度要求。這種模式可以使一切rs_fp_relaxed 按照以下操作:

操作導致-0.0被+0.0代替返回

操作在INF和NAN在未定義。

大多數應用程序可以使用relaxed無任何副作用。這對於一些只提供relaxed精度的架構並需要額外優化的情況來說,這是非常有益的。(如SIMD CPU指令)

1.2 訪問RenderScript API

當開發使用RenderScript開發Android應用程序時,你能通過以下其中一種方式來訪問API:

◆android.renderscript這個API在Android3.0以上是可用的,這些對於RenderScript都是原始API並且當前沒有更新。

◆android.support.v8.renderscript這個API需要通過Support Library才可用,這樣允許你在Android2.2或更高版本中使用他們

官方強烈推薦使用Support Library API來訪問RenderScript。因為它們包含計算框架最新的改進以及支持更廣泛的設備兼容性。下面我們來使用RenderScript Support Library APIs

為了使用這個API,必須配置我們的開發環境。首選需要Android SDK Tool為22.2或更高。Android SDK Build 為18.1.0或更高。你能通過Android SDK Manager來更新安裝版本。注意RSSL(RenderScript Support Library)API目前不支持Android Studio或Gradle編譯。下面我們將在Eclipse中使用RSSL:

1.確保你有必要的Android SDK版本和編譯工具版本

2.打開項目中的project.properties文件

3.添加以下幾行到文件中

renderscript.target=18

renderscript.support.mode=true

sdk.buildtools=18.1.0

 

4.在你的類中使用RenderScript,導入Support Library:

import android.support.v8.renderscript.*;

project.properties設置了以上幾行來控制Android在編譯時的行為:

◆renderscript.target指定生成的字節碼版本。官方推薦你設置這個值為可用的最高值並且設置renderscript.support.mode為true。從11到最新的API LEVEL在這之間設置任意整形值都是合法的。如果你的最小SDK版本在manifest中指定的值也為當前的SDK版本的較高值,那么renderscript.target將被忽略並且將目標值設置為最小SDK版本。

◆renderscript.support.mode如果它在不支持目標版本的設備上運行時,指定生成的字節碼回滾兼容版本

◆sdk.buildtools指Android SDK編譯工具使用的版本。這個值被設置為18.1.0或更高,如果你不指定這個值,會使用你當前最高安裝的編譯工具版本。你應該設置這個值,確保在不同的開發機器上保持一致性

1.3 在Java代碼中使用RenderScript

在Java代碼中使用RS需要依賴android.renderscript或android.support.v8.renderscript包。大多數應用程序遵循基本的使用模式

1.初始化一個RenderScript的context。這個RenderScript context在create(Context)中被創建,確保RenderScript能被使用並且提供一個對象來控制后來的RenderScript對象的生命周期。你應該考慮context創建與一個潛在的長時間運行的操作,因為它可能在不同的硬件塊創建資源,如果可能的話,它不應該在應用程序的關鍵路徑。通常,一個應用程序在一個時間內只有一個單一的RenderScript context。

2.通過一個叫腳本創建至少一個Allocation。一個Allocation是一個RenderScript對象,它提供一個固定的數據量存儲。在腳本中的內核帶來Allocation對象作為它們的輸入輸出,當作為腳本全局變量綁定時,並且Allocation對象在內核中能被rsGetElementAt_type()和 rsSetElementAt_type()訪問。Allocation對象允許從java代碼到RenderScript代碼傳遞數組,相反的方向也可以。Allocation對象通常使用createTyped(RenderScript, Type)或createFromBitmap(RenderScript, Bitmap)來創建。

3.創建任意腳本是必要的。在使用RenderScript時,有2種腳本類型可用:

ScriptC:這些都是用戶定義的腳本描述在上面(1.1寫一個RenderScript內核)小節中。每個腳本有一個Java類反射,通過RenderScript編譯器可以很容易的從java代碼中訪問腳本;這個類命名被ScriptC_filename。例如如果內核位於invert.rs中並且RenderScript context已經位於mRS中,java代碼實例化腳本的方法如下:

ScriptC_invert invert = new ScriptC_invert(mRenderScript);

ScriptIntrinsic:這些是常見的操作:如高斯模糊,回旋,圖像融合。更多信息,請參考ScriptIntrinsic的子類。

4.數據填充Allocation。除了用android.renderscript創建Allocation,在首次創建時一個Allocation也能被空數據填充。為了填充一個Allocation,在Allocation里使用一個copy方法。

5. 設置必要的腳本全局變量。全局變量命令規則最好使用set_globalname。例如,為了設置一個int類型的elements名字變來那個,使用java方法set_elements(int)。RenderScript對象在內核中也能被設置,例如,rs_allocation變量命名為lookup,能被方法set_lookup(allocation)設置。

6.啟動適當的內核。方法來啟動一個給定的內核將被反射到同樣的ScriptC_filename類使用名為forEach_kernelanme()的方法。這些啟動器是異步的,並且啟動將按照他們的啟動順序被序列化。依賴你內核參數,這個方法將帶來一個或者兩個Allcation。默認情況下,一個內核將執行整個輸入或輸出Allcation;通過適當的Script.LaunchOptions作為最后一個參數到forEach方法來執行完一個Allcation的子集。調用函數能使用invoke_functionname方法來啟動,在同樣的ScriptrC_filename類中被反射。

7.出於Allocation對象復制數據。為了使用java代碼從Allocation中訪問數據,數據必須在Allocation中使用一個copy方法復制到java緩沖區, 這些函數將和異步內核一起同步並作為必要的函數啟。

8.拆除RenderScript context。RenderScript context能通過destroy()方法或通過垃圾回收來摧毀。這將導致后面使用任意附屬到context的對象拋出一個異常。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM