在OpenCL示例中更改平台

时间:2015-02-18 06:45:46

标签: android opencl

我正在尝试在与示例程序中的平台不同的平台上运行OpenCL示例。该示例适用于PowerVR,我想在Adreno gpu上运行。所以我添加了相应的libOpencl.so文件。但是我在运行时遇到错误:: 我应该做出哪些其他改变?

    02-18 08:59:04.207: I/Debug(19668): OpenCL lib Loaded
    02-18 08:59:04.216: E/art(19668): dlopen("/data/app/com.example.inversefilter-1/lib/arm/libInverseFilter.so", RTLD_LAZY) failed: dlopen failed: could not load library "jni/../external/libPVROCL.so" needed by "libInverseFilter.so"; caused by library "jni/../external/libPVROCL.so" not found
02-18 08:59:04.338: I/art(19668): Background sticky concurrent mark sweep GC freed 1703(98KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 11MB/11MB, paused 5.199ms total 38.674ms
02-18 08:59:04.496: I/Debug(19668): 853
02-18 08:59:04.496: I/DEBUG(19668): BEFORE runOpencl
02-18 08:59:04.498: E/art(19668): 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)
02-18 08:59:04.498: D/AndroidRuntime(19668): Shutting down VM
02-18 08:59:04.509: E/AndroidRuntime(19668): FATAL EXCEPTION: main
02-18 08:59:04.509: E/AndroidRuntime(19668): Process: com.example.inversefilter, PID: 19668
02-18 08:59:04.509: E/AndroidRuntime(19668): 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)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at com.example.inversefilter.MainActivity.initOpenCL(Native Method)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at com.example.inversefilter.MainActivity.onCreate(MainActivity.java:91)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at android.app.Activity.performCreate(Activity.java:5953)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1128)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2267)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2388)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at android.app.ActivityThread.access$800(ActivityThread.java:148)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1292)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at android.os.Handler.dispatchMessage(Handler.java:102)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at android.os.Looper.loop(Looper.java:135)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at android.app.ActivityThread.main(ActivityThread.java:5312)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at java.lang.reflect.Method.invoke(Native Method)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at java.lang.reflect.Method.invoke(Method.java:372)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
02-18 08:59:04.509: E/AndroidRuntime(19668):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:696)
02-18 08:59:07.510: I/Process(19668): Sending signal. PID: 19668 SIG: 9

我的cpp文件如下:

#include <jni.h>
#include <android/bitmap.h>
#include <android/log.h>

#include <string>
#include <cstring>
#include <sstream>
#include <vector>

/*
 * needed for loadProgram function
 */
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>


#include <sys/time.h>

#include <CL/cl.h>


// Commonly-defined shortcuts for LogCat output from native C applications.
#define  LOG_TAG    "AndroidBasic"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)


/* Container for all OpenCL-specific objects used in the sample.
 *
 * The container consists of the following parts:
 *   - Regular OpenCL objects, used in almost each
 *     OpenCL application.
 *   - Specific OpenCL objects - buffers, used in this
 *     particular sample.
 *
 * For convenience, collect all objects in one structure.
 * Avoid global variables and make easier the process of passing
 * all arguments in functions.
 */
struct OpenCLObjects
{
    // Regular OpenCL objects:
    cl_platform_id platform;
    cl_device_id device;
    cl_context context;
    cl_command_queue queue;
    cl_program program;
    cl_kernel kernel;

    // Objects that are specific for this sample.
    bool isInputBufferInitialized;
    cl_mem inputBuffer;
    cl_mem outputBuffer;
};

// Hold all OpenCL objects.
OpenCLObjects openCLObjects;

/*
 * Load the program out of the file in to a string for opencl compiling.
 */
inline std::string loadProgram(std::string input)
{
    std::ifstream stream(input.c_str());
    if (!stream.is_open()) {
        LOGE("Cannot open input file\n");
        exit(1);
    }
    return std::string( std::istreambuf_iterator<char>(stream),
                        (std::istreambuf_iterator<char>()));
}

