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