OpenCL™ JumpStart Tutorial

Download PDF

1. Introduction

OpenCL™ JumpStart Kit is a plug-in for Microsoft Visual Studio* that enables developers to quickly create OpenCL projects in the Visual Studio IDE. JumpStart is a new feature in the Intel® SDK for OpenCL™ Applications 2014 Beta.

Creating an OpenCL application involves a lot of boiler-plate code: create/query the platform, create/query devices, create context, command queues, etc. At every step proper error checking on the return codes of every API call needs to be done too. JumpStart allows you to create OpenCL projects from existing templates or based on existing samples with all the object creation and OpenCL setup done automatically, so that you can start working on the algorithm or application design. It also allows you to create empty projects. You can read more about JumpStart and its features in the Intel® SDK for OpenCL™ Applications 2014 Beta – User’s Guide.

In this tutorial we will show you how to use JumpStart to create an image processing application for Sobel edge detection of a given image, by creating an OpenCL project based on a project template.

This tutorial is intended for beginners with no prior OpenCL experience to help them get started with OpenCL development.

2. Creating a project from a template

Download and install the Intel SDK for OpenCL Applications.

To create a project from a template, create a new OpenCL project by selecting File: New: Project or by typing Ctrl+Shift+N. Select OpenCL under Visual C++ templates. Select the location of the project and enter a name for the project, say sobel. Click Ok, and the Code Builder wizard will be displayed.

Select "OpenCL Project from template" as shown below and click Next.

You will then be presented with the Template project settings dialog. Enter names for the OpenCL file and kernel as shown below:

In the settings dialog you can set the target device (CPU or GPU) and change other settings as appropriate. For this exercise keep all the default settings and click Finish to complete the project creation. The following are the auto-generated files and a brief explanation.

  • utils.[h|cpp] – define functions for reading OpenCL kernel file and for logging debug and error messages
  • <sample_name>.cpp – is the main file where all the OpenCL host-side processing happens including building the kernel, setting kernel arguments, enqueuing the kernel, etc.
  • <sample_name>.cl – is the main OpenCL kernel file

The project can be built and run successfully, but this doesn’t do much. In the next section we will show how to make simple changes to the project to create our application.

3. Creating Sobel filter application

First we need to add code to open and read the input image file to which we will apply the edge detection. We will use OpenCV for doing so, but it can be done in a number of other ways.


// Read and normalize the input image
Mat ReadInputImage(const std::string &fileName, int flag, int alignCols, int alignRows)
{
	Size dsize;
	Mat img = imread(fileName, flag);

	if (! img.empty())
	{
		// Make sure that the input image size is fit:
		// number of rows is multiple of 8
		// number of columns is multiple of 64
		dsize.height = ((img.rows % alignRows) == 0) ? img.rows : (((img.rows + 
			alignRows - 1) / alignRows) * alignRows);
		dsize.width = ((img.cols % alignCols) == 0) ? img.cols : (((img.cols + 
			alignCols - 1) / alignCols) * alignCols);
		resize(img, img, dsize);
	}

	return img;
}



Since our Sobel kernel accepts an input buffer and an output buffer as arguments, we will modify CreateBufferArguments() function to delete the creation of the second input buffer (delete the below lines):


    // Create second memory object based on host memory inputB
    ocl->srcB = clCreateBuffer(ocl->context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, 
sizeof(cl_uint) * arrayWidth * arrayHeight, inputB, &err);
    if ((ocl->srcB == (cl_mem)0) || (CL_SUCCESS != err))
    {
        LogError("ERROR: Failed to create srcB buffer (%s)n", 
TranslateOpenCLError(err));
        return -1;
    }



Accordingly, we will change the function signature from


    int CreateBufferArguments(ocl_args_d_t *ocl, cl_uint* inputA, cl_uint* inputB, 
	cl_uint* outputC, cl_uint arrayWidth, cl_uint arrayHeight)



