Android + OpenCL 与 Eclipse

Posted

技术标签:

【中文标题】Android + OpenCL 与 Eclipse【英文标题】:Android + OpenCL with Eclipse 【发布时间】:2015-10-26 13:14:27 【问题描述】:

我正在努力让this example 在 Sony Xperia Z3 上工作。在手机上执行时,控制台会打印以下错误:

08-03 09:23:28.403: W/ResourceType(17385): Found multiple library tables,ignoring...
08-03 09:23:28.435: I/Debug(17385): OpenCL lib Loaded
08-03 09:23:28.564: I/Debug(17385): 853
08-03 09:23:28.564: I/DEBUG(17385): BEFORE runOpencl
08-03 09:23:28.567: E/art(17385): No implementation found for void com.example.inversefilter.MainActivity.initOpenCL(java.lang.String) (tried Java_com_example_inversefilter_MainActivity_initOpenCL and Java_com_example_inversefilter_MainActivity_initOpenCL__Ljava_lang_String_2)
08-03 09:23:28.568: D/androidRuntime(17385): Shutting down VM
08-03 09:23:28.570: E/AndroidRuntime(17385): FATAL EXCEPTION: main
08-03 09:23:28.570: E/AndroidRuntime(17385): Process: com.example.inversefilter, PID: 17385
08-03 09:23:28.570: E/AndroidRuntime(17385): java.lang.UnsatisfiedLinkError: No implementation found for void com.example.inversefilter.MainActivity.initOpenCL(java.lang.String) (tried Java_com_example_inversefilter_MainActivity_initOpenCL and Java_com_example_inversefilter_MainActivity_initOpenCL__Ljava_lang_String_2)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at com.example.inversefilter.MainActivity.initOpenCL(Native Method)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at com.example.inversefilter.MainActivity.onCreate(MainActivity.java:91)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at android.app.Activity.performCreate(Activity.java:5933)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2298)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2405)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at android.app.ActivityThread.access$800(ActivityThread.java:149)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1324)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at android.os.Handler.dispatchMessage(Handler.java:102)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at android.os.Looper.loop(Looper.java:211)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at android.app.ActivityThread.main(ActivityThread.java:5317)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at java.lang.reflect.Method.invoke(Native Method)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at java.lang.reflect.Method.invoke(Method.java:372)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1016)
08-03 09:23:28.570: E/AndroidRuntime(17385):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811)
08-03 09:23:28.584: I/Process(17385): Sending signal. PID: 17385 SIG: 9

我认为由于错误,它是某种链接器问题或什么的。问题是,我一般是 Android 开发的新手,尤其是使用 C++ 和 OpenCL。

这是我的 Application.mk

APP_STL := stlport_static
APP_CPPFLAGS := -fexceptions
APP_OPTIM := debug
APP_ABI := armeabi-v7a
APP_PLATFORM := android-14

这是Android.mk

LOCAL_PATH := $(call my-dir)
LOCAL_PATH_EXT := $(call my-dir)/../external/
include $(CLEAR_VARS)

LOCAL_MODULE := InverseFilter

LOCAL_CFLAGS += -DANDROID_CL
LOCAL_CFLAGS += -O3 -ffast-math

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../include

LOCAL_SRC_FILES := InverseFilter.cpp

#LOCAL_LDFLAGS += -ljnigraphics
LOCAL_LDLIBS := -llog -ljnigraphics
LOCAL_LDLIBS += $(LOCAL_PATH)/../external/libOpenCL.so

LOCAL_ARM_MODE := arm

include $(BUILD_SHARED_LIBRARY)LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := InverseFilter
LOCAL_SRC_FILES := InverseFilter.cpp

include $(BUILD_SHARED_LIBRARY)

InverseFilter.cpp 包含以下用于 initOpenCL 的方法

void initOpenCL
(
    JNIEnv* env,
    jobject thisObject,
    jstring kernelName,
    cl_device_type required_device_type,
    OpenCLObjects& openCLObjects
)

/*
 * This function picks and creates all necessary OpenCL objects
 * to be used at each filter iteration. The objects are:
 * OpenCL platform, device, context, command queue, program,
 * and kernel.
 *
 * Almost all of these steps need to be performed in all
 * OpenCL applications before the actual compute kernel calls
 * are performed.
 *
 * For convenience, in this application all basic OpenCL objects
 * are stored in the OpenCLObjects structure,
 * so, this function populates fields of this structure,
 * which is passed as parameter openCLObjects.
 * Consider reviewing the fields before going further.
 * The structure definition is in the beginning of this file.
 */

