f987ae00e78fb35e8aa285a702eca55a40585657
[alexxy/gromacs.git] / src / gromacs / gpu_utils / oclutils.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
5  * Copyright (c) 2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  *  \brief Define utility routines for OpenCL
38  *
39  *  \author Anca Hamuraru <anca@streamcomputing.eu>
40  */
41 #include "gmxpre.h"
42
43 #include "oclutils.h"
44
45 #include <stdlib.h>
46
47 #include <cassert>
48 #include <cstdio>
49
50 #include <string>
51
52 #include "gromacs/gpu_utils/gpu_utils.h"
53 #include "gromacs/utility/fatalerror.h"
54 #include "gromacs/utility/smalloc.h"
55
56 int ocl_copy_H2D(cl_mem             d_dest,
57                  const void*        h_src,
58                  size_t             offset,
59                  size_t             bytes,
60                  GpuApiCallBehavior transferKind,
61                  cl_command_queue   command_queue,
62                  cl_event*          copy_event)
63 {
64     cl_int gmx_unused cl_error;
65
66     if (d_dest == nullptr || h_src == nullptr || bytes == 0)
67     {
68         return -1;
69     }
70
71     switch (transferKind)
72     {
73         case GpuApiCallBehavior::Async:
74             cl_error = clEnqueueWriteBuffer(command_queue, d_dest, CL_FALSE, offset, bytes, h_src,
75                                             0, nullptr, copy_event);
76             break;
77
78         case GpuApiCallBehavior::Sync:
79             cl_error = clEnqueueWriteBuffer(command_queue, d_dest, CL_TRUE, offset, bytes, h_src, 0,
80                                             nullptr, copy_event);
81             break;
82
83         default: throw;
84     }
85     GMX_ASSERT(cl_error == CL_SUCCESS,
86                ("clEnqueueWriteBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
87
88     return 0;
89 }
90
91 /*! \brief Launches asynchronous host to device memory copy.
92  *
93  *  If copy_event is not nullptr, on return it will contain an event object
94  *  identifying this particular host to device operation. The event can further
95  *  be used to queue a wait for this operation or to query profiling information.
96  */
97 int ocl_copy_H2D_async(cl_mem           d_dest,
98                        const void*      h_src,
99                        size_t           offset,
100                        size_t           bytes,
101                        cl_command_queue command_queue,
102                        cl_event*        copy_event)
103 {
104     return ocl_copy_H2D(d_dest, h_src, offset, bytes, GpuApiCallBehavior::Async, command_queue, copy_event);
105 }
106
107 /*! \brief Launches synchronous host to device memory copy.
108  */
109 int ocl_copy_H2D_sync(cl_mem d_dest, const void* h_src, size_t offset, size_t bytes, cl_command_queue command_queue)
110 {
111     return ocl_copy_H2D(d_dest, h_src, offset, bytes, GpuApiCallBehavior::Sync, command_queue, nullptr);
112 }
113
114 int ocl_copy_D2H(void*              h_dest,
115                  cl_mem             d_src,
116                  size_t             offset,
117                  size_t             bytes,
118                  GpuApiCallBehavior transferKind,
119                  cl_command_queue   command_queue,
120                  cl_event*          copy_event)
121 {
122     cl_int gmx_unused cl_error;
123
124     if (h_dest == nullptr || d_src == nullptr || bytes == 0)
125     {
126         return -1;
127     }
128
129     switch (transferKind)
130     {
131         case GpuApiCallBehavior::Async:
132             cl_error = clEnqueueReadBuffer(command_queue, d_src, CL_FALSE, offset, bytes, h_dest, 0,
133                                            nullptr, copy_event);
134             break;
135
136         case GpuApiCallBehavior::Sync:
137             cl_error = clEnqueueReadBuffer(command_queue, d_src, CL_TRUE, offset, bytes, h_dest, 0,
138                                            nullptr, copy_event);
139             break;
140
141         default: throw;
142     }
143     GMX_ASSERT(cl_error == CL_SUCCESS,
144                ("clEnqueueWriteBuffer failed: " + ocl_get_error_string(cl_error)).c_str());
145
146
147     return 0;
148 }
149
150 /*! \brief Launches asynchronous device to host memory copy.
151  *
152  *  If copy_event is not nullptr, on return it will contain an event object
153  *  identifying this particular host to device operation. The event can further
154  *  be used to queue a wait for this operation or to query profiling information.
155  */
156 int ocl_copy_D2H_async(void*            h_dest,
157                        cl_mem           d_src,
158                        size_t           offset,
159                        size_t           bytes,
160                        cl_command_queue command_queue,
161                        cl_event*        copy_event)
162 {
163     return ocl_copy_D2H(h_dest, d_src, offset, bytes, GpuApiCallBehavior::Async, command_queue, copy_event);
164 }
165
166 /*! \brief \brief Allocates nbytes of host memory. Use ocl_free to free memory allocated with this function.
167  *
168  *  \todo
169  *  This function should allocate page-locked memory to help reduce D2H and H2D
170  *  transfer times, similar with pmalloc from pmalloc_cuda.cu.
171  *
172  * \param[in,out]    h_ptr   Pointer where to store the address of the newly allocated buffer.
173  * \param[in]        nbytes  Size in bytes of the buffer to be allocated.
174  */
175 void pmalloc(void** h_ptr, size_t nbytes)
176 {
177     /* Need a temporary type whose size is 1 byte, so that the
178      * implementation of snew_aligned can cope without issuing
179      * warnings. */
180     char** temporary = reinterpret_cast<char**>(h_ptr);
181
182     /* 16-byte alignment is required by the neighbour-searching code,
183      * because it uses four-wide SIMD for bounding-box calculation.
184      * However, when we organize using page-locked memory for
185      * device-host transfers, it will probably need to be aligned to a
186      * 4kb page, like CUDA does. */
187     snew_aligned(*temporary, nbytes, 16);
188 }
189
190 /*! \brief Frees memory allocated with pmalloc.
191  *
192  * \param[in]    h_ptr   Buffer allocated with pmalloc that needs to be freed.
193  */
194 void pfree(void* h_ptr)
195 {
196
197     if (h_ptr)
198     {
199         sfree_aligned(h_ptr);
200     }
201 }
202
203 /*! \brief Convert error code to diagnostic string */
204 std::string ocl_get_error_string(cl_int error)
205 {
206     switch (error)
207     {
208         // run-time and JIT compiler errors
209         case 0: return "CL_SUCCESS";
210         case -1: return "CL_DEVICE_NOT_FOUND";
211         case -2: return "CL_DEVICE_NOT_AVAILABLE";
212         case -3: return "CL_COMPILER_NOT_AVAILABLE";
213         case -4: return "CL_MEM_OBJECT_ALLOCATION_FAILURE";
214         case -5: return "CL_OUT_OF_RESOURCES";
215         case -6: return "CL_OUT_OF_HOST_MEMORY";
216         case -7: return "CL_PROFILING_INFO_NOT_AVAILABLE";
217         case -8: return "CL_MEM_COPY_OVERLAP";
218         case -9: return "CL_IMAGE_FORMAT_MISMATCH";
219         case -10: return "CL_IMAGE_FORMAT_NOT_SUPPORTED";
220         case -11: return "CL_BUILD_PROGRAM_FAILURE";
221         case -12: return "CL_MAP_FAILURE";
222         case -13: return "CL_MISALIGNED_SUB_BUFFER_OFFSET";
223         case -14: return "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST";
224         case -15: return "CL_COMPILE_PROGRAM_FAILURE";
225         case -16: return "CL_LINKER_NOT_AVAILABLE";
226         case -17: return "CL_LINK_PROGRAM_FAILURE";
227         case -18: return "CL_DEVICE_PARTITION_FAILED";
228         case -19: return "CL_KERNEL_ARG_INFO_NOT_AVAILABLE";
229
230         // compile-time errors
231         case -30: return "CL_INVALID_VALUE";
232         case -31: return "CL_INVALID_DEVICE_TYPE";
233         case -32: return "CL_INVALID_PLATFORM";
234         case -33: return "CL_INVALID_DEVICE";
235         case -34: return "CL_INVALID_CONTEXT";
236         case -35: return "CL_INVALID_QUEUE_PROPERTIES";
237         case -36: return "CL_INVALID_COMMAND_QUEUE";
238         case -37: return "CL_INVALID_HOST_PTR";
239         case -38: return "CL_INVALID_MEM_OBJECT";
240         case -39: return "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR";
241         case -40: return "CL_INVALID_IMAGE_SIZE";
242         case -41: return "CL_INVALID_SAMPLER";
243         case -42: return "CL_INVALID_BINARY";
244         case -43: return "CL_INVALID_BUILD_OPTIONS";
245         case -44: return "CL_INVALID_PROGRAM";
246         case -45: return "CL_INVALID_PROGRAM_EXECUTABLE";
247         case -46: return "CL_INVALID_KERNEL_NAME";
248         case -47: return "CL_INVALID_KERNEL_DEFINITION";
249         case -48: return "CL_INVALID_KERNEL";
250         case -49: return "CL_INVALID_ARG_INDEX";
251         case -50: return "CL_INVALID_ARG_VALUE";
252         case -51: return "CL_INVALID_ARG_SIZE";
253         case -52: return "CL_INVALID_KERNEL_ARGS";
254         case -53: return "CL_INVALID_WORK_DIMENSION";
255         case -54: return "CL_INVALID_WORK_GROUP_SIZE";
256         case -55: return "CL_INVALID_WORK_ITEM_SIZE";
257         case -56: return "CL_INVALID_GLOBAL_OFFSET";
258         case -57: return "CL_INVALID_EVENT_WAIT_LIST";
259         case -58: return "CL_INVALID_EVENT";
260         case -59: return "CL_INVALID_OPERATION";
261         case -60: return "CL_INVALID_GL_OBJECT";
262         case -61: return "CL_INVALID_BUFFER_SIZE";
263         case -62: return "CL_INVALID_MIP_LEVEL";
264         case -63: return "CL_INVALID_GLOBAL_WORK_SIZE";
265         case -64: return "CL_INVALID_PROPERTY";
266         case -65: return "CL_INVALID_IMAGE_DESCRIPTOR";
267         case -66: return "CL_INVALID_COMPILER_OPTIONS";
268         case -67: return "CL_INVALID_LINKER_OPTIONS";
269         case -68: return "CL_INVALID_DEVICE_PARTITION_COUNT";
270
271         // extension errors
272         case -1000: return "CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR";
273         case -1001: return "CL_PLATFORM_NOT_FOUND_KHR";
274         case -1002: return "CL_INVALID_D3D10_DEVICE_KHR";
275         case -1003: return "CL_INVALID_D3D10_RESOURCE_KHR";
276         case -1004: return "CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR";
277         case -1005: return "CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR";
278         default: return "Unknown OpenCL error: " + std::to_string(static_cast<int32_t>(error));
279     }
280 }