2016-11-15 1 views
1

demandé également ici sans chance (https://groups.google.com/forum/#!topic/android-developers/Rh_L9Jv_S8Q)Copie vers et de l'allocation de F16 demi-précision dans Android (renderScript)

J'essaie de comprendre comment faire demi-précision en utilisant des types comme half et half4. Le seul problème semble être d'obtenir les nombres de Java à rendu et retour.

Le code Java:

private float[] input; 
private float[] half_output; 
private RenderScript mRS; 
private ScriptC_mono mScript; 
private final int dimen = 15; 
... 

//onCreate 
input = new float[dimen * dimen * 3];  //later loaded from file 182.24 3.98 105.83 226.08 15.2 80.01... 
half_output = new float[dimen * dimen * 3]; 
... 

//function calling renderscript 
mRS = RenderScript.create(this); 
ScriptC_halfPrecision mScript = new ScriptC_halfPrecision(mRS); 

Allocation input2 = Allocation.createSized(mRS, Element.F16(mRS), dimen * dimen * 3); 
input2.copyFromUnchecked(input);   //copy float values to F16 allocation 

Allocation halfIndex = Allocation.createSized(mRS, Element.F16(mRS), dimen * dimen); 
Type.Builder half_output_type = new Type.Builder(mRS, Element.F16(mRS)).setX(dimen * dimen * 3); 
Allocation output3 = Allocation.createTyped(mRS, half_output_type.create()); 

mScript.set_half_in(input2); 
mScript.set_half_out(output3); 
mScript.forEach_half_operation(halfIndex); 

output3.copy1DRangeToUnchecked(0, dimen * dimen * 3, half_output); //copy F16 allocation back to float array 

Le renderScript:

#pragma version(1) 
#pragma rs java_package_name(com.example.android.rs.hellocompute) 

rs_allocation half_in; 
rs_allocation half_out; 

half __attribute__((kernel)) half_operation(uint32_t x) { 
    half4 out = rsGetElementAt_half4(half_in, x); 

    out.x /= 2.0; 
    out.y /= 2.0; 
    out.z /= 2.0; 
    out.w /= 2.0; 

    rsSetElementAt_half4(half_out, out, x); 
} 

J'ai aussi essayé cela au lieu de la dernière ligne indiqué dans le code Java:

float temp_half[] = new float[1]; 
for (int i = 0; i < dimen * dimen * 3; ++i) {  //copy F16 allocation back to float array 
    output3.copy1DRangeToUnchecked(i, 1, temp_half); 
    half_output[i]=temp_half[0]; 
} 

Al l le code ci-dessus fonctionne parfaitement pour float4 variables dans le rendu et F32 allocations dans le java. C'est évidemment parce qu'il n'y a aucun problème allant du rendu float à Java float. Mais en essayant d'aller de Java float (puisqu'il n'y a pas de Java half) à half renderscript et de retour est très difficile. Quelqu'un peut-il me dire comment le faire?

Les deux versions ci-dessus du code java résultent en des valeurs apparemment aléatoires dans le tableau half_output. Ils ne sont évidemment pas aléatoires car ce sont les mêmes valeurs chaque fois que je l'exécute, quelle que soit l'opération dans la fonction half_operation(uint32_t x). J'ai essayé de changer le out.x /= 2.0; (et le code y, z, w correspondant) en out.x /= 2000000.0; ou out.x *= 2000000.0; et toujours les valeurs qui finissent dans le tableau half_output sont les mêmes chaque fois que je l'exécute.

Saisie de 182.24 3.98 105.83 226.08 15.2 80.01...

En utilisant cette java

output3.copy1DRangeToUnchecked(0, dimen * dimen * 3, half_output); //copy F16 allocation back to float array 

Le half_output résultant est 46657.44 27094.48 3891.45 965.1825 36223.44 14959.08...

En utilisant cette java

float temp_half[] = new float[1]; 
for (int i = 0; i < dimen * dimen * 3; ++i) {  //copy F16 allocation back to float array 
    output3.copy1DRangeToUnchecked(i, 1, temp_half); 
    half_output[i]=temp_half[0]; 
} 

Le half_output résultant est 2.3476E-41 2.5546E-41 6.2047E-41 2.5407E-41 1.9802E-41 2.4914E-41...

Encore une fois ce sont les résultats, peu importe ce que je change l'algorithme out.x /= 2.0; à.

Répondre

0

Le problème est que cette copie n'effectue pas de conversion. Il mettra simplement vos valeurs sources FP32 en mémoire, mais quand vous essayez et interprétez ces valeurs comme FP16, elles seront incorrectes.

input2.copyFromUnchecked(input);   //copy float values to F16 allocation 

Vous pourriez le port quelque chose comme la réponse de cette question à renderScript:

32-bit to 16-bit Floating Point Conversion

Si votre entrée n'a pas denorms/infini/nan/débordement haut/bas, cela semble être un ok solution:

uint32_t x = *((uint32_t*)&f); 
uint16_t h = ((x>>16)&0x8000)|((((x&0x7f800000)-0x38000000)>>13)&0x7c00)|((x>>13)&0x03ff); 

vraiment la solution est d'avoir vos valeurs de source dans le fichier au format binaire FP16 déjà. Lisez-les dans un tableau java byte [], puis effectuez la copie dans l'allocation d'entrée fp16. Ensuite, lorsque le noyau de rendu interprète comme fp16, vous ne devriez pas avoir de problème.