/* This function helps to create informative messages in
 * case when OpenCL errors occur. The function returns a string
 * representation for an OpenCL error code.
 * For example, "CL_DEVICE_NOT_FOUND" instead of "-1".
 */
const char* opencl_error_to_str (cl_int error)
{
#define CASE_CL_CONSTANT(NAME) case NAME: return #NAME;

    // Suppose that no combinations are possible.
    switch(error)
    {
        CASE_CL_CONSTANT(CL_SUCCESS)
        CASE_CL_CONSTANT(CL_DEVICE_NOT_FOUND)
        CASE_CL_CONSTANT(CL_DEVICE_NOT_AVAILABLE)
        CASE_CL_CONSTANT(CL_COMPILER_NOT_AVAILABLE)
        CASE_CL_CONSTANT(CL_MEM_OBJECT_ALLOCATION_FAILURE)
        CASE_CL_CONSTANT(CL_OUT_OF_RESOURCES)
        CASE_CL_CONSTANT(CL_OUT_OF_HOST_MEMORY)
        CASE_CL_CONSTANT(CL_PROFILING_INFO_NOT_AVAILABLE)
        CASE_CL_CONSTANT(CL_MEM_COPY_OVERLAP)
        CASE_CL_CONSTANT(CL_IMAGE_FORMAT_MISMATCH)
        CASE_CL_CONSTANT(CL_IMAGE_FORMAT_NOT_SUPPORTED)
        CASE_CL_CONSTANT(CL_BUILD_PROGRAM_FAILURE)
        CASE_CL_CONSTANT(CL_MAP_FAILURE)
        CASE_CL_CONSTANT(CL_MISALIGNED_SUB_BUFFER_OFFSET)
        CASE_CL_CONSTANT(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST)
        CASE_CL_CONSTANT(CL_INVALID_VALUE)
        CASE_CL_CONSTANT(CL_INVALID_DEVICE_TYPE)
        CASE_CL_CONSTANT(CL_INVALID_PLATFORM)
        CASE_CL_CONSTANT(CL_INVALID_DEVICE)
        CASE_CL_CONSTANT(CL_INVALID_CONTEXT)
        CASE_CL_CONSTANT(CL_INVALID_QUEUE_PROPERTIES)
        CASE_CL_CONSTANT(CL_INVALID_COMMAND_QUEUE)
        CASE_CL_CONSTANT(CL_INVALID_HOST_PTR)
        CASE_CL_CONSTANT(CL_INVALID_MEM_OBJECT)
        CASE_CL_CONSTANT(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR)
        CASE_CL_CONSTANT(CL_INVALID_IMAGE_SIZE)
        CASE_CL_CONSTANT(CL_INVALID_SAMPLER)
        CASE_CL_CONSTANT(CL_INVALID_BINARY)
        CASE_CL_CONSTANT(CL_INVALID_BUILD_OPTIONS)
        CASE_CL_CONSTANT(CL_INVALID_PROGRAM)
        CASE_CL_CONSTANT(CL_INVALID_PROGRAM_EXECUTABLE)
        CASE_CL_CONSTANT(CL_INVALID_KERNEL_NAME)
        CASE_CL_CONSTANT(CL_INVALID_KERNEL_DEFINITION)
        CASE_CL_CONSTANT(CL_INVALID_KERNEL)
        CASE_CL_CONSTANT(CL_INVALID_ARG_INDEX)
        CASE_CL_CONSTANT(CL_INVALID_ARG_VALUE)
        CASE_CL_CONSTANT(CL_INVALID_ARG_SIZE)
        CASE_CL_CONSTANT(CL_INVALID_KERNEL_ARGS)
        CASE_CL_CONSTANT(CL_INVALID_WORK_DIMENSION)
        CASE_CL_CONSTANT(CL_INVALID_WORK_GROUP_SIZE)
        CASE_CL_CONSTANT(CL_INVALID_WORK_ITEM_SIZE)
        CASE_CL_CONSTANT(CL_INVALID_GLOBAL_OFFSET)
        CASE_CL_CONSTANT(CL_INVALID_EVENT_WAIT_LIST)
        CASE_CL_CONSTANT(CL_INVALID_EVENT)
        CASE_CL_CONSTANT(CL_INVALID_OPERATION)
        CASE_CL_CONSTANT(CL_INVALID_GL_OBJECT)
        CASE_CL_CONSTANT(CL_INVALID_BUFFER_SIZE)
        CASE_CL_CONSTANT(CL_INVALID_MIP_LEVEL)
        CASE_CL_CONSTANT(CL_INVALID_GLOBAL_WORK_SIZE)
        CASE_CL_CONSTANT(CL_INVALID_PROPERTY)

    default:
        return "UNKNOWN ERROR CODE";
    }

#undef CASE_CL_CONSTANT
}


