64f969fa9d8a84a9092b0965da7f91ccaf474416
[alexxy/gromacs.git] / src / gromacs / gpu_utils / devicebuffer_ocl.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2018, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 #ifndef GMX_GPU_UTILS_DEVICEBUFFER_OCL_H
36 #define GMX_GPU_UTILS_DEVICEBUFFER_OCL_H
37
38 /*! \libinternal \file
39  *  \brief Implements the DeviceBuffer type and routines for OpenCL.
40  *  Should only be included directly by the main DeviceBuffer file devicebuffer.h.
41  *  TODO: the intent is for DeviceBuffer to become a class.
42  *
43  *  \author Aleksei Iupinov <a.yupinov@gmail.com>
44  *
45  *  \inlibraryapi
46  */
47
48 #include "gromacs/gpu_utils/gpu_utils.h" //only for GpuApiCallBehavior
49 #include "gromacs/gpu_utils/gputraits_ocl.h"
50 #include "gromacs/utility/gmxassert.h"
51
52 /*! \libinternal \brief
53  * A minimal cl_mem wrapper that remembers its allocation type.
54  * The only point is making template type deduction possible.
55  */
56 template<typename ValueType>
57 class TypedClMemory
58 {
59     private:
60         //! \brief Underlying data - not nulled right here only because we still have some snew()'s around
61         cl_mem data_;
62     public:
63         //! \brief An assignment operator - the purpose is to make allocation/zeroing work
64         void operator=(cl_mem data){data_ = data; }
65         //! \brief Returns underlying cl_mem transparently
66         operator cl_mem() {return data_; }
67 };
68
69 //! \libinternal \brief A device-side buffer of ValueTypes
70 template<typename ValueType>
71 using DeviceBuffer = TypedClMemory<ValueType>;
72
73 /*! \libinternal \brief
74  * Allocates a device-side buffer.
75  * It is currently a caller's responsibility to call it only on not-yet allocated buffers.
76  *
77  * \tparam        ValueType            Raw value type of the \p buffer.
78  * \param[in,out] buffer               Pointer to the device-side buffer.
79  * \param[in]     numValues            Number of values to accomodate.
80  * \param[in]     context              The buffer's context-to-be.
81  */
82 template <typename ValueType>
83 void allocateDeviceBuffer(DeviceBuffer<ValueType> *buffer,
84                           size_t                   numValues,
85                           Context                  context)
86 {
87     GMX_ASSERT(buffer, "needs a buffer pointer");
88     void  *hostPtr = nullptr;
89     cl_int clError;
90     *buffer = clCreateBuffer(context, CL_MEM_READ_WRITE, numValues * sizeof(ValueType), hostPtr, &clError);
91     GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "clCreateBuffer failure");
92 }
93
94 /*! \brief
95  * Frees a device-side buffer.
96  * This does not reset separately stored size/capacity integers,
97  * as this is planned to be a destructor of DeviceBuffer as a proper class,
98  * and no calls on \p buffer should be made afterwards.
99  *
100  * \param[in] buffer  Pointer to the buffer to free.
101  */
102 template <typename DeviceBuffer>
103 void freeDeviceBuffer(DeviceBuffer *buffer)
104 {
105     GMX_ASSERT(buffer, "needs a buffer pointer");
106     if (*buffer)
107     {
108         GMX_RELEASE_ASSERT(clReleaseMemObject(*buffer) == CL_SUCCESS, "clReleaseMemObject failed");
109     }
110 }
111
112 /*! \brief
113  * Performs the host-to-device data copy, synchronous or asynchronously on request.
114  *
115  * TODO: This is meant to gradually replace cu/ocl_copy_h2d.
116  *
117  * \tparam        ValueType            Raw value type of the \p buffer.
118  * \param[in,out] buffer               Pointer to the device-side buffer
119  * \param[in]     hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
120  * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy into.
121  * \param[in]     numValues            Number of values to copy.
122  * \param[in]     stream               GPU stream to perform asynchronous copy in.
123  * \param[in]     transferKind         Copy type: synchronous or asynchronous.
124  * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
125  *                                     If the pointer is not null, the event can further be used
126  *                                     to queue a wait for this operation or to query profiling information.
127  */
128 template <typename ValueType>
129 void copyToDeviceBuffer(DeviceBuffer<ValueType> *buffer,
130                         const ValueType         *hostBuffer,
131                         size_t                   startingOffset,
132                         size_t                   numValues,
133                         CommandStream            stream,
134                         GpuApiCallBehavior       transferKind,
135                         CommandEvent            *timingEvent)
136 {
137     if (numValues == 0)
138     {
139         return; // such calls are actually made with empty domains
140     }
141     GMX_ASSERT(buffer, "needs a buffer pointer");
142     GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
143     cl_int       clError;
144     const size_t offset = startingOffset * sizeof(ValueType);
145     const size_t bytes  = numValues * sizeof(ValueType);
146     switch (transferKind)
147     {
148         case GpuApiCallBehavior::Async:
149             clError = clEnqueueWriteBuffer(stream, *buffer, CL_FALSE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
150             GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Asynchronous H2D copy failed");
151             break;
152
153         case GpuApiCallBehavior::Sync:
154             clError = clEnqueueWriteBuffer(stream, *buffer, CL_TRUE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
155             GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Synchronous H2D copy failed");
156             break;
157
158         default:
159             throw;
160     }
161 }
162
163 /*! \brief
164  * Performs the device-to-host data copy, synchronous or asynchronously on request.
165  *
166  * TODO: This is meant to gradually replace cu/ocl_copy_d2h.
167  *
168  * \tparam        ValueType            Raw value type of the \p buffer.
169  * \param[in,out] hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
170  * \param[in]     buffer               Pointer to the device-side buffer
171  * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy from.
172  * \param[in]     numValues            Number of values to copy.
173  * \param[in]     stream               GPU stream to perform asynchronous copy in.
174  * \param[in]     transferKind         Copy type: synchronous or asynchronous.
175  * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
176  *                                     If the pointer is not null, the event can further be used
177  *                                     to queue a wait for this operation or to query profiling information.
178  */
179 template <typename ValueType>
180 void copyFromDeviceBuffer(ValueType                     *hostBuffer,
181                           DeviceBuffer<ValueType>       *buffer,
182                           size_t                         startingOffset,
183                           size_t                         numValues,
184                           CommandStream                  stream,
185                           GpuApiCallBehavior             transferKind,
186                           CommandEvent                  *timingEvent)
187 {
188     GMX_ASSERT(buffer, "needs a buffer pointer");
189     GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
190     cl_int       clError;
191     const size_t offset = startingOffset * sizeof(ValueType);
192     const size_t bytes  = numValues * sizeof(ValueType);
193     switch (transferKind)
194     {
195         case GpuApiCallBehavior::Async:
196             clError = clEnqueueReadBuffer(stream, *buffer, CL_FALSE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
197             GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Asynchronous D2H copy failed");
198             break;
199
200         case GpuApiCallBehavior::Sync:
201             clError = clEnqueueReadBuffer(stream, *buffer, CL_TRUE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
202             GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Synchronous D2H copy failed");
203             break;
204
205         default:
206             throw;
207     }
208 }
209
210 /*! \brief
211  * Clears the device buffer asynchronously.
212  *
213  * \tparam        ValueType        Raw value type of the \p buffer.
214  * \param[in,out] buffer           Pointer to the device-side buffer
215  * \param[in]     startingOffset   Offset (in values) at the device-side buffer to start clearing at.
216  * \param[in]     numValues        Number of values to clear.
217  * \param[in]     stream           GPU stream.
218  */
219 template <typename ValueType>
220 void clearDeviceBufferAsync(DeviceBuffer<ValueType> *buffer,
221                             size_t                   startingOffset,
222                             size_t                   numValues,
223                             CommandStream            stream)
224 {
225     GMX_ASSERT(buffer, "needs a buffer pointer");
226     const size_t    offset        = startingOffset * sizeof(ValueType);
227     const size_t    bytes         = numValues * sizeof(ValueType);
228     const ValueType pattern       = 0;
229     const cl_uint   numWaitEvents = 0;
230     const cl_event *waitEvents    = nullptr;
231     cl_event        commandEvent;
232     cl_int          clError = clEnqueueFillBuffer(stream, *buffer, &pattern, sizeof(pattern),
233                                                   offset, bytes,
234                                                   numWaitEvents, waitEvents, &commandEvent);
235     GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Couldn't clear the device buffer");
236 }
237
238 #endif