Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'intern/opensubdiv')
-rw-r--r--intern/opensubdiv/CMakeLists.txt94
-rw-r--r--intern/opensubdiv/SConscript69
-rw-r--r--intern/opensubdiv/gpu_shader_opensubd_display.glsl333
-rw-r--r--intern/opensubdiv/opensubdiv_capi.cc323
-rw-r--r--intern/opensubdiv/opensubdiv_capi.h151
-rw-r--r--intern/opensubdiv/opensubdiv_converter.cc682
-rw-r--r--intern/opensubdiv/opensubdiv_converter_capi.h124
-rw-r--r--intern/opensubdiv/opensubdiv_device_context_cuda.cc237
-rw-r--r--intern/opensubdiv/opensubdiv_device_context_cuda.h54
-rw-r--r--intern/opensubdiv/opensubdiv_device_context_opencl.cc251
-rw-r--r--intern/opensubdiv/opensubdiv_device_context_opencl.h58
-rw-r--r--intern/opensubdiv/opensubdiv_evaluator_capi.cc495
-rw-r--r--intern/opensubdiv/opensubdiv_gpu_capi.cc674
-rw-r--r--intern/opensubdiv/opensubdiv_intern.h46
-rw-r--r--intern/opensubdiv/opensubdiv_utils_capi.cc110
15 files changed, 3701 insertions, 0 deletions
diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt
new file mode 100644
index 00000000000..a4dfe339728
--- /dev/null
+++ b/intern/opensubdiv/CMakeLists.txt
@@ -0,0 +1,94 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2013, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Sergey Sharybin.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ../guardedalloc
+)
+
+set(INC_SYS
+ ${OPENSUBDIV_INCLUDE_DIR}
+ ${GLEW_INCLUDE_PATH}
+)
+
+set(SRC
+ opensubdiv_capi.cc
+ opensubdiv_converter.cc
+ opensubdiv_device_context_cuda.cc
+ opensubdiv_device_context_opencl.cc
+ opensubdiv_evaluator_capi.cc
+ opensubdiv_gpu_capi.cc
+ opensubdiv_utils_capi.cc
+
+ opensubdiv_capi.h
+ opensubdiv_converter_capi.h
+ opensubdiv_device_context_cuda.h
+ opensubdiv_device_context_opencl.h
+ opensubdiv_intern.h
+)
+
+if(WITH_SUBSURF_WERROR)
+ ADD_CHECK_C_COMPILER_FLAG(CMAKE_C_FLAGS C_WERROR -Werror)
+ ADD_CHECK_CXX_COMPILER_FLAG(CMAKE_CXX_FLAGS C_WERROR -Werror)
+endif()
+
+macro(OPENSUBDIV_DEFINE_COMPONENT component)
+ if(${${component}})
+ add_definitions(-D${component})
+ endif()
+endmacro()
+
+OPENSUBDIV_DEFINE_COMPONENT(OPENSUBDIV_HAS_OPENMP)
+# TODO(sergey): OpenCL is not tested and totally unstable atm.
+# OPENSUBDIV_DEFINE_COMPONENT(OPENSUBDIV_HAS_OPENCL)
+# TODO(sergey): CUDA stays disabled for util it's ported to drievr API.
+# OPENSUBDIV_DEFINE_COMPONENT(OPENSUBDIV_HAS_CUDA)
+OPENSUBDIV_DEFINE_COMPONENT(OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK)
+OPENSUBDIV_DEFINE_COMPONENT(OPENSUBDIV_HAS_GLSL_COMPUTE)
+
+data_to_c_simple(gpu_shader_opensubd_display.glsl SRC)
+
+add_definitions(-DGLEW_STATIC)
+
+if(WIN32)
+ add_definitions(-DNOMINMAX)
+endif()
+
+# TODO(sergey): Put CUEW back when CUDA is officially supported by OSD.
+#if(OPENSUBDIV_HAS_CUDA)
+# list(APPEND INC
+# ../../extern/cuew/include
+# )
+# add_definitions(-DOPENSUBDIV_HAS_CUEW)
+#endif()
+
+if(OPENSUBDIV_HAS_OPENCL)
+ list(APPEND INC
+ ../../extern/clew/include
+ )
+ add_definitions(-DOPENSUBDIV_HAS_CLEW)
+endif()
+
+blender_add_lib(bf_intern_opensubdiv "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/intern/opensubdiv/SConscript b/intern/opensubdiv/SConscript
new file mode 100644
index 00000000000..0532bae1c29
--- /dev/null
+++ b/intern/opensubdiv/SConscript
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+#
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2013, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Sergey Sharybin.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+import os
+
+Import('env')
+
+sources = env.Glob('*.cc')
+
+defs = [ 'GLEW_STATIC' ]
+
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'):
+ defs += [ 'NOMINMAX' ]
+
+incs = '. ../guardedalloc #extern/clew/include/'
+incs += ' ' + env['BF_OPENSUBDIV_INC']
+incs += ' #/extern/glew/include'
+
+def checkOpenSubdivHeaderDefine(header, define):
+ include_path = Dir(env.subst(env['BF_OPENSUBDIV_INC'])).abspath
+ header_path = os.path.join(include_path, 'opensubdiv', 'osd', header)
+ if os.path.exists(header_path):
+ defs.append(define)
+ return True
+ return False
+
+checkOpenSubdivHeaderDefine("tbbEvaluator.h", 'OPENSUBDIV_HAS_TBB')
+#if checkOpenSubdivHeaderDefine("clEvaluator.h", 'OPENSUBDIV_HAS_OPENCL'):
+# defs += ['OPENSUBDIV_HAS_CLEW']
+# incs += ' #/extern/clew/include'
+#if checkOpenSubdivHeaderDefine("cudaEvaluator.h", 'OPENSUBDIV_HAS_CUDA'):
+# defs += ['OPENSUBDIV_HAS_CUEW']
+# incs += ' #/extern/cuew/include'
+checkOpenSubdivHeaderDefine("glXFBEvaluator.h", 'OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK')
+checkOpenSubdivHeaderDefine("glComputeEvaluator.h", 'OPENSUBDIV_HAS_GLSL_COMPUTE')
+
+if env['WITH_BF_LIBMV']:
+ defs += ['OPENSUBDIV_HAS_OPENMP']
+
+# generated data files
+sources.extend((
+ os.path.join(env['DATA_SOURCES'], "gpu_shader_opensubd_display.glsl.c"),
+))
+
+env.BlenderLib('bf_intern_opensubdiv', sources, Split(incs), defs, libtype=['extern','player'], priority=[10, 185])
diff --git a/intern/opensubdiv/gpu_shader_opensubd_display.glsl b/intern/opensubdiv/gpu_shader_opensubd_display.glsl
new file mode 100644
index 00000000000..e2574b5989e
--- /dev/null
+++ b/intern/opensubdiv/gpu_shader_opensubd_display.glsl
@@ -0,0 +1,333 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/* ***** Vertex shader ***** */
+
+#extension GL_EXT_geometry_shader4 : enable
+#extension GL_ARB_gpu_shader5 : enable
+#extension GL_ARB_explicit_attrib_location : require
+#extension GL_ARB_uniform_buffer_object : require
+
+struct VertexData {
+ vec4 position;
+ vec3 normal;
+ vec2 uv;
+};
+
+#ifdef VERTEX_SHADER
+
+in vec3 normal;
+in vec4 position;
+
+uniform mat4 modelViewMatrix;
+uniform mat3 normalMatrix;
+
+out block {
+ VertexData v;
+} outpt;
+
+void main()
+{
+ outpt.v.position = modelViewMatrix * position;
+ outpt.v.normal = normalize(normalMatrix * normal);
+ /* Some compilers expects gl_Position to be written.
+ * It's not needed once we explicitly switch to GLSL 1.40 or above.
+ */
+ gl_Position = outpt.v.position;
+}
+
+#endif /* VERTEX_SHADER */
+
+/* ***** geometry shader ***** */
+#ifdef GEOMETRY_SHADER
+
+#ifndef GLSL_COMPAT_WORKAROUND
+layout(lines_adjacency) in;
+#ifndef WIREFRAME
+layout(triangle_strip, max_vertices = 4) out;
+#else
+layout(line_strip, max_vertices = 8) out;
+#endif
+#endif
+
+uniform mat4 modelViewMatrix;
+uniform mat4 projectionMatrix;
+uniform int PrimitiveIdBase;
+uniform int osd_fvar_count;
+uniform int osd_active_uv_offset;
+
+in block {
+ VertexData v;
+} inpt[4];
+
+#define INTERP_FACE_VARYING_2(result, fvarOffset, tessCoord) \
+ { \
+ vec2 v[4]; \
+ int primOffset = (gl_PrimitiveID + PrimitiveIdBase) * 4; \
+ for (int i = 0; i < 4; ++i) { \
+ int index = (primOffset + i) * osd_fvar_count + fvarOffset; \
+ v[i] = vec2(texelFetch(FVarDataBuffer, index).s, \
+ texelFetch(FVarDataBuffer, index + 1).s); \
+ } \
+ result = mix(mix(v[0], v[1], tessCoord.s), \
+ mix(v[3], v[2], tessCoord.s), \
+ tessCoord.t); \
+ }
+
+uniform samplerBuffer FVarDataBuffer;
+
+out block {
+ VertexData v;
+} outpt;
+
+#ifdef FLAT_SHADING
+void emit(int index, vec3 normal)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.normal = normal;
+
+ /* TODO(sergey): Only uniform subdivisions atm. */
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+
+ INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st);
+
+ gl_Position = projectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+# ifdef WIREFRAME
+void emit_edge(int v0, int v1, vec3 normal)
+{
+ emit(v0, normal);
+ emit(v1, normal);
+}
+# endif
+
+#else
+void emit(int index)
+{
+ outpt.v.position = inpt[index].v.position;
+ outpt.v.normal = inpt[index].v.normal;
+
+ /* TODO(sergey): Only uniform subdivisions atm. */
+ vec2 quadst[4] = vec2[](vec2(0,0), vec2(1,0), vec2(1,1), vec2(0,1));
+ vec2 st = quadst[index];
+
+ INTERP_FACE_VARYING_2(outpt.v.uv, osd_active_uv_offset, st);
+
+ gl_Position = projectionMatrix * inpt[index].v.position;
+ EmitVertex();
+}
+
+# ifdef WIREFRAME
+void emit_edge(int v0, int v1)
+{
+ emit(v0);
+ emit(v1);
+}
+# endif
+
+#endif
+
+void main()
+{
+ gl_PrimitiveID = gl_PrimitiveIDIn;
+
+#ifdef FLAT_SHADING
+ vec3 A = (inpt[0].v.position - inpt[1].v.position).xyz;
+ vec3 B = (inpt[3].v.position - inpt[1].v.position).xyz;
+ vec3 flat_normal = normalize(cross(B, A));
+# ifndef WIREFRAME
+ emit(0, flat_normal);
+ emit(1, flat_normal);
+ emit(3, flat_normal);
+ emit(2, flat_normal);
+# else
+ emit_edge(0, 1, flat_normal);
+ emit_edge(1, 2, flat_normal);
+ emit_edge(2, 3, flat_normal);
+ emit_edge(3, 0, flat_normal);
+# endif
+#else
+# ifndef WIREFRAME
+ emit(0);
+ emit(1);
+ emit(3);
+ emit(2);
+# else
+ emit_edge(0, 1);
+ emit_edge(1, 2);
+ emit_edge(2, 3);
+ emit_edge(3, 0);
+# endif
+#endif
+
+ EndPrimitive();
+}
+
+#endif /* GEOMETRY_SHADER */
+
+/* ***** Fragment shader ***** */
+#ifdef FRAGMENT_SHADER
+
+#define MAX_LIGHTS 8
+#define NUM_SOLID_LIGHTS 3
+
+struct LightSource {
+ vec4 position;
+ vec4 ambient;
+ vec4 diffuse;
+ vec4 specular;
+ vec4 spotDirection;
+#ifdef SUPPORT_COLOR_MATERIAL
+ float constantAttenuation;
+ float linearAttenuation;
+ float quadraticAttenuation;
+ float spotCutoff;
+ float spotExponent;
+ float spotCosCutoff;
+#endif
+};
+
+layout(std140) uniform Lighting {
+ LightSource lightSource[MAX_LIGHTS];
+ int num_enabled_lights;
+};
+
+uniform vec4 diffuse;
+uniform vec4 specular;
+uniform float shininess;
+
+uniform sampler2D texture_buffer;
+
+in block {
+ VertexData v;
+} inpt;
+
+void main()
+{
+#ifdef WIREFRAME
+ gl_FragColor = diffuse;
+#else
+ vec3 N = inpt.v.normal;
+
+ if (!gl_FrontFacing)
+ N = -N;
+
+ /* Compute diffuse and specular lighting. */
+ vec3 L_diffuse = vec3(0.0);
+ vec3 L_specular = vec3(0.0);
+
+#ifndef USE_COLOR_MATERIAL
+ /* Assume NUM_SOLID_LIGHTS directional lights. */
+ for (int i = 0; i < NUM_SOLID_LIGHTS; i++) {
+ vec4 Plight = lightSource[i].position;
+#ifdef USE_DIRECTIONAL_LIGHT
+ vec3 l = (Plight.w == 0.0)
+ ? normalize(Plight.xyz)
+ : normalize(inpt.v.position.xyz);
+#else /* USE_DIRECTIONAL_LIGHT */
+ /* TODO(sergey): We can normalize it outside of the shader. */
+ vec3 l = normalize(Plight.xyz);
+#endif /* USE_DIRECTIONAL_LIGHT */
+ vec3 h = normalize(l + vec3(0, 0, 1));
+ float d = max(0.0, dot(N, l));
+ float s = pow(max(0.0, dot(N, h)), shininess);
+ L_diffuse += d * lightSource[i].diffuse.rgb;
+ L_specular += s * lightSource[i].specular.rgb;
+ }
+#else /* USE_COLOR_MATERIAL */
+ vec3 varying_position = inpt.v.position.xyz;
+ vec3 V = (gl_ProjectionMatrix[3][3] == 0.0) ?
+ normalize(varying_position): vec3(0.0, 0.0, -1.0);
+ for (int i = 0; i < num_enabled_lights; i++) {
+ /* todo: this is a slow check for disabled lights */
+ if (lightSource[i].specular.a == 0.0)
+ continue;
+
+ float intensity = 1.0;
+ vec3 light_direction;
+
+ if (lightSource[i].position.w == 0.0) {
+ /* directional light */
+ light_direction = lightSource[i].position.xyz;
+ }
+ else {
+ /* point light */
+ vec3 d = lightSource[i].position.xyz - varying_position;
+ light_direction = normalize(d);
+
+ /* spot light cone */
+ if (lightSource[i].spotCutoff < 90.0) {
+ float cosine = max(dot(light_direction,
+ -lightSource[i].spotDirection.xyz),
+ 0.0);
+ intensity = pow(cosine, lightSource[i].spotExponent);
+ intensity *= step(lightSource[i].spotCosCutoff, cosine);
+ }
+
+ /* falloff */
+ float distance = length(d);
+
+ intensity /= lightSource[i].constantAttenuation +
+ lightSource[i].linearAttenuation * distance +
+ lightSource[i].quadraticAttenuation * distance * distance;
+ }
+
+ /* diffuse light */
+ vec3 light_diffuse = lightSource[i].diffuse.rgb;
+ float diffuse_bsdf = max(dot(N, light_direction), 0.0);
+ L_diffuse += light_diffuse*diffuse_bsdf*intensity;
+
+ /* specular light */
+ vec3 light_specular = lightSource[i].specular.rgb;
+ vec3 H = normalize(light_direction - V);
+
+ float specular_bsdf = pow(max(dot(N, H), 0.0),
+ gl_FrontMaterial.shininess);
+ L_specular += light_specular*specular_bsdf * intensity;
+ }
+#endif /* USE_COLOR_MATERIAL */
+
+ /* Compute diffuse color. */
+#ifdef USE_TEXTURE_2D
+ L_diffuse *= texture2D(texture_buffer, inpt.v.uv).rgb;
+#else
+ L_diffuse *= diffuse.rgb;
+#endif
+
+ /* Sum lighting. */
+ vec3 L = L_diffuse;
+ if (shininess != 0) {
+ L += L_specular * specular.rgb;
+ }
+
+ /* Write out fragment color. */
+ gl_FragColor = vec4(L, diffuse.a);
+#endif
+}
+
+#endif // FRAGMENT_SHADER
diff --git a/intern/opensubdiv/opensubdiv_capi.cc b/intern/opensubdiv/opensubdiv_capi.cc
new file mode 100644
index 00000000000..66598948daf
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_capi.cc
@@ -0,0 +1,323 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2013 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin.
+ * Brecht van Lommel
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "opensubdiv_capi.h"
+
+#ifdef _MSC_VER
+# include "iso646.h"
+#endif
+
+#include <stdlib.h>
+#include <GL/glew.h>
+
+#include <opensubdiv/osd/glMesh.h>
+
+/* CPU Backend */
+#include <opensubdiv/osd/cpuGLVertexBuffer.h>
+#include <opensubdiv/osd/cpuEvaluator.h>
+
+#ifdef OPENSUBDIV_HAS_OPENMP
+# include <opensubdiv/osd/ompEvaluator.h>
+#endif /* OPENSUBDIV_HAS_OPENMP */
+
+#ifdef OPENSUBDIV_HAS_OPENCL
+# include <opensubdiv/osd/clGLVertexBuffer.h>
+# include <opensubdiv/osd/clEvaluator.h>
+# include "opensubdiv_device_context_opencl.h"
+#endif /* OPENSUBDIV_HAS_OPENCL */
+
+#ifdef OPENSUBDIV_HAS_CUDA
+# include <opensubdiv/osd/cudaGLVertexBuffer.h>
+# include <opensubdiv/osd/cudaEvaluator.h>
+# include "opensubdiv_device_context_cuda.h"
+#endif /* OPENSUBDIV_HAS_CUDA */
+
+#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
+# include <opensubdiv/osd/glXFBEvaluator.h>
+# include <opensubdiv/osd/glVertexBuffer.h>
+#endif /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */
+
+#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
+# include <opensubdiv/osd/glComputeEvaluator.h>
+# include <opensubdiv/osd/glVertexBuffer.h>
+#endif /* OPENSUBDIV_HAS_GLSL_COMPUTE */
+
+#include <opensubdiv/osd/glPatchTable.h>
+#include <opensubdiv/far/stencilTable.h>
+
+#include "opensubdiv_intern.h"
+
+#include "MEM_guardedalloc.h"
+
+/* **************** Types declaration **************** */
+
+using OpenSubdiv::Osd::GLMeshInterface;
+using OpenSubdiv::Osd::Mesh;
+using OpenSubdiv::Osd::MeshBitset;
+using OpenSubdiv::Far::StencilTable;
+using OpenSubdiv::Osd::GLPatchTable;
+
+using OpenSubdiv::Osd::Mesh;
+
+/* CPU backend */
+using OpenSubdiv::Osd::CpuGLVertexBuffer;
+using OpenSubdiv::Osd::CpuEvaluator;
+typedef Mesh<CpuGLVertexBuffer,
+ StencilTable,
+ CpuEvaluator,
+ GLPatchTable> OsdCpuMesh;
+
+#ifdef OPENSUBDIV_HAS_OPENMP
+using OpenSubdiv::Osd::OmpEvaluator;
+typedef Mesh<CpuGLVertexBuffer,
+ StencilTable,
+ OmpEvaluator,
+ GLPatchTable> OsdOmpMesh;
+#endif /* OPENSUBDIV_HAS_OPENMP */
+
+#ifdef OPENSUBDIV_HAS_OPENCL
+using OpenSubdiv::Osd::CLEvaluator;
+using OpenSubdiv::Osd::CLGLVertexBuffer;
+using OpenSubdiv::Osd::CLStencilTable;
+/* TODO(sergey): Use CLDeviceCOntext similar to OSD examples? */
+typedef Mesh<CLGLVertexBuffer,
+ CLStencilTable,
+ CLEvaluator,
+ GLPatchTable,
+ CLDeviceContext> OsdCLMesh;
+static CLDeviceContext g_clDeviceContext;
+#endif /* OPENSUBDIV_HAS_OPENCL */
+
+#ifdef OPENSUBDIV_HAS_CUDA
+using OpenSubdiv::Osd::CudaEvaluator;
+using OpenSubdiv::Osd::CudaGLVertexBuffer;
+using OpenSubdiv::Osd::CudaStencilTable;
+typedef Mesh<CudaGLVertexBuffer,
+ CudaStencilTable,
+ CudaEvaluator,
+ GLPatchTable> OsdCudaMesh;
+static CudaDeviceContext g_cudaDeviceContext;
+#endif /* OPENSUBDIV_HAS_CUDA */
+
+#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
+using OpenSubdiv::Osd::GLXFBEvaluator;
+using OpenSubdiv::Osd::GLStencilTableTBO;
+using OpenSubdiv::Osd::GLVertexBuffer;
+typedef Mesh<GLVertexBuffer,
+ GLStencilTableTBO,
+ GLXFBEvaluator,
+ GLPatchTable> OsdGLSLTransformFeedbackMesh;
+#endif /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */
+
+#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
+using OpenSubdiv::Osd::GLComputeEvaluator;
+using OpenSubdiv::Osd::GLStencilTableSSBO;
+using OpenSubdiv::Osd::GLVertexBuffer;
+typedef Mesh<GLVertexBuffer,
+ GLStencilTableSSBO,
+ GLComputeEvaluator,
+ GLPatchTable> OsdGLSLComputeMesh;
+#endif
+
+struct OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner(
+ OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+ int evaluator_type,
+ int level,
+ int /*subdivide_uvs*/)
+{
+ using OpenSubdiv::Far::TopologyRefiner;
+
+ MeshBitset bits;
+ /* TODO(sergey): Adaptive subdivisions are not currently
+ * possible because of the lack of tessellation shader.
+ */
+ bits.set(OpenSubdiv::Osd::MeshAdaptive, 0);
+ bits.set(OpenSubdiv::Osd::MeshUseSingleCreasePatch, 0);
+ bits.set(OpenSubdiv::Osd::MeshInterleaveVarying, 1);
+ bits.set(OpenSubdiv::Osd::MeshFVarData, 1);
+ bits.set(OpenSubdiv::Osd::MeshEndCapBSplineBasis, 1);
+
+ const int num_vertex_elements = 3;
+ const int num_varying_elements = 3;
+
+ GLMeshInterface *mesh = NULL;
+ TopologyRefiner *refiner = (TopologyRefiner*)topology_refiner;
+
+ switch(evaluator_type) {
+#define CHECK_EVALUATOR_TYPE(type, class) \
+ case OPENSUBDIV_EVALUATOR_ ## type: \
+ mesh = new class(refiner, \
+ num_vertex_elements, \
+ num_varying_elements, \
+ level, \
+ bits); \
+ break;
+
+ CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh)
+
+#ifdef OPENSUBDIV_HAS_OPENMP
+ CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh)
+#endif
+
+#ifdef OPENSUBDIV_HAS_OPENCL
+ CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh)
+#endif
+
+#ifdef OPENSUBDIV_HAS_CUDA
+ CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh)
+#endif
+
+#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
+ CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK,
+ OsdGLSLTransformFeedbackMesh)
+#endif
+
+#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
+ CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh)
+#endif
+
+#undef CHECK_EVALUATOR_TYPE
+ }
+
+ if (mesh == NULL) {
+ return NULL;
+ }
+
+ OpenSubdiv_GLMesh *gl_mesh =
+ (OpenSubdiv_GLMesh *) OBJECT_GUARDED_NEW(OpenSubdiv_GLMesh);
+ gl_mesh->evaluator_type = evaluator_type;
+ gl_mesh->descriptor = (OpenSubdiv_GLMeshDescr *) mesh;
+ gl_mesh->topology_refiner = (OpenSubdiv_TopologyRefinerDescr*)refiner;
+
+ return gl_mesh;
+}
+
+void openSubdiv_deleteOsdGLMesh(struct OpenSubdiv_GLMesh *gl_mesh)
+{
+ switch (gl_mesh->evaluator_type) {
+#define CHECK_EVALUATOR_TYPE(type, class) \
+ case OPENSUBDIV_EVALUATOR_ ## type: \
+ delete (class *) gl_mesh->descriptor; \
+ break;
+
+ CHECK_EVALUATOR_TYPE(CPU, OsdCpuMesh)
+
+#ifdef OPENSUBDIV_HAS_OPENMP
+ CHECK_EVALUATOR_TYPE(OPENMP, OsdOmpMesh)
+#endif
+
+#ifdef OPENSUBDIV_HAS_OPENCL
+ CHECK_EVALUATOR_TYPE(OPENCL, OsdCLMesh)
+#endif
+
+#ifdef OPENSUBDIV_HAS_CUDA
+ CHECK_EVALUATOR_TYPE(CUDA, OsdCudaMesh)
+#endif
+
+#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
+ CHECK_EVALUATOR_TYPE(GLSL_TRANSFORM_FEEDBACK,
+ OsdGLSLTransformFeedbackMesh)
+#endif
+
+#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
+ CHECK_EVALUATOR_TYPE(GLSL_COMPUTE, OsdGLSLComputeMesh)
+#endif
+
+#undef CHECK_EVALUATOR_TYPE
+ }
+
+ OBJECT_GUARDED_DELETE(gl_mesh, OpenSubdiv_GLMesh);
+}
+
+unsigned int openSubdiv_getOsdGLMeshPatchIndexBuffer(struct OpenSubdiv_GLMesh *gl_mesh)
+{
+ return ((GLMeshInterface *)gl_mesh->descriptor)->GetPatchTable()->GetPatchIndexBuffer();
+}
+
+unsigned int openSubdiv_getOsdGLMeshVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh)
+{
+ return ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer();
+}
+
+void openSubdiv_osdGLMeshUpdateVertexBuffer(struct OpenSubdiv_GLMesh *gl_mesh,
+ const float *vertex_data,
+ int start_vertex,
+ int num_verts)
+{
+ ((GLMeshInterface *)gl_mesh->descriptor)->UpdateVertexBuffer(vertex_data,
+ start_vertex,
+ num_verts);
+}
+
+void openSubdiv_osdGLMeshRefine(struct OpenSubdiv_GLMesh *gl_mesh)
+{
+ ((GLMeshInterface *)gl_mesh->descriptor)->Refine();
+}
+
+void openSubdiv_osdGLMeshSynchronize(struct OpenSubdiv_GLMesh *gl_mesh)
+{
+ ((GLMeshInterface *)gl_mesh->descriptor)->Synchronize();
+}
+
+void openSubdiv_osdGLMeshBindVertexBuffer(OpenSubdiv_GLMesh *gl_mesh)
+{
+ ((GLMeshInterface *)gl_mesh->descriptor)->BindVertexBuffer();
+}
+
+const struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_getGLMeshTopologyRefiner(
+ OpenSubdiv_GLMesh *gl_mesh)
+{
+ return gl_mesh->topology_refiner;
+}
+
+int openSubdiv_supportGPUDisplay(void)
+{
+ {
+ /* Currently Intel GPUs has hard time working on Windows.
+ *
+ * For until we've got access to a hardware which demonstrates
+ * the issue we disable OpenSubdiv on Intel GPUs.
+ */
+ static bool vendor_checked = false;
+ static bool is_intel = false;
+ if (!vendor_checked) {
+ vendor_checked = true;
+ const char *vendor = (const char *)glGetString(GL_VENDOR);
+ if (vendor != NULL && strstr(vendor, "Intel")) {
+ if(getenv("OPENSUBDIV_ALLOW_INTEL") == NULL) {
+ is_intel = true;
+ }
+ }
+ }
+ if (is_intel) {
+ return false;
+ }
+ }
+
+ return GLEW_EXT_geometry_shader4 &&
+ GLEW_ARB_gpu_shader5 &&
+ GLEW_ARB_uniform_buffer_object;
+}
diff --git a/intern/opensubdiv/opensubdiv_capi.h b/intern/opensubdiv/opensubdiv_capi.h
new file mode 100644
index 00000000000..8010c39647d
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_capi.h
@@ -0,0 +1,151 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2013 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENSUBDIV_CAPI_H__
+#define __OPENSUBDIV_CAPI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Types declaration.
+struct OpenSubdiv_GLMesh;
+struct OpenSubdiv_TopologyRefinerDescr;
+
+typedef struct OpenSubdiv_GLMesh OpenSubdiv_GLMesh;
+
+#ifdef __cplusplus
+struct OpenSubdiv_GLMeshDescr;
+typedef struct OpenSubdiv_GLMesh {
+ int evaluator_type;
+ OpenSubdiv_GLMeshDescr *descriptor;
+ OpenSubdiv_TopologyRefinerDescr *topology_refiner;
+} OpenSubdiv_GLMesh;
+#endif
+
+// Keep this a bitmask os it's possible to pass available
+// evaluators to Blender.
+enum {
+ OPENSUBDIV_EVALUATOR_CPU = (1 << 0),
+ OPENSUBDIV_EVALUATOR_OPENMP = (1 << 1),
+ OPENSUBDIV_EVALUATOR_OPENCL = (1 << 2),
+ OPENSUBDIV_EVALUATOR_CUDA = (1 << 3),
+ OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK = (1 << 4),
+ OPENSUBDIV_EVALUATOR_GLSL_COMPUTE = (1 << 5),
+};
+
+enum {
+ OPENSUBDIV_SCHEME_CATMARK,
+ OPENSUBDIV_SCHEME_BILINEAR,
+ OPENSUBDIV_SCHEME_LOOP,
+};
+
+/* TODO(sergey): Re-name and avoid bad level data access. */
+OpenSubdiv_GLMesh *openSubdiv_createOsdGLMeshFromTopologyRefiner(
+ struct OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+ int evaluator_type,
+ int level,
+ int subdivide_uvs);
+
+void openSubdiv_deleteOsdGLMesh(OpenSubdiv_GLMesh *gl_mesh);
+unsigned int openSubdiv_getOsdGLMeshPatchIndexBuffer(
+ OpenSubdiv_GLMesh *gl_mesh);
+unsigned int openSubdiv_getOsdGLMeshVertexBuffer(OpenSubdiv_GLMesh *gl_mesh);
+void openSubdiv_osdGLMeshUpdateVertexBuffer(OpenSubdiv_GLMesh *gl_mesh,
+ const float *vertex_data,
+ int start_vertex,
+ int num_verts);
+void openSubdiv_osdGLMeshRefine(OpenSubdiv_GLMesh *gl_mesh);
+void openSubdiv_osdGLMeshSynchronize(OpenSubdiv_GLMesh *gl_mesh);
+void openSubdiv_osdGLMeshBindVertexBuffer(OpenSubdiv_GLMesh *gl_mesh);
+
+const struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_getGLMeshTopologyRefiner(
+ OpenSubdiv_GLMesh *gl_mesh);
+
+/* ** Initialize/Deinitialize global OpenGL drawing buffers/GLSL programs ** */
+bool openSubdiv_osdGLDisplayInit(void);
+void openSubdiv_osdGLDisplayDeinit(void);
+
+/* ** Evaluator API ** */
+
+struct OpenSubdiv_EvaluatorDescr;
+typedef struct OpenSubdiv_EvaluatorDescr OpenSubdiv_EvaluatorDescr;
+
+/* TODO(sergey): Avoid bad-level data access, */
+OpenSubdiv_EvaluatorDescr *openSubdiv_createEvaluatorDescr(
+ struct OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+ int subsurf_level);
+
+void openSubdiv_deleteEvaluatorDescr(OpenSubdiv_EvaluatorDescr *evaluator_descr);
+
+void openSubdiv_setEvaluatorCoarsePositions(OpenSubdiv_EvaluatorDescr *evaluator_descr,
+ float *positions,
+ int start_vert,
+ int num_vert);
+
+void openSubdiv_setEvaluatorVaryingData(OpenSubdiv_EvaluatorDescr *evaluator_descr,
+ float *varying_data,
+ int start_vert,
+ int num_vert);
+
+void openSubdiv_evaluateLimit(OpenSubdiv_EvaluatorDescr *evaluator_descr,
+ int osd_face_index,
+ float face_u, float face_v,
+ float P[3],
+ float dPdu[3],
+ float dPdv[3]);
+
+void openSubdiv_evaluateVarying(OpenSubdiv_EvaluatorDescr *evaluator_descr,
+ int osd_face_index,
+ float face_u, float face_v,
+ float varying[3]);
+
+/* ** Actual drawing ** */
+
+/* Initialize all the invariants which stays the same for every single path,
+ * for example lighting model stays untouched for the whole mesh.
+ *
+ * TODO(sergey): Some of the stuff could be initialized once for all meshes.
+ */
+void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl,
+ int active_uv_index);
+
+/* Draw specified patches. */
+void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh,
+ int fill_quads,
+ int start_patch,
+ int num_patches);
+
+/* ** Utility functions ** */
+int openSubdiv_supportGPUDisplay(void);
+int openSubdiv_getAvailableEvaluators(void);
+void openSubdiv_init(void);
+void openSubdiv_cleanup(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // __OPENSUBDIV_CAPI_H__
diff --git a/intern/opensubdiv/opensubdiv_converter.cc b/intern/opensubdiv/opensubdiv_converter.cc
new file mode 100644
index 00000000000..3fadde68d32
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_converter.cc
@@ -0,0 +1,682 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <cstdio>
+#include <vector>
+
+#ifdef _MSC_VER
+# include "iso646.h"
+#endif
+
+#include <opensubdiv/far/topologyRefinerFactory.h>
+
+#include "opensubdiv_converter_capi.h"
+#include "opensubdiv_intern.h"
+
+#include <stack>
+
+#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
+namespace {
+
+inline void reverse_face_verts(int *face_verts, int num_verts)
+{
+ int last_vert = face_verts[num_verts - 1];
+ for (int i = num_verts - 1; i > 0; --i) {
+ face_verts[i] = face_verts[i - 1];
+ }
+ face_verts[0] = last_vert;
+}
+
+} /* namespace */
+#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */
+
+namespace OpenSubdiv {
+namespace OPENSUBDIV_VERSION {
+namespace Far {
+
+namespace {
+
+template <typename T>
+inline int findInArray(T array, int value)
+{
+ return (int)(std::find(array.begin(), array.end(), value) - array.begin());
+}
+
+#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
+inline int get_loop_winding(int vert0_of_face, int vert1_of_face)
+{
+ int delta_face = vert1_of_face - vert0_of_face;
+ if (abs(delta_face) != 1) {
+ if (delta_face > 0) {
+ delta_face = -1;
+ }
+ else {
+ delta_face = 1;
+ }
+ }
+ return delta_face;
+}
+
+inline void reverse_face_loops(IndexArray face_verts, IndexArray face_edges)
+{
+ for (int i = 0; i < face_verts.size() / 2; ++i) {
+ int j = face_verts.size() - i - 1;
+ if (i != j) {
+ std::swap(face_verts[i], face_verts[j]);
+ std::swap(face_edges[i], face_edges[j]);
+ }
+ }
+ reverse_face_verts(&face_verts[0], face_verts.size());
+}
+
+inline void check_oriented_vert_connectivity(const int num_vert_edges,
+ const int num_vert_faces,
+ const int *vert_edges,
+ const int *vert_faces,
+ const int *dst_vert_edges,
+ const int *dst_vert_faces)
+{
+# ifndef NDEBUG
+ for (int i = 0; i < num_vert_faces; ++i) {
+ bool found = false;
+ for (int j = 0; j < num_vert_faces; ++j) {
+ if (vert_faces[i] == dst_vert_faces[j]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ assert(!"vert-faces connectivity ruined");
+ }
+ }
+ for (int i = 0; i < num_vert_edges; ++i) {
+ bool found = false;
+ for (int j = 0; j < num_vert_edges; ++j) {
+ if (vert_edges[i] == dst_vert_edges[j]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ assert(!"vert-edges connectivity ruined");
+ }
+ }
+# else
+ (void)num_vert_edges;
+ (void)num_vert_faces;
+ (void)vert_edges;
+ (void)vert_faces;
+ (void)dst_vert_edges;
+ (void)dst_vert_faces;
+# endif
+}
+#endif
+
+} /* namespace */
+
+template <>
+inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::resizeComponentTopology(
+ TopologyRefiner& refiner,
+ const OpenSubdiv_Converter& conv)
+{
+ /* Faces and face-verts */
+ const int num_faces = conv.get_num_faces(&conv);
+ setNumBaseFaces(refiner, num_faces);
+ for (int face = 0; face < num_faces; ++face) {
+ const int num_verts = conv.get_num_face_verts(&conv, face);
+ setNumBaseFaceVertices(refiner, face, num_verts);
+ }
+ /* Edges and edge-faces. */
+ const int num_edges = conv.get_num_edges(&conv);
+ setNumBaseEdges(refiner, num_edges);
+ for (int edge = 0; edge < num_edges; ++edge) {
+ const int num_edge_faces = conv.get_num_edge_faces(&conv, edge);
+ setNumBaseEdgeFaces(refiner, edge, num_edge_faces);
+ }
+ /* Vertices and vert-faces and vert-edges/ */
+ const int num_verts = conv.get_num_verts(&conv);
+ setNumBaseVertices(refiner, num_verts);
+ for (int vert = 0; vert < num_verts; ++vert) {
+ const int num_vert_edges = conv.get_num_vert_edges(&conv, vert),
+ num_vert_faces = conv.get_num_vert_faces(&conv, vert);
+ setNumBaseVertexEdges(refiner, vert, num_vert_edges);
+ setNumBaseVertexFaces(refiner, vert, num_vert_faces);
+ }
+ return true;
+}
+
+template <>
+inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::assignComponentTopology(
+ TopologyRefiner& refiner,
+ const OpenSubdiv_Converter& conv)
+{
+ using Far::IndexArray;
+ /* Face relations. */
+ const int num_faces = conv.get_num_faces(&conv);
+ for (int face = 0; face < num_faces; ++face) {
+ IndexArray dst_face_verts = getBaseFaceVertices(refiner, face);
+ conv.get_face_verts(&conv, face, &dst_face_verts[0]);
+ IndexArray dst_face_edges = getBaseFaceEdges(refiner, face);
+ conv.get_face_edges(&conv, face, &dst_face_edges[0]);
+ }
+ /* Edge relations. */
+ const int num_edges = conv.get_num_edges(&conv);
+ for (int edge = 0; edge < num_edges; ++edge) {
+ /* Edge-vertices */
+ IndexArray dst_edge_verts = getBaseEdgeVertices(refiner, edge);
+ conv.get_edge_verts(&conv, edge, &dst_edge_verts[0]);
+ /* Edge-faces */
+ IndexArray dst_edge_faces = getBaseEdgeFaces(refiner, edge);
+ conv.get_edge_faces(&conv, edge, &dst_edge_faces[0]);
+ }
+#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
+ /* Make face normals consistent. */
+ bool *face_used = new bool[num_faces];
+ memset(face_used, 0, sizeof(bool) * num_faces);
+ std::stack<int> traverse_stack;
+ int face_start = 0, num_traversed_faces = 0;
+ /* Traverse all islands. */
+ while (num_traversed_faces != num_faces) {
+ /* Find first face of any untraversed islands. */
+ while (face_used[face_start]) {
+ ++face_start;
+ }
+ /* Add first face to the stack. */
+ traverse_stack.push(face_start);
+ face_used[face_start] = true;
+ /* Go over whole connected component. */
+ while (!traverse_stack.empty()) {
+ int face = traverse_stack.top();
+ traverse_stack.pop();
+ IndexArray face_edges = getBaseFaceEdges(refiner, face);
+ ConstIndexArray face_verts = getBaseFaceVertices(refiner, face);
+ for (int edge_index = 0; edge_index < face_edges.size(); ++edge_index) {
+ const int edge = face_edges[edge_index];
+ ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
+ if (edge_faces.size() != 2) {
+ /* Can't make consistent normals for non-manifolds. */
+ continue;
+ }
+ ConstIndexArray edge_verts = getBaseEdgeVertices(refiner, edge);
+ /* Get winding of the reference face. */
+ int vert0_of_face = findInArray(face_verts, edge_verts[0]),
+ vert1_of_face = findInArray(face_verts, edge_verts[1]);
+ int delta_face = get_loop_winding(vert0_of_face, vert1_of_face);
+ for (int edge_face = 0; edge_face < edge_faces.size(); ++edge_face) {
+ int other_face = edge_faces[edge_face];
+ /* Never re-traverse faces, only move forward. */
+ if (face_used[other_face]) {
+ continue;
+ }
+ IndexArray other_face_verts = getBaseFaceVertices(refiner,
+ other_face);
+ int vert0_of_other_face = findInArray(other_face_verts,
+ edge_verts[0]),
+ vert1_of_other_face = findInArray(other_face_verts,
+ edge_verts[1]);
+ int delta_other_face = get_loop_winding(vert0_of_other_face,
+ vert1_of_other_face);
+ if (delta_face * delta_other_face > 0) {
+ IndexArray other_face_verts = getBaseFaceVertices(refiner,
+ other_face),
+ other_face_edges = getBaseFaceEdges(refiner,
+ other_face);
+ reverse_face_loops(other_face_verts,
+ other_face_edges);
+ }
+ traverse_stack.push(other_face);
+ face_used[other_face] = true;
+ }
+ }
+ ++num_traversed_faces;
+ }
+ }
+#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */
+ /* Vertex relations */
+ const int num_verts = conv.get_num_verts(&conv);
+ for (int vert = 0; vert < num_verts; ++vert) {
+
+ /* Vert-Faces */
+ IndexArray dst_vert_faces = getBaseVertexFaces(refiner, vert);
+ int num_vert_faces = conv.get_num_vert_faces(&conv, vert);
+ int *vert_faces = new int[num_vert_faces];
+ conv.get_vert_faces(&conv, vert, vert_faces);
+ /* Vert-Edges */
+ IndexArray dst_vert_edges = getBaseVertexEdges(refiner, vert);
+ int num_vert_edges = conv.get_num_vert_edges(&conv, vert);
+ int *vert_edges = new int[num_vert_edges];
+ conv.get_vert_edges(&conv, vert, vert_edges);
+#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
+ /* ** Order vertex edges and faces in a CCW order. ** */
+ memset(face_used, 0, sizeof(bool) * num_faces);
+ /* Number of edges and faces added to the ordered array. */
+ int edge_count_ordered = 0, face_count_ordered = 0;
+ /* Add loose edges straight into the edges array. */
+ bool has_fan_connections = false;
+ for (int i = 0; i < num_vert_edges; ++i) {
+ IndexArray edge_faces = getBaseEdgeFaces(refiner, vert_edges[i]);
+ if (edge_faces.size() == 0) {
+ dst_vert_edges[edge_count_ordered++] = vert_edges[i];
+ }
+ else if (edge_faces.size() > 2) {
+ has_fan_connections = true;
+ }
+ }
+ if (has_fan_connections) {
+ /* OpenSubdiv currently doesn't give us clues how to handle
+ * fan face connections. and since handling such connections
+ * complicates the loop below we simply don't do special
+ * orientation for them.
+ */
+ memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
+ memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
+ delete [] vert_edges;
+ delete [] vert_faces;
+ continue;
+ }
+ /* Perform at max numbder of vert-edges iteration and try to avoid
+ * deadlock here for malformed mesh.
+ */
+ for (int global_iter = 0; global_iter < num_vert_edges; ++global_iter) {
+ /* Numbr of edges and faces which are still to be ordered. */
+ int num_vert_edges_remained = num_vert_edges - edge_count_ordered,
+ num_vert_faces_remained = num_vert_faces - face_count_ordered;
+ if (num_vert_edges_remained == 0 && num_vert_faces_remained == 0) {
+ /* All done, nothing to do anymore. */
+ break;
+ }
+ /* Face, edge and face-vertex inndex to start traversal from. */
+ int face_start = -1, edge_start = -1, face_vert_start = -1;
+ if (num_vert_edges_remained == num_vert_faces_remained) {
+ /* Vertex is eitehr complete manifold or is connected to seevral
+ * manifold islands (hourglass-like configuration), can pick up
+ * random edge unused and start from it.
+ */
+ /* TODO(sergey): Start from previous edge from which traversal
+ * began at previous iteration.
+ */
+ for (int i = 0; i < num_vert_edges; ++i) {
+ face_start = vert_faces[i];
+ if (!face_used[face_start]) {
+ ConstIndexArray
+ face_verts = getBaseFaceVertices(refiner, face_start),
+ face_edges = getBaseFaceEdges(refiner, face_start);
+ face_vert_start = findInArray(face_verts, vert);
+ edge_start = face_edges[face_vert_start];
+ break;
+ }
+ }
+ }
+ else {
+ /* Special handle of non-manifold vertex. */
+ for (int i = 0; i < num_vert_edges; ++i) {
+ bool start_found = false;
+ edge_start = vert_edges[i];
+ IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_start);
+ if (edge_faces.size() == 1) {
+ face_start = edge_faces[0];
+ if (!face_used[face_start]) {
+ ConstIndexArray
+ face_verts = getBaseFaceVertices(refiner, face_start),
+ face_edges = getBaseFaceEdges(refiner, face_start);
+ face_vert_start = findInArray(face_verts, vert);
+ if (edge_start == face_edges[face_vert_start]) {
+ start_found = true;
+ break;
+ }
+ }
+ }
+ if (start_found) {
+ break;
+ }
+ /* Reset indices for sanity check below. */
+ face_start = edge_start = face_vert_start = -1;
+ }
+ }
+ /* Sanity check. */
+ assert(face_start != -1 &&
+ edge_start != -1 &&
+ face_vert_start != -1);
+ /* Traverse faces starting from the current one. */
+ int edge_first = edge_start;
+ dst_vert_faces[face_count_ordered++] = face_start;
+ dst_vert_edges[edge_count_ordered++] = edge_start;
+ face_used[face_start] = true;
+ while (edge_count_ordered < num_vert_edges) {
+ IndexArray face_verts = getBaseFaceVertices(refiner, face_start);
+ IndexArray face_edges = getBaseFaceEdges(refiner, face_start);
+ int face_edge_start = face_vert_start;
+ int face_edge_next = (face_edge_start > 0) ? (face_edge_start - 1) : (face_verts.size() - 1);
+ Index edge_next = face_edges[face_edge_next];
+ if (edge_next == edge_first) {
+ /* Multiple manifolds found, stop for now and handle rest
+ * in the next iteration.
+ */
+ break;
+ }
+ dst_vert_edges[edge_count_ordered++] = edge_next;
+ if (face_count_ordered < num_vert_faces) {
+ IndexArray edge_faces = getBaseEdgeFaces(refiner, edge_next);
+ assert(edge_faces.size() != 0);
+ if (edge_faces.size() == 1) {
+ assert(edge_faces[0] == face_start);
+ break;
+ }
+ else if (edge_faces.size() != 2) {
+ break;
+ }
+ assert(edge_faces.size() == 2);
+ face_start = edge_faces[(edge_faces[0] == face_start) ? 1 : 0];
+ face_vert_start = findInArray(getBaseFaceEdges(refiner, face_start), edge_next);
+ dst_vert_faces[face_count_ordered++] = face_start;
+ face_used[face_start] = true;
+ }
+ edge_start = edge_next;
+ }
+ }
+ /* Verify ordering doesn't ruin connectivity information. */
+ assert(face_count_ordered == num_vert_faces);
+ assert(edge_count_ordered == num_vert_edges);
+ check_oriented_vert_connectivity(num_vert_edges,
+ num_vert_faces,
+ vert_edges,
+ vert_faces,
+ &dst_vert_edges[0],
+ &dst_vert_faces[0]);
+ /* For the release builds we're failing mesh construction so instead
+ * of nasty bugs the unsupported mesh will simply disappear from the
+ * viewport.
+ */
+ if (face_count_ordered != num_vert_faces ||
+ edge_count_ordered != num_vert_edges)
+ {
+ delete [] vert_edges;
+ delete [] vert_faces;
+ return false;
+ }
+#else /* OPENSUBDIV_ORIENT_TOPOLOGY */
+ memcpy(&dst_vert_edges[0], vert_edges, sizeof(int) * num_vert_edges);
+ memcpy(&dst_vert_faces[0], vert_faces, sizeof(int) * num_vert_faces);
+#endif /* OPENSUBDIV_ORIENT_TOPOLOGY */
+ delete [] vert_edges;
+ delete [] vert_faces;
+ }
+#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
+ delete [] face_used;
+#endif
+ populateBaseLocalIndices(refiner);
+ return true;
+};
+
+template <>
+inline bool TopologyRefinerFactory<OpenSubdiv_Converter>::assignComponentTags(
+ TopologyRefiner& refiner,
+ const OpenSubdiv_Converter& conv)
+{
+ typedef OpenSubdiv::Sdc::Crease Crease;
+
+ int num_edges = conv.get_num_edges(&conv);
+ for (int edge = 0; edge < num_edges; ++edge) {
+ float sharpness;
+ ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
+ if (edge_faces.size() == 2) {
+ sharpness = conv.get_edge_sharpness(&conv, edge);
+ }
+ else {
+ /* Non-manifold edges must be sharp. */
+ sharpness = Crease::SHARPNESS_INFINITE;
+ }
+ setBaseEdgeSharpness(refiner, edge, sharpness);
+ }
+
+ /* OpenSubdiv expects non-manifold vertices to be sharp but at the
+ * time it handles correct cases when vertex is a corner of plane.
+ * Currently mark verts which are adjacent to a loose edge as sharp,
+ * but this decision needs some more investigation.
+ */
+ int num_vert = conv.get_num_verts(&conv);
+ for (int vert = 0; vert < num_vert; ++vert) {
+ ConstIndexArray vert_edges = getBaseVertexEdges(refiner, vert);
+ for (int edge_index = 0; edge_index < vert_edges.size(); ++edge_index) {
+ int edge = vert_edges[edge_index];
+ ConstIndexArray edge_faces = getBaseEdgeFaces(refiner, edge);
+ if (edge_faces.size() == 0) {
+ setBaseVertexSharpness(refiner, vert, Crease::SHARPNESS_INFINITE);
+ break;
+ }
+ }
+ if (vert_edges.size() == 2) {
+ int edge0 = vert_edges[0],
+ edge1 = vert_edges[1];
+ float sharpness0 = conv.get_edge_sharpness(&conv, edge0),
+ sharpness1 = conv.get_edge_sharpness(&conv, edge1);
+ float sharpness = std::min(sharpness0, sharpness1);
+ setBaseVertexSharpness(refiner, vert, sharpness);
+ }
+ }
+
+ return true;
+}
+
+template <>
+inline void TopologyRefinerFactory<OpenSubdiv_Converter>::reportInvalidTopology(
+ TopologyError /*errCode*/,
+ const char *msg,
+ const OpenSubdiv_Converter& /*mesh*/)
+{
+ printf("OpenSubdiv Error: %s\n", msg);
+}
+
+} /* namespace Far */
+} /* namespace OPENSUBDIV_VERSION */
+} /* namespace OpenSubdiv */
+
+namespace {
+
+OpenSubdiv::Sdc::SchemeType get_capi_scheme_type(OpenSubdiv_SchemeType type)
+{
+ switch(type) {
+ case OSD_SCHEME_BILINEAR:
+ return OpenSubdiv::Sdc::SCHEME_BILINEAR;
+ case OSD_SCHEME_CATMARK:
+ return OpenSubdiv::Sdc::SCHEME_CATMARK;
+ case OSD_SCHEME_LOOP:
+ return OpenSubdiv::Sdc::SCHEME_LOOP;
+ }
+ assert(!"Unknown sceme type passed via C-API");
+ return OpenSubdiv::Sdc::SCHEME_CATMARK;
+}
+
+} /* namespace */
+
+struct OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr(
+ OpenSubdiv_Converter *converter)
+{
+ typedef OpenSubdiv::Sdc::Options Options;
+
+ using OpenSubdiv::Far::TopologyRefinerFactory;
+ OpenSubdiv::Sdc::SchemeType scheme_type =
+ get_capi_scheme_type(converter->get_type(converter));
+ Options options;
+ options.SetVtxBoundaryInterpolation(Options::VTX_BOUNDARY_EDGE_ONLY);
+ options.SetCreasingMethod(Options::CREASE_UNIFORM);
+ options.SetFVarLinearInterpolation(Options::FVAR_LINEAR_ALL);
+
+ TopologyRefinerFactory<OpenSubdiv_Converter>::Options
+ topology_options(scheme_type, options);
+#ifdef OPENSUBDIV_VALIDATE_TOPOLOGY
+ topology_options.validateFullTopology = true;
+#endif
+ /* We don't use guarded allocation here so we can re-use the refiner
+ * for GL mesh creation directly.
+ */
+ return (struct OpenSubdiv_TopologyRefinerDescr*)
+ TopologyRefinerFactory<OpenSubdiv_Converter>::Create(
+ *converter,
+ topology_options);
+}
+
+void openSubdiv_deleteTopologyRefinerDescr(
+ OpenSubdiv_TopologyRefinerDescr *topology_refiner)
+{
+ delete (OpenSubdiv::Far::TopologyRefiner *)topology_refiner;
+}
+
+int openSubdiv_topologyRefinerGetSubdivLevel(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
+{
+ using OpenSubdiv::Far::TopologyRefiner;
+ const TopologyRefiner *refiner = (const TopologyRefiner *)topology_refiner;
+ return refiner->GetMaxLevel();
+}
+
+int openSubdiv_topologyRefinerGetNumVerts(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
+{
+ using OpenSubdiv::Far::TopologyLevel;
+ using OpenSubdiv::Far::TopologyRefiner;
+ const TopologyRefiner *refiner = (const TopologyRefiner *)topology_refiner;
+ const TopologyLevel &base_level = refiner->GetLevel(0);
+ return base_level.GetNumVertices();
+}
+
+int openSubdiv_topologyRefinerGetNumEdges(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
+{
+ using OpenSubdiv::Far::TopologyLevel;
+ using OpenSubdiv::Far::TopologyRefiner;
+ const TopologyRefiner *refiner = (const TopologyRefiner *)topology_refiner;
+ const TopologyLevel &base_level = refiner->GetLevel(0);
+ return base_level.GetNumEdges();
+}
+
+int openSubdiv_topologyRefinerGetNumFaces(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner)
+{
+ using OpenSubdiv::Far::TopologyLevel;
+ using OpenSubdiv::Far::TopologyRefiner;
+ const TopologyRefiner *refiner = (const TopologyRefiner *)topology_refiner;
+ const TopologyLevel &base_level = refiner->GetLevel(0);
+ return base_level.GetNumFaces();
+}
+
+int openSubdiv_topologyRefinerGetNumFaceVerts(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+ int face)
+{
+ using OpenSubdiv::Far::TopologyLevel;
+ using OpenSubdiv::Far::TopologyRefiner;
+ const TopologyRefiner *refiner = (const TopologyRefiner *)topology_refiner;
+ const TopologyLevel &base_level = refiner->GetLevel(0);
+ return base_level.GetFaceVertices(face).size();
+}
+
+int openSubdiv_topologyRefnerCompareConverter(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+ OpenSubdiv_Converter *converter)
+{
+ using OpenSubdiv::Far::ConstIndexArray;
+ using OpenSubdiv::Far::TopologyRefiner;
+ using OpenSubdiv::Far::TopologyLevel;
+ const TopologyRefiner *refiner = (const TopologyRefiner *)topology_refiner;
+ const TopologyLevel &base_level = refiner->GetLevel(0);
+ const int num_verts = base_level.GetNumVertices();
+ const int num_edges = base_level.GetNumEdges();
+ const int num_faces = base_level.GetNumFaces();
+ /* Quick preliminary check. */
+ OpenSubdiv::Sdc::SchemeType scheme_type =
+ get_capi_scheme_type(converter->get_type(converter));
+ if (scheme_type != refiner->GetSchemeType()) {
+ return false;
+ }
+ if (converter->get_num_verts(converter) != num_verts ||
+ converter->get_num_edges(converter) != num_edges ||
+ converter->get_num_faces(converter) != num_faces)
+ {
+ return false;
+ }
+ /* Compare all edges. */
+ for (int edge = 0; edge < num_edges; ++edge) {
+ ConstIndexArray edge_verts = base_level.GetEdgeVertices(edge);
+ int conv_edge_verts[2];
+ converter->get_edge_verts(converter, edge, conv_edge_verts);
+ if (conv_edge_verts[0] != edge_verts[0] ||
+ conv_edge_verts[1] != edge_verts[1])
+ {
+ return false;
+ }
+ }
+ /* Compare all faces. */
+ std::vector<int> conv_face_verts;
+ for (int face = 0; face < num_faces; ++face) {
+ ConstIndexArray face_verts = base_level.GetFaceVertices(face);
+ if (face_verts.size() != converter->get_num_face_verts(converter,
+ face))
+ {
+ return false;
+ }
+ conv_face_verts.resize(face_verts.size());
+ converter->get_face_verts(converter, face, &conv_face_verts[0]);
+ bool direct_match = true;
+ for (int i = 0; i < face_verts.size(); ++i) {
+ if (conv_face_verts[i] != face_verts[i]) {
+ direct_match = false;
+ break;
+ }
+ }
+ if (!direct_match) {
+ /* If face didn't match in direct direction we also test if it
+ * matches in reversed direction. This is because conversion might
+ * reverse loops to make normals consistent.
+ */
+#ifdef OPENSUBDIV_ORIENT_TOPOLOGY
+ reverse_face_verts(&conv_face_verts[0], conv_face_verts.size());
+ for (int i = 0; i < face_verts.size(); ++i) {
+ if (conv_face_verts[i] != face_verts[i]) {
+ return false;
+ }
+ }
+#else
+ return false;
+#endif
+ }
+ }
+ /* Compare sharpness. */
+ for (int edge = 0; edge < num_edges; ++edge) {
+ ConstIndexArray edge_faces = base_level.GetEdgeFaces(edge);
+ float sharpness = base_level.GetEdgeSharpness(edge);
+ float conv_sharpness;
+ if (edge_faces.size() == 2) {
+ conv_sharpness = converter->get_edge_sharpness(converter, edge);
+ }
+ else {
+ conv_sharpness = OpenSubdiv::Sdc::Crease::SHARPNESS_INFINITE;
+ }
+ if (sharpness != conv_sharpness) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/intern/opensubdiv/opensubdiv_converter_capi.h b/intern/opensubdiv/opensubdiv_converter_capi.h
new file mode 100644
index 00000000000..ac1e8301a42
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_converter_capi.h
@@ -0,0 +1,124 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENSUBDIV_CONVERTER_CAPI_H__
+#define __OPENSUBDIV_CONVERTER_CAPI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct OpenSubdiv_TopologyRefinerDescr;
+typedef struct OpenSubdiv_TopologyRefinerDescr OpenSubdiv_TopologyRefinerDescr;
+
+typedef struct OpenSubdiv_Converter OpenSubdiv_Converter;
+
+typedef enum OpenSubdiv_SchemeType {
+ OSD_SCHEME_BILINEAR,
+ OSD_SCHEME_CATMARK,
+ OSD_SCHEME_LOOP,
+} OpenSubdiv_SchemeType;
+
+typedef struct OpenSubdiv_Converter {
+ /* TODO(sergey): Needs to be implemented. */
+ /* OpenSubdiv::Sdc::Options get_options() const; */
+
+ OpenSubdiv_SchemeType (*get_type)(const OpenSubdiv_Converter *converter);
+
+ int (*get_num_faces)(const OpenSubdiv_Converter *converter);
+ int (*get_num_edges)(const OpenSubdiv_Converter *converter);
+ int (*get_num_verts)(const OpenSubdiv_Converter *converter);
+
+ /* Face relationships. */
+ int (*get_num_face_verts)(const OpenSubdiv_Converter *converter,
+ int face);
+ void (*get_face_verts)(const OpenSubdiv_Converter *converter,
+ int face,
+ int *face_verts);
+ void (*get_face_edges)(const OpenSubdiv_Converter *converter,
+ int face,
+ int *face_edges);
+
+ /* Edge relationships. */
+ void (*get_edge_verts)(const OpenSubdiv_Converter *converter,
+ int edge,
+ int *edge_verts);
+ int (*get_num_edge_faces)(const OpenSubdiv_Converter *converter,
+ int edge);
+ void (*get_edge_faces)(const OpenSubdiv_Converter *converter,
+ int edge,
+ int *edge_faces);
+ float (*get_edge_sharpness)(const OpenSubdiv_Converter *converter,
+ int edge);
+
+ /* Vertex relationships. */
+ int (*get_num_vert_edges)(const OpenSubdiv_Converter *converter, int vert);
+ void (*get_vert_edges)(const OpenSubdiv_Converter *converter,
+ int vert,
+ int *vert_edges);
+ int (*get_num_vert_faces)(const OpenSubdiv_Converter *converter, int vert);
+ void (*get_vert_faces)(const OpenSubdiv_Converter *converter,
+ int vert,
+ int *vert_faces);
+
+ void (*free_user_data)(const OpenSubdiv_Converter *converter);
+ void *user_data;
+} OpenSubdiv_Converter;
+
+OpenSubdiv_TopologyRefinerDescr *openSubdiv_createTopologyRefinerDescr(
+ OpenSubdiv_Converter *converter);
+
+void openSubdiv_deleteTopologyRefinerDescr(
+ OpenSubdiv_TopologyRefinerDescr *topology_refiner);
+
+/* TODO(sergey): Those calls are not strictly related on conversion.
+ * needs some dedicated file perhaps.
+ */
+
+int openSubdiv_topologyRefinerGetSubdivLevel(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner);
+
+int openSubdiv_topologyRefinerGetNumVerts(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner);
+
+int openSubdiv_topologyRefinerGetNumEdges(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner);
+
+int openSubdiv_topologyRefinerGetNumFaces(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner);
+
+int openSubdiv_topologyRefinerGetNumFaceVerts(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+ int face);
+
+int openSubdiv_topologyRefnerCompareConverter(
+ const OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+ OpenSubdiv_Converter *converter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OPENSUBDIV_CONVERTER_CAPI_H__ */
diff --git a/intern/opensubdiv/opensubdiv_device_context_cuda.cc b/intern/opensubdiv/opensubdiv_device_context_cuda.cc
new file mode 100644
index 00000000000..81c52f5d6cf
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_device_context_cuda.cc
@@ -0,0 +1,237 @@
+/*
+ * Adopted from OpenSubdiv with the following license:
+ *
+ * Copyright 2015 Pixar
+ *
+ * Licensed under the Apache License, Version 2.0 (the "Apache License")
+ * with the following modification; you may not use this file except in
+ * compliance with the Apache License and the following modification to it:
+ * Section 6. Trademarks. is deleted and replaced with:
+ *
+ * 6. Trademarks. This License does not grant permission to use the trade
+ * names, trademarks, service marks, or product names of the Licensor
+ * and its affiliates, except as required to comply with Section 4(c) of
+ * the License and to reproduce the content of the NOTICE file.
+ *
+ * You may obtain a copy of the Apache License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Apache License with the above modification is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the Apache License for the specific
+ * language governing permissions and limitations under the Apache License.
+ */
+
+#ifdef OPENSUBDIV_HAS_CUDA
+
+#ifdef _MSC_VER
+# include "iso646.h"
+#endif
+
+#include "opensubdiv_device_context_cuda.h"
+
+#if defined(_WIN32)
+# include <windows.h>
+#elif defined(__APPLE__)
+# include <OpenGL/OpenGL.h>
+#else
+# include <X11/Xlib.h>
+# include <GL/glx.h>
+#endif
+
+#include <cstdio>
+#include <algorithm>
+#include <cuda.h>
+#include <cuda_runtime_api.h>
+#include <cuda_gl_interop.h>
+
+#define message(fmt, ...)
+//#define message(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
+#define error(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
+
+static int _GetCudaDeviceForCurrentGLContext()
+{
+ // Find and use the CUDA device for the current GL context
+ unsigned int interopDeviceCount = 0;
+ int interopDevices[1];
+ cudaError_t status = cudaGLGetDevices(&interopDeviceCount, interopDevices,
+ 1, cudaGLDeviceListCurrentFrame);
+ if (status == cudaErrorNoDevice or interopDeviceCount != 1) {
+ message("CUDA no interop devices found.\n");
+ return 0;
+ }
+ int device = interopDevices[0];
+
+#if defined(_WIN32)
+ return device;
+
+#elif defined(__APPLE__)
+ return device;
+
+#else // X11
+ Display * display = glXGetCurrentDisplay();
+ int screen = DefaultScreen(display);
+ if (device != screen) {
+ error("The CUDA interop device (%d) does not match "
+ "the screen used by the current GL context (%d), "
+ "which may cause slow performance on systems "
+ "with multiple GPU devices.", device, screen);
+ }
+ message("CUDA init using device for current GL context: %d\n", device);
+ return device;
+#endif
+}
+
+/* From "NVIDIA GPU Computing SDK 4.2/C/common/inc/cutil_inline_runtime.h": */
+
+/* Beginning of GPU Architecture definitions */
+inline int _ConvertSMVer2Cores_local(int major, int minor)
+{
+ /* Defines for GPU Architecture types (using the SM version to determine
+ * the # of cores per SM
+ */
+ typedef struct {
+ int SM; /* 0xMm (hexidecimal notation),
+ * M = SM Major version,
+ * and m = SM minor version
+ */
+ int Cores;
+ } sSMtoCores;
+
+ sSMtoCores nGpuArchCoresPerSM[] =
+ { { 0x10, 8 }, /* Tesla Generation (SM 1.0) G80 class */
+ { 0x11, 8 }, /* Tesla Generation (SM 1.1) G8x class */
+ { 0x12, 8 }, /* Tesla Generation (SM 1.2) G9x class */
+ { 0x13, 8 }, /* Tesla Generation (SM 1.3) GT200 class */
+ { 0x20, 32 }, /* Fermi Generation (SM 2.0) GF100 class */
+ { 0x21, 48 }, /* Fermi Generation (SM 2.1) GF10x class */
+ { 0x30, 192}, /* Fermi Generation (SM 3.0) GK10x class */
+ { -1, -1 }
+ };
+
+ int index = 0;
+ while (nGpuArchCoresPerSM[index].SM != -1) {
+ if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor)) {
+ return nGpuArchCoresPerSM[index].Cores;
+ }
+ index++;
+ }
+ printf("MapSMtoCores undefined SMversion %d.%d!\n", major, minor);
+ return -1;
+}
+/* End of GPU Architecture definitions. */
+
+/* This function returns the best GPU (with maximum GFLOPS) */
+inline int cutGetMaxGflopsDeviceId()
+{
+ int current_device = 0, sm_per_multiproc = 0;
+ int max_compute_perf = 0, max_perf_device = -1;
+ int device_count = 0, best_SM_arch = 0;
+ int compat_major, compat_minor;
+
+ cuDeviceGetCount(&device_count);
+ /* Find the best major SM Architecture GPU device. */
+ while (current_device < device_count) {
+ cuDeviceComputeCapability(&compat_major, &compat_minor, current_device);
+ if (compat_major > 0 && compat_major < 9999) {
+ best_SM_arch = std::max(best_SM_arch, compat_major);
+ }
+ current_device++;
+ }
+
+ /* Find the best CUDA capable GPU device. */
+ current_device = 0;
+ while (current_device < device_count) {
+ cuDeviceComputeCapability(&compat_major, &compat_minor, current_device);
+ if (compat_major == 9999 && compat_minor == 9999) {
+ sm_per_multiproc = 1;
+ } else {
+ sm_per_multiproc = _ConvertSMVer2Cores_local(compat_major,
+ compat_minor);
+ }
+ int multi_processor_count;
+ cuDeviceGetAttribute(&multi_processor_count,
+ CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT,
+ current_device);
+ int clock_rate;
+ cuDeviceGetAttribute(&clock_rate,
+ CU_DEVICE_ATTRIBUTE_CLOCK_RATE,
+ current_device);
+ int compute_perf = multi_processor_count * sm_per_multiproc * clock_rate;
+ if (compute_perf > max_compute_perf) {
+ /* If we find GPU with SM major > 2, search only these */
+ if (best_SM_arch > 2) {
+ /* If our device==dest_SM_arch, choose this, or else pass. */
+ if (compat_major == best_SM_arch) {
+ max_compute_perf = compute_perf;
+ max_perf_device = current_device;
+ }
+ } else {
+ max_compute_perf = compute_perf;
+ max_perf_device = current_device;
+ }
+ }
+ ++current_device;
+ }
+ return max_perf_device;
+}
+
+bool CudaDeviceContext::HAS_CUDA_VERSION_4_0()
+{
+#ifdef OPENSUBDIV_HAS_CUDA
+ static bool cudaInitialized = false;
+ static bool cudaLoadSuccess = true;
+ if (!cudaInitialized) {
+ cudaInitialized = true;
+
+# ifdef OPENSUBDIV_HAS_CUEW
+ cudaLoadSuccess = cuewInit() == CUEW_SUCCESS;
+ if (!cudaLoadSuccess) {
+ fprintf(stderr, "Loading CUDA failed.\n");
+ }
+# endif
+ // Need to initialize CUDA here so getting device
+ // with the maximum FPLOS works fine.
+ if (cuInit(0) == CUDA_SUCCESS) {
+ // This is to deal with cases like NVidia Optimus,
+ // when there might be CUDA library installed but
+ // NVidia card is not being active.
+ if (cutGetMaxGflopsDeviceId() < 0) {
+ cudaLoadSuccess = false;
+ }
+ }
+ else {
+ cudaLoadSuccess = false;
+ }
+ }
+ return cudaLoadSuccess;
+#else
+ return false;
+#endif
+}
+
+CudaDeviceContext::CudaDeviceContext()
+ : _initialized(false) {
+}
+
+CudaDeviceContext::~CudaDeviceContext() {
+ cudaDeviceReset();
+}
+
+bool CudaDeviceContext::Initialize()
+{
+ /* See if any cuda device is available. */
+ int deviceCount = 0;
+ cudaGetDeviceCount(&deviceCount);
+ message("CUDA device count: %d\n", deviceCount);
+ if (deviceCount <= 0) {
+ return false;
+ }
+ cudaGLSetGLDevice(_GetCudaDeviceForCurrentGLContext());
+ _initialized = true;
+ return true;
+}
+
+#endif /* OPENSUBDIV_HAS_CUDA */
diff --git a/intern/opensubdiv/opensubdiv_device_context_cuda.h b/intern/opensubdiv/opensubdiv_device_context_cuda.h
new file mode 100644
index 00000000000..eb30b76f507
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_device_context_cuda.h
@@ -0,0 +1,54 @@
+/*
+ * Adopted from OpenSubdiv with the following license:
+ *
+ * Copyright 2013 Pixar
+ *
+ * Licensed under the Apache License, Version 2.0 (the "Apache License")
+ * with the following modification; you may not use this file except in
+ * compliance with the Apache License and the following modification to it:
+ * Section 6. Trademarks. is deleted and replaced with:
+ *
+ * 6. Trademarks. This License does not grant permission to use the trade
+ * names, trademarks, service marks, or product names of the Licensor
+ * and its affiliates, except as required to comply with Section 4(c) of
+ * the License and to reproduce the content of the NOTICE file.
+ *
+ * You may obtain a copy of the Apache License at
+ *
+ * http: //www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Apache License with the above modification is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the Apache License for the specific
+ * language governing permissions and limitations under the Apache License.
+ *
+ */
+
+#ifndef __OPENSUBDIV_DEV_CE_CONTEXT_CUDA_H__
+#define __OPENSUBDIV_DEV_CE_CONTEXT_CUDA_H__
+
+struct ID3D11Device;
+
+class CudaDeviceContext {
+public:
+ CudaDeviceContext();
+ ~CudaDeviceContext();
+
+ static bool HAS_CUDA_VERSION_4_0();
+
+ /* Initialze cuda device from the current GL context. */
+ bool Initialize();
+
+ /* Initialze cuda device from the ID3D11Device/ */
+ bool Initialize(ID3D11Device *device);
+
+ /* Returns true if the cuda device has already been initialized. */
+ bool IsInitialized() const {
+ return _initialized;
+ }
+private:
+ bool _initialized;
+};
+
+#endif /* __OPENSUBDIV_DEV_CE_CONTEXT_OPENCL_H__ */
diff --git a/intern/opensubdiv/opensubdiv_device_context_opencl.cc b/intern/opensubdiv/opensubdiv_device_context_opencl.cc
new file mode 100644
index 00000000000..4cacdc9e845
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_device_context_opencl.cc
@@ -0,0 +1,251 @@
+/*
+ * Adopted from OpenSubdiv with the following license:
+ *
+ * Copyright 2015 Pixar
+ *
+ * Licensed under the Apache License, Version 2.0 (the "Apache License")
+ * with the following modification; you may not use this file except in
+ * compliance with the Apache License and the following modification to it:
+ * Section 6. Trademarks. is deleted and replaced with:
+ *
+ * 6. Trademarks. This License does not grant permission to use the trade
+ * names, trademarks, service marks, or product names of the Licensor
+ * and its affiliates, except as required to comply with Section 4(c) of
+ * the License and to reproduce the content of the NOTICE file.
+ *
+ * You may obtain a copy of the Apache License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Apache License with the above modification is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the Apache License for the specific
+ * language governing permissions and limitations under the Apache License.
+ *
+ */
+
+#ifdef OPENSUBDIV_HAS_OPENCL
+
+#ifdef _MSC_VER
+# include "iso646.h"
+#endif
+
+#include "opensubdiv_device_context_opencl.h"
+
+#if defined(_WIN32)
+# include <windows.h>
+#elif defined(__APPLE__)
+# include <OpenGL/OpenGL.h>
+#else
+# include <GL/glx.h>
+#endif
+
+#include <cstdio>
+#include <cstring>
+#include <string>
+
+#define message(...) // fprintf(stderr, __VA_ARGS__)
+#define error(...) fprintf(stderr, __VA_ARGS__)
+
+/* Returns the first found platform. */
+static cl_platform_id findPlatform() {
+ cl_uint numPlatforms;
+ cl_int ciErrNum = clGetPlatformIDs(0, NULL, &numPlatforms);
+ if (ciErrNum != CL_SUCCESS) {
+ error("Error %d in clGetPlatformIDs call.\n", ciErrNum);
+ return NULL;
+ }
+ if (numPlatforms == 0) {
+ error("No OpenCL platform found.\n");
+ return NULL;
+ }
+ cl_platform_id *clPlatformIDs = new cl_platform_id[numPlatforms];
+ ciErrNum = clGetPlatformIDs(numPlatforms, clPlatformIDs, NULL);
+ char chBuffer[1024];
+ for (cl_uint i = 0; i < numPlatforms; ++i) {
+ ciErrNum = clGetPlatformInfo(clPlatformIDs[i], CL_PLATFORM_NAME,
+ 1024, chBuffer,NULL);
+ if (ciErrNum == CL_SUCCESS) {
+ cl_platform_id platformId = clPlatformIDs[i];
+ delete[] clPlatformIDs;
+ return platformId;
+ }
+ }
+ delete[] clPlatformIDs;
+ return NULL;
+}
+
+/* Return. the device in clDevices which supports the extension. */
+static int findExtensionSupportedDevice(cl_device_id *clDevices,
+ int numDevices,
+ const char *extensionName) {
+ /* Find a device that supports sharing with GL/D3D11
+ * (SLI / X-fire configurations)
+ */
+ cl_int ciErrNum;
+ for (int i = 0; i < numDevices; ++i) {
+ /* Get extensions string size. */
+ size_t extensionSize;
+ ciErrNum = clGetDeviceInfo(clDevices[i],
+ CL_DEVICE_EXTENSIONS, 0, NULL,
+ &extensionSize);
+ if (ciErrNum != CL_SUCCESS) {
+ error("Error %d in clGetDeviceInfo\n", ciErrNum);
+ return -1;
+ }
+ if (extensionSize > 0) {
+ /* Get extensions string. */
+ char *extensions = new char[extensionSize];
+ ciErrNum = clGetDeviceInfo(clDevices[i], CL_DEVICE_EXTENSIONS,
+ extensionSize, extensions,
+ &extensionSize);
+ if (ciErrNum != CL_SUCCESS) {
+ error("Error %d in clGetDeviceInfo\n", ciErrNum);
+ delete[] extensions;
+ continue;
+ }
+ std::string extString(extensions);
+ delete[] extensions;
+ /* Parse string. This is bit deficient since the extentions
+ * is space separated.
+ *
+ * The actual string would be "cl_khr_d3d11_sharing"
+ * or "cl_nv_d3d11_sharing"
+ */
+ if (extString.find(extensionName) != std::string::npos) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+CLDeviceContext::CLDeviceContext()
+ : _clContext(NULL),
+ _clCommandQueue(NULL) {
+}
+
+CLDeviceContext::~CLDeviceContext() {
+ if (_clCommandQueue)
+ clReleaseCommandQueue(_clCommandQueue);
+ if (_clContext)
+ clReleaseContext(_clContext);
+}
+
+bool CLDeviceContext::HAS_CL_VERSION_1_1()
+{
+#ifdef OPENSUBDIV_HAS_CLEW
+ static bool clewInitialized = false;
+ static bool clewLoadSuccess;
+ if (not clewInitialized) {
+ clewInitialized = true;
+ clewLoadSuccess = clewInit() == CLEW_SUCCESS;
+ if (!clewLoadSuccess) {
+ error("Loading OpenCL failed.\n");
+ }
+ }
+ return clewLoadSuccess;
+#endif
+ return true;
+}
+
+bool CLDeviceContext::Initialize()
+{
+#ifdef OPENSUBDIV_HAS_CLEW
+ if (!clGetPlatformIDs) {
+ error("Error clGetPlatformIDs function not bound.\n");
+ return false;
+ }
+#endif
+ cl_int ciErrNum;
+ cl_platform_id cpPlatform = findPlatform();
+
+#if defined(_WIN32)
+ cl_context_properties props[] = {
+ CL_GL_CONTEXT_KHR, (cl_context_properties)wglGetCurrentContext(),
+ CL_WGL_HDC_KHR, (cl_context_properties)wglGetCurrentDC(),
+ CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform,
+ 0
+ };
+#elif defined(__APPLE__)
+ CGLContextObj kCGLContext = CGLGetCurrentContext();
+ CGLShareGroupObj kCGLShareGroup = CGLGetShareGroup(kCGLContext);
+ cl_context_properties props[] = {
+ CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, (cl_context_properties)kCGLShareGroup,
+ 0
+ };
+#else
+ cl_context_properties props[] = {
+ CL_GL_CONTEXT_KHR, (cl_context_properties)glXGetCurrentContext(),
+ CL_GLX_DISPLAY_KHR, (cl_context_properties)glXGetCurrentDisplay(),
+ CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform,
+ 0
+ };
+#endif
+
+#if defined(__APPLE__)
+ _clContext = clCreateContext(props, 0, NULL, clLogMessagesToStdoutAPPLE,
+ NULL, &ciErrNum);
+ if (ciErrNum != CL_SUCCESS) {
+ error("Error %d in clCreateContext\n", ciErrNum);
+ return false;
+ }
+
+ size_t devicesSize = 0;
+ clGetGLContextInfoAPPLE(_clContext, kCGLContext,
+ CL_CGL_DEVICES_FOR_SUPPORTED_VIRTUAL_SCREENS_APPLE,
+ 0, NULL, &devicesSize);
+ int numDevices = int(devicesSize / sizeof(cl_device_id));
+ if (numDevices == 0) {
+ error("No sharable devices.\n");
+ return false;
+ }
+ cl_device_id *clDevices = new cl_device_id[numDevices];
+ clGetGLContextInfoAPPLE(_clContext, kCGLContext,
+ CL_CGL_DEVICES_FOR_SUPPORTED_VIRTUAL_SCREENS_APPLE,
+ numDevices * sizeof(cl_device_id), clDevices, NULL);
+ int clDeviceUsed = 0;
+
+#else // not __APPLE__
+ /* Get the number of GPU devices available to the platform. */
+ cl_uint numDevices = 0;
+ clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 0, NULL, &numDevices);
+ if (numDevices == 0) {
+ error("No CL GPU device found.\n");
+ return false;
+ }
+
+ /* Create the device list. */
+ cl_device_id *clDevices = new cl_device_id[numDevices];
+ clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, numDevices, clDevices, NULL);
+
+ const char *extension = "cl_khr_gl_sharing";
+ int clDeviceUsed = findExtensionSupportedDevice(clDevices, numDevices,
+ extension);
+
+ if (clDeviceUsed < 0) {
+ error("No device found that supports CL/GL context sharing\n");
+ delete[] clDevices;
+ return false;
+ }
+
+ _clContext = clCreateContext(props, 1, &clDevices[clDeviceUsed],
+ NULL, NULL, &ciErrNum);
+#endif // not __APPLE__
+ if (ciErrNum != CL_SUCCESS) {
+ error("Error %d in clCreateContext\n", ciErrNum);
+ delete[] clDevices;
+ return false;
+ }
+ _clCommandQueue = clCreateCommandQueue(_clContext, clDevices[clDeviceUsed],
+ 0, &ciErrNum);
+ delete[] clDevices;
+ if (ciErrNum != CL_SUCCESS) {
+ error("Error %d in clCreateCommandQueue\n", ciErrNum);
+ return false;
+ }
+ return true;
+}
+
+#endif /* OPENSUBDIV_HAS_OPENCL */
diff --git a/intern/opensubdiv/opensubdiv_device_context_opencl.h b/intern/opensubdiv/opensubdiv_device_context_opencl.h
new file mode 100644
index 00000000000..a640dce1f07
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_device_context_opencl.h
@@ -0,0 +1,58 @@
+/*
+ * Adopted from OpenSubdiv with the following license:
+ *
+ * Copyright 2015 Pixar
+ *
+ * Licensed under the Apache License, Version 2.0 (the "Apache License")
+ * with the following modification; you may not use this file except in
+ * compliance with the Apache License and the following modification to it:
+ * Section 6. Trademarks. is deleted and replaced with:
+ *
+ * 6. Trademarks. This License does not grant permission to use the trade
+ * names, trademarks, service marks, or product names of the Licensor
+ * and its affiliates, except as required to comply with Section 4(c) of
+ * the License and to reproduce the content of the NOTICE file.
+ *
+ * You may obtain a copy of the Apache License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Apache License with the above modification is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the Apache License for the specific
+ * language governing permissions and limitations under the Apache License.
+ *
+ */
+
+#ifndef __OPENSUBDIV_DEV_CE_CONTEXT_OPENCL_H__
+#define __OPENSUBDIV_DEV_CE_CONTEXT_OPENCL_H__
+
+#include <opensubdiv/osd/opencl.h>
+
+class CLDeviceContext {
+public:
+ CLDeviceContext();
+ ~CLDeviceContext();
+
+ static bool HAS_CL_VERSION_1_1 ();
+
+ bool Initialize();
+
+ bool IsInitialized() const {
+ return (_clContext != NULL);
+ }
+
+ cl_context GetContext() const {
+ return _clContext;
+ }
+ cl_command_queue GetCommandQueue() const {
+ return _clCommandQueue;
+ }
+
+protected:
+ cl_context _clContext;
+ cl_command_queue _clCommandQueue;
+};
+
+#endif /* __OPENSUBDIV_DEV_CE_CONTEXT_OPENCL_H__ */
diff --git a/intern/opensubdiv/opensubdiv_evaluator_capi.cc b/intern/opensubdiv/opensubdiv_evaluator_capi.cc
new file mode 100644
index 00000000000..0da6b9daba8
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_evaluator_capi.cc
@@ -0,0 +1,495 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "opensubdiv_capi.h"
+
+#include <cstdio>
+#include <vector>
+
+#ifdef _MSC_VER
+# include "iso646.h"
+#endif
+
+#include <opensubdiv/far/patchMap.h>
+#include <opensubdiv/far/patchTable.h>
+#include <opensubdiv/far/patchTableFactory.h>
+#include <opensubdiv/osd/cpuEvaluator.h>
+#include <opensubdiv/osd/cpuPatchTable.h>
+#include <opensubdiv/osd/cpuVertexBuffer.h>
+#include <opensubdiv/osd/mesh.h>
+#include <opensubdiv/osd/types.h>
+
+#include "opensubdiv_intern.h"
+
+#include "MEM_guardedalloc.h"
+
+using OpenSubdiv::Osd::BufferDescriptor;
+using OpenSubdiv::Osd::PatchCoord;
+using OpenSubdiv::Far::PatchMap;
+using OpenSubdiv::Far::PatchTable;
+using OpenSubdiv::Far::PatchTableFactory;
+using OpenSubdiv::Far::StencilTable;
+using OpenSubdiv::Far::StencilTableFactory;
+using OpenSubdiv::Far::TopologyRefiner;
+
+namespace {
+
+/* Helper class to wrap numerous of patch coords into a buffer.
+ * Used to pass coordinates to the CPU evaluator. Other evaluators
+ * are not supported.
+ */
+class PatchCoordBuffer : public std::vector<PatchCoord> {
+public:
+ static PatchCoordBuffer *Create(int size)
+ {
+ PatchCoordBuffer *buffer = new PatchCoordBuffer();
+ buffer->resize(size);
+ return buffer;
+ }
+ PatchCoord *BindCpuBuffer() {
+ return (PatchCoord*)&(*this)[0];
+ }
+ int GetNumVertices() {
+ return size();
+ }
+ void UpdateData(const PatchCoord *patch_coords,
+ int num_patch_coords)
+ {
+ memcpy(&(*this)[0],
+ (void*)patch_coords,
+ num_patch_coords * sizeof(PatchCoord));
+ }
+};
+
+/* Helper class to wrap single of patch coord into a buffer.
+ * Used to pass coordinates to the CPU evaluator. Other evaluators
+ * are not supported.
+ */
+class SinglePatchCoordBuffer {
+public:
+ SinglePatchCoordBuffer() {
+ }
+ SinglePatchCoordBuffer(const PatchCoord& patch_coord)
+ : patch_coord_(patch_coord){
+ }
+ static SinglePatchCoordBuffer *Create()
+ {
+ SinglePatchCoordBuffer *buffer = new SinglePatchCoordBuffer();
+ return buffer;
+ }
+ PatchCoord *BindCpuBuffer() {
+ return (PatchCoord*)&patch_coord_;
+ }
+ int GetNumVertices() {
+ return 1;
+ }
+ void UpdateData(const PatchCoord& patch_coord)
+ {
+ patch_coord_ = patch_coord;
+ }
+protected:
+ PatchCoord patch_coord_;
+};
+
+/* Helper class which is aimed to be used in cases when buffer
+ * is small enough and better to be allocated in stack rather
+ * than in heap.
+ *
+ * TODO(sergey): Check if bare arrays could be sued by CPU evalautor.
+ */
+template <int element_size, int num_verts>
+class StackAllocatedBuffer {
+public:
+ static PatchCoordBuffer *Create(int size)
+ {
+ StackAllocatedBuffer<element_size, num_verts> *buffer =
+ new StackAllocatedBuffer<element_size, num_verts>();
+ return buffer;
+ }
+ float *BindCpuBuffer() {
+ return &data_[0];
+ }
+ int GetNumVertices() {
+ return num_verts;
+ }
+ /* TODO(sergey): Support UpdateData(). */
+protected:
+ float data_[element_size * num_verts];
+};
+
+/* Volatile evaluator which can be used from threads.
+ *
+ * TODO(sergey): Make it possible to evaluate coordinates in chuncks.
+ */
+template<typename SRC_VERTEX_BUFFER,
+ typename EVAL_VERTEX_BUFFER,
+ typename STENCIL_TABLE,
+ typename PATCH_TABLE,
+ typename EVALUATOR,
+ typename DEVICE_CONTEXT = void>
+class VolatileEvalOutput {
+public:
+ typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache;
+
+ VolatileEvalOutput(const StencilTable *vertex_stencils,
+ const StencilTable *varying_stencils,
+ int num_coarse_verts,
+ int num_total_verts,
+ const PatchTable *patch_table,
+ EvaluatorCache *evaluator_cache = NULL,
+ DEVICE_CONTEXT *device_context = NULL)
+ : src_desc_( /*offset*/ 0, /*length*/ 3, /*stride*/ 3),
+ src_varying_desc_(/*offset*/ 0, /*length*/ 3, /*stride*/ 3),
+ num_coarse_verts_(num_coarse_verts),
+ evaluator_cache_ (evaluator_cache),
+ device_context_(device_context)
+ {
+ using OpenSubdiv::Osd::convertToCompatibleStencilTable;
+ src_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_verts, device_context_);
+ src_varying_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_verts, device_context_);
+ patch_table_ = PATCH_TABLE::Create(patch_table, device_context_);
+ patch_coords_ = NULL;
+ vertex_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(vertex_stencils,
+ device_context_);
+ varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(varying_stencils,
+ device_context_);
+ }
+
+ ~VolatileEvalOutput()
+ {
+ delete src_data_;
+ delete src_varying_data_;
+ delete patch_table_;
+ delete vertex_stencils_;
+ delete varying_stencils_;
+ }
+
+ void UpdateData(const float *src, int start_vertex, int num_vertices)
+ {
+ src_data_->UpdateData(src, start_vertex, num_vertices, device_context_);
+ }
+
+ void UpdateVaryingData(const float *src, int start_vertex, int num_vertices)
+ {
+ src_varying_data_->UpdateData(src,
+ start_vertex,
+ num_vertices,
+ device_context_);
+ }
+
+ void Refine()
+ {
+ BufferDescriptor dst_desc = src_desc_;
+ dst_desc.offset += num_coarse_verts_ * src_desc_.stride;
+
+ const EVALUATOR *eval_instance =
+ OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_,
+ src_desc_,
+ dst_desc,
+ device_context_);
+
+ EVALUATOR::EvalStencils(src_data_, src_desc_,
+ src_data_, dst_desc,
+ vertex_stencils_,
+ eval_instance,
+ device_context_);
+
+ dst_desc = src_varying_desc_;
+ dst_desc.offset += num_coarse_verts_ * src_varying_desc_.stride;
+ eval_instance =
+ OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_,
+ src_varying_desc_,
+ dst_desc,
+ device_context_);
+
+ EVALUATOR::EvalStencils(src_varying_data_, src_varying_desc_,
+ src_varying_data_, dst_desc,
+ varying_stencils_,
+ eval_instance,
+ device_context_);
+ }
+
+ void EvalPatchCoord(PatchCoord& patch_coord, float P[3])
+ {
+ StackAllocatedBuffer<6, 1> vertex_data;
+ BufferDescriptor vertex_desc(0, 3, 6);
+ SinglePatchCoordBuffer patch_coord_buffer(patch_coord);
+ const EVALUATOR *eval_instance =
+ OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_,
+ src_desc_,
+ vertex_desc,
+ device_context_);
+ EVALUATOR::EvalPatches(src_data_, src_desc_,
+ &vertex_data, vertex_desc,
+ patch_coord_buffer.GetNumVertices(),
+ &patch_coord_buffer,
+ patch_table_, eval_instance, device_context_);
+ float *refined_verts = vertex_data.BindCpuBuffer();
+ memcpy(P, refined_verts, sizeof(float) * 3);
+ }
+
+ void EvalPatchesWithDerivatives(PatchCoord& patch_coord,
+ float P[3],
+ float dPdu[3],
+ float dPdv[3])
+ {
+ StackAllocatedBuffer<6, 1> vertex_data, derivatives;
+ BufferDescriptor vertex_desc(0, 3, 6),
+ du_desc(0, 3, 6),
+ dv_desc(3, 3, 6);
+ SinglePatchCoordBuffer patch_coord_buffer(patch_coord);
+ const EVALUATOR *eval_instance =
+ OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_,
+ src_desc_,
+ vertex_desc,
+ du_desc,
+ dv_desc,
+ device_context_);
+ EVALUATOR::EvalPatches(src_data_, src_desc_,
+ &vertex_data, vertex_desc,
+ &derivatives, du_desc,
+ &derivatives, dv_desc,
+ patch_coord_buffer.GetNumVertices(),
+ &patch_coord_buffer,
+ patch_table_, eval_instance, device_context_);
+ float *refined_verts = vertex_data.BindCpuBuffer();
+ memcpy(P, refined_verts, sizeof(float) * 3);
+ if (dPdu != NULL || dPdv != NULL) {
+ float *refined_drivatives = derivatives.BindCpuBuffer();
+ if (dPdu) {
+ memcpy(dPdu, refined_drivatives, sizeof(float) * 3);
+ }
+ if (dPdv) {
+ memcpy(dPdv, refined_drivatives + 3, sizeof(float) * 3);
+ }
+ }
+ }
+
+ void EvalPatchVarying(PatchCoord& patch_coord,
+ float varying[3]) {
+ StackAllocatedBuffer<3, 1> varying_data;
+ BufferDescriptor varying_desc(0, 3, 3);
+ SinglePatchCoordBuffer patch_coord_buffer(patch_coord);
+ EVALUATOR const *eval_instance =
+ OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(evaluator_cache_,
+ src_varying_desc_,
+ varying_desc,
+ device_context_);
+
+ EVALUATOR::EvalPatches(src_varying_data_, src_varying_desc_,
+ &varying_data, varying_desc,
+ patch_coord_buffer.GetNumVertices(),
+ &patch_coord_buffer,
+ patch_table_, eval_instance, device_context_);
+ float *refined_varying = varying_data.BindCpuBuffer();
+ memcpy(varying, refined_varying, sizeof(float) * 3);
+ }
+private:
+ SRC_VERTEX_BUFFER *src_data_;
+ SRC_VERTEX_BUFFER *src_varying_data_;
+ PatchCoordBuffer *patch_coords_;
+ PATCH_TABLE *patch_table_;
+ BufferDescriptor src_desc_;
+ BufferDescriptor src_varying_desc_;
+ int num_coarse_verts_;
+
+ const STENCIL_TABLE *vertex_stencils_;
+ const STENCIL_TABLE *varying_stencils_;
+
+ EvaluatorCache *evaluator_cache_;
+ DEVICE_CONTEXT *device_context_;
+};
+
+} /* namespace */
+
+typedef VolatileEvalOutput<OpenSubdiv::Osd::CpuVertexBuffer,
+ OpenSubdiv::Osd::CpuVertexBuffer,
+ OpenSubdiv::Far::StencilTable,
+ OpenSubdiv::Osd::CpuPatchTable,
+ OpenSubdiv::Osd::CpuEvaluator> CpuEvalOutput;
+
+typedef struct OpenSubdiv_EvaluatorDescr {
+ CpuEvalOutput *eval_output;
+ const PatchMap *patch_map;
+ const PatchTable *patch_table;
+} OpenSubdiv_EvaluatorDescr;
+
+OpenSubdiv_EvaluatorDescr *openSubdiv_createEvaluatorDescr(
+ OpenSubdiv_TopologyRefinerDescr *topology_refiner,
+ int subsurf_level)
+{
+ /* TODO(sergey): Look into re-using refiner with GLMesh. */
+ TopologyRefiner *refiner = (TopologyRefiner *)topology_refiner;
+ if(refiner == NULL) {
+ /* Happens on bad topology. */
+ return NULL;
+ }
+
+ const StencilTable *vertex_stencils = NULL;
+ const StencilTable *varying_stencils = NULL;
+ int num_total_verts = 0;
+
+ /* Apply uniform refinement to the mesh so that we can use the
+ * limit evaluation API features.
+ */
+ TopologyRefiner::UniformOptions options(subsurf_level);
+ refiner->RefineUniform(options);
+
+ /* Generate stencil table to update the bi-cubic patches control
+ * vertices after they have been re-posed (both for vertex & varying
+ * interpolation).
+ */
+ StencilTableFactory::Options soptions;
+ soptions.generateOffsets = true;
+ soptions.generateIntermediateLevels = false;
+
+ vertex_stencils = StencilTableFactory::Create(*refiner, soptions);
+
+ soptions.interpolationMode = StencilTableFactory::INTERPOLATE_VARYING;
+ varying_stencils = StencilTableFactory::Create(*refiner, soptions);
+
+ /* Generate bi-cubic patch table for the limit surface. */
+ PatchTableFactory::Options poptions;
+ poptions.SetEndCapType(PatchTableFactory::Options::ENDCAP_BSPLINE_BASIS);
+
+ const PatchTable *patch_table = PatchTableFactory::Create(*refiner, poptions);
+
+ /* Append local points stencils. */
+ /* TODO(sergey): Do we really need to worry about local points stencils? */
+ if (const StencilTable *local_point_stencil_table =
+ patch_table->GetLocalPointStencilTable())
+ {
+ const StencilTable *table =
+ StencilTableFactory::AppendLocalPointStencilTable(*refiner,
+ vertex_stencils,
+ local_point_stencil_table);
+ delete vertex_stencils;
+ vertex_stencils = table;
+ }
+ if (const StencilTable *local_point_varying_stencil_table =
+ patch_table->GetLocalPointVaryingStencilTable())
+ {
+ const StencilTable *table =
+ StencilTableFactory::AppendLocalPointStencilTable(*refiner,
+ varying_stencils,
+ local_point_varying_stencil_table);
+ delete varying_stencils;
+ varying_stencils = table;
+ }
+
+ /* Total number of vertices = coarse verts + refined verts + gregory basis verts. */
+ num_total_verts = vertex_stencils->GetNumControlVertices() +
+ vertex_stencils->GetNumStencils();
+
+ const int num_coarse_verts = refiner->GetLevel(0).GetNumVertices();
+
+ CpuEvalOutput *eval_output = new CpuEvalOutput(vertex_stencils,
+ varying_stencils,
+ num_coarse_verts,
+ num_total_verts,
+ patch_table);
+
+ OpenSubdiv::Far::PatchMap *patch_map = new PatchMap(*patch_table);
+
+ OpenSubdiv_EvaluatorDescr *evaluator_descr;
+ evaluator_descr = OBJECT_GUARDED_NEW(OpenSubdiv_EvaluatorDescr);
+ evaluator_descr->eval_output = eval_output;
+ evaluator_descr->patch_map = patch_map;
+ evaluator_descr->patch_table = patch_table;
+
+ /* TOOD(sergey): Look into whether w've got duplicated stencils arrays. */
+ delete varying_stencils;
+ delete vertex_stencils;
+
+ delete refiner;
+
+ return evaluator_descr;
+}
+
+void openSubdiv_deleteEvaluatorDescr(OpenSubdiv_EvaluatorDescr *evaluator_descr)
+{
+ delete evaluator_descr->eval_output;
+ delete evaluator_descr->patch_map;
+ delete evaluator_descr->patch_table;
+ OBJECT_GUARDED_DELETE(evaluator_descr, OpenSubdiv_EvaluatorDescr);
+}
+
+void openSubdiv_setEvaluatorCoarsePositions(OpenSubdiv_EvaluatorDescr *evaluator_descr,
+ float *positions,
+ int start_vert,
+ int num_verts)
+{
+ /* TODO(sergey): Add sanity check on indices. */
+ evaluator_descr->eval_output->UpdateData(positions, start_vert, num_verts);
+ /* TODO(sergey): Consider moving this to a separate call,
+ * so we can updatwe coordinates in chunks.
+ */
+ evaluator_descr->eval_output->Refine();
+}
+
+void openSubdiv_setEvaluatorVaryingData(OpenSubdiv_EvaluatorDescr *evaluator_descr,
+ float *varying_data,
+ int start_vert,
+ int num_verts)
+{
+ /* TODO(sergey): Add sanity check on indices. */
+ evaluator_descr->eval_output->UpdateVaryingData(varying_data, start_vert, num_verts);
+ /* TODO(sergey): Get rid of this ASAP. */
+ evaluator_descr->eval_output->Refine();
+}
+
+void openSubdiv_evaluateLimit(OpenSubdiv_EvaluatorDescr *evaluator_descr,
+ int osd_face_index,
+ float face_u, float face_v,
+ float P[3],
+ float dPdu[3],
+ float dPdv[3])
+{
+ assert((face_u >= 0.0f) && (face_u <= 1.0f) && (face_v >= 0.0f) && (face_v <= 1.0f));
+ const PatchTable::PatchHandle *handle =
+ evaluator_descr->patch_map->FindPatch(osd_face_index, face_u, face_v);
+ PatchCoord patch_coord(*handle, face_u, face_v);
+ if (dPdu != NULL || dPdv != NULL) {
+ evaluator_descr->eval_output->EvalPatchesWithDerivatives(patch_coord,
+ P,
+ dPdu,
+ dPdv);
+ }
+ else {
+ evaluator_descr->eval_output->EvalPatchCoord(patch_coord, P);
+ }
+}
+
+void openSubdiv_evaluateVarying(OpenSubdiv_EvaluatorDescr *evaluator_descr,
+ int osd_face_index,
+ float face_u, float face_v,
+ float varying[3])
+{
+ assert((face_u >= 0.0f) && (face_u <= 1.0f) && (face_v >= 0.0f) && (face_v <= 1.0f));
+ const PatchTable::PatchHandle *handle =
+ evaluator_descr->patch_map->FindPatch(osd_face_index, face_u, face_v);
+ PatchCoord patch_coord(*handle, face_u, face_v);
+ evaluator_descr->eval_output->EvalPatchVarying(patch_coord, varying);
+}
diff --git a/intern/opensubdiv/opensubdiv_gpu_capi.cc b/intern/opensubdiv/opensubdiv_gpu_capi.cc
new file mode 100644
index 00000000000..31996a1bab8
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_gpu_capi.cc
@@ -0,0 +1,674 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2013 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/* Do some compatibility hacks in order to make
+ * the code working with GPU_material pipeline.
+ */
+#define GLSL_COMPAT_WORKAROUND
+
+#include "opensubdiv_capi.h"
+
+#ifdef _MSC_VER
+# include "iso646.h"
+#endif
+
+#include <cstdio>
+#include <cmath>
+#include <GL/glew.h>
+
+#include <opensubdiv/osd/glMesh.h>
+
+#ifdef OPENSUBDIV_HAS_CUDA
+# include <opensubdiv/osd/cudaGLVertexBuffer.h>
+#endif /* OPENSUBDIV_HAS_CUDA */
+
+#include <opensubdiv/osd/cpuGLVertexBuffer.h>
+#include <opensubdiv/osd/cpuEvaluator.h>
+
+using OpenSubdiv::Osd::GLMeshInterface;
+
+extern "C" char datatoc_gpu_shader_opensubd_display_glsl[];
+
+#define MAX_LIGHTS 8
+typedef struct Light {
+ float position[4];
+ float ambient[4];
+ float diffuse[4];
+ float specular[4];
+ float spot_direction[4];
+#ifdef SUPPORT_COLOR_MATERIAL
+ float constant_attenuation;
+ float linear_attenuation;
+ float quadratic_attenuation;
+ float spot_cutoff;
+ float spot_exponent;
+ float spot_cos_cutoff;
+#endif
+} Light;
+
+typedef struct Lighting {
+ Light lights[MAX_LIGHTS];
+ int num_enabled;
+} Lighting;
+
+typedef struct Transform {
+ float projection_matrix[16];
+ float model_view_matrix[16];
+ float normal_matrix[9];
+} Transform;
+
+static bool g_use_osd_glsl = false;
+static int g_active_uv_index = -1;
+
+static GLuint g_flat_fill_solid_program = 0;
+static GLuint g_flat_fill_texture2d_program = 0;
+static GLuint g_smooth_fill_solid_program = 0;
+static GLuint g_smooth_fill_texture2d_program = 0;
+static GLuint g_wireframe_program = 0;
+
+static GLuint g_lighting_ub = 0;
+static Lighting g_lighting_data;
+static Transform g_transform;
+
+/* TODO(sergey): This is actually duplicated code from BLI. */
+namespace {
+void copy_m3_m3(float m1[3][3], float m2[3][3])
+{
+ /* destination comes first: */
+ memcpy(&m1[0], &m2[0], 9 * sizeof(float));
+}
+
+void copy_m3_m4(float m1[3][3], float m2[4][4])
+{
+ m1[0][0] = m2[0][0];
+ m1[0][1] = m2[0][1];
+ m1[0][2] = m2[0][2];
+
+ m1[1][0] = m2[1][0];
+ m1[1][1] = m2[1][1];
+ m1[1][2] = m2[1][2];
+
+ m1[2][0] = m2[2][0];
+ m1[2][1] = m2[2][1];
+ m1[2][2] = m2[2][2];
+}
+
+void adjoint_m3_m3(float m1[3][3], float m[3][3])
+{
+ m1[0][0] = m[1][1] * m[2][2] - m[1][2] * m[2][1];
+ m1[0][1] = -m[0][1] * m[2][2] + m[0][2] * m[2][1];
+ m1[0][2] = m[0][1] * m[1][2] - m[0][2] * m[1][1];
+
+ m1[1][0] = -m[1][0] * m[2][2] + m[1][2] * m[2][0];
+ m1[1][1] = m[0][0] * m[2][2] - m[0][2] * m[2][0];
+ m1[1][2] = -m[0][0] * m[1][2] + m[0][2] * m[1][0];
+
+ m1[2][0] = m[1][0] * m[2][1] - m[1][1] * m[2][0];
+ m1[2][1] = -m[0][0] * m[2][1] + m[0][1] * m[2][0];
+ m1[2][2] = m[0][0] * m[1][1] - m[0][1] * m[1][0];
+}
+
+float determinant_m3_array(float m[3][3])
+{
+ return (m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) -
+ m[1][0] * (m[0][1] * m[2][2] - m[0][2] * m[2][1]) +
+ m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1]));
+}
+
+bool invert_m3_m3(float m1[3][3], float m2[3][3])
+{
+ float det;
+ int a, b;
+ bool success;
+
+ /* calc adjoint */
+ adjoint_m3_m3(m1, m2);
+
+ /* then determinant old matrix! */
+ det = determinant_m3_array(m2);
+
+ success = (det != 0.0f);
+
+ if (det != 0.0f) {
+ det = 1.0f / det;
+ for (a = 0; a < 3; a++) {
+ for (b = 0; b < 3; b++) {
+ m1[a][b] *= det;
+ }
+ }
+ }
+
+ return success;
+}
+
+bool invert_m3(float m[3][3])
+{
+ float tmp[3][3];
+ bool success;
+
+ success = invert_m3_m3(tmp, m);
+ copy_m3_m3(m, tmp);
+
+ return success;
+}
+
+void transpose_m3(float mat[3][3])
+{
+ float t;
+
+ t = mat[0][1];
+ mat[0][1] = mat[1][0];
+ mat[1][0] = t;
+ t = mat[0][2];
+ mat[0][2] = mat[2][0];
+ mat[2][0] = t;
+ t = mat[1][2];
+ mat[1][2] = mat[2][1];
+ mat[2][1] = t;
+}
+
+GLuint compileShader(GLenum shaderType,
+ const char *section,
+ const char *define)
+{
+ const char *sources[3];
+ char sdefine[64];
+ sprintf(sdefine, "#define %s\n#define GLSL_COMPAT_WORKAROUND\n", section);
+
+ sources[0] = define;
+ sources[1] = sdefine;
+ sources[2] = datatoc_gpu_shader_opensubd_display_glsl;
+
+ GLuint shader = glCreateShader(shaderType);
+ glShaderSource(shader, 3, sources, NULL);
+ glCompileShader(shader);
+
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (status == GL_FALSE) {
+ GLchar emsg[1024];
+ glGetShaderInfoLog(shader, sizeof(emsg), 0, emsg);
+ fprintf(stderr, "Error compiling GLSL shader (%s): %s\n", section, emsg);
+ fprintf(stderr, "Section: %s\n", sdefine);
+ fprintf(stderr, "Defines: %s\n", define);
+ fprintf(stderr, "Source: %s\n", sources[2]);
+ return 0;
+ }
+
+ return shader;
+}
+
+GLuint linkProgram(const char *define)
+{
+ GLuint vertexShader = compileShader(GL_VERTEX_SHADER,
+ "VERTEX_SHADER",
+ define);
+ if (vertexShader == 0) {
+ return 0;
+ }
+ GLuint geometryShader = compileShader(GL_GEOMETRY_SHADER,
+ "GEOMETRY_SHADER",
+ define);
+ if (geometryShader == 0) {
+ return 0;
+ }
+ GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER,
+ "FRAGMENT_SHADER",
+ define);
+ if (fragmentShader == 0) {
+ return 0;
+ }
+
+ GLuint program = glCreateProgram();
+
+ glAttachShader(program, vertexShader);
+ glAttachShader(program, geometryShader);
+ glAttachShader(program, fragmentShader);
+
+ glBindAttribLocation(program, 0, "position");
+ glBindAttribLocation(program, 1, "normal");
+
+#ifdef GLSL_COMPAT_WORKAROUND
+ glProgramParameteriEXT(program,
+ GL_GEOMETRY_INPUT_TYPE_EXT,
+ GL_LINES_ADJACENCY_EXT);
+
+ if (strstr(define, "WIREFRAME") == NULL) {
+ glProgramParameteriEXT(program,
+ GL_GEOMETRY_OUTPUT_TYPE_EXT,
+ GL_TRIANGLE_STRIP);
+
+ glProgramParameteriEXT(program,
+ GL_GEOMETRY_VERTICES_OUT_EXT,
+ 4);
+ }
+ else {
+ glProgramParameteriEXT(program,
+ GL_GEOMETRY_OUTPUT_TYPE_EXT,
+ GL_LINE_STRIP);
+
+ glProgramParameteriEXT(program,
+ GL_GEOMETRY_VERTICES_OUT_EXT,
+ 8);
+ }
+#endif
+
+ glLinkProgram(program);
+
+ glDeleteShader(vertexShader);
+ glDeleteShader(geometryShader);
+ glDeleteShader(fragmentShader);
+
+ GLint status;
+ glGetProgramiv(program, GL_LINK_STATUS, &status);
+ if (status == GL_FALSE) {
+ GLchar emsg[1024];
+ glGetProgramInfoLog(program, sizeof(emsg), 0, emsg);
+ fprintf(stderr, "Error linking GLSL program : %s\n", emsg);
+ fprintf(stderr, "Defines: %s\n", define);
+ glDeleteProgram(program);
+ return 0;
+ }
+
+ glUniformBlockBinding(program,
+ glGetUniformBlockIndex(program, "Lighting"),
+ 0);
+
+ glProgramUniform1i(program,
+ glGetUniformLocation(program, "texture_buffer"),
+ 0); /* GL_TEXTURE0 */
+
+ glProgramUniform1i(program,
+ glGetUniformLocation(program, "FVarDataBuffer"),
+ 31); /* GL_TEXTURE31 */
+
+ return program;
+}
+
+void bindProgram(GLMeshInterface * /*mesh*/,
+ int program)
+{
+ glUseProgram(program);
+
+ /* Matricies */
+ glUniformMatrix4fv(glGetUniformLocation(program, "modelViewMatrix"),
+ 1, false,
+ g_transform.model_view_matrix);
+ glUniformMatrix4fv(glGetUniformLocation(program, "projectionMatrix"),
+ 1, false,
+ g_transform.projection_matrix);
+ glUniformMatrix3fv(glGetUniformLocation(program, "normalMatrix"),
+ 1, false,
+ g_transform.normal_matrix);
+
+ /* Ligthing */
+ glBindBuffer(GL_UNIFORM_BUFFER, g_lighting_ub);
+ glBufferSubData(GL_UNIFORM_BUFFER,
+ 0, sizeof(g_lighting_data), &g_lighting_data);
+ glBindBuffer(GL_UNIFORM_BUFFER, 0);
+
+ glBindBufferBase(GL_UNIFORM_BUFFER, 0, g_lighting_ub);
+
+ /* Color */
+ GLboolean use_lighting;
+ glGetBooleanv(GL_LIGHTING, &use_lighting);
+
+ if (use_lighting) {
+ float color[4];
+ glGetMaterialfv(GL_FRONT, GL_DIFFUSE, color);
+ glUniform4fv(glGetUniformLocation(program, "diffuse"), 1, color);
+
+ glGetMaterialfv(GL_FRONT, GL_SPECULAR, color);
+ glUniform4fv(glGetUniformLocation(program, "specular"), 1, color);
+
+ glGetMaterialfv(GL_FRONT, GL_SHININESS, color);
+ glUniform1f(glGetUniformLocation(program, "shininess"), color[0]);
+ }
+ else {
+ float color[4];
+ glGetFloatv(GL_CURRENT_COLOR, color);
+ glUniform4fv(glGetUniformLocation(program, "diffuse"), 1, color);
+ }
+
+ /* TODO(sergey): Bring face varying back. */
+#if 0
+ /* Face-vertex data */
+ if (mesh->GetDrawContext()->GetFvarDataTextureBuffer()) {
+ glActiveTexture(GL_TEXTURE31);
+ glBindTexture(GL_TEXTURE_BUFFER,
+ mesh->GetDrawContext()->GetFvarDataTextureBuffer());
+ glActiveTexture(GL_TEXTURE0);
+ }
+#endif
+
+ /* TODO(sergey): Bring face varying back. */
+ glUniform1i(glGetUniformLocation(program, "osd_fvar_count"),
+ 0/* * mesh->GetFVarCount()*/);
+
+ glUniform1i(glGetUniformLocation(program, "osd_active_uv_offset"),
+ g_active_uv_index * 2);
+}
+
+} /* namespace */
+
+bool openSubdiv_osdGLDisplayInit(void)
+{
+ static bool need_init = true;
+ static bool init_success = false;
+ if (need_init) {
+ g_flat_fill_solid_program = linkProgram("#define FLAT_SHADING\n");
+ g_flat_fill_texture2d_program = linkProgram("#define USE_TEXTURE_2D\n#define FLAT_SHADING\n");
+ g_smooth_fill_solid_program = linkProgram("#define SMOOTH_SHADING\n");
+ g_smooth_fill_texture2d_program = linkProgram("#define USE_TEXTURE_2D\n#define SMOOTH_SHADING\n");
+ g_wireframe_program = linkProgram("#define WIREFRAME\n");
+
+ glGenBuffers(1, &g_lighting_ub);
+ glBindBuffer(GL_UNIFORM_BUFFER, g_lighting_ub);
+ glBufferData(GL_UNIFORM_BUFFER,
+ sizeof(g_lighting_data), NULL, GL_STATIC_DRAW);
+
+ need_init = false;
+ init_success = g_flat_fill_solid_program != 0 &&
+ g_flat_fill_texture2d_program != 0 &&
+ g_smooth_fill_solid_program != 0 &&
+ g_smooth_fill_texture2d_program != 0 &&
+ g_wireframe_program;
+ }
+ return init_success;
+}
+
+void openSubdiv_osdGLDisplayDeinit(void)
+{
+ if (g_lighting_ub != 0) {
+ glDeleteBuffers(1, &g_lighting_ub);
+ }
+ if (g_flat_fill_solid_program) {
+ glDeleteProgram(g_flat_fill_solid_program);
+ }
+ if (g_flat_fill_texture2d_program) {
+ glDeleteProgram(g_flat_fill_texture2d_program);
+ }
+ if (g_smooth_fill_solid_program) {
+ glDeleteProgram(g_flat_fill_solid_program);
+ }
+ if (g_smooth_fill_texture2d_program) {
+ glDeleteProgram(g_smooth_fill_texture2d_program);
+ }
+ if (g_wireframe_program) {
+ glDeleteProgram(g_wireframe_program);
+ }
+}
+
+void openSubdiv_osdGLMeshDisplayPrepare(int use_osd_glsl,
+ int active_uv_index)
+{
+ g_use_osd_glsl = use_osd_glsl != 0;
+ g_active_uv_index = active_uv_index;
+
+ /* Update transformation matricies. */
+ glGetFloatv(GL_PROJECTION_MATRIX, g_transform.projection_matrix);
+ glGetFloatv(GL_MODELVIEW_MATRIX, g_transform.model_view_matrix);
+
+ copy_m3_m4((float (*)[3])g_transform.normal_matrix,
+ (float (*)[4])g_transform.model_view_matrix);
+ invert_m3((float (*)[3])g_transform.normal_matrix);
+ transpose_m3((float (*)[3])g_transform.normal_matrix);
+
+ /* Update OpenGL lights positions, colors etc. */
+ g_lighting_data.num_enabled = 0;
+ for (int i = 0; i < MAX_LIGHTS; ++i) {
+ GLboolean enabled;
+ glGetBooleanv(GL_LIGHT0 + i, &enabled);
+ if (enabled) {
+ g_lighting_data.num_enabled++;
+ }
+
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_POSITION,
+ g_lighting_data.lights[i].position);
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_AMBIENT,
+ g_lighting_data.lights[i].ambient);
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_DIFFUSE,
+ g_lighting_data.lights[i].diffuse);
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_SPECULAR,
+ g_lighting_data.lights[i].specular);
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_SPOT_DIRECTION,
+ g_lighting_data.lights[i].spot_direction);
+#ifdef SUPPORT_COLOR_MATERIAL
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_CONSTANT_ATTENUATION,
+ &g_lighting_data.lights[i].constant_attenuation);
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_LINEAR_ATTENUATION,
+ &g_lighting_data.lights[i].linear_attenuation);
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_QUADRATIC_ATTENUATION,
+ &g_lighting_data.lights[i].quadratic_attenuation);
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_SPOT_CUTOFF,
+ &g_lighting_data.lights[i].spot_cutoff);
+ glGetLightfv(GL_LIGHT0 + i,
+ GL_SPOT_EXPONENT,
+ &g_lighting_data.lights[i].spot_exponent);
+ g_lighting_data.lights[i].spot_cos_cutoff =
+ cos(g_lighting_data.lights[i].spot_cutoff);
+#endif
+ }
+}
+
+static GLuint preapre_patchDraw(GLMeshInterface *mesh,
+ bool fill_quads)
+{
+ GLint program = 0;
+ if (!g_use_osd_glsl) {
+ glGetIntegerv(GL_CURRENT_PROGRAM, &program);
+ if (program) {
+ GLint model;
+ glGetIntegerv(GL_SHADE_MODEL, &model);
+
+ GLint location = glGetUniformLocation(program, "osd_flat_shading");
+ if (location != -1) {
+ glUniform1i(location, model == GL_FLAT);
+ }
+
+ /* TODO(sergey): Bring this back. */
+#if 0
+ /* Face-vertex data */
+ if (mesh->GetDrawContext()->GetFvarDataTextureBuffer()) {
+ glActiveTexture(GL_TEXTURE31);
+ glBindTexture(GL_TEXTURE_BUFFER,
+ mesh->GetDrawContext()->GetFvarDataTextureBuffer());
+ glActiveTexture(GL_TEXTURE0);
+
+ GLint location = glGetUniformLocation(program, "osd_fvar_count");
+ if (location != -1) {
+ glUniform1i(location, mesh->GetFVarCount());
+ }
+
+ location = glGetUniformLocation(program, "osd_active_uv_offset");
+ if (location != -1) {
+ glUniform1i(location,
+ g_active_uv_index * 2);
+ }
+ }
+#endif
+
+ }
+ return program;
+ }
+
+ if (fill_quads) {
+ int model;
+ GLboolean use_texture_2d;
+ glGetIntegerv(GL_SHADE_MODEL, &model);
+ glGetBooleanv(GL_TEXTURE_2D, &use_texture_2d);
+ if (model == GL_FLAT) {
+ if (use_texture_2d) {
+ program = g_flat_fill_texture2d_program;
+ }
+ else {
+ program = g_flat_fill_solid_program;
+ }
+ }
+ else {
+ if (use_texture_2d) {
+ program = g_smooth_fill_texture2d_program;
+ }
+ else {
+ program = g_smooth_fill_solid_program;
+ }
+ }
+ }
+ else {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ program = g_wireframe_program;
+ }
+
+ bindProgram(mesh, program);
+
+ return program;
+}
+
+static void perform_drawElements(GLuint program,
+ int patch_index,
+ int num_elements,
+ int start_element)
+{
+ if (program) {
+ glUniform1i(glGetUniformLocation(program, "PrimitiveIdBase"),
+ patch_index);
+ }
+ glDrawElements(GL_LINES_ADJACENCY,
+ num_elements,
+ GL_UNSIGNED_INT,
+ (void *)(start_element * sizeof(unsigned int)));
+}
+
+static void finish_patchDraw(bool fill_quads)
+{
+ /* TODO(sergey): Some of the stuff could be done once after the whole
+ * mesh is displayed.
+ */
+
+ /* Restore state. */
+ if (!fill_quads) {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+ glBindVertexArray(0);
+
+ if (g_use_osd_glsl) {
+ /* TODO(sergey): Store previously used program and roll back to it? */
+ glUseProgram(0);
+ }
+}
+
+static void draw_partition_patches_range(GLMeshInterface *mesh,
+ GLuint program,
+ int start_patch,
+ int num_patches)
+{
+ int traversed_patches = 0, num_remained_patches = num_patches;
+ const OpenSubdiv::Osd::PatchArrayVector& patches =
+ mesh->GetPatchTable()->GetPatchArrays();
+ for (int i = 0; i < (int)patches.size(); ++i) {
+ const OpenSubdiv::Osd::PatchArray& patch = patches[i];
+ OpenSubdiv::Far::PatchDescriptor desc = patch.GetDescriptor();
+ OpenSubdiv::Far::PatchDescriptor::Type patchType = desc.GetType();
+
+ if (patchType == OpenSubdiv::Far::PatchDescriptor::QUADS) {
+ const int num_block_patches = patch.GetNumPatches();
+ if (start_patch >= traversed_patches &&
+ start_patch < traversed_patches + num_block_patches)
+ {
+ const int num_control_verts = desc.GetNumControlVertices();
+ const int start_draw_patch = start_patch - traversed_patches;
+ const int num_draw_patches = std::min(num_remained_patches,
+ num_block_patches - start_draw_patch);
+ perform_drawElements(program,
+ i,
+ num_draw_patches * num_control_verts,
+ patch.GetIndexBase() + start_draw_patch * num_control_verts);
+ num_remained_patches -= num_draw_patches;
+ }
+ if (num_remained_patches == 0) {
+ break;
+ }
+ traversed_patches += num_block_patches;
+ }
+ }
+}
+
+static void draw_all_patches(GLMeshInterface *mesh,
+ GLuint program)
+{
+ const OpenSubdiv::Osd::PatchArrayVector& patches =
+ mesh->GetPatchTable()->GetPatchArrays();
+ for (int i = 0; i < (int)patches.size(); ++i) {
+ const OpenSubdiv::Osd::PatchArray& patch = patches[i];
+ OpenSubdiv::Far::PatchDescriptor desc = patch.GetDescriptor();
+ OpenSubdiv::Far::PatchDescriptor::Type patchType = desc.GetType();
+
+ if (patchType == OpenSubdiv::Far::PatchDescriptor::QUADS) {
+ perform_drawElements(program,
+ i,
+ patch.GetNumPatches() * desc.GetNumControlVertices(),
+ patch.GetIndexBase());
+ }
+ }
+}
+
+void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh,
+ int fill_quads,
+ int start_patch,
+ int num_patches)
+{
+ GLMeshInterface *mesh =
+ (GLMeshInterface *)(gl_mesh->descriptor);
+
+ /* Make sure all global invariants are initialized. */
+ if (!openSubdiv_osdGLDisplayInit()) {
+ return;
+ }
+
+ /* Setup GLSL/OpenGL to draw patches in current context. */
+ GLuint program = preapre_patchDraw(mesh, fill_quads != 0);
+
+ if (start_patch != -1) {
+ draw_partition_patches_range(mesh,
+ program,
+ start_patch,
+ num_patches);
+ }
+ else {
+ draw_all_patches(mesh, program);
+ }
+
+ /* Finish patch drawing by restoring all changes to the OpenGL context. */
+ finish_patchDraw(fill_quads != 0);
+}
diff --git a/intern/opensubdiv/opensubdiv_intern.h b/intern/opensubdiv/opensubdiv_intern.h
new file mode 100644
index 00000000000..ccb32f9d0ac
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_intern.h
@@ -0,0 +1,46 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENSUBDIV_INTERN_H__
+#define __OPENSUBDIV_INTERN_H__
+
+/* Perform full topology validation when exporting it to OpenSubdiv. */
+#ifdef NDEBUG
+# undef OPENSUBDIV_VALIDATE_TOPOLOGY
+#else
+/* TODO(sergey): Always disabled for now, the check doesn't handle
+ * multiple non-manifolds from the OpenSubdiv side currently.
+ */
+# undef OPENSUBDIV_VALIDATE_TOPOLOGY
+#endif
+
+/* Currently OpenSubdiv expects topology to be oriented,
+ * but sometimes it's handy to disable orientation code
+ * to check whether it causes some weird issues by using
+ * pre-oriented model.
+ */
+#define OPENSUBDIV_ORIENT_TOPOLOGY
+
+#endif /* __OPENSUBDIV_INTERN_H__ */
diff --git a/intern/opensubdiv/opensubdiv_utils_capi.cc b/intern/opensubdiv/opensubdiv_utils_capi.cc
new file mode 100644
index 00000000000..a945484ba61
--- /dev/null
+++ b/intern/opensubdiv/opensubdiv_utils_capi.cc
@@ -0,0 +1,110 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2013 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Sergey Sharybin.
+ * Brecht van Lommel
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "opensubdiv_capi.h"
+
+#include <cstring>
+#include <GL/glew.h>
+
+#ifdef _MSC_VER
+# include "iso646.h"
+#endif
+
+#ifdef OPENSUBDIV_HAS_OPENCL
+# include "opensubdiv_device_context_opencl.h"
+#endif /* OPENSUBDIV_HAS_OPENCL */
+
+#ifdef OPENSUBDIV_HAS_CUDA
+# include "opensubdiv_device_context_cuda.h"
+#endif /* OPENSUBDIV_HAS_CUDA */
+
+int openSubdiv_getAvailableEvaluators(void)
+{
+ if (!openSubdiv_supportGPUDisplay()) {
+ return 0;
+ }
+
+ int flags = OPENSUBDIV_EVALUATOR_CPU;
+
+#ifdef OPENSUBDIV_HAS_OPENMP
+ flags |= OPENSUBDIV_EVALUATOR_OPENMP;
+#endif /* OPENSUBDIV_HAS_OPENMP */
+
+#ifdef OPENSUBDIV_HAS_OPENCL
+ if (CLDeviceContext::HAS_CL_VERSION_1_1()) {
+ flags |= OPENSUBDIV_EVALUATOR_OPENCL;
+ }
+#endif /* OPENSUBDIV_HAS_OPENCL */
+
+#ifdef OPENSUBDIV_HAS_CUDA
+ if (CudaDeviceContext::HAS_CUDA_VERSION_4_0()) {
+ flags |= OPENSUBDIV_EVALUATOR_CUDA;
+ }
+#endif /* OPENSUBDIV_HAS_OPENCL */
+
+#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK
+ if (GLEW_ARB_texture_buffer_object) {
+ flags |= OPENSUBDIV_EVALUATOR_GLSL_TRANSFORM_FEEDBACK;
+ }
+#endif /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */
+
+#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE
+ static bool vendor_checked = false;
+ static bool disable_glsl_compute = false;
+ /* Force disable GLSL Compute on AMD hardware because it has really
+ * hard time evaluating required shaders.
+ */
+ if (!vendor_checked) {
+ vendor_checked = true;
+ const char *vendor = (const char *)glGetString(GL_VENDOR);
+ const char *renderer = (const char *)glGetString(GL_RENDERER);
+ if (vendor != NULL && renderer != NULL) {
+ if (strstr(vendor, "ATI") ||
+ strstr(renderer, "Mesa DRI R") ||
+ (strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")))
+ {
+ disable_glsl_compute = true;
+ }
+ }
+ }
+ if (!disable_glsl_compute) {
+ flags |= OPENSUBDIV_EVALUATOR_GLSL_COMPUTE;
+ }
+#endif /* OPENSUBDIV_HAS_GLSL_COMPUTE */
+
+ return flags;
+}
+
+void openSubdiv_init(void)
+{
+ /* Ensure all OpenGL strings are cached. */
+ (void)openSubdiv_getAvailableEvaluators();
+}
+
+void openSubdiv_cleanup(void)
+{
+ openSubdiv_osdGLDisplayDeinit();
+}