Extend GPU traits class
[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,2019, 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/devicebuffer_datatype.h"
49 #include "gromacs/gpu_utils/gpu_utils.h" //only for GpuApiCallBehavior
50 #include "gromacs/gpu_utils/gputraits_ocl.h"
51 #include "gromacs/utility/gmxassert.h"
52
53 /*! \libinternal \brief
54  * Allocates a device-side buffer.
55  * It is currently a caller's responsibility to call it only on not-yet allocated buffers.
56  *
57  * \tparam        ValueType            Raw value type of the \p buffer.
58  * \param[in,out] buffer               Pointer to the device-side buffer.
59  * \param[in]     numValues            Number of values to accomodate.
60  * \param[in]     deviceContext        The buffer's device context-to-be.
61  */
62 template <typename ValueType>
63 void allocateDeviceBuffer(DeviceBuffer<ValueType> *buffer,
64                           size_t                   numValues,
65                           DeviceContext            deviceContext)
66 {
67     GMX_ASSERT(buffer, "needs a buffer pointer");
68     void  *hostPtr = nullptr;
69     cl_int clError;
70     *buffer = clCreateBuffer(deviceContext, CL_MEM_READ_WRITE, numValues * sizeof(ValueType), hostPtr, &clError);
71     GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "clCreateBuffer failure");
72 }
73
74 /*! \brief
75  * Frees a device-side buffer.
76  * This does not reset separately stored size/capacity integers,
77  * as this is planned to be a destructor of DeviceBuffer as a proper class,
78  * and no calls on \p buffer should be made afterwards.
79  *
80  * \param[in] buffer  Pointer to the buffer to free.
81  */
82 template <typename DeviceBuffer>
83 void freeDeviceBuffer(DeviceBuffer *buffer)
84 {
85     GMX_ASSERT(buffer, "needs a buffer pointer");
86     if (*buffer)
87     {
88         GMX_RELEASE_ASSERT(clReleaseMemObject(*buffer) == CL_SUCCESS, "clReleaseMemObject failed");
89     }
90 }
91
92 /*! \brief
93  * Performs the host-to-device data copy, synchronous or asynchronously on request.
94  *
95  * TODO: This is meant to gradually replace cu/ocl_copy_h2d.
96  *
97  * \tparam        ValueType            Raw value type of the \p buffer.
98  * \param[in,out] buffer               Pointer to the device-side buffer
99  * \param[in]     hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
100  * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy into.
101  * \param[in]     numValues            Number of values to copy.
102  * \param[in]     stream               GPU stream to perform asynchronous copy in.
103  * \param[in]     transferKind         Copy type: synchronous or asynchronous.
104  * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
105  *                                     If the pointer is not null, the event can further be used
106  *                                     to queue a wait for this operation or to query profiling information.
107  */
108 template <typename ValueType>
109 void copyToDeviceBuffer(DeviceBuffer<ValueType> *buffer,
110                         const ValueType         *hostBuffer,
111                         size_t                   startingOffset,
112                         size_t                   numValues,
113                         CommandStream            stream,
114                         GpuApiCallBehavior       transferKind,
115                         CommandEvent            *timingEvent)
116 {
117     if (numValues == 0)
118     {
119         return; // such calls are actually made with empty domains
120     }
121     GMX_ASSERT(buffer, "needs a buffer pointer");
122     GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
123     cl_int       clError;
124     const size_t offset = startingOffset * sizeof(ValueType);
125     const size_t bytes  = numValues * sizeof(ValueType);
126     switch (transferKind)
127     {
128         case GpuApiCallBehavior::Async:
129             clError = clEnqueueWriteBuffer(stream, *buffer, CL_FALSE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
130             GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Asynchronous H2D copy failed");
131             break;
132
133         case GpuApiCallBehavior::Sync:
134             clError = clEnqueueWriteBuffer(stream, *buffer, CL_TRUE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
135             GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Synchronous H2D copy failed");
136             break;
137
138         default:
139             throw;
140     }
141 }
142
143 /*! \brief
144  * Performs the device-to-host data copy, synchronous or asynchronously on request.
145  *
146  * TODO: This is meant to gradually replace cu/ocl_copy_d2h.
147  *
148  * \tparam        ValueType            Raw value type of the \p buffer.
149  * \param[in,out] hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
150  * \param[in]     buffer               Pointer to the device-side buffer
151  * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy from.
152  * \param[in]     numValues            Number of values to copy.
153  * \param[in]     stream               GPU stream to perform asynchronous copy in.
154  * \param[in]     transferKind         Copy type: synchronous or asynchronous.
155  * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
156  *                                     If the pointer is not null, the event can further be used
157  *                                     to queue a wait for this operation or to query profiling information.
158  */
159 template <typename ValueType>
160 void copyFromDeviceBuffer(ValueType                     *hostBuffer,
161                           DeviceBuffer<ValueType>       *buffer,
162                           size_t                         startingOffset,
163                           size_t                         numValues,
164                           CommandStream                  stream,
165                           GpuApiCallBehavior             transferKind,
166                           CommandEvent                  *timingEvent)
167 {
168     GMX_ASSERT(buffer, "needs a buffer pointer");
169     GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
170     cl_int       clError;
171     const size_t offset = startingOffset * sizeof(ValueType);
172     const size_t bytes  = numValues * sizeof(ValueType);
173     switch (transferKind)
174     {
175         case GpuApiCallBehavior::Async:
176             clError = clEnqueueReadBuffer(stream, *buffer, CL_FALSE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
177             GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Asynchronous D2H copy failed");
178             break;
179
180         case GpuApiCallBehavior::Sync:
181             clError = clEnqueueReadBuffer(stream, *buffer, CL_TRUE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
182             GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Synchronous D2H copy failed");
183             break;
184
185         default:
186             throw;
187     }
188 }
189
190 /*! \brief
191  * Clears the device buffer asynchronously.
192  *
193  * \tparam        ValueType        Raw value type of the \p buffer.
194  * \param[in,out] buffer           Pointer to the device-side buffer
195  * \param[in]     startingOffset   Offset (in values) at the device-side buffer to start clearing at.
196  * \param[in]     numValues        Number of values to clear.
197  * \param[in]     stream           GPU stream.
198  */
199 template <typename ValueType>
200 void clearDeviceBufferAsync(DeviceBuffer<ValueType> *buffer,
201                             size_t                   startingOffset,
202                             size_t                   numValues,
203                             CommandStream            stream)
204 {
205     GMX_ASSERT(buffer, "needs a buffer pointer");
206     const size_t    offset        = startingOffset * sizeof(ValueType);
207     const size_t    bytes         = numValues * sizeof(ValueType);
208     const ValueType pattern       = 0;
209     const cl_uint   numWaitEvents = 0;
210     const cl_event *waitEvents    = nullptr;
211     cl_event        commandEvent;
212     cl_int          clError = clEnqueueFillBuffer(stream, *buffer, &pattern, sizeof(pattern),
213                                                   offset, bytes,
214                                                   numWaitEvents, waitEvents, &commandEvent);
215     GMX_RELEASE_ASSERT(clError == CL_SUCCESS, "Couldn't clear the device buffer");
216 }
217
218 #endif