• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "image_openclsetup.h"
16 #include "image_opencl_wrapper.h"
17 #include <cstdio>
18 #include <cstring>
19 #include "securec.h"
20 #include "vpe_log.h"
21 
22 // OpenCL Init
23 // Enable MY_DEBUG to support printf in kernel.
24 #define MY_TUNING
25 #ifdef MY_DEBUG
printf_callback(const char * buffer,size_t len,size_t complete,void * usr_data)26 static void printf_callback(const char *buffer, size_t len, size_t complete, void *usr_data)
27 {
28     VPE_LOGI("output length: %{public}d\noutput data: \n%{public}s\n", (cl_uint)len, buffer);
29 }
30 #endif
31 
InitContext(ClContext * pCtx)32 static cl_int InitContext(ClContext *pCtx)
33 {
34     cl_int status = CL_SUCCESS;
35     // Get platform number
36     pCtx->numPlatforms = 0;
37     status = clGetPlatformIDs(0, nullptr, &pCtx->numPlatforms);  // ~{DZ4fP9B)~}
38     CHECK_AND_RETURN_RET_LOG(!((status != CL_SUCCESS) || (pCtx->numPlatforms == 0)), status,
39         "[GPU]: Fail to get platform IDs1. (clGetPlatformIDs).");
40     VPE_LOGI("(clGetPlatformIDs). status = %{public}d, numPlatforms = %{public}d\n", status, pCtx->numPlatforms);
41 
42     // Allocate tables for platform IDs, devices list header, devices number and device index
43     cl_uint numValue = pCtx->numPlatforms;
44     pCtx->platforms = reinterpret_cast<cl_platform_id *>(malloc(pCtx->numPlatforms * sizeof(cl_platform_id)));
45     CHECK_AND_RETURN_RET_LOG(pCtx->platforms != nullptr, CL_INVALID_VALUE, "pCtx platforms null!");
46     CHECK_AND_LOG(memset_sp(pCtx->platforms, numValue * sizeof(cl_platform_id), 0,
47         numValue * sizeof(cl_platform_id)) == 0, "memsetFail");
48     pCtx->numDevices = reinterpret_cast<cl_uint *>(malloc(pCtx->numPlatforms * sizeof(cl_uint)));
49     CHECK_AND_RETURN_RET_LOG(pCtx->numDevices != nullptr, CL_INVALID_VALUE, "pCtx numDevices null!");
50     CHECK_AND_LOG(memset_sp(pCtx->numDevices, numValue * sizeof(cl_uint), 0, numValue * sizeof(cl_uint)) == 0,
51         "memsetFail");
52     pCtx->idxDevices = reinterpret_cast<cl_uint *>(malloc(pCtx->numPlatforms * sizeof(cl_uint)));
53     CHECK_AND_RETURN_RET_LOG(pCtx->idxDevices != nullptr, CL_INVALID_VALUE, "pCtx idxDevices null!");
54     CHECK_AND_LOG(memset_sp(pCtx->idxDevices, numValue * sizeof(cl_uint), 0, numValue * sizeof(cl_uint)) == 0,
55         "memsetFail");
56     pCtx->devices = reinterpret_cast<cl_device_id **>(malloc(pCtx->numPlatforms * sizeof(cl_device_id *)));
57     CHECK_AND_RETURN_RET_LOG(pCtx->devices != nullptr, CL_INVALID_VALUE, "pCtx devices null!");
58     CHECK_AND_LOG(memset_sp(pCtx->devices, numValue * sizeof(cl_device_id *), 0,
59         numValue * sizeof(cl_device_id *)) == 0, "memsetFail");
60     for (cl_uint i = 0; i < pCtx->numPlatforms; i++) {
61         pCtx->devices[i] = nullptr;
62     }
63     return status;
64 }
65 
GetDevice(ClContext * pCtx)66 static cl_int GetDevice(ClContext *pCtx)
67 {
68     cl_int status = CL_SUCCESS;
69     // Get platforms
70     status = clGetPlatformIDs(pCtx->numPlatforms, pCtx->platforms, nullptr);
71     CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status,
72         "[GPU]: Fail to get platform IDs2. (clGetPlatformIDs).");
73 
74     // Get all devices for each of platform
75     for (cl_uint i = 0; i < pCtx->numPlatforms; i++) {
76         cl_uint numDevices = 0;
77         status = clGetDeviceIDs(pCtx->platforms[i], CL_DEVICE_TYPE_ALL, 0, nullptr, &numDevices);
78         CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status,
79             "[GPU]: Fail to get device IDs3. (clGetDeviceIDs).");
80 
81         pCtx->numDevices[i] = numDevices;
82         pCtx->idxDevices[i] = (cl_uint)(-1);
83 
84         if (numDevices == 0) {
85             pCtx->devices[i] = nullptr;
86             continue;
87         }
88 
89         pCtx->devices[i] = reinterpret_cast<cl_device_id *>(malloc(numDevices * sizeof(cl_device_id)));
90         CHECK_AND_RETURN_RET_LOG(pCtx->devices[i] != nullptr, -1, "pCtx devices[i] null!");
91 
92         status = clGetDeviceIDs(pCtx->platforms[i], CL_DEVICE_TYPE_ALL, numDevices, pCtx->devices[i], nullptr);
93         CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status,
94             "[GPU]: Fail to get device IDs4. (clGetDeviceIDs).");
95     }
96     return status;
97 }
98 
ChoosePlatform(ClContext * pCtx,cl_uchar infoBuf[],size_t lenInfoBuf,const char * vendorName)99 static cl_int ChoosePlatform(ClContext *pCtx, cl_uchar infoBuf[], size_t lenInfoBuf, const char *vendorName)
100 {
101     cl_int status = CL_SUCCESS;
102     CHECK_AND_RETURN_RET_LOG(lenInfoBuf <= INFO_BUFFER_LENGTH, CL_DEVICE_NOT_FOUND, "lenInfoBuf>128!");
103     // Choose vendorName platform to work
104     pCtx->idxPlatforms = (cl_uint)(-1);
105     if (pCtx->numPlatforms == 1) {
106         pCtx->idxPlatforms = 0;
107     } else {
108         for (cl_uint i = 0; i < pCtx->numPlatforms; i++) {
109             // Get vendor name
110             status = clGetPlatformInfo(pCtx->platforms[i], CL_PLATFORM_VENDOR, INFO_BUFFER_LENGTH, infoBuf, nullptr);
111             CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status,
112                 "[GPU]: Fail to get platform Info. (clGetPlatformInfo). ");
113             if (!strcmp(static_cast<const char *>(static_cast<void *>(infoBuf)), vendorName)) {
114                 pCtx->idxPlatforms = i;
115                 break;
116             }
117         }
118     }
119 
120     // exit to host program if no vendorName is found.
121     if ((cl_uint)(-1) == pCtx->idxPlatforms) {
122         VPE_LOGE("[GPU]: There is no  platform to use. exit.\n");
123         status = CL_DEVICE_NOT_AVAILABLE;
124         return status;
125     }
126     return status;
127 }
128 
ChooseDevice(ClContext * pCtx,cl_device_id * targetDevice,cl_uchar infoBuf[],size_t lenInfoBuf,char * deviceName)129 static cl_int ChooseDevice(ClContext *pCtx, cl_device_id *targetDevice, cl_uchar infoBuf[], size_t lenInfoBuf,
130     char *deviceName)
131 {
132     cl_int status = CL_SUCCESS;
133     CHECK_AND_RETURN_RET_LOG(lenInfoBuf <= INFO_BUFFER_LENGTH, CL_DEVICE_NOT_FOUND, "lenInfoBuf>128!");
134     // Choose GPU device to work
135     pCtx->idxDevices[pCtx->idxPlatforms] = (cl_uint)(-1);
136     for (cl_uint i = 0; i < pCtx->numDevices[pCtx->idxPlatforms]; i++) {
137         status =
138             clGetDeviceInfo(pCtx->devices[pCtx->idxPlatforms][i], CL_DEVICE_TYPE, INFO_BUFFER_LENGTH, infoBuf, nullptr);
139         CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status,
140             "[GPU]: Fail to get device Info. (clGetDeviceInfo).");
141         {
142             *targetDevice = pCtx->devices[pCtx->idxPlatforms][i];
143             pCtx->idxDevices[pCtx->idxPlatforms] = i;
144             break;
145         }
146     }
147     if (deviceName != nullptr) {
148         constexpr int deviceLength = 32;
149         status = clGetDeviceInfo(pCtx->devices[pCtx->idxPlatforms][pCtx->idxDevices[pCtx->idxPlatforms]],
150             CL_DEVICE_NAME,
151             deviceLength,
152             deviceName,
153             nullptr);
154         CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status,
155             "[GPU]: Fail to get device Info. (clGetDeviceInfo).");
156         deviceName[strlen(deviceName)] = '\0';
157     }
158     // exit to host program if no GPU device of vendorName is found.
159     CHECK_AND_RETURN_RET_LOG(!((cl_uint)(-1) == pCtx->idxDevices[pCtx->idxPlatforms]), CL_DEVICE_NOT_FOUND,
160         "[GPU]: There is no  GPU to use. exit.");
161     return status;
162 }
163 
CreateContext(ClContext * pCtx,cl_device_id targetDevice)164 static cl_int CreateContext(ClContext *pCtx, cl_device_id targetDevice)
165 {
166     cl_int status = CL_SUCCESS;
167     pCtx->context = clCreateContext(
168         nullptr, pCtx->numDevices[pCtx->idxPlatforms], pCtx->devices[pCtx->idxPlatforms], nullptr, nullptr, &status);
169 
170     CHECK_AND_RETURN_RET_LOG(((status == CL_SUCCESS) || (pCtx->context == nullptr)), status,
171         "Error: Fail to create CL context.");
172 
173     const cl_bitfield props[] = {CL_QUEUE_PRIORITY_KHR, CL_QUEUE_PRIORITY_LOW_KHR, 0};
174     pCtx->cmdQueueGPU = clCreateCommandQueueWithProperties(pCtx->context, targetDevice, props, &status);
175     CHECK_AND_RETURN_RET_LOG(!((status != CL_SUCCESS) || (pCtx->cmdQueueGPU == nullptr)), status,
176         "Error: Fail to create CL cmdQueueGPU.");
177 
178     return status;
179 }
180 
181 // These code need to be modified in the future if there are more platforms and devices
182 // and need other platform/device select policies.
InitOpenCL(ClContext * pCtx,const char * vendorName,char * deviceName)183 static cl_int InitOpenCL(ClContext *pCtx, const char *vendorName, char *deviceName)
184 {
185     cl_uchar infoBuf[INFO_BUFFER_LENGTH] = {0};
186     cl_int status;
187     cl_device_id targetDevice = (cl_device_id)0;
188 
189     status = InitContext(pCtx);
190     CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status, "InitContext fail!");
191 
192     status = GetDevice(pCtx);
193     CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status, "GetDevice fail!");
194 
195     status = ChoosePlatform(pCtx, infoBuf, sizeof(infoBuf), vendorName);
196     CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status, "ChoosePlatform fail!");
197 
198     status = ChooseDevice(pCtx, &targetDevice, infoBuf, sizeof(infoBuf), deviceName);
199     CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status, "ChooseDevice fail!");
200 
201     // Create context
202     status = CreateContext(pCtx, targetDevice);
203     CHECK_AND_RETURN_RET_LOG(status == CL_SUCCESS, status, "CreateContext fail!");
204 
205     return status;
206 }
207 
CleanDevice(ClContext * pCtx)208 static void CleanDevice(ClContext *pCtx)
209 {
210     if (pCtx->cmdQueueGPU) {
211         // Get cmd queue ref count to ensure no memory leak.
212         cl_uchar infoBuf[INFO_BUFFER_LENGTH] = {0};
213         int status =
214             clGetCommandQueueInfo(pCtx->cmdQueueGPU, CL_QUEUE_REFERENCE_COUNT, sizeof(infoBuf), infoBuf, nullptr);
215         CHECK_AND_LOG(status == CL_SUCCESS, "[GPU] clGetCommandQueueInfo Failed!");
216         cl_int *infoBufTmp = reinterpret_cast<cl_int *>(infoBuf);
217         VPE_LOGI("[GPU]: cmd Queue ref count before release it : %{public}d\n", *infoBufTmp);
218         status = clReleaseCommandQueue(pCtx->cmdQueueGPU);
219         CHECK_AND_LOG(status == CL_SUCCESS, "[GPU] clReleaseCommandQueue Failed!");
220     }
221     if (pCtx->context) {
222         cl_uchar infoBuf[INFO_BUFFER_LENGTH] = {0};
223         // Get context ref count to ensure no memory leak.
224         int status = clGetContextInfo(pCtx->context, CL_CONTEXT_REFERENCE_COUNT, sizeof(infoBuf), infoBuf, nullptr);
225         CHECK_AND_LOG(status == CL_SUCCESS, "[GPU] clGetContextInfo Failed!");
226         cl_int *infoBufTmp = reinterpret_cast<cl_int *>(infoBuf);
227         VPE_LOGI("[GPU]: Context ref count before release it : %{public}d\n", *infoBufTmp);
228         status = clReleaseContext(pCtx->context);
229         CHECK_AND_LOG(status == CL_SUCCESS, "[GPU] clReleaseContext Failed!");
230     }
231     if (pCtx->devices != nullptr) {
232         for (cl_uint i = 0; i < pCtx->numPlatforms; i++) {
233             if (pCtx->devices[i] != nullptr) {
234                 free(pCtx->devices[i]);
235                 pCtx->devices[i] = nullptr;
236             }
237         }
238         free(pCtx->devices);
239         pCtx->devices = nullptr;
240     }
241     if (pCtx->idxDevices != nullptr) {
242         free(pCtx->idxDevices);
243         pCtx->idxDevices = nullptr;
244     }
245     if (pCtx->numDevices != nullptr) {
246         free(pCtx->numDevices);
247         pCtx->numDevices = nullptr;
248     }
249     if (pCtx->platforms != nullptr) {
250         free(pCtx->platforms);
251         pCtx->platforms = nullptr;
252     }
253     if (pCtx != nullptr) {
254         free(pCtx);
255         pCtx = nullptr;
256     }
257 }
258 
259 namespace OHOS {
260 namespace Media {
261 namespace VideoProcessingEngine {
CleanOpencl(ClContext * pCtx)262 void CleanOpencl(ClContext *pCtx)
263 {
264     if (pCtx == nullptr) {
265         VPE_LOGI("[GPU]: WARNING: ClneanOpencl input hadle is nullptr.\n");
266     } else {
267         // Release infrastructure like CmdQ/context. Show ref count to see if there is memory leak.
268         // Free the whole clContext created by setupOpencl.
269         CleanDevice(pCtx);
270     }
271     OHOS::Media::VideoProcessingEngine::ClDeInitOpenCL();
272 }
273 
SetupOpencl(void ** pHandle,const char * vendorName,char * deviceName)274 int SetupOpencl(void **pHandle, const char *vendorName, char *deviceName)
275 {
276     ClContext *pCtx = reinterpret_cast<ClContext *>(calloc(1, sizeof(ClContext)));
277     if (pCtx == nullptr) {
278         VPE_LOGE("[GPU]: SetupOpencl FAIL to allocate OpenCL Foundation context.\n");
279         *pHandle = nullptr;
280         return CL_MEM_OBJECT_ALLOCATION_FAILURE;
281     }
282 
283     // Init CL infrastructure
284     cl_int status = InitOpenCL(pCtx, vendorName, deviceName);
285     if (status != CL_SUCCESS) {
286         CleanOpencl(pCtx);
287         *pHandle = nullptr;
288         return status;
289     }
290 
291     *(reinterpret_cast<ClContext **>(pHandle)) = pCtx;
292     return static_cast<int>(status);
293 }
294 } // namespace VideoProcessingEngine
295 } // namespace Media
296 } // namespace OHOS