1

我正在使用带有 Android 4.3 的 Nexus 4 设备。此问题也可以在运行 Android 4.2.2 的 Lenovo K900 中重现。

代码没有在 GPU 上运行,只是在 CPU 上运行,因为我通过 ADB 检查了 CPU 使用率,它显示运行程序时 CPU 使用率超过 90%。

在粘贴代码之前,我尝试总结一下我遇到的问题。在我的项目中,我需要不断地处理一个(或多个)图像并将处理后的结果存储到另一个缓冲区中。根据我使用的算法的性质,我需要按图像行并行化图像处理操作(同时处理不同的图像行)。为此,我创建了一个只有行索引的分配,并使用此分配来调用 foreach 函数。我还在 RS 端创建了一个全局指针,并在 Java 端将另一个 1D Allocation 绑定到它,以便 RS 代码可以使用该指针将结果写入缓冲区。同时,我还需要为每次运行多次执行 foreach 函数。所以在Java中调用foreach函数时,我把它放在Java端的一个for循环中。然而,我遇到了一件很奇怪的事情。

在 MainActivity.java 中:

package com.example.slowrs;

import java.io.IOException;
import java.io.InputStream;

import com.example.slowrs.R;

import android.os.Bundle;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.Type;
import android.app.Activity;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.*;
import android.renderscript.*;

public class MainActivity extends Activity implements Button.OnClickListener{
    private Bitmap mBitmap;
    private RenderScript mRS;
    private ScriptC_test mTestScript;

    private Allocation mImgAlloc;
    private Allocation mRowAlloc;

    private TextView mTextView;
    private ImageView imgView;

    private String TAG = "test";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRS = RenderScript.create(this);

        mBitmap = getImageFromAssetsFile("input.png");
        imgView = (ImageView)findViewById(R.id.display);
        imgView.setImageBitmap(mBitmap);
        imgView.setOnClickListener(this);

        mTextView = (TextView)findViewById(R.id.label);

        mImgAlloc = Allocation.createFromBitmap(mRS, mBitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
        Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS));
        tb.setX(1); tb.setY(mImgAlloc.getType().getY());
        Type type = tb.create();
        // Parallelize w.r.t this
        mRowAlloc = Allocation.createTyped(mRS, type, Allocation.USAGE_SCRIPT);

        Type.Builder tb1 = new Type.Builder(mRS, Element.I32(mRS));
        tb1.setX(mImgAlloc.getType().getX()*mImgAlloc.getType().getY()); tb1.setY(1);
        Type type1 = tb1.create();
        Allocation newBufferAlloc = Allocation.createTyped(mRS, type1, Allocation.USAGE_SCRIPT);

        mTestScript = new ScriptC_test(mRS, getResources(), R.raw.test);
        mTestScript.set_image(mImgAlloc);
        mTestScript.bind_buffer(newBufferAlloc);
        mTestScript.set_imgWidth(mImgAlloc.getType().getX());
    }

    public void onClick(View v) {  
        // TODO Auto-generated method stub  
        Log.i(TAG, "touched");

        long timeBeforeExe = System.nanoTime();         

        for(int i = 0; i < 150; i++){
            mTestScript.forEach_slowTest(mRowAlloc);
        }

        long ct = System.nanoTime();
        long offset = ct - timeBeforeExe;
        float offsetInMs = (float)(offset)/1000000;
        mTextView.setText("Time: " + Float.toString(offsetInMs) + "ms");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    private Bitmap getImageFromAssetsFile(String fileName)
    {  
        Bitmap image = null;  
        AssetManager am = getResources().getAssets();  
        try  
        {  
            InputStream is = am.open(fileName);  
            image = BitmapFactory.decodeStream(is);  
            is.close();  
        }  
        catch (IOException e)  
        {  
            e.printStackTrace();  
        }  

        return image;
    }
}

在 test.rs 中:

#pragma version(1)
#pragma rs java_package_name(com.example.slowrs)
#pragma rs_fp_relaxed

int* buffer;
rs_allocation image;
int imgWidth;

void __attribute__((kernel)) slowTest(uchar in, uint32_t x, uint32_t y){
    for(int col = 0; col < imgWidth; col++){
        const uchar4 rightImgNextPixel = *(const uchar4*)rsGetElementAt(image, col, y);
        buffer[y * imgWidth + col] = rightImgNextPixel.x + 10;      
        //buffer[y * imgWidth + col] = 10;
    }
}

在activity_main.xml(布局)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <LinearLayout 
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:id="@+id/toplevel">

        <ImageView
            android:id="@+id/display"
            android:layout_width="320dip"
            android:layout_height="266dip" />

        <TextView
            android:id="@+id/label"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"
            android:text="Time:"
            android:padding="2dp"
            android:textSize="16sp"
            android:gravity="center" />
    </LinearLayout>
</RelativeLayout>

我粘贴的三个文件包含重现此问题的所有内容。基本上,我在代码中所做的是将图像加载到分配并显示在屏幕上。一旦图像被点击,onClick() 函数就会运行并调用 foreach 函数。

input.png

只是一个普通的 640*480 png 文件,我放在项目的 assets 文件夹中。任何具有相同大小的图像都可以。

我遇到的问题如下。当我轻轻点击图像(大约每秒一次)时,一切都是文件,UI 上的文本显示整个图像处理过程非常快(在几毫秒内)完成。但是,如果我更快地点击图像(尽可能快,基本上每秒点击 5 到 6 次),情况就会发生变化。UI 上的文字显示,某些点击需要 500 多毫秒才能完成(在 Nexus 4 上),而其他点击仍需要几毫秒。就我看到的,慢传比快传慢了100多倍,这很奇怪。

经过一些测试,我发现有两件事可以让这种突然的减速消失。我要么做

for(int i = 0; i < 1; i++){
    mTestScript.forEach_slowTest(mRowAlloc);
}

即在 Java 中使 for 循环更小,或者,

void __attribute__((kernel)) slowTest(uchar in, uint32_t x, uint32_t y){
    for(int col = 0; col < imgWidth; col++){
        const uchar4 rightImgNextPixel = *(const uchar4*)rsGetElementAt(image, col, y);
        //buffer[y * imgWidth + col] = rightImgNextPixel.x + 10;        
        buffer[y * imgWidth + col] = 10;
    }
}

在缓冲区中设置新值时不要参考 rightImgNextPixel.x。两者中的任何一个都会使减速消失。你可以自己测试一下。但是,我无法解释其中任何一个的原因。

发生了什么?这个问题让我抓狂,严重影响图像处理任务的性能。请帮忙,谢谢!

4

1 回答 1

4

您没有测量实际的执行时间。尝试添加 rs.finish() 或从您的操作中读取结果。RS 是异步的,它将操作排队,直到缓冲区填满或需要结果。因此内核启动的循环只是排队。

相关,我建议使用来自内核的返回值来写入输出缓冲区或 rsSetElementAt_uchar4 而不是绑定全局指针。RS 不保证 2D 内存的布局,并且在某些情况下,由于内存的步幅与宽度不同,此代码不会生成正确的结果。

于 2013-11-13T17:22:51.870 回答