/* The following macro is used after each OpenCL call
 * to check if OpenCL error occurs. In the case when ERR != CL_SUCCESS
 * the macro forms an error message with OpenCL error code mnemonic,
 * puts it to LogCat, and returns from a caller function.
 *
 * The approach helps to implement consistent error handling tactics
 * because it is important to catch OpenCL errors as soon as
 * possible to avoid missing the origin of the problem.
 *
 * You may chose a different way to do that. The macro is
 * simple and context-specific as it assumes you use it in a function
 * that doesn't have a return value, so it just returns in the end.
 */
#define SAMPLE_CHECK_ERRORS(ERR)                                                      \
    if(ERR != CL_SUCCESS)                                                             \
    {                                                                                 \
        LOGE                                                                          \
        (                                                                             \
            "OpenCL error with code %s happened in file %s at line %d. Exiting.\n",   \
            opencl_error_to_str(ERR), __FILE__, __LINE__                              \
        );                                                                            \
                                                                                      \
        return;                                                                       \
    }


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
    );
}


void shutdownOpenCL (OpenCLObjects& openCLObjects)
{

    cl_int err = CL_SUCCESS;

    if(openCLObjects.isInputBufferInitialized)
    {
        err = clReleaseMemObject(openCLObjects.inputBuffer);
        SAMPLE_CHECK_ERRORS(err);
    }

    err = clReleaseKernel(openCLObjects.kernel);
    SAMPLE_CHECK_ERRORS(err);

    err = clReleaseProgram(openCLObjects.program);
    SAMPLE_CHECK_ERRORS(err);

    err = clReleaseCommandQueue(openCLObjects.queue);
    SAMPLE_CHECK_ERRORS(err);

    err = clReleaseContext(openCLObjects.context);
    SAMPLE_CHECK_ERRORS(err);

}


extern "C" void Java_com_example_inversefilter_MainActivity_shutdownOpenCL
(
    JNIEnv* env,
    jobject thisObject
)
{
    shutdownOpenCL(openCLObjects);
    LOGD("shutdownOpenCL(openCLObjects) was called");
}


/*
 * Effect step.
 * This function is called each time you need to process the image.
 *
 * The function consists of the following parts:
 *   - reading input image content if changed
 *   - running OpenCL kernel on the input image
 *   - reading results of image processing
 */
