Cleanup: fix compiler warnings.
[blender.git] / intern / opensubdiv / opensubdiv_device_context_cuda.cc
1 /*
2  * Adopted from OpenSubdiv with the following license:
3  *
4  *   Copyright 2015 Pixar
5  *
6  *   Licensed under the Apache License, Version 2.0 (the "Apache License")
7  *   with the following modification; you may not use this file except in
8  *   compliance with the Apache License and the following modification to it:
9  *   Section 6. Trademarks. is deleted and replaced with:
10  *
11  *   6. Trademarks. This License does not grant permission to use the trade
12  *      names, trademarks, service marks, or product names of the Licensor
13  *      and its affiliates, except as required to comply with Section 4(c) of
14  *      the License and to reproduce the content of the NOTICE file.
15  *
16  *   You may obtain a copy of the Apache License at
17  *
18  *       http://www.apache.org/licenses/LICENSE-2.0
19  *
20  *   Unless required by applicable law or agreed to in writing, software
21  *   distributed under the Apache License with the above modification is
22  *   distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23  *   KIND, either express or implied. See the Apache License for the specific
24  *   language governing permissions and limitations under the Apache License.
25  */
26
27 #ifdef OPENSUBDIV_HAS_CUDA
28
29 #ifdef _MSC_VER
30 #  include "iso646.h"
31 #endif
32
33 #include "opensubdiv_device_context_cuda.h"
34
35 #if defined(_WIN32)
36 #  include <windows.h>
37 #elif defined(__APPLE__)
38 #  include <OpenGL/OpenGL.h>
39 #else
40 #  include <X11/Xlib.h>
41 #  include <GL/glx.h>
42 #endif
43
44 #include <cstdio>
45 #include <algorithm>
46 #include <cuda.h>
47 #include <cuda_runtime_api.h>
48 #include <cuda_gl_interop.h>
49
50 #define message(fmt, ...)
51 //#define message(fmt, ...)  fprintf(stderr, fmt, __VA_ARGS__)
52 #define error(fmt, ...)  fprintf(stderr, fmt, __VA_ARGS__)
53
54 static int _GetCudaDeviceForCurrentGLContext()
55 {
56         // Find and use the CUDA device for the current GL context
57         unsigned int interopDeviceCount = 0;
58         int interopDevices[1];
59         cudaError_t status = cudaGLGetDevices(&interopDeviceCount, interopDevices,
60                                               1,  cudaGLDeviceListCurrentFrame);
61         if (status == cudaErrorNoDevice or interopDeviceCount != 1) {
62                 message("CUDA no interop devices found.\n");
63                 return 0;
64         }
65         int device = interopDevices[0];
66
67 #if defined(_WIN32)
68         return device;
69
70 #elif defined(__APPLE__)
71         return device;
72
73 #else  // X11
74         Display * display = glXGetCurrentDisplay();
75         int screen = DefaultScreen(display);
76         if (device != screen) {
77                 error("The CUDA interop device (%d) does not match "
78                       "the screen used by the current GL context (%d), "
79                       "which may cause slow performance on systems "
80                       "with multiple GPU devices.", device, screen);
81         }
82         message("CUDA init using device for current GL context: %d\n", device);
83         return device;
84 #endif
85 }
86
87 /* From "NVIDIA GPU Computing SDK 4.2/C/common/inc/cutil_inline_runtime.h": */
88
89 /* Beginning of GPU Architecture definitions */
90 inline int _ConvertSMVer2Cores_local(int major, int minor)
91 {
92         /* Defines for GPU Architecture types (using the SM version to determine
93          * the # of cores per SM
94          */
95         typedef struct {
96                 int SM; /* 0xMm (hexidecimal notation),
97                          * M = SM Major version,
98                          * and m = SM minor version
99                          */
100                 int Cores;
101         } sSMtoCores;
102
103         sSMtoCores nGpuArchCoresPerSM[] =
104                 { { 0x10,  8 },  /* Tesla Generation (SM 1.0) G80 class */
105                   { 0x11,  8 },  /* Tesla Generation (SM 1.1) G8x class */
106                   { 0x12,  8 },  /* Tesla Generation (SM 1.2) G9x class */
107                   { 0x13,  8 },  /* Tesla Generation (SM 1.3) GT200 class */
108                   { 0x20, 32 },  /* Fermi Generation (SM 2.0) GF100 class */
109                   { 0x21, 48 },  /* Fermi Generation (SM 2.1) GF10x class */
110                   { 0x30, 192},  /* Fermi Generation (SM 3.0) GK10x class */
111                   {   -1, -1 }
112                 };
113
114         int index = 0;
115         while (nGpuArchCoresPerSM[index].SM != -1) {
116                 if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) {
117                         return nGpuArchCoresPerSM[index].Cores;
118                 }
119                 index++;
120         }
121         printf("MapSMtoCores undefined SMversion %d.%d!\n", major, minor);
122         return -1;
123 }
124 /* End of GPU Architecture definitions. */
125
126 /* This function returns the best GPU (with maximum GFLOPS) */
127 inline int cutGetMaxGflopsDeviceId()
128 {
129         int current_device   = 0, sm_per_multiproc = 0;
130         int max_compute_perf = 0, max_perf_device  = -1;
131         int device_count     = 0, best_SM_arch     = 0;
132         int compat_major, compat_minor;
133
134         cuDeviceGetCount(&device_count);
135         /* Find the best major SM Architecture GPU device. */
136         while (current_device < device_count) {
137                 cuDeviceComputeCapability(&compat_major, &compat_minor, current_device);
138                 if (compat_major > 0 && compat_major < 9999) {
139                         best_SM_arch = std::max(best_SM_arch, compat_major);
140                 }
141                 current_device++;
142         }
143
144         /* Find the best CUDA capable GPU device. */
145         current_device = 0;
146         while (current_device < device_count) {
147                 cuDeviceComputeCapability(&compat_major, &compat_minor, current_device);
148                 if (compat_major == 9999 && compat_minor == 9999) {
149                         sm_per_multiproc = 1;
150                 } else {
151                         sm_per_multiproc = _ConvertSMVer2Cores_local(compat_major,
152                                                                      compat_minor);
153                 }
154                 int multi_processor_count;
155                 cuDeviceGetAttribute(&multi_processor_count,
156                                      CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT,
157                                      current_device);
158                 int clock_rate;
159                 cuDeviceGetAttribute(&clock_rate,
160                                      CU_DEVICE_ATTRIBUTE_CLOCK_RATE,
161                                      current_device);
162                 int compute_perf = multi_processor_count * sm_per_multiproc * clock_rate;
163                 if (compute_perf > max_compute_perf) {
164                         /* If we find GPU with SM major > 2, search only these */
165                         if (best_SM_arch > 2) {
166                                 /* If our device==dest_SM_arch, choose this, or else pass. */
167                                 if (compat_major == best_SM_arch) {
168                                         max_compute_perf = compute_perf;
169                                         max_perf_device = current_device;
170                                 }
171                         } else {
172                                 max_compute_perf = compute_perf;
173                                 max_perf_device = current_device;
174                         }
175                 }
176                 ++current_device;
177         }
178         return max_perf_device;
179 }
180
181 bool CudaDeviceContext::HAS_CUDA_VERSION_4_0()
182 {
183 #ifdef OPENSUBDIV_HAS_CUDA
184         static bool cudaInitialized = false;
185         static bool cudaLoadSuccess = true;
186         if (!cudaInitialized) {
187                 cudaInitialized = true;
188
189 #  ifdef OPENSUBDIV_HAS_CUEW
190                 cudaLoadSuccess = cuewInit(CUEW_INIT_CUDA) == CUEW_SUCCESS;
191                 if (!cudaLoadSuccess) {
192                         fprintf(stderr, "Loading CUDA failed.\n");
193                 }
194 #  endif
195                 // Need to initialize CUDA here so getting device
196                 // with the maximum FPLOS works fine.
197                 if (cuInit(0) == CUDA_SUCCESS) {
198                         // This is to deal with cases like NVidia Optimus,
199                         // when there might be CUDA library installed but
200                         // NVidia card is not being active.
201                         if (cutGetMaxGflopsDeviceId() < 0) {
202                                 cudaLoadSuccess = false;
203                         }
204                 }
205                 else {
206                         cudaLoadSuccess = false;
207                 }
208         }
209         return cudaLoadSuccess;
210 #else
211         return false;
212 #endif
213 }
214
215 CudaDeviceContext::CudaDeviceContext()
216     : _initialized(false) {
217 }
218
219 CudaDeviceContext::~CudaDeviceContext() {
220         cudaDeviceReset();
221 }
222
223 bool CudaDeviceContext::Initialize()
224 {
225         /* See if any cuda device is available. */
226         int deviceCount = 0;
227         cudaGetDeviceCount(&deviceCount);
228         message("CUDA device count: %d\n", deviceCount);
229         if (deviceCount <= 0) {
230                 return false;
231         }
232         cudaGLSetGLDevice(_GetCudaDeviceForCurrentGLContext());
233         _initialized = true;
234         return true;
235 }
236
237 #endif  /* OPENSUBDIV_HAS_CUDA */