opencl demo 在华为荣耀8上测试

关于这个网上的资料很多,参考了各位前辈的文章,然后结合自己在使用过程中遇到的各种坑,摘录如下。
参考了这位前辈的文章:https://blog.csdn.net/wjskeepmaking/article/details/70080315

1.0 新建工程

1.1 新建一个android工程

工程类型选择,empty activity,在主逻辑中添加代码,这里只是添加了3个label,分别添加了3个textView,用来显示opencl的一些信息,并且用Opencl做一个简单的运算。

// 实例化我们的ocl测试对象
OpenclTest ocl = new OpenclTest();

// 用3个lable显示一些信息
TextView testView2=(TextView)findViewById(R.id.textView2);
TextView testView4=(TextView)findViewById(R.id.textView4);
TextView testView6=(TextView)findViewById(R.id.textView6);
testView6.setText(ocl.testopencl());
testView2.setText(ocl.getPlatformName());
testView4.setText(ocl.getDeviceName());

以下,我们分别定义这些缺失的jni类:
首先是activity_main.xml布局文件,一个大的垂直方向的layout,里面包含三个水平方向的layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/LinearLayout2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="58dp"
            android:text="OpenCL平台名称: "
            tools:layout_constraintTop_toTopOf="parent"
            tools:layout_editor_absoluteX="8dp" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="57dp"
        android:text="TextView"
        tools:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteX="87dp" />
    </LinearLayout>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/LinearLayout3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="OpenCL设备名称: "
            tools:layout_editor_absoluteX="40dp"
            tools:layout_editor_absoluteY="123dp" />

        <TextView
            android:id="@+id/textView4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView"
            tools:layout_editor_absoluteX="87dp"
            tools:layout_editor_absoluteY="123dp" />

    </LinearLayout>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/LinearLayout4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/textView5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="a[1024]={1}累加的值位: "
            tools:layout_editor_absoluteX="36dp"
            tools:layout_editor_absoluteY="187dp" />

        <TextView
        android:id="@+id/textView6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        tools:layout_editor_absoluteX="87dp"
        tools:layout_editor_absoluteY="178dp" />

    </LinearLayout>


</LinearLayout>

很简单,不再赘述。

1.2 定义jni接口java文件

接下来我们添加一个新的Java文件OpenclTest.java,定义我们的ocl jni接口:

package com.example.testjni;

public class OpenclTest {
    static {
        System.loadLibrary("ocl"); //之后ndk-build出来的so文件
        System.loadLibrary("GLES_mali"); // 手机里面pull出来的opencl库so文件
    }

    public String testOpencl(){
        return testopencl();
    }
    public String getplatformName(){
        return getPlatformName();
    }
    public String getdeviceName(){
        return getDeviceName();
    }

    // 定义jni接口
    public native String  testopencl();
    public native String getPlatformName();
    public native String getDeviceName();
}

这里特别要注意的是:opencl库的名称在不同平台下是不一样的!在高通平台叫libOpenCL.so,而在麒麟或者mtk平台叫libGLES_mali.so
对于Mali GPU,OpenCL Driver驱动对应系统"/system/vendor/lib/egl/libGLES_mali.so"
接下来,就可以开始用ndk编译我们的ocl.so了。

2.0 ndk编译

2.1 生成jni cpp头文件

javac OpenclTest.java -h .
# 这样就生成了`OpenclTest.class            com_example_ocl_demo_OpenclTest.h` 这两个文件

mkdir jni
mv com_example_ocl_demo_OpenclTest.h jni
cd jni

2.2 编写ocl.cpp实现上述jni接口

ocl.cpp内容如下

// 这里我指定了使用opencl的版本:1.2,因为之后的opencl头文件是我从github上下载
// 下来的,默认是最新版本,文档中说明了需要在include CL 头文件之前添加版本限定。
// 添不添加无所谓?
#define CL_TARGET_OPENCL_VERSION 120

#include <jni.h>
#include <CL/cl.h>
#include<malloc.h>
#include<stdio.h>
#include<stdlib.h>
#include"com_example_ocl_demo_OpenclTest.h"  // 我们刚刚生成的头文件
#include <android/log.h>