using namespace std;

// Will be used at each effect iteration,
// and means that you haven't yet initialized
// the inputBuffer object.
openCLObjects.isInputBufferInitialized = false;

// Search for the Intel OpenCL platform.
// Platform name includes "Intel" as a substring, consider this
// method to be a recommendation for Intel OpenCL platform search.
const char* required_platform_subname = "PowerVR";

// The following variable stores return codes for all OpenCL calls.
// In the code it is used with the SAMPLE_CHECK_ERRORS macro defined
// before this function.
cl_int err = CL_SUCCESS;

/* -----------------------------------------------------------------------
 * Step 1: Query for all available OpenCL platforms on the system.
 * Enumerate all platforms and pick one which name has
 * required_platform_subname as a sub-string.
 */

cl_uint num_of_platforms = 0;
// Get total number of the available platforms.
err = clGetPlatformIDs(0, 0, &num_of_platforms);
SAMPLE_CHECK_ERRORS(err);
LOGD("Number of available platforms: %u", num_of_platforms);

vector<cl_platform_id> platforms(num_of_platforms);
// Get IDs for all platforms.
err = clGetPlatformIDs(num_of_platforms, &platforms[0], 0);
SAMPLE_CHECK_ERRORS(err);

//cl_uint selected_platform_index = num_of_platforms;

LOGD("Platform names:");

cl_uint i = 0;
    // Get the length for the i-th platform name.
    size_t platform_name_length = 0;
    err = clGetPlatformInfo(
        platforms[i],
        CL_PLATFORM_NAME,
        0,
        0,
        &platform_name_length
    );
    SAMPLE_CHECK_ERRORS(err);

    // Get the name itself for the i-th platform.
    vector<char> platform_name(platform_name_length);
    err = clGetPlatformInfo(
        platforms[i],
        CL_PLATFORM_NAME,
        platform_name_length,
        &platform_name[0],
        0
    );
    SAMPLE_CHECK_ERRORS(err);

//selected_platform_index = 0;
openCLObjects.platform = platforms[0];


/* -----------------------------------------------------------------------
 * Step 2: Create context with a device of the specified type.
 * Required device type is passed as function argument required_device_type.
 * Use this function to create context for any CPU or GPU OpenCL device.
 */

cl_context_properties context_props[] = 
    CL_CONTEXT_PLATFORM,
    cl_context_properties(openCLObjects.platform),
    0
;

openCLObjects.context =
    clCreateContextFromType
    (
        context_props,
        required_device_type,
        0,
        0,
        &err
    );
SAMPLE_CHECK_ERRORS(err);

/* -----------------------------------------------------------------------
 * Step 3: Query for OpenCL device that was used for context creation.
 */

err = clGetContextInfo
(
    openCLObjects.context,
    CL_CONTEXT_DEVICES,
    sizeof(openCLObjects.device),
    &openCLObjects.device,
    0
);
SAMPLE_CHECK_ERRORS(err);

/* -----------------------------------------------------------------------
 * Step 4: Create OpenCL program from its source code.
 * The file name is passed bij java.
 * Convert the jstring to const char* and append the needed directory path.
 */
const char* fileName = env->GetStringUTFChars(kernelName, 0);
std::string fileDir;
fileDir.append("/data/data/com.example.inversefilter/app_execdir/");
fileDir.append(fileName);
fileDir.append(".cl");
std::string kernelSource = loadProgram(fileDir);
//std::string to const char* needed for the clCreateProgramWithSource       function
const char* kernelSourceChar = kernelSource.c_str();

openCLObjects.program =
    clCreateProgramWithSource
    (
        openCLObjects.context,
        1,
        &kernelSourceChar,
        0,
        &err
    );

SAMPLE_CHECK_ERRORS(err);

/* -----------------------------------------------------------------------
 * Step 5: Build the program.
 * During creation a program is not built. Call the build function explicitly.
 * This example utilizes the create-build sequence, still other options are applicable,
 * for example, when a program consists of several parts, some of which are libraries.
 * Consider using clCompileProgram and clLinkProgram as alternatives.
 * Also consider looking into a dedicated chapter in the OpenCL specification
 * for more information on applicable alternatives and options.
 */

err = clBuildProgram(openCLObjects.program, 0, 0, 0, 0, 0);