void nativeInverseOpenCL
(
    JNIEnv* env,
    jobject thisObject,
    OpenCLObjects& openCLObjects,
    jobject inputBitmap,
    jobject outputBitmap
)
{
    using namespace std;

    AndroidBitmapInfo bitmapInfo;
    AndroidBitmap_getInfo(env, inputBitmap, &bitmapInfo);

    size_t bufferSize = bitmapInfo.height * bitmapInfo.stride;

    cl_uint rowPitch = bitmapInfo.stride / 4;
        cl_int err = CL_SUCCESS;

    // In this application the input buffer is allocated and initialized on demand
    // with the first call or when the Java side requests an update of the input image.

        if(openCLObjects.isInputBufferInitialized)
        {

            err = clReleaseMemObject(openCLObjects.inputBuffer);
            SAMPLE_CHECK_ERRORS(err);
        }

        LOGD("Creating input buffer in OpenCL");

        void* inputPixels = 0;
        AndroidBitmap_lockPixels(env, inputBitmap, &inputPixels);


        openCLObjects.inputBuffer =
            clCreateBuffer
            (
                openCLObjects.context,
                CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                bufferSize,   // Buffer size in bytes.
                inputPixels,  // Bytes for initialization.
                &err
            );
        SAMPLE_CHECK_ERRORS(err);

        openCLObjects.isInputBufferInitialized = true;

        // Do not forget to unlock pixels in the bitmap object.
        AndroidBitmap_unlockPixels(env, inputBitmap);


    void* outputPixels = 0;
    AndroidBitmap_lockPixels(env, outputBitmap, &outputPixels);

    cl_mem outputBuffer =
        clCreateBuffer
        (
            openCLObjects.context,
            CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR,
            bufferSize,    // Buffer size in bytes, same as the input buffer.
            outputPixels,  // Area, above which the buffer is created.
            &err
        );
    SAMPLE_CHECK_ERRORS(err);


    err = clSetKernelArg(openCLObjects.kernel, 0, sizeof(openCLObjects.inputBuffer), &openCLObjects.inputBuffer);
    SAMPLE_CHECK_ERRORS(err);

    err = clSetKernelArg(openCLObjects.kernel, 1, sizeof(outputBuffer), &outputBuffer);
    SAMPLE_CHECK_ERRORS(err);

    err = clSetKernelArg(openCLObjects.kernel, 2, sizeof(cl_uint), &rowPitch);
    SAMPLE_CHECK_ERRORS(err);


    /* Define the global iteration space for clEnqueueNDRangeKernel.
     * Every work-item running a kernel processes a single pixel
     * in the image. The global iteration space (globalSize)
     * is two-dimensional and exactly matches the image dimensions.
     *
     * To learn more about the OpenCL NDRange and find details on how
     * kernels are executed on OpenCL devices, refer to the
     * OpenCL specification, chapter 3.2 Execution Model.
     */
    size_t globalSize[2] = { bitmapInfo.width, bitmapInfo.height };


    timeval start;
    timeval end;

    gettimeofday(&start, NULL);

    err =
        clEnqueueNDRangeKernel
        (
            openCLObjects.queue,
            openCLObjects.kernel,
            2,
            0,
            globalSize,
            0,
            0, 0, 0
        );
    SAMPLE_CHECK_ERRORS(err);

    err = clFinish(openCLObjects.queue);
    SAMPLE_CHECK_ERRORS(err);

    gettimeofday(&end, NULL);

    float ndrangeDuration =
        (end.tv_sec + end.tv_usec * 1e-6) - (start.tv_sec + start.tv_usec * 1e-6);

    LOGD("NDRangeKernel time: %f", ndrangeDuration);
    err = clEnqueueReadBuffer (openCLObjects.queue,
            outputBuffer,
            true,
            0,
            bufferSize,
            outputPixels,
            0,
            0,
            0);
    SAMPLE_CHECK_ERRORS(err);

    // Call clFinish to guarantee that the output region is updated.
    err = clFinish(openCLObjects.queue);
    SAMPLE_CHECK_ERRORS(err);

    err = clReleaseMemObject(outputBuffer);
    SAMPLE_CHECK_ERRORS(err);

    AndroidBitmap_unlockPixels(env, outputBitmap);

    LOGD("nativeInverseOpenCL ends successfully");
}

extern "C" void Java_com_example_inversefilter_MainActivity_nativeInverseOpenCL
(
    JNIEnv* env,
    jobject thisObject,
    jobject inputBitmap,
    jobject outputBitmap
)
{
    nativeInverseOpenCL
    (
        env,
        thisObject,
        openCLObjects,
        inputBitmap,
        outputBitmap
    );
}

有人可以帮忙吗?

0 个答案:

没有答案