From a040157e5dd7227fc61ee2608f30f2492db167be Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 20 Jul 2015 15:18:35 +0200 Subject: OpenSubdiv: Add new OpenSubdiv related files This includes C-API bindings in intern/opensubdiv and CMAke module which finds the OpenSubdiv library. This filea are not in use so far, making it a separate commit to make actual integration commit more clear. --- intern/opensubdiv/CMakeLists.txt | 90 +++ intern/opensubdiv/SConscript | 67 +++ intern/opensubdiv/gpu_shader_opensubd_display.glsl | 335 +++++++++++ intern/opensubdiv/opensubdiv_capi.cc | 303 ++++++++++ intern/opensubdiv/opensubdiv_capi.h | 150 +++++ intern/opensubdiv/opensubdiv_converter.cc | 341 +++++++++++ intern/opensubdiv/opensubdiv_converter_capi.h | 120 ++++ .../opensubdiv/opensubdiv_device_context_cuda.cc | 237 ++++++++ intern/opensubdiv/opensubdiv_device_context_cuda.h | 54 ++ .../opensubdiv/opensubdiv_device_context_opencl.cc | 251 ++++++++ .../opensubdiv/opensubdiv_device_context_opencl.h | 58 ++ intern/opensubdiv/opensubdiv_evaluator_capi.cc | 499 ++++++++++++++++ intern/opensubdiv/opensubdiv_gpu_capi.cc | 659 +++++++++++++++++++++ intern/opensubdiv/opensubdiv_intern.h | 36 ++ intern/opensubdiv/opensubdiv_partitioned.h | 77 +++ intern/opensubdiv/opensubdiv_utils_capi.cc | 83 +++ 16 files changed, 3360 insertions(+) create mode 100644 intern/opensubdiv/CMakeLists.txt create mode 100644 intern/opensubdiv/SConscript create mode 100644 intern/opensubdiv/gpu_shader_opensubd_display.glsl create mode 100644 intern/opensubdiv/opensubdiv_capi.cc create mode 100644 intern/opensubdiv/opensubdiv_capi.h create mode 100644 intern/opensubdiv/opensubdiv_converter.cc create mode 100644 intern/opensubdiv/opensubdiv_converter_capi.h create mode 100644 intern/opensubdiv/opensubdiv_device_context_cuda.cc create mode 100644 intern/opensubdiv/opensubdiv_device_context_cuda.h create mode 100644 intern/opensubdiv/opensubdiv_device_context_opencl.cc create mode 100644 intern/opensubdiv/opensubdiv_device_context_opencl.h create mode 100644 intern/opensubdiv/opensubdiv_evaluator_capi.cc create mode 100644 intern/opensubdiv/opensubdiv_gpu_capi.cc create mode 100644 intern/opensubdiv/opensubdiv_intern.h create mode 100644 intern/opensubdiv/opensubdiv_partitioned.h create mode 100644 intern/opensubdiv/opensubdiv_utils_capi.cc (limited to 'intern') diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt new file mode 100644 index 00000000000..3f88ba5d662 --- /dev/null +++ b/intern/opensubdiv/CMakeLists.txt @@ -0,0 +1,90 @@ +# ***** 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 + opensubdiv_partitioned.h +) + +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..58926c6a94a --- /dev/null +++ b/intern/opensubdiv/SConscript @@ -0,0 +1,67 @@ +#!/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("tbbComputeController.h", 'OPENSUBDIV_HAS_TBB') +checkOpenSubdivHeaderDefine("gcdComputeController.h", 'OPENSUBDIV_HAS_GCD') +if checkOpenSubdivHeaderDefine("clComputeController.h", 'OPENSUBDIV_HAS_OPENCL'): + defs += ['OPENSUBDIV_HAS_CLEW'] + incs += ' #/extern/clew/include' +if checkOpenSubdivHeaderDefine("cudaComputeController.h", 'OPENSUBDIV_HAS_CUDA'): + defs += ['OPENSUBDIV_HAS_CUEW'] + incs += ' #/extern/cuew/include' +checkOpenSubdivHeaderDefine("glslTransformFeedbackComputeController.h", 'OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK') +checkOpenSubdivHeaderDefine("osd/glslComputeController.h", 'OPENSUBDIV_HAS_GLSL_COMPUTE') + +# 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..fb46971bce8 --- /dev/null +++ b/intern/opensubdiv/gpu_shader_opensubd_display.glsl @@ -0,0 +1,335 @@ +/* + * ***** 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 + +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); +} + +#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; + float constantAttenuation; + float linearAttenuation; + float quadraticAttenuation; + float spotCutoff; + float spotExponent; + float spotCosCutoff; +}; + +uniform Lighting { + LightSource lightSource[MAX_LIGHTS]; + int num_enabled_lights; +}; + +uniform vec4 diffuse; +uniform vec4 specular; +uniform float shininess; + +uniform sampler2D texture_buffer; +uniform bool use_color_material; +uniform bool use_texture_2d; + +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); + + if (use_color_material == false) { + /* Assume NUM_SOLID_LIGHTS directional lights. */ + for (int i = 0; i < NUM_SOLID_LIGHTS; i++) { + vec3 light_direction = lightSource[i].position.xyz; + + /* 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; + + vec4 Plight = lightSource[i].position; + vec3 l = (Plight.w == 0.0) + ? normalize(Plight.xyz) : normalize(Plight.xyz - + inpt.v.position.xyz); + + /* Specular light. */ + vec3 light_specular = lightSource[i].specular.rgb; + vec3 H = normalize(l + vec3(0,0,1)); + + float specular_bsdf = pow(max(dot(N, H), 0.0), + shininess); + L_specular += light_specular * specular_bsdf; + } + } + else { + 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; + } + } + + /* Compute diffuse color. */ + float alpha; + if (use_texture_2d) { + L_diffuse *= texture2D(texture_buffer, inpt.v.uv).rgb; + } + else { + L_diffuse *= diffuse.rgb; + } + alpha = diffuse.a; + + /* Sum lighting. */ + vec3 L = /*gl_FrontLightModelProduct.sceneColor.rgb +*/ L_diffuse; + L += L_specular * specular.rgb; + + /* Write out fragment color. */ + gl_FragColor = vec4(L, alpha); +#endif +} + +#endif // FRAGMENT_SHADER diff --git a/intern/opensubdiv/opensubdiv_capi.cc b/intern/opensubdiv/opensubdiv_capi.cc new file mode 100644 index 00000000000..717af4fa4f2 --- /dev/null +++ b/intern/opensubdiv/opensubdiv_capi.cc @@ -0,0 +1,303 @@ +/* + * ***** 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 + +#include + +/* CPU Backend */ +#include +#include + +#ifdef OPENSUBDIV_HAS_OPENMP +# include +#endif /* OPENSUBDIV_HAS_OPENMP */ + +#ifdef OPENSUBDIV_HAS_OPENCL +# include +# include +# include "opensubdiv_device_context_opencl.h" +#endif /* OPENSUBDIV_HAS_OPENCL */ + +#ifdef OPENSUBDIV_HAS_CUDA +# include +# include +# include "opensubdiv_device_context_cuda.h" +#endif /* OPENSUBDIV_HAS_CUDA */ + +#ifdef OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK +# include +# include +#endif /* OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK */ + +#ifdef OPENSUBDIV_HAS_GLSL_COMPUTE +# include +# include +#endif /* OPENSUBDIV_HAS_GLSL_COMPUTE */ + +#include +#include + +#include "opensubdiv_intern.h" +#include "opensubdiv_partitioned.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::PartitionedMesh; + +/* CPU backend */ +using OpenSubdiv::Osd::CpuGLVertexBuffer; +using OpenSubdiv::Osd::CpuEvaluator; +typedef PartitionedMesh OsdCpuMesh; + +#ifdef OPENSUBDIV_HAS_OPENMP +using OpenSubdiv::Osd::OmpEvaluator; +typedef PartitionedMesh 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 PartitionedMesh 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 PartitionedMesh 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 PartitionedMesh 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 PartitionedMesh 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, 0); + bits.set(OpenSubdiv::Osd::MeshFVarData, 1); + bits.set(OpenSubdiv::Osd::MeshEndCapBSplineBasis, 1); + // bits.set(Osd::MeshEndCapGregoryBasis, 1); + // bits.set(Osd::MeshEndCapLegacyGregory, 1); + + const int num_vertex_elements = 6; + const int num_varying_elements = 0; + + 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) +{ + return GL_EXT_geometry_shader4 && + GL_ARB_gpu_shader5 && + glProgramParameteriEXT; +} diff --git a/intern/opensubdiv/opensubdiv_capi.h b/intern/opensubdiv/opensubdiv_capi.h new file mode 100644 index 00000000000..c86e739f12b --- /dev/null +++ b/intern/opensubdiv/opensubdiv_capi.h @@ -0,0 +1,150 @@ +/* + * ***** 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 ** */ +void 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 patches which corresponds to a given partition. */ +void openSubdiv_osdGLMeshDisplay(OpenSubdiv_GLMesh *gl_mesh, + int fill_quads, + int start_partition, + int num_partitions); + +/* ** Utility functions ** */ +int openSubdiv_supportGPUDisplay(void); +int openSubdiv_getAvailableEvaluators(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..f5f3547a9cb --- /dev/null +++ b/intern/opensubdiv/opensubdiv_converter.cc @@ -0,0 +1,341 @@ +/* + * ***** 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 +#include + +#ifdef _MSC_VER +# include "iso646.h" +#endif + +#include + +#include "opensubdiv_converter_capi.h" +#include "opensubdiv_intern.h" + +namespace OpenSubdiv { +namespace OPENSUBDIV_VERSION { +namespace Far { + +namespace { + +template +inline int findInArray(T array, int value) +{ + return (int)(std::find(array.begin(), array.end(), value) - array.begin()); +} + +} /* namespace */ + +template <> +inline bool TopologyRefinerFactory::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::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]); + } + /* 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_edges = conv.get_num_vert_edges(&conv, vert); + int *vert_edges = new int[num_vert_edges]; + conv.get_vert_edges(&conv, vert, vert_edges); + /* Vert-Edges */ + IndexArray dst_vert_edges = getBaseVertexEdges(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); + /* Order vertex edges and faces in a CCW order. */ + Index face_start = INDEX_INVALID; + Index edge_start = INDEX_INVALID; + int face_vert_start = 0; + if (num_vert_edges == num_vert_faces) { + face_start = vert_faces[0]; + face_vert_start = findInArray(getBaseFaceVertices(refiner, face_start), vert); + edge_start = getBaseFaceEdges(refiner, face_start)[face_vert_start]; + } else { + for (int i = 0; i < num_vert_edges; ++i) { + IndexArray edge_faces = getBaseEdgeFaces(refiner, vert_edges[i]); + if (edge_faces.size() == 1) { + edge_start = vert_edges[i]; + face_start = edge_faces[0]; + face_vert_start = findInArray(getBaseFaceVertices(refiner, face_start), vert); + if (edge_start == (getBaseFaceEdges(refiner, face_start)[face_vert_start])) { + break; + } + } + } + } + int edge_count_ordered = 1; + int face_count_ordered = 1; + dst_vert_faces[0] = face_start; + dst_vert_edges[0] = edge_start; + while (edge_count_ordered < num_vert_edges) { + IndexArray fVerts = getBaseFaceVertices(refiner, face_start); + IndexArray fEdges = getBaseFaceEdges(refiner, face_start); + int feStart = face_vert_start; + int feNext = feStart ? (feStart - 1) : (fVerts.size() - 1); + Index eNext = fEdges[feNext]; + dst_vert_edges[edge_count_ordered++] = eNext; + if (face_count_ordered < num_vert_faces) { + IndexArray edge_faces = getBaseEdgeFaces(refiner, eNext); + face_start = edge_faces[edge_faces[0] == face_start]; + face_vert_start = findInArray(getBaseFaceEdges(refiner, face_start), eNext); + dst_vert_faces[face_count_ordered++] = face_start; + } + edge_start = eNext; + } + + delete [] vert_edges; + delete [] vert_faces; + } + populateBaseLocalIndices(refiner); + return true; +}; + +template <> +inline bool TopologyRefinerFactory::assignComponentTags( + TopologyRefiner& refiner, + const OpenSubdiv_Converter& conv) +{ + int num_edges = conv.get_num_edges(&conv); + for (int edge = 0; edge < num_edges; ++edge) { + float sharpness = conv.get_edge_sharpness(&conv, edge); + setBaseEdgeSharpness(refiner, edge, sharpness); + } + return true; +} + +template <> +inline void TopologyRefinerFactory::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) +{ + using OpenSubdiv::Far::TopologyRefinerFactory; + OpenSubdiv::Sdc::SchemeType scheme_type = + get_capi_scheme_type(converter->get_type(converter)); + OpenSubdiv::Sdc::Options options; + options.SetVtxBoundaryInterpolation(OpenSubdiv::Sdc::Options::VTX_BOUNDARY_EDGE_AND_CORNER); + options.SetFVarLinearInterpolation(OpenSubdiv::Sdc::Options::FVAR_LINEAR_ALL); + + TopologyRefinerFactory::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::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_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 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]); + for (int i = 0; i < face_verts.size(); ++i) { + if (conv_face_verts[i] != face_verts[i]) { + return false; + } + } + } + /* Compare sharpness. */ + for (int edge = 0; edge < num_edges; ++edge) { + float sharpness = base_level.GetEdgeSharpness(edge); + float conv_sharpness = converter->get_edge_sharpness(converter, edge); + 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..7c96d3d8b96 --- /dev/null +++ b/intern/opensubdiv/opensubdiv_converter_capi.h @@ -0,0 +1,120 @@ +/* + * ***** 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 fiel 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_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 +#elif defined(__APPLE__) +# include +#else +# include +# include +#endif + +#include +#include +#include +#include +#include + +#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 +#elif defined(__APPLE__) +# include +#else +# include +#endif + +#include +#include +#include + +#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 + +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..b8527fda4e5 --- /dev/null +++ b/intern/opensubdiv/opensubdiv_evaluator_capi.cc @@ -0,0 +1,499 @@ +/* + * ***** 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 +#include + +#ifdef _MSC_VER +# include "iso646.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 { +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 +class StackAllocatedBuffer { +public: + static PatchCoordBuffer *Create(int size) + { + StackAllocatedBuffer *buffer = + new StackAllocatedBuffer(); + 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 +class VolatileEvalOutput { +public: + typedef OpenSubdiv::Osd::EvaluatorCacheT 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(vertex_stencils, + device_context_); + varying_stencils_ = convertToCompatibleStencilTable(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_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_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_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_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_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 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 adaptive refinement to the mesh so that we can use the + * limit evaluation API features. + * + * TODO(sergey): Once OpenSubdiv supports uniform meshes in limit + * evlauation we need to switch to uniform here, which will match + * original Blender subsurf. + */ + TopologyRefiner::AdaptiveOptions options(subsurf_level); + refiner->RefineAdaptive(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 = true; + + 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..8b3f1446e73 --- /dev/null +++ b/intern/opensubdiv/opensubdiv_gpu_capi.cc @@ -0,0 +1,659 @@ +/* + * ***** 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 +#include +#include + +#include + +#ifdef OPENSUBDIV_HAS_CUDA +# include +#endif /* OPENSUBDIV_HAS_CUDA */ + +#include +#include + +#include "opensubdiv_partitioned.h" + +//using OpenSubdiv::FarPatchTables; +using OpenSubdiv::Osd::GLMeshInterface; +//sing OpenSubdiv::PartitionedGLMeshInterface; + +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]; + float constant_attenuation; + float linear_attenuation; + float quadratic_attenuation; + float spot_cutoff; + float spot_exponent; + float spot_cos_cutoff; + float pad[2]; +} Light; + +typedef struct Lighting { + Light lights[MAX_LIGHTS]; + int num_enabled; + int pad[3]; +} 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_program = 0; +static GLuint g_smooth_fill_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]); + exit(1); + } + + return shader; +} + +GLuint linkProgram(const char *define) +{ + GLuint vertexShader = compileShader(GL_VERTEX_SHADER, + "VERTEX_SHADER", + define); + GLuint geometryShader = compileShader(GL_GEOMETRY_SHADER, + "GEOMETRY_SHADER", + define); + GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, + "FRAGMENT_SHADER", + define); + + 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); + exit(1); + } + + 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(PartitionedGLMeshInterface * /*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, use_color_material, use_texture_2d; + glGetBooleanv(GL_LIGHTING, &use_lighting); + glGetBooleanv(GL_COLOR_MATERIAL, &use_color_material); + glGetBooleanv(GL_TEXTURE_2D, &use_texture_2d); + + glUniform1i(glGetUniformLocation(program, "use_color_material"), + use_color_material); + glUniform1i(glGetUniformLocation(program, "use_texture_2d"), + use_texture_2d); + + 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 */ + +void openSubdiv_osdGLDisplayInit(void) +{ + static bool need_init = true; + if (need_init) { + g_flat_fill_program = linkProgram("#define FLAT_SHADING\n"); + g_smooth_fill_program = linkProgram("#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; + } +} + +void openSubdiv_osdGLDisplayDeinit(void) +{ + if (g_lighting_ub != 0) { + glDeleteBuffers(1, &g_lighting_ub); + } + if (g_flat_fill_program) { + glDeleteProgram(g_flat_fill_program); + } + if (g_smooth_fill_program) { + glDeleteProgram(g_flat_fill_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); + 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); + } +} + +static GLuint preapre_patchDraw(PartitionedGLMeshInterface *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; + } + + program = g_smooth_fill_program; + if (fill_quads) { + int model; + glGetIntegerv(GL_SHADE_MODEL, &model); + if (model == GL_FLAT) { + program = g_flat_fill_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) +{ + int mode = GL_QUADS; + if (program) { + glUniform1i(glGetUniformLocation(program, "PrimitiveIdBase"), + patch_index); + } + mode = GL_LINES_ADJACENCY; + glDrawElements(mode, + 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(PartitionedGLMeshInterface *mesh, + GLuint program, + int start_partition, + int num_partitions) +{ +#if 0 + /* Glue patches from all partitions in the range together. */ + int patch_index = -1, start_element = -1, num_elements = 0; + for (int partition = start_partition; + partition < start_partition + num_partitions; + ++partition) + { + OsdDrawContext::PatchArrayVector const &patches = + mesh->GetPatchArrays(partition); + for (int i = 0; i < (int)patches.size(); ++i) { + OsdDrawContext::PatchArray const &patch = patches[i]; + OsdDrawContext::PatchDescriptor desc = patch.GetDescriptor(); + OpenSubdiv::FarPatchTables::Type patchType = desc.GetType(); + if (patchType == OpenSubdiv::FarPatchTables::QUADS) { + if (start_element == -1) { + patch_index = patch.GetPatchIndex(); + start_element = patch.GetVertIndex(); + } + + assert(patch.GetVertIndex() == start_element + num_elements); + num_elements += patch.GetNumIndices(); + } + else { + assert(!"Discontinuitied are not supported yet."); + } + } + } + + /* Perform actual draw. */ + perform_drawElements(program, + patch_index, + num_elements, + start_element); +#else + (void)mesh; + (void)program; + (void)start_partition; + (void)num_partitions; +#endif +} + +static void draw_all_patches(PartitionedGLMeshInterface *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_partition, + int num_partitions) +{ + PartitionedGLMeshInterface *mesh = + (PartitionedGLMeshInterface *)(gl_mesh->descriptor); + + /* Make sure all global invariants are initialized. */ + openSubdiv_osdGLDisplayInit(); + + /* Setup GLSL/OpenGL to draw patches in current context. */ + GLuint program = preapre_patchDraw(mesh, fill_quads != 0); + + if (start_partition != -1) { +#if 0 + draw_partition_patches_range(mesh, + program, + start_partition, + num_partitions); +#else + (void)num_partitions; + if(start_partition == 0) { + draw_all_patches(mesh, program); + } +#endif + } + 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..034677f76fd --- /dev/null +++ b/intern/opensubdiv/opensubdiv_intern.h @@ -0,0 +1,36 @@ +/* + * ***** 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 +# define OPENSUBDIV_VALIDATE_TOPOLOGY +#endif + +#endif /* __OPENSUBDIV_INTERN_H__ */ diff --git a/intern/opensubdiv/opensubdiv_partitioned.h b/intern/opensubdiv/opensubdiv_partitioned.h new file mode 100644 index 00000000000..7394a83cd0a --- /dev/null +++ b/intern/opensubdiv/opensubdiv_partitioned.h @@ -0,0 +1,77 @@ +/* + * ***** 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_PATITIONED_H__ +#define __OPENSUBDIV_PATITIONED_H__ + +#include +#include +#include + +namespace OpenSubdiv { +namespace OPENSUBDIV_VERSION { +namespace Osd { + +/* TODO(sergey): Re-implement partitioning. */ + +#if 0 +template +class PartitionedMeshInterface : public MeshInterface { + typedef PATCH_TABLE PatchTable; + typedef typename PatchTable::VertexBufferBinding VertexBufferBinding; + +public: +}; + +typedef PartitionedMeshInterface PartitionedGLMeshInterface; + +#endif + +#if 0 +template +class PartitionedMesh : public Mesh +{ +}; +#endif + +#define PartitionedGLMeshInterface GLMeshInterface +#define PartitionedMesh Mesh + +} /* namespace Osd */ +} /* namespace OPENSUBDIV_VERSION */ + +using namespace OPENSUBDIV_VERSION; + +} /* namespace OpenSubdiv */ + +#endif /* __OPENSUBDIV_PATITIONED_H__ */ diff --git a/intern/opensubdiv/opensubdiv_utils_capi.cc b/intern/opensubdiv/opensubdiv_utils_capi.cc new file mode 100644 index 00000000000..5a2d017ed26 --- /dev/null +++ b/intern/opensubdiv/opensubdiv_utils_capi.cc @@ -0,0 +1,83 @@ +/* + * ***** 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 + +#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 + flags |= OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; +#endif /* OPENSUBDIV_HAS_GLSL_COMPUTE */ + + return flags; +} + +void openSubdiv_cleanup(void) +{ + openSubdiv_osdGLDisplayDeinit(); +} -- cgit v1.2.3