if(err == CL_BUILD_PROGRAM_FAILURE)

    size_t log_length = 0;
    err = clGetProgramBuildInfo(
        openCLObjects.program,
        openCLObjects.device,
        CL_PROGRAM_BUILD_LOG,
        0,
        0,
        &log_length
    );
    SAMPLE_CHECK_ERRORS(err);

    vector<char> log(log_length);

    err = clGetProgramBuildInfo(
        openCLObjects.program,
        openCLObjects.device,
        CL_PROGRAM_BUILD_LOG,
        log_length,
        &log[0],
        0
    );
    SAMPLE_CHECK_ERRORS(err);

    LOGE
    (
        "Error happened during the build of OpenCL program.\nBuild log:%s",
        &log[0]
    );

    return;


/* -----------------------------------------------------------------------
 * Step 6: Extract kernel from the built program.
 * An OpenCL program consists of kernels. Each kernel can be called (enqueued) from
 * the host part of an application.
 * First create a kernel to call it from the existing program.
 * Creating a kernel via clCreateKernel is similar to obtaining an entry point of a specific function
 * in an OpenCL program.
 */

openCLObjects.kernel = clCreateKernel(openCLObjects.program, "inverseKernel", &err);
SAMPLE_CHECK_ERRORS(err);

/* -----------------------------------------------------------------------
 * Step 7: Create command queue.
 * OpenCL kernels are enqueued for execution to a particular device through
 * special objects called command queues. Command queue provides ordering
 * of calls and other OpenCL commands.
 * This sample uses a simple in-order OpenCL command queue that doesn't
 * enable execution of two kernels in parallel on a target device.
 */

openCLObjects.queue =
    clCreateCommandQueue
    (
        openCLObjects.context,
        openCLObjects.device,
        0,    // Creating queue properties, refer to the OpenCL specification for details.
        &err
    );
SAMPLE_CHECK_ERRORS(err);

// -----------------------------------------------------------------------

LOGD("initOpenCL finished successfully");



extern "C" void Java_com_example_inversefilter_MainActivity_initOpenCL
(
JNIEnv* env,
jobject thisObject,
jstring kernelName
)

initOpenCL
(
    env,
    thisObject,
    kernelName,
    CL_DEVICE_TYPE_GPU,
    openCLObjects
);

这部分是在 MainActivity.java 中发生错误的地方

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
     //outputImageView = (ImageView)findViewById(R.id.imageView1);
     outputImageView = (ImageView)findViewById(R.id.imageView2);

        inputBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.leopard);
        Log.i("Debug", String.valueOf(inputBitmap.getWidth()));
        int imageWidth  = inputBitmap.getWidth();
        int imageHeight = inputBitmap.getHeight();
        // Two bitmap objects for the simple double-buffering scheme, where first bitmap object is rendered,
        // while the second one is being updated, then vice versa.
        outputBitmap = Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_8888);

    Log.i("DEBUG","BEFORE runOpencl");
    String kernelName = "inverse";
    copyFile("inverse.cl");
    initOpenCL(kernelName);
    nativeInverseOpenCL(
            inputBitmap,
            outputBitmap
        );
    shutdownOpenCL();
    Log.i("DEBUG","AFTER runOpencl");
    Log.i("Debug", String.valueOf(outputBitmap.getWidth()));

    outputImageView.setImageBitmap(outputBitmap);

非常感谢您的每一个帮助。

【问题讨论】:

【参考方案1】:

您对 JNI 函数的命名有误。 查看您的错误消息:“未找到 void com.example.inversefilter.MainActivity.initOpenCL(java.lang.String) 的实现(已尝试 Java_com_example_inversefilter_MainActivity_initOpenCL 和 Java_com_example_inversefilter_MainActivity_initOpenCL__Ljava_lang_String_2)”

根据您发布的内容,我无法确定您在 JAVA 代码中定义 initOpenCL jni 函数的位置。

假设com.example.inversefilter.jniLib.java中存在以下定义

public static native void initOpenCL();

你的 JNI 接口应该是:

Java_com_example_inversefilter_jniLib_initOpenCL

最简单的方法是让 javah 为您生成 JNI 接口以避免任何命名错误。检查这个:javah - C Header and Stub File Generator

【讨论】:

以上是关于Android + OpenCL 与 Eclipse的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法打破android中openCL内存的限制?

在 Android 中调用 OpenCL 函数时未定义的引用

Android Studio配置并运行OpenCL的报错

为啥 Google 选择 RenderScript 而不是 OpenCL [关闭]

在 OpenCL 内核 (android) 中读取 GL_UNSIGNED_BYTE OpenGL texture2D

失败经验分享android下使用支持opencl的cv::dft()