#define LOG_TAG "test"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#define LEN(arr) sizeof(arr) / sizeof(arr[0])
#define N 1024
#define NUM_THREAD 128

cl_uint num_device;
cl_uint num_platform;
cl_platform_id *platform;
cl_device_id *devices;
cl_int err;
cl_context context;
 cl_command_queue cmdQueue;
 cl_mem buffer,sum_buffer;
 cl_program program ;
 cl_kernel kernel;
 // cl 代码,需要动态编译的
 // 但是在一般的工程中,我们更喜欢把cl函数放到单独的“cl”文件中,这种方法后面会讲到
 const char* src[] = {
		 "  __kernel void redution(  \n"
		 "	__global int *data,     \n"
		 "	__global int *output,   \n"
		 "	__local int *data_local   \n"
		 "	)  \n"
		" {   \n"
		 "	int gid=get_group_id(0);   \n"
		 "	int tid=get_global_id(0);    \n"
		 "	int size=get_local_size(0);   \n"
		 "	int id=get_local_id(0);     \n"
		 "	data_local[id]=data[tid];   \n"
		 "	barrier(CLK_LOCAL_MEM_FENCE);   \n"
		 "	for(int i=size/2;i>0;i>>=1){    \n"
		 "		if(id<i){   \n"
		 "			data_local[id]+=data_local[id+i];   \n"
		 "		}   \n"
		 "		barrier(CLK_LOCAL_MEM_FENCE);   \n"
		 "	}    \n"
		 "	if(id==0){    \n"
		 "		output[gid]=data_local[0];   \n"
		 "	}    \n"
		" }   \n"

 };
int num_block;



// getPlatformName 的实现
 JNIEXPORT jstring Java_com_example_ocl_1demo_OpenclTest_getPlatformName(JNIEnv *env , jobject thisobject)
 {
	 	 char buffer[1024];
	 	 clGetPlatformInfo(platform[0],CL_PLATFORM_NAME,sizeof(buffer),buffer,NULL);
	 	return env->NewStringUTF(buffer);
 }

// getDeviceName 的实现
 JNIEXPORT jstring JNICALL Java_com_example_ocl_1demo_OpenclTest_getDeviceName(JNIEnv *env , jobject thisobject)
 {

	 char buffer[1024];
	 clGetDeviceInfo(devices[0],CL_DEVICE_NAME,sizeof(buffer),buffer,NULL);
	return env->NewStringUTF(buffer);
 }

// 初始化ocl设备
 void Init_OpenCL()
 {
	 size_t nameLen1;
	 char platformName[1024];

	 err = clGetPlatformIDs(0, 0, &num_platform);
	 platform=(cl_platform_id*)malloc(sizeof(cl_platform_id)*num_platform);
	 err = clGetPlatformIDs(num_platform, platform, NULL);

	 err=clGetDeviceIDs(platform[0],CL_DEVICE_TYPE_GPU,0,NULL,&num_device);
	 devices=(cl_device_id*)malloc(sizeof(cl_device_id)*num_device);
	 err=clGetDeviceIDs(platform[0],CL_DEVICE_TYPE_GPU,num_device,devices,NULL);

 }

 void Context_cmd()
 {
	 context=clCreateContext(NULL,num_device,devices,NULL,NULL,&err);
	 cmdQueue=clCreateCommandQueue(context,devices[0],0,&err);
 }

 void Create_Buffer(int *data)
 {

	 buffer=clCreateBuffer(context,CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR,sizeof(int)*N,data,&err);
	 sum_buffer=clCreateBuffer(context,CL_MEM_WRITE_ONLY,sizeof(int)*num_block,0,&err);
 }

 void Create_program()
 {
	 program=clCreateProgramWithSource(context, LEN(src), src, NULL, NULL);
	 err=clBuildProgram(program,num_device,devices,NULL,NULL,NULL);
	 kernel = clCreateKernel(program, "redution", NULL);
 }

void Set_arg()
{
	err=clSetKernelArg(kernel,0,sizeof(cl_mem),&buffer);
	err=clSetKernelArg(kernel,1,sizeof(cl_mem),&sum_buffer);
	err=clSetKernelArg(kernel,2,sizeof(int)*NUM_THREAD,NULL);
}

