Skip to content

Commit 3b59ae5

Browse files
karlqi2000JoeOster
andauthored
DPC++/OpenCL interoperability sample (oneapi-src#500)
* Add License Signed-off-by: Karl Qi <karl.qi@intel.com> * OpenCL DPC++ Interoperability Samples * Update Readme.md Spelling corrections * Update README Signed-off-by: Karl Qi <karl.qi@intel.com> * README.md * Typo * Change to use cmake rather than make, and add more to README Signed-off-by: Karl Qi <karl.qi@intel.com> * Add DevCloud and Output Details Signed-off-by: Karl Qi <karl.qi@intel.com> * Change DevCloud URL Signed-off-by: Karl Qi <karl.qi@intel.com> * Use constexpr instead of #define and static const Signed-off-by: Karl Qi <karl.qi@intel.com> * Remove Windows as Supported Platform Signed-off-by: Karl Qi <karl.qi@intel.com> * Using SYCL 2020 style buffer Signed-off-by: Karl Qi <karl.qi@intel.com> * misc grammar and punctuation updates Co-authored-by: JoeOster <52936608+JoeOster@users.noreply.github.com>
1 parent 1b04720 commit 3b59ae5

File tree

9 files changed

+348
-0
lines changed

9 files changed

+348
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
cmake_minimum_required (VERSION 3.0)
2+
3+
set(CMAKE_CXX_COMPILER dpcpp)
4+
5+
# Set default build type to RelWithDebInfo if not specified
6+
if (NOT CMAKE_BUILD_TYPE)
7+
message (STATUS "Default CMAKE_BUILD_TYPE not set using Release with Debug Info")
8+
set (CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE
9+
STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel"
10+
FORCE)
11+
endif()
12+
project (opencl_interop)
13+
add_subdirectory (src)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Copyright Intel Corporation
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# DPC++ OpenCL&trade; Interoperability Example
2+
3+
This examples demonstrate how DPC++ can interact with OpenCL&trade;. This code shample will show programmers to incrementally migrate from
4+
OpenCL to DPC++. Two usage scenarios are shown. First is a DPC++ program that compiles and runs an OpenCL kernel. The second program converts OpenCL objects to DPC++.
5+
6+
For more information on migrating from OpenCL to DPC++, see [Migrating OpenCL Designs to DPC++](https://software.intel.com/content/www/us/en/develop/articles/migrating-opencl-designs-to-dpcpp.html).
7+
8+
| Optimized for | Description
9+
|:--- |:---
10+
| OS | Linux* Ubuntu* 18.04, 20
11+
| Hardware | Skylake or newer
12+
| Software | Intel&reg; oneAPI DPC++/C++ Compiler, Intel Devcloud
13+
| What you will learn | How OpenCL code can interact with DPC++ with the Intel&reg; oneAPI DPC++/C++ Compiler
14+
| Time to complete | 10 minutes
15+
16+
## Purpose
17+
For users migrating from OpenCL to DPC++, interoperability allows the migration to take place piecemeal so that the migration of all kernels does not have to occur simultaneously.
18+
19+
## Key Implementation Details
20+
The common OpenCL to DPC++ conversion scenarios are covered.
21+
1. In dpcpp_with_opencl_kernel.dp.cpp, the DPC++ program compiles and runs an OpenCL kernel. (For this, OpenCL must be set as the backend and not Level 0, the environment variable SYCL_DEVICE_FILTER=OPENCL is used)
22+
2. In dpcpp_with_opencl_objects.dp.cpp, the program converts OpenCL objects (Memory Objects, Platform, Context, Program, Kernel) to DPC++ and execute the program.
23+
24+
## License
25+
Code samples are licensed under the MIT license. See
26+
[License.txt](https://github.com/oneapi-src/oneAPI-samples/blob/master/License.txt) for details.
27+
28+
Third party program Licenses can be found here: [third-party-programs.txt](https://github.com/oneapi-src/oneAPI-samples/blob/master/third-party-programs.txt)
29+
30+
## Building the Program
31+
32+
> Note: if you have not already done so, set up your CLI
33+
> environment by sourcing the setvars script located in
34+
> the root of your oneAPI installation.
35+
>
36+
> Linux Sudo: . /opt/intel/oneapi/setvars.sh
37+
> Linux User: . ~/intel/oneapi/setvars.sh
38+
> Windows: C:\Program Files(x86)\Intel\oneAPI\setvars.bat
39+
40+
### Running Samples In DevCloud
41+
If running a sample in the Intel DevCloud, remember that you must specify the compute node (CPU, GPU, FPGA) and whether to run in batch or interactive mode. For more information, see the Intel® oneAPI Base Toolkit Get Started Guide (https://devcloud.intel.com/oneapi/get_started/baseToolkitSamples/)
42+
43+
### On a Linux* System
44+
Perform the following steps:
45+
1. Build the program
46+
```
47+
$ mkdir build
48+
$ cd build
49+
$ cmake ..
50+
$ make
51+
```
52+
53+
2. Run the program:
54+
```
55+
make run_prog1
56+
make run_prog2
57+
```
58+
59+
3. Clean the program using:
60+
```
61+
make clean
62+
```
63+
64+
### Example of Output
65+
```
66+
Device: Intel(R) HD Graphics 630 [0x5912]
67+
PASSED!
68+
Built target run_prog1
69+
70+
Kernel Loading Done
71+
Platforms Found: 3
72+
Using Platform: Intel(R) FPGA Emulation Platform for OpenCL(TM)
73+
Devices Found: 1
74+
Device: Intel(R) FPGA Emulation Device
75+
Passed!
76+
Built target run_prog2
77+
```

‎DirectProgramming/DPC++/OpenCLInterop/bin/.gitkeep‎

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"guid": "E3DE34BF-E5B6-44AF-8722-B2A5A6BE8D57",
3+
"name": "DPC++ OpenCL Interoperability Samples",
4+
"categories": [ "Toolkit/DirectProgramming/DPC++/opencl_interop/" ],
5+
"description": "Samples showing DPC++ OpenCL Interoperability",
6+
"toolchain": [ "dpcpp" ],
7+
"languages": [ { "cpp": { "properties": { "projectOptions": [ { "projectType": "makefile" } ] } } } ],
8+
"os": [ "linux" ],
9+
"builder": [ "ide", "make" ],
10+
"targetDevice": [ "CPU", "GPU" ],
11+
"ciTests": {
12+
"linux": [
13+
{
14+
"env": [ "source /opt/intel/oneapi/setvars.sh" ],
15+
"steps": [
16+
"mkdir build",
17+
"cd build",
18+
"cmake ..",
19+
"make",
20+
"make run_prog1",
21+
"make run_prog2",
22+
"make clean"
23+
]
24+
}
25+
]
26+
}
27+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++17")
2+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
3+
4+
add_executable(prog1 dpcpp_with_opencl_kernel.dp.cpp)
5+
target_link_libraries(prog1 OpenCL sycl)
6+
add_custom_target(run_prog1 ./prog1)
7+
8+
file(COPY ../src/vector_add_kernel.cl DESTINATION .)
9+
add_executable(prog2 dpcpp_with_opencl_objects.dp.cpp)
10+
target_link_libraries(prog2 OpenCL sycl)
11+
add_custom_target(run_prog2 ./prog2)
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//==============================================================
2+
// Copyright © 2021 Intel Corporation
3+
//
4+
// SPDX-License-Identifier: MIT
5+
// =============================================================
6+
7+
#include <stdlib.h>
8+
#include <CL/sycl.hpp>
9+
using namespace sycl;
10+
11+
constexpr int N = 1024;
12+
13+
void dpcpp_code(int *a, int *b, int *c) {
14+
queue q{default_selector()}; // Create Command Queue Targeting GPU
15+
std::cout << "Device: " << q.get_device().get_info<info::device::name>()
16+
<< std::endl;
17+
18+
program p(q.get_context()); // Create program from the same context as q
19+
20+
// Compile OpenCL vecAdd kernel. which is expressed as a C++ Raw String
21+
// as indicated by R”
22+
p.build_with_source(R"( __kernel void vecAdd(__global int *a,
23+
__global int *b,
24+
__global int *c)
25+
{
26+
int i=get_global_id(0);
27+
c[i] = a[i] + b[i];
28+
} )");
29+
30+
buffer buf_a(a, range(N));
31+
buffer buf_b(b, range(N));
32+
buffer buf_c(c, range(N));
33+
34+
q.submit([&](handler &h) {
35+
accessor A(buf_a, h, read_only);
36+
accessor B(buf_b, h, read_only);
37+
accessor C(buf_c, h, write_only);
38+
// Set buffers as arguments to the kernel
39+
h.set_args(A, B, C);
40+
// Launch vecAdd kernel from the p program object across N elements.
41+
h.parallel_for(range(N), p.get_kernel("vecAdd"));
42+
});
43+
}
44+
45+
int main(int argc, char **argv) {
46+
// Ensure to use OpenCL backend for OpenCL Kernel Compilation
47+
putenv((char *)"SYCL_DEVICE_FILTER=OPENCL");
48+
49+
size_t bytes = sizeof(int) * N;
50+
51+
int *a = (int *)malloc(bytes);
52+
int *b = (int *)malloc(bytes);
53+
int *c = (int *)malloc(bytes);
54+
for (int i = 0; i < N; ++i) {
55+
a[i] = i;
56+
b[i] = i * 2;
57+
}
58+
dpcpp_code(a, b, c);
59+
60+
for (int i = 0; i < N; ++i) {
61+
if (c[i] != i * 3) {
62+
std::cout << "FAILED!" << std::endl;
63+
return -1;
64+
}
65+
}
66+
std::cout << "PASSED!" << std::endl;
67+
68+
free(a);
69+
free(b);
70+
free(c);
71+
return 0;
72+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
//==============================================================
2+
// Copyright © 2021 Intel Corporation
3+
//
4+
// SPDX-License-Identifier: MIT
5+
// =============================================================
6+
7+
#include <CL/opencl.h>
8+
#include <stdio.h>
9+
#include <CL/sycl.hpp>
10+
using namespace sycl;
11+
12+
constexpr int MAX_SOURCE_SIZE = 0x100000;
13+
constexpr int N = 1024;
14+
15+
int main(int argc, char **argv) {
16+
size_t bytes = sizeof(float) * N;
17+
18+
cl_float *host_a = (cl_float *)malloc(bytes);
19+
cl_float *host_b = (cl_float *)malloc(bytes);
20+
cl_float *host_c = (cl_float *)malloc(bytes);
21+
for (int i = 0; i < N; ++i) {
22+
host_a[i] = i;
23+
host_b[i] = i * 2;
24+
}
25+
26+
FILE *fp;
27+
char *source_str;
28+
size_t source_size;
29+
fp = fopen("vector_add_kernel.cl", "r");
30+
if (!fp) {
31+
std::cerr << "Failed to load kernel file." << std::endl;
32+
}
33+
source_str = (char *)malloc(MAX_SOURCE_SIZE);
34+
source_size = fread(source_str, 1, MAX_SOURCE_SIZE, fp);
35+
fclose(fp);
36+
std::cout << "Kernel Loading Done" << std::endl;
37+
38+
// Get platform and device information
39+
cl_device_id device_id = NULL;
40+
cl_uint ret_num_devices;
41+
cl_uint ret_num_platforms;
42+
cl_int ret = clGetPlatformIDs(0, NULL, &ret_num_platforms);
43+
std::cout << "Platforms Found: " << ret_num_platforms << std::endl;
44+
45+
cl_platform_id *ocl_platforms = (cl_platform_id *)malloc(ret_num_platforms);
46+
ret = clGetPlatformIDs(ret_num_platforms, ocl_platforms, NULL);
47+
// Set Platform to Use
48+
int platform_index = 0;
49+
platform sycl_platform(ocl_platforms[platform_index]);
50+
std::cout << "Using Platform: "
51+
<< sycl_platform.get_info<info::platform::name>() << std::endl;
52+
53+
ret = clGetDeviceIDs(ocl_platforms[platform_index], CL_DEVICE_TYPE_ALL, 1,
54+
&device_id, &ret_num_devices);
55+
std::cout << "Devices Found: " << ret_num_devices << std::endl;
56+
57+
// Create an OpenCL context and queue
58+
cl_context ocl_context =
59+
clCreateContext(NULL, 1, &device_id, NULL, NULL, &ret);
60+
cl_command_queue ocl_queue =
61+
clCreateCommandQueueWithProperties(ocl_context, device_id, 0, &ret);
62+
63+
// Create a program from the kernel source, and build it
64+
cl_program ocl_program =
65+
clCreateProgramWithSource(ocl_context, 1, (const char **)&source_str,
66+
(const size_t *)&source_size, &ret);
67+
ret = clBuildProgram(ocl_program, 1, &device_id, NULL, NULL, NULL);
68+
69+
// OpenCL Kernel and Memory Objects
70+
cl_kernel ocl_kernel = clCreateKernel(ocl_program, "vector_add", &ret);
71+
cl_mem ocl_buf_a =
72+
clCreateBuffer(ocl_context, CL_MEM_READ_ONLY, bytes, NULL, NULL);
73+
cl_mem ocl_buf_b =
74+
clCreateBuffer(ocl_context, CL_MEM_READ_ONLY, bytes, NULL, NULL);
75+
cl_mem ocl_buf_c =
76+
clCreateBuffer(ocl_context, CL_MEM_READ_ONLY, bytes, NULL, NULL);
77+
clEnqueueWriteBuffer(ocl_queue, ocl_buf_a, CL_TRUE, 0, bytes, host_a, 0, NULL,
78+
NULL);
79+
clEnqueueWriteBuffer(ocl_queue, ocl_buf_b, CL_TRUE, 0, bytes, host_b, 0, NULL,
80+
NULL);
81+
82+
{ // DPC++ Application Scope
83+
// Construct SYCL versions of the context, queue, kernel, and buffers
84+
context sycl_context(ocl_context);
85+
queue sycl_queue(ocl_queue, sycl_context);
86+
std::cout << "Device: "
87+
<< sycl_queue.get_device().get_info<info::device::name>()
88+
<< std::endl;
89+
kernel sycl_kernel(ocl_kernel, sycl_context);
90+
buffer<int, 1> sycl_buf_a(ocl_buf_a, sycl_context);
91+
buffer<int, 1> sycl_buf_b(ocl_buf_b, sycl_context);
92+
buffer<int, 1> sycl_buf_c(ocl_buf_c, sycl_context);
93+
sycl_queue.submit([&](handler &h) {
94+
// Create accessors for each of the buffers
95+
accessor a_accessor(sycl_buf_a, h, read_only);
96+
accessor b_accessor(sycl_buf_b, h, read_only);
97+
accessor c_accessor(sycl_buf_c, h, write_only);
98+
// Map kernel arguments to accessors
99+
h.set_args(a_accessor, b_accessor, c_accessor);
100+
// Launch Kernel
101+
h.parallel_for(range<1>(N), sycl_kernel);
102+
});
103+
}
104+
// Read buffer content back to host array
105+
clEnqueueReadBuffer(ocl_queue, ocl_buf_c, CL_TRUE, 0, bytes, host_c, 0, NULL,
106+
NULL);
107+
108+
for (int i = 0; i < N; ++i) {
109+
if (host_c[i] != i * 3) {
110+
std::cout << "Failed!" << std::endl;
111+
return -1;
112+
}
113+
}
114+
std::cout << "Passed!" << std::endl;
115+
116+
ret = clReleaseCommandQueue(ocl_queue);
117+
ret = clReleaseKernel(ocl_kernel);
118+
ret = clReleaseProgram(ocl_program);
119+
ret = clReleaseMemObject(ocl_buf_a);
120+
ret = clReleaseMemObject(ocl_buf_b);
121+
ret = clReleaseMemObject(ocl_buf_c);
122+
ret = clReleaseContext(ocl_context);
123+
free(host_a);
124+
free(host_b);
125+
free(host_c);
126+
return 0;
127+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//==============================================================
2+
// Copyright © 2021 Intel Corporation
3+
//
4+
// SPDX-License-Identifier: MIT
5+
// =============================================================
6+
7+
__kernel void vector_add(__global const float *x, __global const float *y,
8+
__global float *restrict z) {
9+
// get index of the work item
10+
int index = get_global_id(0);
11+
// add the vector elements
12+
z[index] = x[index] + y[index];
13+
}

0 commit comments

Comments
 (0)