Cleanup: fix compiler warnings.
[blender.git] / intern / opensubdiv / opensubdiv_capi.cc
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2013 Blender Foundation.
17  * All rights reserved.
18  */
19
20 #include "opensubdiv_capi.h"
21
22 #ifdef _MSC_VER
23 #  include "iso646.h"
24 #endif
25
26 #include <stdlib.h>
27 #include <GL/glew.h>
28
29 #include <opensubdiv/version.h>
30 #include <opensubdiv/osd/glMesh.h>
31
32 /* CPU Backend */
33 #include <opensubdiv/osd/cpuGLVertexBuffer.h>
34 #include <opensubdiv/osd/cpuEvaluator.h>
35
36 #ifdef OPENSUBDIV_HAS_OPENMP
37 #  include <opensubdiv/osd/ompEvaluator.h>
38 #endif  /* OPENSUBDIV_HAS_OPENMP */
39
40 #ifdef OPENSUBDIV_HAS_OPENCL
41 #  include <opensubdiv/osd/clGLVertexBuffer.h>
42 #  include <opensubdiv/osd/clEvaluator.h>
43 #  include "opensubdiv_device_context_opencl.h"
44 #endif  /* OPENSUBDIV_HAS_OPENCL */
45
46 #ifdef OPENSUBDIV_HAS_CUDA
47 #  include <opensubdiv/osd/cudaGLVertexBuffer.h>
48 #  include <opensubdiv/osd/cudaEvaluator.h>
49 #  include "opensubdiv_device_context_cuda.h"
50 #endif  /* OPENSUBDIV_HAS_CUDA */
51
52 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
53 #  include <opensubdiv/osd/glXFBEvaluator.h>
54 #  include <opensubdiv/osd/glVertexBuffer.h>
55 #endif  /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */
56
57 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
58 #  include <opensubdiv/osd/glComputeEvaluator.h>
59 #  include <opensubdiv/osd/glVertexBuffer.h>
60 #endif  /* OPENSUBDIV_HAS_GLSL_COMPUTE */
61
62 #include <opensubdiv/osd/glPatchTable.h>
63 #include <opensubdiv/far/stencilTable.h>
64 #include <opensubdiv/far/primvarRefiner.h>
65
66 #include "opensubdiv_intern.h"
67 #include "opensubdiv_topology_refiner.h"
68
69 #include "MEM_guardedalloc.h"
70
71 #include <string>
72 #include <vector>
73
74 using std::string;
75 using std::vector;
76
77 #define STRINGIFY_ARG(x) "" #x
78 #define STRINGIFY_APPEND(a, b) "" a #b
79 #define STRINGIFY(x) STRINGIFY_APPEND("", x)
80
81 /* **************** Types declaration **************** */
82
83 using OpenSubdiv::Osd::GLMeshInterface;
84 using OpenSubdiv::Osd::Mesh;
85 using OpenSubdiv::Osd::MeshBitset;
86 using OpenSubdiv::Far::StencilTable;
87 using OpenSubdiv::Osd::GLPatchTable;
88
89 using OpenSubdiv::Osd::Mesh;
90
91 /* CPU backend */
92 using OpenSubdiv::Osd::CpuGLVertexBuffer;
93 using OpenSubdiv::Osd::CpuEvaluator;
94 typedef Mesh<CpuGLVertexBuffer,
95              StencilTable,
96              CpuEvaluator,
97              GLPatchTable> OsdCpuMesh;
98
99 #ifdef OPENSUBDIV_HAS_OPENMP
100 using OpenSubdiv::Osd::OmpEvaluator;
101 typedef Mesh<CpuGLVertexBuffer,
102              StencilTable,
103              OmpEvaluator,
104              GLPatchTable> OsdOmpMesh;
105 #endif  /* OPENSUBDIV_HAS_OPENMP */
106
107 #ifdef OPENSUBDIV_HAS_OPENCL
108 using OpenSubdiv::Osd::CLEvaluator;
109 using OpenSubdiv::Osd::CLGLVertexBuffer;
110 using OpenSubdiv::Osd::CLStencilTable;
111 /* TODO(sergey): Use CLDeviceCOntext similar to OSD examples? */
112 typedef Mesh<CLGLVertexBuffer,
113              CLStencilTable,
114              CLEvaluator,
115              GLPatchTable,
116              CLDeviceContext> OsdCLMesh;
117 static CLDeviceContext g_clDeviceContext;
118 #endif  /* OPENSUBDIV_HAS_OPENCL */
119
120 #ifdef OPENSUBDIV_HAS_CUDA
121 using OpenSubdiv::Osd::CudaEvaluator;
122 using OpenSubdiv::Osd::CudaGLVertexBuffer;
123 using OpenSubdiv::Osd::CudaStencilTable;
124 typedef Mesh<CudaGLVertexBuffer,
125              CudaStencilTable,
126              CudaEvaluator,
127              GLPatchTable> OsdCudaMesh;
128 static CudaDeviceContext g_cudaDeviceContext;
129 #endif  /* OPENSUBDIV_HAS_CUDA */
130
131 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
132 using OpenSubdiv::Osd::GLXFBEvaluator;
133 using OpenSubdiv::Osd::GLStencilTableTBO;
134 using OpenSubdiv::Osd::GLVertexBuffer;
135 typedef Mesh<GLVertexBuffer,
136              GLStencilTableTBO,
137              GLXFBEvaluator,
138              GLPatchTable> OsdGLSLTransformFeedbackMesh;
139 #endif  /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */
140
141 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
142 using OpenSubdiv::Osd::GLComputeEvaluator;
143 using OpenSubdiv::Osd::GLStencilTableSSBO;
144 using OpenSubdiv::Osd::GLVertexBuffer;
145 typedef Mesh<GLVertexBuffer,
146              GLStencilTableSSBO,
147              GLComputeEvaluator,
148              GLPatchTable> OsdGLSLComputeMesh;
149 #endif
150
151 namespace {
152
153 #if !defined(OPENSUBDIV_VERSION_NUMBER) && !defined(OPENSUBDIV_VERSION_MINOR)
154 void stringSplit(vector<string>* tokens,
155                  const string& str,
156                  const string& separators,
157                  bool skip_empty) {
158         size_t token_start = 0, token_length = 0;
159         for (size_t i = 0; i < str.length(); ++i) {
160                 const char ch = str[i];
161                 if (separators.find(ch) == string::npos) {
162                         /* Append non-separator char to a token. */
163                         ++token_length;
164                 } else {
165                         /* Append current token to the list (if any). */
166                         if (token_length > 0 || !skip_empty) {
167                                 string token = str.substr(token_start, token_length);
168                                 tokens->push_back(token);
169                         }
170                         /* Re-set token pointers, */
171                         token_start = i + 1;
172                         token_length = 0;
173                 }
174         }
175         /* Append token which might be at the end of the string. */
176         if ((token_length != 0) ||
177             (!skip_empty && token_start > 0 &&
178              separators.find(str[token_start-1]) != string::npos)) {
179                 string token = str.substr(token_start, token_length);
180                 tokens->push_back(token);
181         }
182 }
183 #endif
184
185 struct FVarVertex {
186         float u, v;
187         void Clear() {
188                 u = v = 0.0f;
189         }
190         void AddWithWeight(FVarVertex const & src, float weight) {
191                 u += weight * src.u;
192                 v += weight * src.v;
193         }
194 };
195
196 static void interpolate_fvar_data(OpenSubdiv::Far::TopologyRefiner& refiner,
197                                   const std::vector<float> uvs,
198                                   std::vector<float> &fvar_data) {
199         /* TODO(sergey): Make it somehow more generic way. */
200         const int fvar_width = 2;
201         const int max_level = refiner.GetMaxLevel();
202         size_t fvar_data_offset = 0, values_offset = 0;
203         for (int channel = 0; channel < refiner.GetNumFVarChannels(); ++channel) {
204                 const int num_values = refiner.GetLevel(0).GetNumFVarValues(channel) * 2,
205                           num_values_max = refiner.GetLevel(max_level).GetNumFVarValues(channel),
206                           num_values_total = refiner.GetNumFVarValuesTotal(channel);
207                 if (num_values_total <= 0) {
208                         continue;
209                 }
210                 OpenSubdiv::Far::PrimvarRefiner primvar_refiner(refiner);
211                 if (refiner.IsUniform()) {
212                         /* For uniform we only keep the highest level of refinement. */
213                         fvar_data.resize(fvar_data.size() + num_values_max * fvar_width);
214                         std::vector<FVarVertex> buffer(num_values_total - num_values_max);
215                         FVarVertex *src = &buffer[0];
216                         memcpy(src, &uvs[values_offset], num_values * sizeof(float));
217                         /* Defer the last level to treat separately with its alternate
218                          * destination.
219                          */
220                         for (int level = 1; level < max_level; ++level) {
221                                 FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
222                                 primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
223                                 src = dst;
224                         }
225                         FVarVertex *dst = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]);
226                         primvar_refiner.InterpolateFaceVarying(max_level, src, dst, channel);
227                         fvar_data_offset += num_values_max * fvar_width;
228                 } else {
229                         /* For adaptive we keep all levels. */
230                         fvar_data.resize(fvar_data.size() + num_values_total * fvar_width);
231                         FVarVertex *src = reinterpret_cast<FVarVertex *>(&fvar_data[fvar_data_offset]);
232                         memcpy(src, &uvs[values_offset], num_values * sizeof(float));
233                         for (int level = 1; level <= max_level; ++level) {
234                                 FVarVertex *dst = src + refiner.GetLevel(level-1).GetNumFVarValues(channel);
235                                 primvar_refiner.InterpolateFaceVarying(level, src, dst, channel);
236                                 src = dst;
237                         }
238                         fvar_data_offset += num_values_total * fvar_width;
239                 }
240                 values_offset += num_values;
241         }
242 }
243
244 }  // namespace
245
246 struct OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner(
247         OpenSubdiv_TopologyRefinerDescr *topology_refiner,
248         int evaluator_type,
249         int level)
250 {
251         using OpenSubdiv::Far::TopologyRefiner;
252
253         MeshBitset bits;
254         /* TODO(sergey): Adaptive subdivisions are not currently
255          * possible because of the lack of tessellation shader.
256          */
257         bits.set(OpenSubdiv::Osd::MeshAdaptive, 0);
258         bits.set(OpenSubdiv::Osd::MeshUseSingleCreasePatch, 0);
259         bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, 1);
260         bits.set(OpenSubdiv::Osd::MeshFVarData, 1);
261         bits.set(OpenSubdiv::Osd::MeshEndCapBSplineBasis, 1);
262
263         const int num_vertex_elements = 3;
264         const int num_varying_elements = 3;
265
266         GLMeshInterface *mesh = NULL;
267         TopologyRefiner *refiner = topology_refiner->osd_refiner;
268
269         switch(evaluator_type) {
270 #define CHECK_EVALUATOR_TYPE(type, class) \
271                 case OPENSUBDIV_EVALUATOR_ ## type: \
272                         mesh = new class(refiner, \
273                                          num_vertex_elements, \
274                                          num_varying_elements, \
275                                          level, \
276                                          bits); \
277                         break;
278
279                 CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh)
280
281 #ifdef OPENSUBDIV_HAS_OPENMP
282                 CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh)
283 #endif
284
285 #ifdef OPENSUBDIV_HAS_OPENCL
286                 CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh)
287 #endif
288
289 #ifdef OPENSUBDIV_HAS_CUDA
290                 CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh)
291 #endif
292
293 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
294                 CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK,
295                                      OsdGLSLTransformFeedbackMesh)
296 #endif
297
298 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
299                 CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh)
300 #endif
301
302 #undef CHECK_EVALUATOR_TYPE
303         }
304
305         if (mesh == NULL) {
306                 return NULL;
307         }
308
309         OpenSubdiv_GLMesh *gl_mesh =
310                 (OpenSubdiv_GLMesh *) OBJECT_GUARDED_NEW(OpenSubdiv_GLMesh);
311         gl_mesh->evaluator_type = evaluator_type;
312         gl_mesh->descriptor = (OpenSubdiv_GLMeshDescr *) mesh;
313         gl_mesh->topology_refiner = topology_refiner;
314
315         if (refiner->GetNumFVarChannels() > 0) {
316                 std::vector<float> fvar_data;
317                 interpolate_fvar_data(*refiner, topology_refiner->uvs, fvar_data);
318                 openSubdiv_osdGLAllocFVar(topology_refiner, gl_mesh, &fvar_data[0]);
319         }
320         else {
321                 gl_mesh->fvar_data = NULL;
322         }
323
324         return gl_mesh;
325 }
326
327 void openSubdiv_deleteOsdGLMesh(struct OpenSubdiv_GLMesh *gl_mesh)
328 {
329         openSubdiv_osdGLDestroyFVar(gl_mesh);
330         switch (gl_mesh->evaluator_type) {
331 #define CHECK_EVALUATOR_TYPE(type, class) \
332                 case OPENSUBDIV_EVALUATOR_ ## type: \
333                         delete (class *) gl_mesh->descriptor; \
334                         break;
335
336                 CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh)
337
338 #ifdef OPENSUBDIV_HAS_OPENMP
339                 CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh)
340 #endif
341
342 #ifdef OPENSUBDIV_HAS_OPENCL
343                 CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh)
344 #endif
345
346 #ifdef OPENSUBDIV_HAS_CUDA
347                 CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh)
348 #endif
349
350 #ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
351                 CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK,
352                                      OsdGLSLTransformFeedbackMesh)
353 #endif
354
355 #ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
356                 CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh)
357 #endif
358
359 #undef CHECK_EVALUATOR_TYPE
360         }
361
362         /* NOTE: OSD refiner was owned by gl_mesh, no need to free it here. */
363         OBJECT_GUARDED_DELETE(gl_mesh->topology_refiner, OpenSubdiv_TopologyRefinerDescr);
364         OBJECT_GUARDED_DELETE(gl_mesh, OpenSubdiv_GLMesh);
365 }
366
367 unsigned int openSubdiv_getOsdGLMeshPatchIndexBuffer(struct OpenSubdiv_GLMesh *gl_mesh)
368 {
369         return ((GLMeshInterface *)gl_mesh->descriptor)->GetPatchTable()->GetPatchIndexBuffer();
370 }
371
372 unsigned int openSubdiv_getOsdGLMeshVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh)
373 {
374         return ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer();
375 }
376
377 void openSubdiv_osdGLMeshUpdateVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh,
378                                             const float *vertex_data,
379                                             int start_vertex,
380                                             int num_verts)
381 {
382         ((GLMeshInterface *)gl_mesh->descriptor)->UpdateVertexBuffer(vertex_data,
383                                                                      start_vertex,
384                                                                      num_verts);
385 }
386
387 void openSubdiv_osdGLMeshRefine(struct OpenSubdiv_GLMesh *gl_mesh)
388 {
389         ((GLMeshInterface *)gl_mesh->descriptor)->Refine();
390 }
391
392 void openSubdiv_osdGLMeshSynchronize(struct OpenSubdiv_GLMesh *gl_mesh)
393 {
394         ((GLMeshInterface *)gl_mesh->descriptor)->Synchronize();
395 }
396
397 void openSubdiv_osdGLMeshBindVertexBuffer(OpenSubdiv_GLMesh *gl_mesh)
398 {
399         ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer();
400 }
401
402 const struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_getGLMeshTopologyRefiner(
403         OpenSubdiv_GLMesh *gl_mesh)
404 {
405         return gl_mesh->topology_refiner;
406 }
407
408 int openSubdiv_supportGPUDisplay(void)
409 {
410         // TODO: simplify extension check once Blender adopts GL 3.2
411         return openSubdiv_gpu_legacy_support() &&
412                (GLEW_VERSION_3_2 ||
413                (GLEW_VERSION_3_1 && GLEW_EXT_geometry_shader4) ||
414                (GLEW_VERSION_3_0 &&
415                 GLEW_EXT_geometry_shader4 &&
416                 GLEW_ARB_uniform_buffer_object &&
417                 (GLEW_ARB_texture_buffer_object || GLEW_EXT_texture_buffer_object)));
418         /* also ARB_explicit_attrib_location? */
419 }
420
421 int openSubdiv_getVersionHex(void)
422 {
423 #if defined(OPENSUBDIV_VERSION_NUMBER)
424         return OPENSUBDIV_VERSION_NUMBER;
425 #elif defined(OPENSUBDIV_VERSION_MAJOR)
426         return OPENSUBDIV_VERSION_MAJOR * 10000 +
427                OPENSUBDIV_VERSION_MINOR * 100 +
428                OPENSUBDIV_VERSION_PATCH;
429 #elif defined(OPENSUBDIV_VERSION)
430         const char* version = STRINGIFY(OPENSUBDIV_VERSION);
431         if (version[0] == 'v') {
432                 version += 1;
433         }
434         int major = 0, minor = 0, patch = 0;
435         vector<string> tokens;
436         stringSplit(&tokens, version, "_", true);
437         if (tokens.size() == 3) {
438                 major = atoi(tokens[0].c_str());
439                 minor = atoi(tokens[1].c_str());
440                 patch = atoi(tokens[2].c_str());
441         }
442         return major * 10000 + minor * 100 + patch;
443 #else
444         return 0;
445 #endif
446 }