add BLI_strcpy_rlen, replace strcat, which was used in misleading way.
[blender.git] / intern / cycles / util / util_cuda.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21
22 #include "util_cuda.h"
23 #include "util_debug.h"
24 #include "util_dynlib.h"
25 #include "util_path.h"
26 #include "util_string.h"
27
28 /* function defininitions */
29
30 tcuInit *cuInit;
31 tcuDriverGetVersion *cuDriverGetVersion;
32 tcuDeviceGet *cuDeviceGet;
33 tcuDeviceGetCount *cuDeviceGetCount;
34 tcuDeviceGetName *cuDeviceGetName;
35 tcuDeviceComputeCapability *cuDeviceComputeCapability;
36 tcuDeviceTotalMem *cuDeviceTotalMem;
37 tcuDeviceGetProperties *cuDeviceGetProperties;
38 tcuDeviceGetAttribute *cuDeviceGetAttribute;
39 tcuCtxCreate *cuCtxCreate;
40 tcuCtxDestroy *cuCtxDestroy;
41 tcuCtxAttach *cuCtxAttach;
42 tcuCtxDetach *cuCtxDetach;
43 tcuCtxPushCurrent *cuCtxPushCurrent;
44 tcuCtxPopCurrent *cuCtxPopCurrent;
45 tcuCtxGetDevice *cuCtxGetDevice;
46 tcuCtxSynchronize *cuCtxSynchronize;
47 tcuModuleLoad *cuModuleLoad;
48 tcuModuleLoadData *cuModuleLoadData;
49 tcuModuleLoadDataEx *cuModuleLoadDataEx;
50 tcuModuleLoadFatBinary *cuModuleLoadFatBinary;
51 tcuModuleUnload *cuModuleUnload;
52 tcuModuleGetFunction *cuModuleGetFunction;
53 tcuModuleGetGlobal *cuModuleGetGlobal;
54 tcuModuleGetTexRef *cuModuleGetTexRef;
55 tcuModuleGetSurfRef *cuModuleGetSurfRef;
56 tcuMemGetInfo *cuMemGetInfo;
57 tcuMemAlloc *cuMemAlloc;
58 tcuMemAllocPitch *cuMemAllocPitch;
59 tcuMemFree *cuMemFree;
60 tcuMemGetAddressRange *cuMemGetAddressRange;
61 tcuMemAllocHost *cuMemAllocHost;
62 tcuMemFreeHost *cuMemFreeHost;
63 tcuMemHostAlloc *cuMemHostAlloc;
64 tcuMemHostGetDevicePointer *cuMemHostGetDevicePointer;
65 tcuMemHostGetFlags *cuMemHostGetFlags;
66 tcuMemcpyHtoD *cuMemcpyHtoD;
67 tcuMemcpyDtoH *cuMemcpyDtoH;
68 tcuMemcpyDtoD *cuMemcpyDtoD;
69 tcuMemcpyDtoA *cuMemcpyDtoA;
70 tcuMemcpyAtoD *cuMemcpyAtoD;
71 tcuMemcpyHtoA *cuMemcpyHtoA;
72 tcuMemcpyAtoH *cuMemcpyAtoH;
73 tcuMemcpyAtoA *cuMemcpyAtoA;
74 tcuMemcpy2D *cuMemcpy2D;
75 tcuMemcpy2DUnaligned *cuMemcpy2DUnaligned;
76 tcuMemcpy3D *cuMemcpy3D;
77 tcuMemcpyHtoDAsync *cuMemcpyHtoDAsync;
78 tcuMemcpyDtoHAsync *cuMemcpyDtoHAsync;
79 tcuMemcpyDtoDAsync *cuMemcpyDtoDAsync;
80 tcuMemcpyHtoAAsync *cuMemcpyHtoAAsync;
81 tcuMemcpyAtoHAsync *cuMemcpyAtoHAsync;
82 tcuMemcpy2DAsync *cuMemcpy2DAsync;
83 tcuMemcpy3DAsync *cuMemcpy3DAsync;
84 tcuMemsetD8 *cuMemsetD8;
85 tcuMemsetD16 *cuMemsetD16;
86 tcuMemsetD32 *cuMemsetD32;
87 tcuMemsetD2D8 *cuMemsetD2D8;
88 tcuMemsetD2D16 *cuMemsetD2D16;
89 tcuMemsetD2D32 *cuMemsetD2D32;
90 tcuFuncSetBlockShape *cuFuncSetBlockShape;
91 tcuFuncSetSharedSize *cuFuncSetSharedSize;
92 tcuFuncGetAttribute *cuFuncGetAttribute;
93 tcuFuncSetCacheConfig *cuFuncSetCacheConfig;
94 tcuArrayCreate *cuArrayCreate;
95 tcuArrayGetDescriptor *cuArrayGetDescriptor;
96 tcuArrayDestroy *cuArrayDestroy;
97 tcuArray3DCreate *cuArray3DCreate;
98 tcuArray3DGetDescriptor *cuArray3DGetDescriptor;
99 tcuTexRefCreate *cuTexRefCreate;
100 tcuTexRefDestroy *cuTexRefDestroy;
101 tcuTexRefSetArray *cuTexRefSetArray;
102 tcuTexRefSetAddress *cuTexRefSetAddress;
103 tcuTexRefSetAddress2D *cuTexRefSetAddress2D;
104 tcuTexRefSetFormat *cuTexRefSetFormat;
105 tcuTexRefSetAddressMode *cuTexRefSetAddressMode;
106 tcuTexRefSetFilterMode *cuTexRefSetFilterMode;
107 tcuTexRefSetFlags *cuTexRefSetFlags;
108 tcuTexRefGetAddress *cuTexRefGetAddress;
109 tcuTexRefGetArray *cuTexRefGetArray;
110 tcuTexRefGetAddressMode *cuTexRefGetAddressMode;
111 tcuTexRefGetFilterMode *cuTexRefGetFilterMode;
112 tcuTexRefGetFormat *cuTexRefGetFormat;
113 tcuTexRefGetFlags *cuTexRefGetFlags;
114 tcuSurfRefSetArray *cuSurfRefSetArray;
115 tcuSurfRefGetArray *cuSurfRefGetArray;
116 tcuParamSetSize *cuParamSetSize;
117 tcuParamSeti *cuParamSeti;
118 tcuParamSetf *cuParamSetf;
119 tcuParamSetv *cuParamSetv;
120 tcuParamSetTexRef *cuParamSetTexRef;
121 tcuLaunch *cuLaunch;
122 tcuLaunchGrid *cuLaunchGrid;
123 tcuLaunchGridAsync *cuLaunchGridAsync;
124 tcuEventCreate *cuEventCreate;
125 tcuEventRecord *cuEventRecord;
126 tcuEventQuery *cuEventQuery;
127 tcuEventSynchronize *cuEventSynchronize;
128 tcuEventDestroy *cuEventDestroy;
129 tcuEventElapsedTime *cuEventElapsedTime;
130 tcuStreamCreate *cuStreamCreate;
131 tcuStreamQuery *cuStreamQuery;
132 tcuStreamSynchronize *cuStreamSynchronize;
133 tcuStreamDestroy *cuStreamDestroy;
134 tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource;
135 tcuGraphicsSubResourceGetMappedArray *cuGraphicsSubResourceGetMappedArray;
136 tcuGraphicsResourceGetMappedPointer *cuGraphicsResourceGetMappedPointer;
137 tcuGraphicsResourceSetMapFlags *cuGraphicsResourceSetMapFlags;
138 tcuGraphicsMapResources *cuGraphicsMapResources;
139 tcuGraphicsUnmapResources *cuGraphicsUnmapResources;
140 tcuGetExportTable *cuGetExportTable;
141 tcuCtxSetLimit *cuCtxSetLimit;
142 tcuCtxGetLimit *cuCtxGetLimit;
143 tcuGLCtxCreate *cuGLCtxCreate;
144 tcuGraphicsGLRegisterBuffer *cuGraphicsGLRegisterBuffer;
145 tcuGraphicsGLRegisterImage *cuGraphicsGLRegisterImage;
146 tcuCtxSetCurrent *cuCtxSetCurrent;
147
148 CCL_NAMESPACE_BEGIN
149
150 /* utility macros */
151 #define CUDA_LIBRARY_FIND_CHECKED(name) \
152         name = (t##name*)dynamic_library_find(lib, #name);
153
154 #define CUDA_LIBRARY_FIND(name) \
155         name = (t##name*)dynamic_library_find(lib, #name); \
156         assert(name);
157
158 #define CUDA_LIBRARY_FIND_V2(name) \
159         name = (t##name*)dynamic_library_find(lib, #name "_v2"); \
160         assert(name);
161
162 /* initialization function */
163
164 bool cuLibraryInit()
165 {
166         static bool initialized = false;
167         static bool result = false;
168
169         if(initialized)
170                 return result;
171         
172         initialized = true;
173
174         /* library paths */
175 #ifdef _WIN32
176         /* expected in c:/windows/system or similar, no path needed */
177         const char *path = "nvcuda.dll";
178 #elif defined(__APPLE__)
179         /* default installation path */
180         const char *path = "/usr/local/cuda/lib/libcuda.dylib";
181 #else
182         const char *path = "libcuda.so";
183 #endif
184
185         /* load library */
186         DynamicLibrary *lib = dynamic_library_open(path);
187
188         if(lib == NULL)
189                 return false;
190
191         /* detect driver version */
192         int driver_version = 1000;
193
194         CUDA_LIBRARY_FIND_CHECKED(cuDriverGetVersion);
195         if(cuDriverGetVersion)
196                 cuDriverGetVersion(&driver_version);
197
198         /* we require version 4.0 */
199         if(driver_version < 4000)
200                 return false;
201
202         /* fetch all function pointers */
203         CUDA_LIBRARY_FIND(cuInit);
204         CUDA_LIBRARY_FIND(cuDeviceGet);
205         CUDA_LIBRARY_FIND(cuDeviceGetCount);
206         CUDA_LIBRARY_FIND(cuDeviceGetName);
207         CUDA_LIBRARY_FIND(cuDeviceComputeCapability);
208         CUDA_LIBRARY_FIND(cuDeviceTotalMem);
209         CUDA_LIBRARY_FIND(cuDeviceGetProperties);
210         CUDA_LIBRARY_FIND(cuDeviceGetAttribute);
211         CUDA_LIBRARY_FIND(cuCtxCreate);
212         CUDA_LIBRARY_FIND(cuCtxDestroy);
213         CUDA_LIBRARY_FIND(cuCtxAttach);
214         CUDA_LIBRARY_FIND(cuCtxDetach);
215         CUDA_LIBRARY_FIND(cuCtxPushCurrent);
216         CUDA_LIBRARY_FIND(cuCtxPopCurrent);
217         CUDA_LIBRARY_FIND(cuCtxGetDevice);
218         CUDA_LIBRARY_FIND(cuCtxSynchronize);
219         CUDA_LIBRARY_FIND(cuModuleLoad);
220         CUDA_LIBRARY_FIND(cuModuleLoadData);
221         CUDA_LIBRARY_FIND(cuModuleUnload);
222         CUDA_LIBRARY_FIND(cuModuleGetFunction);
223         CUDA_LIBRARY_FIND(cuModuleGetGlobal);
224         CUDA_LIBRARY_FIND(cuModuleGetTexRef);
225         CUDA_LIBRARY_FIND(cuMemGetInfo);
226         CUDA_LIBRARY_FIND(cuMemAlloc);
227         CUDA_LIBRARY_FIND(cuMemAllocPitch);
228         CUDA_LIBRARY_FIND(cuMemFree);
229         CUDA_LIBRARY_FIND(cuMemGetAddressRange);
230         CUDA_LIBRARY_FIND(cuMemAllocHost);
231         CUDA_LIBRARY_FIND(cuMemFreeHost);
232         CUDA_LIBRARY_FIND(cuMemHostAlloc);
233         CUDA_LIBRARY_FIND(cuMemHostGetDevicePointer);
234         CUDA_LIBRARY_FIND(cuMemcpyHtoD);
235         CUDA_LIBRARY_FIND(cuMemcpyDtoH);
236         CUDA_LIBRARY_FIND(cuMemcpyDtoD);
237         CUDA_LIBRARY_FIND(cuMemcpyDtoA);
238         CUDA_LIBRARY_FIND(cuMemcpyAtoD);
239         CUDA_LIBRARY_FIND(cuMemcpyHtoA);
240         CUDA_LIBRARY_FIND(cuMemcpyAtoH);
241         CUDA_LIBRARY_FIND(cuMemcpyAtoA);
242         CUDA_LIBRARY_FIND(cuMemcpy2D);
243         CUDA_LIBRARY_FIND(cuMemcpy2DUnaligned);
244         CUDA_LIBRARY_FIND(cuMemcpy3D);
245         CUDA_LIBRARY_FIND(cuMemcpyHtoDAsync);
246         CUDA_LIBRARY_FIND(cuMemcpyDtoHAsync);
247         CUDA_LIBRARY_FIND(cuMemcpyHtoAAsync);
248         CUDA_LIBRARY_FIND(cuMemcpyAtoHAsync);
249         CUDA_LIBRARY_FIND(cuMemcpy2DAsync);
250         CUDA_LIBRARY_FIND(cuMemcpy3DAsync);
251         CUDA_LIBRARY_FIND(cuMemsetD8);
252         CUDA_LIBRARY_FIND(cuMemsetD16);
253         CUDA_LIBRARY_FIND(cuMemsetD32);
254         CUDA_LIBRARY_FIND(cuMemsetD2D8);
255         CUDA_LIBRARY_FIND(cuMemsetD2D16);
256         CUDA_LIBRARY_FIND(cuMemsetD2D32);
257         CUDA_LIBRARY_FIND(cuFuncSetBlockShape);
258         CUDA_LIBRARY_FIND(cuFuncSetSharedSize);
259         CUDA_LIBRARY_FIND(cuFuncGetAttribute);
260         CUDA_LIBRARY_FIND(cuArrayCreate);
261         CUDA_LIBRARY_FIND(cuArrayGetDescriptor);
262         CUDA_LIBRARY_FIND(cuArrayDestroy);
263         CUDA_LIBRARY_FIND(cuArray3DCreate);
264         CUDA_LIBRARY_FIND(cuArray3DGetDescriptor);
265         CUDA_LIBRARY_FIND(cuTexRefCreate);
266         CUDA_LIBRARY_FIND(cuTexRefDestroy);
267         CUDA_LIBRARY_FIND(cuTexRefSetArray);
268         CUDA_LIBRARY_FIND(cuTexRefSetAddress);
269         CUDA_LIBRARY_FIND(cuTexRefSetAddress2D);
270         CUDA_LIBRARY_FIND(cuTexRefSetFormat);
271         CUDA_LIBRARY_FIND(cuTexRefSetAddressMode);
272         CUDA_LIBRARY_FIND(cuTexRefSetFilterMode);
273         CUDA_LIBRARY_FIND(cuTexRefSetFlags);
274         CUDA_LIBRARY_FIND(cuTexRefGetAddress);
275         CUDA_LIBRARY_FIND(cuTexRefGetArray);
276         CUDA_LIBRARY_FIND(cuTexRefGetAddressMode);
277         CUDA_LIBRARY_FIND(cuTexRefGetFilterMode);
278         CUDA_LIBRARY_FIND(cuTexRefGetFormat);
279         CUDA_LIBRARY_FIND(cuTexRefGetFlags);
280         CUDA_LIBRARY_FIND(cuParamSetSize);
281         CUDA_LIBRARY_FIND(cuParamSeti);
282         CUDA_LIBRARY_FIND(cuParamSetf);
283         CUDA_LIBRARY_FIND(cuParamSetv);
284         CUDA_LIBRARY_FIND(cuParamSetTexRef);
285         CUDA_LIBRARY_FIND(cuLaunch);
286         CUDA_LIBRARY_FIND(cuLaunchGrid);
287         CUDA_LIBRARY_FIND(cuLaunchGridAsync);
288         CUDA_LIBRARY_FIND(cuEventCreate);
289         CUDA_LIBRARY_FIND(cuEventRecord);
290         CUDA_LIBRARY_FIND(cuEventQuery);
291         CUDA_LIBRARY_FIND(cuEventSynchronize);
292         CUDA_LIBRARY_FIND(cuEventDestroy);
293         CUDA_LIBRARY_FIND(cuEventElapsedTime);
294         CUDA_LIBRARY_FIND(cuStreamCreate);
295         CUDA_LIBRARY_FIND(cuStreamQuery);
296         CUDA_LIBRARY_FIND(cuStreamSynchronize);
297         CUDA_LIBRARY_FIND(cuStreamDestroy);
298
299         /* cuda 2.1 */
300         CUDA_LIBRARY_FIND(cuModuleLoadDataEx);
301         CUDA_LIBRARY_FIND(cuModuleLoadFatBinary);
302         CUDA_LIBRARY_FIND(cuGLCtxCreate);
303         CUDA_LIBRARY_FIND(cuGraphicsGLRegisterBuffer);
304         CUDA_LIBRARY_FIND(cuGraphicsGLRegisterImage);
305
306         /* cuda 2.3 */
307         CUDA_LIBRARY_FIND(cuMemHostGetFlags);
308         CUDA_LIBRARY_FIND(cuGraphicsGLRegisterBuffer);
309         CUDA_LIBRARY_FIND(cuGraphicsGLRegisterImage);
310
311         /* cuda 3.0 */
312         CUDA_LIBRARY_FIND(cuMemcpyDtoDAsync);
313         CUDA_LIBRARY_FIND(cuFuncSetCacheConfig);
314         CUDA_LIBRARY_FIND(cuGraphicsUnregisterResource);
315         CUDA_LIBRARY_FIND(cuGraphicsSubResourceGetMappedArray);
316         CUDA_LIBRARY_FIND(cuGraphicsResourceGetMappedPointer);
317         CUDA_LIBRARY_FIND(cuGraphicsResourceSetMapFlags);
318         CUDA_LIBRARY_FIND(cuGraphicsMapResources);
319         CUDA_LIBRARY_FIND(cuGraphicsUnmapResources);
320         CUDA_LIBRARY_FIND(cuGetExportTable);
321
322         /* cuda 3.1 */
323         CUDA_LIBRARY_FIND(cuModuleGetSurfRef);
324         CUDA_LIBRARY_FIND(cuSurfRefSetArray);
325         CUDA_LIBRARY_FIND(cuSurfRefGetArray);
326         CUDA_LIBRARY_FIND(cuCtxSetLimit);
327         CUDA_LIBRARY_FIND(cuCtxGetLimit);
328
329         /* functions which changed 3.1 -> 3.2 for 64 bit stuff, the cuda library
330          * has both the old ones for compatibility and new ones with _v2 postfix,
331          * we load the _v2 ones here. */
332         CUDA_LIBRARY_FIND_V2(cuDeviceTotalMem);
333         CUDA_LIBRARY_FIND_V2(cuCtxCreate);
334         CUDA_LIBRARY_FIND_V2(cuModuleGetGlobal);
335         CUDA_LIBRARY_FIND_V2(cuMemGetInfo);
336         CUDA_LIBRARY_FIND_V2(cuMemAlloc);
337         CUDA_LIBRARY_FIND_V2(cuMemAllocPitch);
338         CUDA_LIBRARY_FIND_V2(cuMemFree);
339         CUDA_LIBRARY_FIND_V2(cuMemGetAddressRange);
340         CUDA_LIBRARY_FIND_V2(cuMemAllocHost);
341         CUDA_LIBRARY_FIND_V2(cuMemHostGetDevicePointer);
342         CUDA_LIBRARY_FIND_V2(cuMemcpyHtoD);
343         CUDA_LIBRARY_FIND_V2(cuMemcpyDtoH);
344         CUDA_LIBRARY_FIND_V2(cuMemcpyDtoD);
345         CUDA_LIBRARY_FIND_V2(cuMemcpyDtoA);
346         CUDA_LIBRARY_FIND_V2(cuMemcpyAtoD);
347         CUDA_LIBRARY_FIND_V2(cuMemcpyHtoA);
348         CUDA_LIBRARY_FIND_V2(cuMemcpyAtoH);
349         CUDA_LIBRARY_FIND_V2(cuMemcpyAtoA);
350         CUDA_LIBRARY_FIND_V2(cuMemcpyHtoAAsync);
351         CUDA_LIBRARY_FIND_V2(cuMemcpyAtoHAsync);
352         CUDA_LIBRARY_FIND_V2(cuMemcpy2D);
353         CUDA_LIBRARY_FIND_V2(cuMemcpy2DUnaligned);
354         CUDA_LIBRARY_FIND_V2(cuMemcpy3D);
355         CUDA_LIBRARY_FIND_V2(cuMemcpyHtoDAsync);
356         CUDA_LIBRARY_FIND_V2(cuMemcpyDtoHAsync);
357         CUDA_LIBRARY_FIND_V2(cuMemcpyDtoDAsync);
358         CUDA_LIBRARY_FIND_V2(cuMemcpy2DAsync);
359         CUDA_LIBRARY_FIND_V2(cuMemcpy3DAsync);
360         CUDA_LIBRARY_FIND_V2(cuMemsetD8);
361         CUDA_LIBRARY_FIND_V2(cuMemsetD16);
362         CUDA_LIBRARY_FIND_V2(cuMemsetD32);
363         CUDA_LIBRARY_FIND_V2(cuMemsetD2D8);
364         CUDA_LIBRARY_FIND_V2(cuMemsetD2D16);
365         CUDA_LIBRARY_FIND_V2(cuMemsetD2D32);
366         CUDA_LIBRARY_FIND_V2(cuArrayCreate);
367         CUDA_LIBRARY_FIND_V2(cuArrayGetDescriptor);
368         CUDA_LIBRARY_FIND_V2(cuArray3DCreate);
369         CUDA_LIBRARY_FIND_V2(cuArray3DGetDescriptor);
370         CUDA_LIBRARY_FIND_V2(cuTexRefSetAddress);
371         CUDA_LIBRARY_FIND_V2(cuTexRefSetAddress2D);
372         CUDA_LIBRARY_FIND_V2(cuTexRefGetAddress);
373         CUDA_LIBRARY_FIND_V2(cuGraphicsResourceGetMappedPointer);
374         CUDA_LIBRARY_FIND_V2(cuGLCtxCreate);
375
376         /* cuda 4.0 */
377         CUDA_LIBRARY_FIND(cuCtxSetCurrent);
378
379         if(cuHavePrecompiledKernels())
380                 result = true;
381 #ifndef _WIN32
382         else if(cuCompilerPath() != "")
383                 result = true;
384 #endif
385
386         return result;
387 }
388
389 bool cuHavePrecompiledKernels()
390 {
391         string cubins_path = path_get("lib");
392
393         return path_exists(cubins_path);
394 }
395
396 string cuCompilerPath()
397 {
398 #ifdef _WIN32
399         const char *defaultpaths[] = {"C:/CUDA/bin", NULL};
400         const char *executable = "nvcc.exe";
401 #else
402         const char *defaultpaths[] = {"/Developer/NVIDIA/CUDA-4.2/bin", "/usr/local/cuda-4.2/bin", "/usr/local/cuda/bin", NULL};
403         const char *executable = "nvcc";
404 #endif
405
406         const char *binpath = getenv("CUDA_BIN_PATH");
407
408         string nvcc;
409
410         if(binpath) {
411                 nvcc = path_join(binpath, executable);
412                 if(path_exists(nvcc))
413                         return nvcc;
414         }
415
416         for(int i = 0; defaultpaths[i]; i++) {
417                 nvcc = path_join(defaultpaths[i], executable);
418                 if(path_exists(nvcc))
419                         return nvcc;
420         }
421
422 #ifndef _WIN32
423         {
424                 FILE *handle = popen("which nvcc", "r");
425                 if(handle) {
426                         char buffer[4096] = {0};
427                         int len = fread(buffer, 1, sizeof(buffer) - 1, handle);
428                         buffer[len] = '\0';
429                         pclose(handle);
430
431                         if(buffer[0])
432                                 return "nvcc";
433                 }
434         }
435 #endif
436
437         return "";
438 }
439
440 CCL_NAMESPACE_END
441