to


    int CreateBufferArguments(ocl_args_d_t *ocl, cl_uchar* inputA, cl_uchar* outputC, 
	cl_uint arrayWidth, cl_uint arrayHeight)



Next we will modify the SetKernelArguments() function as shown below:


// Set the arguments for the kernel
cl_uint SetKernelArguments(ocl_args_d_t *ocl, cl_uint width, cl_uint height)
{
    cl_int err = CL_SUCCESS;

    err  =  clSetKernelArg(ocl->kernel, 0, sizeof(cl_mem), (void *) &ocl->srcA);
    //err  |= clSetKernelArg(ocl->kernel, 1, sizeof(cl_mem), (void *) &ocl->srcB);
    err  |= clSetKernelArg(ocl->kernel, 1, sizeof(cl_mem), (void *) &ocl->dstMem);
    err  |= clSetKernelArg(ocl->kernel, 2, sizeof(cl_mem), (void *) &width);
    err  |= clSetKernelArg(ocl->kernel, 3, sizeof(cl_mem), (void *) &height);
    if (err != CL_SUCCESS)
    {
        LogError("ERROR: Failed to set input g_kernel arguments, returned %sn", 
		TranslateOpenCLError(err));
    }

    return err;
}



Modify ReadOutputBuffer to return unsigned char data instead of unsigned int.


// Execute the kernel
cl_uchar *ReadOutputBuffer(ocl_args_d_t *ocl, cl_uint arrayWidth, cl_uint arrayHeight)
{
    …
    …
    tmpPtr = clEnqueueMapBuffer(ocl->commandQueue, ocl->dstMem, true, CL_MAP_READ, 0, 
		sizeof(cl_uchar) * arrayWidth * arrayHeight, 0, NULL, NULL, &err);
    …
    …

    return (cl_uchar *)tmpPtr;
}



Finally in the main() routine, change the code to use the image data for OpenCL processing instead of the random input. Delete the input generation and buffer allocation of inputA, inputB, and output. Make the following changes:


// Read the input image
	Mat img_src = ReadInputImage(OCL_SAMPLE_IMAGE_NAME, CV_8UC1, 8, 64);
	if (img_src.empty())
	{
		LogError("Cannot read image file: %sn", OCL_SAMPLE_IMAGE_NAME);
		return -1;
	}

	Mat img_dst = Mat::zeros(img_src.size(), CV_8UC1);

	imshow("Before:", img_src);
	waitKey();


	cl_uint arrayWidth  = img_src.cols;
	cl_uint arrayHeight = img_src.rows;


    // Create OpenCL buffers from host memory
    // These buffers will be used later by the OpenCL kernel
    if (CL_SUCCESS != CreateBufferArguments(&ocl, img_src.ptr(), img_dst.ptr(), 
                                            arrayWidth, arrayHeight))
    {
        return -1;
    }



Pass the required kernel arguments:


// Set kernel arguments
if (CL_SUCCESS != SetKernelArguments(&ocl, arrayWidth, arrayHeight))



Read the output buffer and display the result of applying the kernel to the input image:


// Read the output buffer (map/unmap it to the host memory)
    if (NULL == ReadOutputBuffer(&ocl, arrayWidth, arrayHeight))
    {
        return -1;
    }

    

    imshow("After:", img_dst);
    waitKey();



4. Summary

In this basic tutorial we showed how to quickly create an OpenCL application using the new JumpStart kit which is part of the Intel SDK for OpenCL Applications 2014. We showed two ways to create a project and showed how to make minimal changes to implement an OpenCL sobel edge detection application.

 

Intel and the Intel logo are trademarks of Intel Corporation in the U.S. and/or other countries.
Copyright © 2014 Intel Corporation. All rights reserved.
*Other names and brands may be claimed as the property of others.

OpenCL and the OpenCL logo are trademarks of Apple Inc and are used by permission by Khronos.

 

For more complete information about compiler optimizations, see our Optimization Notice.