void Execution()
{
	const size_t globalWorkSize[1]={N};
		const size_t localWorkSize[1]={NUM_THREAD};
	  err=clEnqueueNDRangeKernel(cmdQueue,kernel,1,NULL,globalWorkSize,localWorkSize,0,NULL,NULL);
	  clFinish(cmdQueue);
}

void CopyOutResult(int*out)
{
	err=clEnqueueReadBuffer(cmdQueue,sum_buffer,CL_TRUE,0,sizeof(int)*num_block,out,0,NULL,NULL);
}


int  test()
{
	int* in,*out;
	 num_block=N/NUM_THREAD;
	in=(int*)malloc(sizeof(int)*N);
	out=(int*)malloc(sizeof(int)*num_block);
	for(int i=0;i<N;i++){
		in[i]=1;
	}
	Init_OpenCL();
	Context_cmd();
	Create_Buffer(in);
	Create_program();
	Set_arg();
	Execution();
	CopyOutResult(out);
	int sum=0;
	for(int i=0;i<num_block;i++){
		sum+=out[i];
	}
	return sum;
}

// testopencl 的实现
 JNIEXPORT jstring JNICALL Java_com_example_ocl_1demo_OpenclTest_testopencl (JNIEnv * env, jobject thisobject)
{
    char result[10];
    sprintf(result,"%d\n",test());
	 return env->NewStringUTF(result);
}

2.3 下载opencl头文件

这里编译还需要依赖opencl的头问题,从github上下载:

git clone https://github.com/KhronosGroup/OpenCL-Headers

把其中的CL整个文件夹拷贝到当前目录即可。当前目录结构:

$ tree .
.
├── CL
│   ├── cl.h
│   ├── cl_d3d10.h
│   ├── cl_d3d11.h
│   ├── cl_dx9_media_sharing.h
│   ├── cl_dx9_media_sharing_intel.h
│   ├── cl_egl.h
│   ├── cl_ext.h
│   ├── cl_ext_intel.h
│   ├── cl_gl.h
│   ├── cl_gl_ext.h
│   ├── cl_platform.h
│   ├── cl_va_api_media_sharing_intel.h
│   ├── cl_version.h
│   └── opencl.h
├── com_example_ocl_demo_OpenclTest.h
└── ocl.cpp

2.4 编写Android.mk Application.mk

在使用ndk编译cpp文件之前,我们还需要编写编译脚本,在android studio中便是 Android.mk Application.mk这两个文件。
新建Android.mk

# 其实就是指定目录、文件名及最终生成的模块名
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ocl
LOCAL_SRC_FILES := ocl.cpp
# libGLES_mali.so 是从手机中pull出来的,放到 app/src/main/jniLibs/armeabi-v7a/ 中
# 在我的手机上,使用 `adb pull /system/vendor/lib/egl/libGLES_mali.so /path-to-your-project/app/src/main/jniLibs/armeabi-v7a/`
# 同时需要添加 so 路径,否则会提示说找不到。
LOCAL_LDFLAGS += -llog -L/path-to-your-project/app/src/main/jniLibs/armeabi-v7a/ -lGLES_mali
include $(BUILD_SHARED_LIBRARY)

新建Application.mk

APP_STL := c++_static
APP_CPPFLAGS := -frtti -fexceptions -std=c++0x
APP_ABI := armeabi-v7a
# 你的安卓版本对应的 API level
APP_PLATFORM := android-26

2.5 ndk编译生成ocl.so

直接

ndk-build --debug # 编译我们的cpp文件
# 拷贝生成的ocl.so到 jniLibs/armeabi-v7a/ 目录下
cp ../libs/armeabi-v7a/libocl.so ../../../../../jniLibs/armeabi-v7a/

3.0 编译测试运行demo

编译,安装apk,运行,发现闪退:

java.lang.UnsatisfiedLinkError: dlopen failed: library "android.hardware.graphics.common@1.0.so" not found

查看Log发现无法找到某个so文件。
我们查看下 ocl.so 需要依赖的文件:

$ arm-linux-androideabi-readelf -dW libocl.so
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x00000001 (NEEDED)                     Shared library: [liblog.so]
 0x00000001 (NEEDED)                     Shared library: [libGLES_mali.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x0000000e (SONAME)                     Library soname: [libocl.so]

查看下 libGLES_mali.so 需要依赖的文件:

$ arm-linux-androideabi-readelf -dW libGLES_mali.so
 0x00000001 (NEEDED)                     Shared library: [android.hardware.graphics.common@1.0.so]
 0x00000001 (NEEDED)                     Shared library: [libutils.so]
 0x00000001 (NEEDED)                     Shared library: [liblog.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x00000001 (NEEDED)                     Shared library: [libc++.so]
 0x00000001 (NEEDED)                     Shared library: [libz.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x0000000e (SONAME)                     Library soname: [libGLES_mali.so]

果然是依赖 android.hardware.graphics.common@1.0.so 这个so,我们从手机里pull出来放在 jniLibs/armeabi-v7a/ 中

adb pull /system/lib/android.hardware.graphics.common@1.0.so /path-to-your-project/app/src/main/jniLibs/armeabi-v7a/

发现还是报上面那个错误,google了一下,发现有个帖子和我一样的情况。按照他的方法:
首先,rename android.hardware.graphics.common@1.0.solibfoo.so,同时修改android.mk添加-lfoo,然后在主
activity启动时,就load该so文件,(“Loaded libfoo.so as soon as possible”),再重新测试,发现该so终于被load成功了,
但是该so文件依赖的so文件还是缺失:

java.lang.UnsatisfiedLinkError: dlopen failed: library "libhidlbase.so" not found

我们查看下:

arm-linux-androideabi-readelf -dW  libfoo.so
 0x00000001 (NEEDED)                     Shared library: [libhidltransport.so]
 0x00000001 (NEEDED)                     Shared library: [libhwbinder.so]
 0x00000001 (NEEDED)                     Shared library: [liblog.so]
 0x00000001 (NEEDED)                     Shared library: [libutils.so]
 0x00000001 (NEEDED)                     Shared library: [libcutils.so]
 0x00000001 (NEEDED)                     Shared library: [libc++.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]

没办法,只能一个个从手机里面拷贝出来,相当繁琐。究其原因,其实是从Android 7.0开始,出于安全原因,某些系统so库无法被第三方应用直接load,没有办法,只能一个一个把所有依赖的so文件
都拷贝出来了。

adb pull \
/system/lib/libhidlbase.so \
/system/lib/libhidltransport.so \
/system/lib/libhwbinder.so \
/system/lib/libutils.so \
/system/lib/libcutils.so \
/system/lib/libc++.so \
/system/lib/libbase.so \
/system/lib/liblzma.so \
/system/lib/libunwind.so \
/system/lib/libvndksupport.so \
/system/lib/libbacktrace.so \
/path-to-your-project/app/src/main/jniLibs/armeabi-v7a/

终于,测试成功,demo成功运行在我的荣耀8,打印出平台名称“ARM Platfrom”,以及设备名称“Mali-T880”,以及ocl计算的一个例子结果输出。

3.1 cl文件单独编译

在编写OpenCL代码时,为了方便起见,我们更喜欢将kernel源代码放在单独的文件中(一般为*.cl)。这各做的缺点在于,程序需要在运行时动态读入文件中的代码为字符串,然后再传递给OpenCL的RT编译、执行。
因此,在可执行文件之外,我们还需要单独分发*.cl文件。比如在cpp文件中加入如下代码:

    FILE *program_handle;
    size_t program_size;
    char *program_buffer;

    // 把cl文件的源代码读到内存中
    program_handle = fopen(TEST_PROGRAM_FILE, "r");
    if(program_handle == NULL) {
        perror("Couldn't find the program file");
        exit(1);
    }
    fseek(program_handle, 0, SEEK_END);
    program_size = ftell(program_handle);
    rewind(program_handle);
    program_buffer = (char*)malloc(program_size + 1);
    program_buffer[program_size] = '\0';
    fread(program_buffer, sizeof(char), program_size, program_handle);
    fclose(program_handle);

    // 编译生成内核对象kernel
    program = clCreateProgramWithSource(context, program_size, (const char**)&program_buffer, NULL, NULL);
    free(program_buffer);

在pc上运行没问题,但是在Android平台,会找不到cl文件,导致fopen失败,在安卓上运行时,cl代码的相对路径可能和你编译时的相对路径不一致。
解决的办法就是把cl文件放入assets文件夹中,安卓代码调用getAssets().open("xxx.cl");然后用BufferedReader去读出所有的文本作为一个
string对象通过参数传递给jni层,这种方式是在运行时导入,另外一种方式可以在编译时包含,通过”include” 预编译命令将cl文件做为字符串导入,可以参考这篇博客

4.0 遇到的一些问题

aarch64-linux-android/bin/ld: skipping incompatible /Users/stv/work/proj/opencl_test/ocl_demo/app/src/main/jniLibs/arm64-v8a//libGLES_mali.so when searching for -lGLES_mali

在用ndk编译的时候,如果ABI选择arm64-v8a就会报错,看起来应该是libGLES_mali.so是32的导致的,所以,只能选择用32位编译。ABI选择armeabi-v7a

dlopen failed: "/data/app/com.example.testjni-JnlWe2K6cm55UsjxdLoz3Q==/lib/arm64/libocl.so" is 32-bit instead of 64-bit

也是32、64位的问题,
libocl.so是我再32位下armeabi-v7a 编译出来的,放到了jniLibs/arm64-v8a中,就报上面的错误了。
所以,arm64-v8a文件夹中必须放64位的程序!而且,这个arm64-v8a是优先查找的目录,如果找不到,才会到armeabi-v7a中找.

"xxx.so" is not accessible for the namespace...

谷歌从Android N开始,除了那些在Android NDK提供的库之外,限制了应用对系统私有库的加载。既然是限制了,想通过以前的方法去加载库已经行不通了。
所以目前的解决方法就是把应用需要加载的库和需要依赖的库从系统中pull出来,然后集成到自己的应用当中。就像我们之前所做的一样。还有一点,刚开始
调试了很久,也是这个报错。但是问题根源是我开始使用的是libOpenCL.so,而不是libGLES_mali.so。前者是高通平台的opencl driver驱动。
后者才是我华为手机上的驱动。可以参考这里

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该工具是用来下载所有华为手机ROM的神器!! 暂定系统要求: Windows Vista SP1的+ .Net框架4.5.2 拨号连接+ 说明: 寻找一个服务器以华为,找到的数据团结在一个共同的基础。 描述: 该程序正在寻找仓储和固件华为的设备。目前,常见的数据库是从不同的设备超过5000种不同的固件。今后,基地将通过团队团队的MT,谁想要找到您的设备的最新固件普通用户来补充。 由于没有为您的手机的简化版本与固件为您的设备显示的交易,下载和解压。 PC版的可能性: 搜索华为固件的各种设备 下载固件使用流式传输加载(下载速度增加) 通过代理服务器发送固件的设备 机会Android版本: 查看,下载,解压缩固件发现PC版的帮助 安装使用本地代理服务器固件 Предположительные системные требования: Windows Vista SP1+ .Net Framework 4.5.2 Dial-Up соединение+ Краткое описание: Ищет ПО на серверах Huawei, найденные данные объединяет в общую базу. Описание: Программа занимается поиском и складированием прошивок для устройств компании Huawei. На данный момент в общей базе данных находится более 5000 различных прошивок, от разных устройств. В дальнейшем база будет пополняться за счет команды Team MT и простых пользователей желающих найти самую свежую прошивку для своего устройства. Так же есть упрощенная версия для телефона которая занимается отображением прошивок для вашего устройства, скачиванием и распаковкой. Возможности PC версии: Поиск прошивок для различных устройств Huawei Скачивание прошивок при помощи потоковой загрузки(Увеличивает скорость скачивания) Отправка прошивок на устройство при помощи прокси сервера Возможности Android версии: Просмотр, скачивание, распаковка прошивок найденных при помощи PC версии Установка прошивок при помощи локального прокси сервера

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值