diff options
Diffstat (limited to 'intern/opensubdiv')
-rw-r--r-- | intern/opensubdiv/CMakeLists.txt | 11 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/eval_output.cc | 35 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/eval_output.h | 582 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/eval_output_cpu.cc | 23 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/eval_output_cpu.h | 66 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/eval_output_gpu.cc | 120 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/eval_output_gpu.h | 71 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc | 47 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/evaluator_cache_impl.h | 38 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/evaluator_capi.cc | 118 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/evaluator_impl.cc | 649 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/evaluator_impl.h | 69 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/patch_map.cc | 212 | ||||
-rw-r--r-- | intern/opensubdiv/internal/evaluator/patch_map.h | 264 | ||||
-rw-r--r-- | intern/opensubdiv/opensubdiv_evaluator_capi.h | 101 |
15 files changed, 1900 insertions, 506 deletions
diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt index bce8a8baa84..f141689791f 100644 --- a/intern/opensubdiv/CMakeLists.txt +++ b/intern/opensubdiv/CMakeLists.txt @@ -72,9 +72,20 @@ if(WITH_OPENSUBDIV) internal/device/device_context_openmp.h # Evaluator. + internal/evaluator/eval_output.cc + internal/evaluator/eval_output.h + internal/evaluator/eval_output_cpu.cc + internal/evaluator/eval_output_cpu.h + internal/evaluator/eval_output_gpu.cc + internal/evaluator/eval_output_gpu.h + internal/evaluator/evaluator_cache_impl.cc + internal/evaluator/evaluator_cache_impl.h internal/evaluator/evaluator_capi.cc internal/evaluator/evaluator_impl.cc internal/evaluator/evaluator_impl.h + internal/evaluator/patch_map.cc + internal/evaluator/patch_map.h + # Topology. internal/topology/mesh_topology.cc diff --git a/intern/opensubdiv/internal/evaluator/eval_output.cc b/intern/opensubdiv/internal/evaluator/eval_output.cc new file mode 100644 index 00000000000..58f835ab2e0 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/eval_output.cc @@ -0,0 +1,35 @@ +// Copyright 2021 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "internal/evaluator/eval_output.h" + +namespace blender { +namespace opensubdiv { + +bool is_adaptive(CpuPatchTable *patch_table) +{ + return patch_table->GetPatchArrayBuffer()[0].GetDescriptor().IsAdaptive(); +} + +bool is_adaptive(GLPatchTable *patch_table) +{ + return patch_table->GetPatchArrays()[0].GetDescriptor().IsAdaptive(); +} + +} // namespace opensubdiv +} // namespace blender diff --git a/intern/opensubdiv/internal/evaluator/eval_output.h b/intern/opensubdiv/internal/evaluator/eval_output.h new file mode 100644 index 00000000000..a55b89001a4 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/eval_output.h @@ -0,0 +1,582 @@ +// Copyright 2021 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_EVAL_OUTPUT_H_ +#define OPENSUBDIV_EVAL_OUTPUT_H_ + +#include <opensubdiv/osd/cpuPatchTable.h> +#include <opensubdiv/osd/glPatchTable.h> +#include <opensubdiv/osd/mesh.h> +#include <opensubdiv/osd/types.h> + +#include "internal/base/type.h" +#include "internal/evaluator/evaluator_impl.h" + +using OpenSubdiv::Far::PatchTable; +using OpenSubdiv::Far::StencilTable; +using OpenSubdiv::Osd::BufferDescriptor; +using OpenSubdiv::Osd::CpuPatchTable; +using OpenSubdiv::Osd::GLPatchTable; +using OpenSubdiv::Osd::PatchCoord; + +namespace blender { +namespace opensubdiv { + +// Base class for the implementation of the evaluators. +class EvalOutputAPI::EvalOutput { + public: + virtual ~EvalOutput() = default; + + virtual void updateData(const float *src, int start_vertex, int num_vertices) = 0; + + virtual void updateVaryingData(const float *src, int start_vertex, int num_vertices) = 0; + + virtual void updateFaceVaryingData(const int face_varying_channel, + const float *src, + int start_vertex, + int num_vertices) = 0; + + virtual void refine() = 0; + + // NOTE: P must point to a memory of at least float[3]*num_patch_coords. + virtual void evalPatches(const PatchCoord *patch_coord, + const int num_patch_coords, + float *P) = 0; + + // NOTE: P, dPdu, dPdv must point to a memory of at least float[3]*num_patch_coords. + virtual void evalPatchesWithDerivatives(const PatchCoord *patch_coord, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv) = 0; + + // NOTE: varying must point to a memory of at least float[3]*num_patch_coords. + virtual void evalPatchesVarying(const PatchCoord *patch_coord, + const int num_patch_coords, + float *varying) = 0; + + virtual void evalPatchesFaceVarying(const int face_varying_channel, + const PatchCoord *patch_coord, + const int num_patch_coords, + float face_varying[2]) = 0; + + // The following interfaces are dependant on the actual evaluator type (CPU, OpenGL, etc.) which + // have slightly different APIs to access patch arrays, as well as different types for their + // data structure. They need to be overridden in the specific instances of the EvalOutput derived + // classes if needed, while the interfaces above are overriden through VolatileEvalOutput. + + virtual void fillPatchArraysBuffer(OpenSubdiv_Buffer * /*patch_arrays_buffer*/) + { + } + + virtual void wrapPatchIndexBuffer(OpenSubdiv_Buffer * /*patch_index_buffer*/) + { + } + + virtual void wrapPatchParamBuffer(OpenSubdiv_Buffer * /*patch_param_buffer*/) + { + } + + virtual void wrapSrcBuffer(OpenSubdiv_Buffer * /*src_buffer*/) + { + } + + virtual void fillFVarPatchArraysBuffer(const int /*face_varying_channel*/, + OpenSubdiv_Buffer * /*patch_arrays_buffer*/) + { + } + + virtual void wrapFVarPatchIndexBuffer(const int /*face_varying_channel*/, + OpenSubdiv_Buffer * /*patch_index_buffer*/) + { + } + + virtual void wrapFVarPatchParamBuffer(const int /*face_varying_channel*/, + OpenSubdiv_Buffer * /*patch_param_buffer*/) + { + } + + virtual void wrapFVarSrcBuffer(const int /*face_varying_channel*/, + OpenSubdiv_Buffer * /*src_buffer*/) + { + } +}; + +namespace { + +// Buffer which implements API required by OpenSubdiv and uses an existing memory as an underlying +// storage. +template<typename T> class RawDataWrapperBuffer { + public: + RawDataWrapperBuffer(T *data) : data_(data) + { + } + + T *BindCpuBuffer() + { + return data_; + } + + int BindVBO() + { + return 0; + } + + // TODO(sergey): Support UpdateData(). + + protected: + T *data_; +}; + +template<typename T> class RawDataWrapperVertexBuffer : public RawDataWrapperBuffer<T> { + public: + RawDataWrapperVertexBuffer(T *data, int num_vertices) + : RawDataWrapperBuffer<T>(data), num_vertices_(num_vertices) + { + } + + int GetNumVertices() + { + return num_vertices_; + } + + protected: + int num_vertices_; +}; + +class ConstPatchCoordWrapperBuffer : public RawDataWrapperVertexBuffer<const PatchCoord> { + public: + ConstPatchCoordWrapperBuffer(const PatchCoord *data, int num_vertices) + : RawDataWrapperVertexBuffer(data, num_vertices) + { + } +}; +} // namespace + +// Discriminators used in FaceVaryingVolatileEval in order to detect whether we are using adaptive +// patches as the CPU and OpenGL PatchTable have different APIs. +bool is_adaptive(CpuPatchTable *patch_table); +bool is_adaptive(GLPatchTable *patch_table); + +template<typename EVAL_VERTEX_BUFFER, + typename STENCIL_TABLE, + typename PATCH_TABLE, + typename EVALUATOR, + typename DEVICE_CONTEXT = void> +class FaceVaryingVolatileEval { + public: + typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache; + + FaceVaryingVolatileEval(int face_varying_channel, + const StencilTable *face_varying_stencils, + int face_varying_width, + PATCH_TABLE *patch_table, + EvaluatorCache *evaluator_cache = NULL, + DEVICE_CONTEXT *device_context = NULL) + : face_varying_channel_(face_varying_channel), + src_face_varying_desc_(0, face_varying_width, face_varying_width), + patch_table_(patch_table), + evaluator_cache_(evaluator_cache), + device_context_(device_context) + { + using OpenSubdiv::Osd::convertToCompatibleStencilTable; + num_coarse_face_varying_vertices_ = face_varying_stencils->GetNumControlVertices(); + const int num_total_face_varying_vertices = face_varying_stencils->GetNumControlVertices() + + face_varying_stencils->GetNumStencils(); + src_face_varying_data_ = EVAL_VERTEX_BUFFER::Create( + 2, num_total_face_varying_vertices, device_context); + face_varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(face_varying_stencils, + device_context_); + } + + ~FaceVaryingVolatileEval() + { + delete src_face_varying_data_; + delete face_varying_stencils_; + } + + void updateData(const float *src, int start_vertex, int num_vertices) + { + src_face_varying_data_->UpdateData(src, start_vertex, num_vertices, device_context_); + } + + void refine() + { + BufferDescriptor dst_face_varying_desc = src_face_varying_desc_; + dst_face_varying_desc.offset += num_coarse_face_varying_vertices_ * + src_face_varying_desc_.stride; + const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( + evaluator_cache_, src_face_varying_desc_, dst_face_varying_desc, device_context_); + // in and out points to same buffer so output is put directly after coarse vertices, needed in + // adaptive mode + EVALUATOR::EvalStencils(src_face_varying_data_, + src_face_varying_desc_, + src_face_varying_data_, + dst_face_varying_desc, + face_varying_stencils_, + eval_instance, + device_context_); + } + + // NOTE: face_varying must point to a memory of at least float[2]*num_patch_coords. + void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *face_varying) + { + RawDataWrapperBuffer<float> face_varying_data(face_varying); + BufferDescriptor face_varying_desc(0, 2, 2); + ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); + const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( + evaluator_cache_, src_face_varying_desc_, face_varying_desc, device_context_); + + BufferDescriptor src_desc = get_src_varying_desc(); + + EVALUATOR::EvalPatchesFaceVarying(src_face_varying_data_, + src_desc, + &face_varying_data, + face_varying_desc, + patch_coord_buffer.GetNumVertices(), + &patch_coord_buffer, + patch_table_, + face_varying_channel_, + eval_instance, + device_context_); + } + + EVAL_VERTEX_BUFFER *getSrcBuffer() const + { + return src_face_varying_data_; + } + + int getFVarSrcBufferOffset() const + { + BufferDescriptor src_desc = get_src_varying_desc(); + return src_desc.offset; + } + + PATCH_TABLE *getPatchTable() const + { + return patch_table_; + } + + private: + BufferDescriptor get_src_varying_desc() const + { + // src_face_varying_data_ always contains coarse vertices at the beginning. + // In adaptive mode they are followed by number of blocks for intermediate + // subdivision levels, and this is what OSD expects in this mode. + // In non-adaptive mode (generateIntermediateLevels == false), + // they are followed by max subdivision level, but they break interpolation as OSD + // expects only one subd level in this buffer. + // So in non-adaptive mode we put offset into buffer descriptor to skip over coarse vertices. + BufferDescriptor src_desc = src_face_varying_desc_; + if (!is_adaptive(patch_table_)) { + src_desc.offset += num_coarse_face_varying_vertices_ * src_face_varying_desc_.stride; + } + return src_desc; + } + + protected: + int face_varying_channel_; + + BufferDescriptor src_face_varying_desc_; + + int num_coarse_face_varying_vertices_; + EVAL_VERTEX_BUFFER *src_face_varying_data_; + const STENCIL_TABLE *face_varying_stencils_; + + // NOTE: We reference this, do not own it. + PATCH_TABLE *patch_table_; + + EvaluatorCache *evaluator_cache_; + DEVICE_CONTEXT *device_context_; +}; + +// Volatile evaluator which can be used from threads. +// +// TODO(sergey): Make it possible to evaluate coordinates in chunks. +// TODO(sergey): Make it possible to evaluate multiple face varying layers. +// (or maybe, it's cheap to create new evaluator for existing +// topology to evaluate all needed face varying layers?) +template<typename SRC_VERTEX_BUFFER, + typename EVAL_VERTEX_BUFFER, + typename STENCIL_TABLE, + typename PATCH_TABLE, + typename EVALUATOR, + typename DEVICE_CONTEXT = void> +class VolatileEvalOutput : public EvalOutputAPI::EvalOutput { + public: + typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache; + typedef FaceVaryingVolatileEval<EVAL_VERTEX_BUFFER, + STENCIL_TABLE, + PATCH_TABLE, + EVALUATOR, + DEVICE_CONTEXT> + FaceVaryingEval; + + VolatileEvalOutput(const StencilTable *vertex_stencils, + const StencilTable *varying_stencils, + const vector<const StencilTable *> &all_face_varying_stencils, + const int face_varying_width, + const PatchTable *patch_table, + EvaluatorCache *evaluator_cache = NULL, + DEVICE_CONTEXT *device_context = NULL) + : src_desc_(0, 3, 3), + src_varying_desc_(0, 3, 3), + face_varying_width_(face_varying_width), + evaluator_cache_(evaluator_cache), + device_context_(device_context) + { + // Total number of vertices = coarse points + refined points + local points. + int num_total_vertices = vertex_stencils->GetNumControlVertices() + + vertex_stencils->GetNumStencils(); + num_coarse_vertices_ = vertex_stencils->GetNumControlVertices(); + using OpenSubdiv::Osd::convertToCompatibleStencilTable; + src_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_); + src_varying_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_); + patch_table_ = PATCH_TABLE::Create(patch_table, device_context_); + vertex_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(vertex_stencils, + device_context_); + varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(varying_stencils, + device_context_); + // Create evaluators for every face varying channel. + face_varying_evaluators.reserve(all_face_varying_stencils.size()); + int face_varying_channel = 0; + for (const StencilTable *face_varying_stencils : all_face_varying_stencils) { + face_varying_evaluators.push_back(new FaceVaryingEval(face_varying_channel, + face_varying_stencils, + face_varying_width, + patch_table_, + evaluator_cache_, + device_context_)); + ++face_varying_channel; + } + } + + ~VolatileEvalOutput() override + { + delete src_data_; + delete src_varying_data_; + delete patch_table_; + delete vertex_stencils_; + delete varying_stencils_; + for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) { + delete face_varying_evaluator; + } + } + + // TODO(sergey): Implement binding API. + + void updateData(const float *src, int start_vertex, int num_vertices) override + { + src_data_->UpdateData(src, start_vertex, num_vertices, device_context_); + } + + void updateVaryingData(const float *src, int start_vertex, int num_vertices) override + { + src_varying_data_->UpdateData(src, start_vertex, num_vertices, device_context_); + } + + void updateFaceVaryingData(const int face_varying_channel, + const float *src, + int start_vertex, + int num_vertices) override + { + assert(face_varying_channel >= 0); + assert(face_varying_channel < face_varying_evaluators.size()); + face_varying_evaluators[face_varying_channel]->updateData(src, start_vertex, num_vertices); + } + + bool hasVaryingData() const + { + // return varying_stencils_ != NULL; + // TODO(sergey): Check this based on actual topology. + return false; + } + + bool hasFaceVaryingData() const + { + return face_varying_evaluators.size() != 0; + } + + void refine() override + { + // Evaluate vertex positions. + BufferDescriptor dst_desc = src_desc_; + dst_desc.offset += num_coarse_vertices_ * src_desc_.stride; + const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( + evaluator_cache_, src_desc_, dst_desc, device_context_); + EVALUATOR::EvalStencils(src_data_, + src_desc_, + src_data_, + dst_desc, + vertex_stencils_, + eval_instance, + device_context_); + // Evaluate varying data. + if (hasVaryingData()) { + BufferDescriptor dst_varying_desc = src_varying_desc_; + dst_varying_desc.offset += num_coarse_vertices_ * src_varying_desc_.stride; + eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( + evaluator_cache_, src_varying_desc_, dst_varying_desc, device_context_); + EVALUATOR::EvalStencils(src_varying_data_, + src_varying_desc_, + src_varying_data_, + dst_varying_desc, + varying_stencils_, + eval_instance, + device_context_); + } + // Evaluate face-varying data. + if (hasFaceVaryingData()) { + for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) { + face_varying_evaluator->refine(); + } + } + } + + // NOTE: P must point to a memory of at least float[3]*num_patch_coords. + void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *P) override + { + RawDataWrapperBuffer<float> P_data(P); + // TODO(sergey): Support interleaved vertex-varying data. + BufferDescriptor P_desc(0, 3, 3); + ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); + const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( + evaluator_cache_, src_desc_, P_desc, device_context_); + EVALUATOR::EvalPatches(src_data_, + src_desc_, + &P_data, + P_desc, + patch_coord_buffer.GetNumVertices(), + &patch_coord_buffer, + patch_table_, + eval_instance, + device_context_); + } + + // NOTE: P, dPdu, dPdv must point to a memory of at least float[3]*num_patch_coords. + void evalPatchesWithDerivatives(const PatchCoord *patch_coord, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv) override + { + assert(dPdu); + assert(dPdv); + RawDataWrapperBuffer<float> P_data(P); + RawDataWrapperBuffer<float> dPdu_data(dPdu), dPdv_data(dPdv); + // TODO(sergey): Support interleaved vertex-varying data. + BufferDescriptor P_desc(0, 3, 3); + BufferDescriptor dpDu_desc(0, 3, 3), pPdv_desc(0, 3, 3); + ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); + const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( + evaluator_cache_, src_desc_, P_desc, dpDu_desc, pPdv_desc, device_context_); + EVALUATOR::EvalPatches(src_data_, + src_desc_, + &P_data, + P_desc, + &dPdu_data, + dpDu_desc, + &dPdv_data, + pPdv_desc, + patch_coord_buffer.GetNumVertices(), + &patch_coord_buffer, + patch_table_, + eval_instance, + device_context_); + } + + // NOTE: varying must point to a memory of at least float[3]*num_patch_coords. + void evalPatchesVarying(const PatchCoord *patch_coord, + const int num_patch_coords, + float *varying) override + { + RawDataWrapperBuffer<float> varying_data(varying); + BufferDescriptor varying_desc(3, 3, 6); + ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); + const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( + evaluator_cache_, src_varying_desc_, varying_desc, device_context_); + EVALUATOR::EvalPatchesVarying(src_varying_data_, + src_varying_desc_, + &varying_data, + varying_desc, + patch_coord_buffer.GetNumVertices(), + &patch_coord_buffer, + patch_table_, + eval_instance, + device_context_); + } + + void evalPatchesFaceVarying(const int face_varying_channel, + const PatchCoord *patch_coord, + const int num_patch_coords, + float face_varying[2]) override + { + assert(face_varying_channel >= 0); + assert(face_varying_channel < face_varying_evaluators.size()); + face_varying_evaluators[face_varying_channel]->evalPatches( + patch_coord, num_patch_coords, face_varying); + } + + SRC_VERTEX_BUFFER *getSrcBuffer() const + { + return src_data_; + } + + PATCH_TABLE *getPatchTable() const + { + return patch_table_; + } + + SRC_VERTEX_BUFFER *getFVarSrcBuffer(const int face_varying_channel) const + { + return face_varying_evaluators[face_varying_channel]->getSrcBuffer(); + } + + int getFVarSrcBufferOffset(const int face_varying_channel) const + { + return face_varying_evaluators[face_varying_channel]->getFVarSrcBufferOffset(); + } + + PATCH_TABLE *getFVarPatchTable(const int face_varying_channel) const + { + return face_varying_evaluators[face_varying_channel]->getPatchTable(); + } + + private: + SRC_VERTEX_BUFFER *src_data_; + SRC_VERTEX_BUFFER *src_varying_data_; + PATCH_TABLE *patch_table_; + BufferDescriptor src_desc_; + BufferDescriptor src_varying_desc_; + + int num_coarse_vertices_; + + const STENCIL_TABLE *vertex_stencils_; + const STENCIL_TABLE *varying_stencils_; + + int face_varying_width_; + vector<FaceVaryingEval *> face_varying_evaluators; + + EvaluatorCache *evaluator_cache_; + DEVICE_CONTEXT *device_context_; +}; + +} // namespace opensubdiv +} // namespace blender + +#endif // OPENSUBDIV_EVAL_OUTPUT_H_ diff --git a/intern/opensubdiv/internal/evaluator/eval_output_cpu.cc b/intern/opensubdiv/internal/evaluator/eval_output_cpu.cc new file mode 100644 index 00000000000..02a41cb1580 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/eval_output_cpu.cc @@ -0,0 +1,23 @@ +// Copyright 2021 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +namespace blender { +namespace opensubdiv { + +} +} // namespace blender diff --git a/intern/opensubdiv/internal/evaluator/eval_output_cpu.h b/intern/opensubdiv/internal/evaluator/eval_output_cpu.h new file mode 100644 index 00000000000..58bae7a322e --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/eval_output_cpu.h @@ -0,0 +1,66 @@ +// Copyright 2021 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_EVAL_OUTPUT_CPU_H_ +#define OPENSUBDIV_EVAL_OUTPUT_CPU_H_ + +#include "internal/evaluator/eval_output.h" + +#include <opensubdiv/osd/cpuEvaluator.h> +#include <opensubdiv/osd/cpuPatchTable.h> +#include <opensubdiv/osd/cpuVertexBuffer.h> + +using OpenSubdiv::Far::StencilTable; +using OpenSubdiv::Osd::CpuEvaluator; +using OpenSubdiv::Osd::CpuVertexBuffer; + +namespace blender { +namespace opensubdiv { + +// Note: Define as a class instead of typedef to make it possible +// to have anonymous class in opensubdiv_evaluator_internal.h +class CpuEvalOutput : public VolatileEvalOutput<CpuVertexBuffer, + CpuVertexBuffer, + StencilTable, + CpuPatchTable, + CpuEvaluator> { + public: + CpuEvalOutput(const StencilTable *vertex_stencils, + const StencilTable *varying_stencils, + const vector<const StencilTable *> &all_face_varying_stencils, + const int face_varying_width, + const PatchTable *patch_table, + EvaluatorCache *evaluator_cache = NULL) + : VolatileEvalOutput<CpuVertexBuffer, + CpuVertexBuffer, + StencilTable, + CpuPatchTable, + CpuEvaluator>(vertex_stencils, + varying_stencils, + all_face_varying_stencils, + face_varying_width, + patch_table, + evaluator_cache) + { + } +}; + +} // namespace opensubdiv +} // namespace blender + +#endif // OPENSUBDIV_EVAL_OUTPUT_CPU_H_ diff --git a/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc b/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc new file mode 100644 index 00000000000..b352ed2c014 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/eval_output_gpu.cc @@ -0,0 +1,120 @@ +// Copyright 2021 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#include "internal/evaluator/eval_output_gpu.h" + +#include "opensubdiv_evaluator_capi.h" + +using OpenSubdiv::Osd::PatchArray; +using OpenSubdiv::Osd::PatchArrayVector; + +namespace blender { +namespace opensubdiv { + +namespace { + +static void buildPatchArraysBufferFromVector(const PatchArrayVector &patch_arrays, + OpenSubdiv_Buffer *patch_arrays_buffer) +{ + const size_t patch_array_size = sizeof(PatchArray); + const size_t patch_array_byte_site = patch_array_size * patch_arrays.size(); + patch_arrays_buffer->device_alloc(patch_arrays_buffer, patch_arrays.size()); + patch_arrays_buffer->bind_gpu(patch_arrays_buffer); + patch_arrays_buffer->device_update( + patch_arrays_buffer, 0, patch_array_byte_site, &patch_arrays[0]); +} + +} // namespace + +GpuEvalOutput::GpuEvalOutput(const StencilTable *vertex_stencils, + const StencilTable *varying_stencils, + const vector<const StencilTable *> &all_face_varying_stencils, + const int face_varying_width, + const PatchTable *patch_table, + VolatileEvalOutput::EvaluatorCache *evaluator_cache) + : VolatileEvalOutput<GLVertexBuffer, + GLVertexBuffer, + GLStencilTableSSBO, + GLPatchTable, + GLComputeEvaluator>(vertex_stencils, + varying_stencils, + all_face_varying_stencils, + face_varying_width, + patch_table, + evaluator_cache) +{ +} + +void GpuEvalOutput::fillPatchArraysBuffer(OpenSubdiv_Buffer *patch_arrays_buffer) +{ + GLPatchTable *patch_table = getPatchTable(); + buildPatchArraysBufferFromVector(patch_table->GetPatchArrays(), patch_arrays_buffer); +} + +void GpuEvalOutput::wrapPatchIndexBuffer(OpenSubdiv_Buffer *patch_index_buffer) +{ + GLPatchTable *patch_table = getPatchTable(); + patch_index_buffer->wrap_device_handle(patch_index_buffer, patch_table->GetPatchIndexBuffer()); +} + +void GpuEvalOutput::wrapPatchParamBuffer(OpenSubdiv_Buffer *patch_param_buffer) +{ + GLPatchTable *patch_table = getPatchTable(); + patch_param_buffer->wrap_device_handle(patch_param_buffer, patch_table->GetPatchParamBuffer()); +} + +void GpuEvalOutput::wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer) +{ + GLVertexBuffer *vertex_buffer = getSrcBuffer(); + src_buffer->wrap_device_handle(src_buffer, vertex_buffer->BindVBO()); +} + +void GpuEvalOutput::fillFVarPatchArraysBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_arrays_buffer) +{ + GLPatchTable *patch_table = getFVarPatchTable(face_varying_channel); + buildPatchArraysBufferFromVector(patch_table->GetFVarPatchArrays(face_varying_channel), + patch_arrays_buffer); +} + +void GpuEvalOutput::wrapFVarPatchIndexBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_index_buffer) +{ + GLPatchTable *patch_table = getFVarPatchTable(face_varying_channel); + patch_index_buffer->wrap_device_handle( + patch_index_buffer, patch_table->GetFVarPatchIndexBuffer(face_varying_channel)); +} + +void GpuEvalOutput::wrapFVarPatchParamBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_param_buffer) +{ + GLPatchTable *patch_table = getFVarPatchTable(face_varying_channel); + patch_param_buffer->wrap_device_handle( + patch_param_buffer, patch_table->GetFVarPatchParamBuffer(face_varying_channel)); +} + +void GpuEvalOutput::wrapFVarSrcBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *src_buffer) +{ + GLVertexBuffer *vertex_buffer = getFVarSrcBuffer(face_varying_channel); + src_buffer->buffer_offset = getFVarSrcBufferOffset(face_varying_channel); + src_buffer->wrap_device_handle(src_buffer, vertex_buffer->BindVBO()); +} + +} // namespace opensubdiv +} // namespace blender diff --git a/intern/opensubdiv/internal/evaluator/eval_output_gpu.h b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h new file mode 100644 index 00000000000..783efd484aa --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h @@ -0,0 +1,71 @@ +// Copyright 2021 Blender Foundation. All rights reserved. +// +// 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. +// +// Author: Sergey Sharybin + +#ifndef OPENSUBDIV_EVAL_OUTPUT_GPU_H_ +#define OPENSUBDIV_EVAL_OUTPUT_GPU_H_ + +#include "internal/evaluator/eval_output.h" + +#include <opensubdiv/osd/glComputeEvaluator.h> +#include <opensubdiv/osd/glPatchTable.h> +#include <opensubdiv/osd/glVertexBuffer.h> + +using OpenSubdiv::Osd::GLComputeEvaluator; +using OpenSubdiv::Osd::GLStencilTableSSBO; +using OpenSubdiv::Osd::GLVertexBuffer; + +namespace blender { +namespace opensubdiv { + +class GpuEvalOutput : public VolatileEvalOutput<GLVertexBuffer, + GLVertexBuffer, + GLStencilTableSSBO, + GLPatchTable, + GLComputeEvaluator> { + public: + GpuEvalOutput(const StencilTable *vertex_stencils, + const StencilTable *varying_stencils, + const vector<const StencilTable *> &all_face_varying_stencils, + const int face_varying_width, + const PatchTable *patch_table, + EvaluatorCache *evaluator_cache = NULL); + + void fillPatchArraysBuffer(OpenSubdiv_Buffer *patch_arrays_buffer) override; + + void wrapPatchIndexBuffer(OpenSubdiv_Buffer *patch_index_buffer) override; + + void wrapPatchParamBuffer(OpenSubdiv_Buffer *patch_param_buffer) override; + + void wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer) override; + + void fillFVarPatchArraysBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_arrays_buffer) override; + + void wrapFVarPatchIndexBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_index_buffer) override; + + void wrapFVarPatchParamBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_param_buffer) override; + + void wrapFVarSrcBuffer(const int face_varying_channel, OpenSubdiv_Buffer *src_buffer) override; +}; + +} // namespace opensubdiv +} // namespace blender + +#endif // OPENSUBDIV_EVAL_OUTPUT_GPU_H_ diff --git a/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc new file mode 100644 index 00000000000..2fffcefa460 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.cc @@ -0,0 +1,47 @@ +// Copyright 2021 Blender Foundation. All rights reserved. +// +// 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. + +#include "internal/evaluator/evaluator_cache_impl.h" + +#include "internal/evaluator/eval_output_gpu.h" + +OpenSubdiv_EvaluatorCacheImpl::OpenSubdiv_EvaluatorCacheImpl() +{ +} + +OpenSubdiv_EvaluatorCacheImpl::~OpenSubdiv_EvaluatorCacheImpl() +{ + delete static_cast<blender::opensubdiv::GpuEvalOutput::EvaluatorCache *>(eval_cache); +} + +OpenSubdiv_EvaluatorCacheImpl *openSubdiv_createEvaluatorCacheInternal( + eOpenSubdivEvaluator evaluator_type) +{ + if (evaluator_type != eOpenSubdivEvaluator::OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) { + return nullptr; + } + OpenSubdiv_EvaluatorCacheImpl *evaluator_cache; + evaluator_cache = new OpenSubdiv_EvaluatorCacheImpl; + blender::opensubdiv::GpuEvalOutput::EvaluatorCache *eval_cache; + eval_cache = new blender::opensubdiv::GpuEvalOutput::EvaluatorCache(); + evaluator_cache->eval_cache = eval_cache; + return evaluator_cache; +} + +void openSubdiv_deleteEvaluatorCacheInternal(OpenSubdiv_EvaluatorCacheImpl *evaluator_cache) +{ + delete evaluator_cache; +} diff --git a/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.h b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.h new file mode 100644 index 00000000000..060a78e5cbd --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/evaluator_cache_impl.h @@ -0,0 +1,38 @@ +// Copyright 2021 Blender Foundation. All rights reserved. +// +// 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. + +#ifndef OPENSUBDIV_EVALUATOR_CACHE_IMPL_H_ +#define OPENSUBDIV_EVALUATOR_CACHE_IMPL_H_ + +#include "internal/base/memory.h" + +#include "opensubdiv_capi_type.h" + +struct OpenSubdiv_EvaluatorCacheImpl { + public: + OpenSubdiv_EvaluatorCacheImpl(); + ~OpenSubdiv_EvaluatorCacheImpl(); + + void *eval_cache; + MEM_CXX_CLASS_ALLOC_FUNCS("OpenSubdiv_EvaluatorCacheImpl"); +}; + +OpenSubdiv_EvaluatorCacheImpl *openSubdiv_createEvaluatorCacheInternal( + eOpenSubdivEvaluator evaluator_type); + +void openSubdiv_deleteEvaluatorCacheInternal(OpenSubdiv_EvaluatorCacheImpl *evaluator_cache); + +#endif // OPENSUBDIV_EVALUATOR_CACHE_IMPL_H_ diff --git a/intern/opensubdiv/internal/evaluator/evaluator_capi.cc b/intern/opensubdiv/internal/evaluator/evaluator_capi.cc index 5f8d6cddfd5..567afd3a763 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_capi.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_capi.cc @@ -18,9 +18,12 @@ #include "opensubdiv_evaluator_capi.h" +#include <opensubdiv/osd/glslPatchShaderSource.h> + #include "MEM_guardedalloc.h" #include <new> +#include "internal/evaluator/evaluator_cache_impl.h" #include "internal/evaluator/evaluator_impl.h" namespace { @@ -132,6 +135,74 @@ void evaluateFaceVarying(OpenSubdiv_Evaluator *evaluator, face_varying_channel, ptex_face_index, face_u, face_v, face_varying); } +void getPatchMap(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *patch_map_handles, + struct OpenSubdiv_Buffer *patch_map_quadtree, + int *min_patch_face, + int *max_patch_face, + int *max_depth, + int *patches_are_triangular) +{ + evaluator->impl->eval_output->getPatchMap(patch_map_handles, + patch_map_quadtree, + min_patch_face, + max_patch_face, + max_depth, + patches_are_triangular); +} + +void fillPatchArraysBuffer(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *patch_array_buffer) +{ + evaluator->impl->eval_output->fillPatchArraysBuffer(patch_array_buffer); +} + +void wrapPatchIndexBuffer(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *patch_index_buffer) +{ + evaluator->impl->eval_output->wrapPatchIndexBuffer(patch_index_buffer); +} + +void wrapPatchParamBuffer(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *patch_param_buffer) +{ + evaluator->impl->eval_output->wrapPatchParamBuffer(patch_param_buffer); +} + +void wrapSrcBuffer(struct OpenSubdiv_Evaluator *evaluator, struct OpenSubdiv_Buffer *src_buffer) +{ + evaluator->impl->eval_output->wrapSrcBuffer(src_buffer); +} + +void fillFVarPatchArraysBuffer(struct OpenSubdiv_Evaluator *evaluator, + const int face_varying_channel, + struct OpenSubdiv_Buffer *patch_array_buffer) +{ + evaluator->impl->eval_output->fillFVarPatchArraysBuffer(face_varying_channel, + patch_array_buffer); +} + +void wrapFVarPatchIndexBuffer(struct OpenSubdiv_Evaluator *evaluator, + const int face_varying_channel, + struct OpenSubdiv_Buffer *patch_index_buffer) +{ + evaluator->impl->eval_output->wrapFVarPatchIndexBuffer(face_varying_channel, patch_index_buffer); +} + +void wrapFVarPatchParamBuffer(struct OpenSubdiv_Evaluator *evaluator, + const int face_varying_channel, + struct OpenSubdiv_Buffer *patch_param_buffer) +{ + evaluator->impl->eval_output->wrapFVarPatchParamBuffer(face_varying_channel, patch_param_buffer); +} + +void wrapFVarSrcBuffer(struct OpenSubdiv_Evaluator *evaluator, + const int face_varying_channel, + struct OpenSubdiv_Buffer *src_buffer) +{ + evaluator->impl->eval_output->wrapFVarSrcBuffer(face_varying_channel, src_buffer); +} + void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator) { evaluator->setCoarsePositions = setCoarsePositions; @@ -149,16 +220,32 @@ void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator) evaluator->evaluateFaceVarying = evaluateFaceVarying; evaluator->evaluatePatchesLimit = evaluatePatchesLimit; + + evaluator->getPatchMap = getPatchMap; + + evaluator->fillPatchArraysBuffer = fillPatchArraysBuffer; + evaluator->wrapPatchIndexBuffer = wrapPatchIndexBuffer; + evaluator->wrapPatchParamBuffer = wrapPatchParamBuffer; + evaluator->wrapSrcBuffer = wrapSrcBuffer; + + evaluator->fillFVarPatchArraysBuffer = fillFVarPatchArraysBuffer; + evaluator->wrapFVarPatchIndexBuffer = wrapFVarPatchIndexBuffer; + evaluator->wrapFVarPatchParamBuffer = wrapFVarPatchParamBuffer; + evaluator->wrapFVarSrcBuffer = wrapFVarSrcBuffer; } } // namespace OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner( - OpenSubdiv_TopologyRefiner *topology_refiner) + OpenSubdiv_TopologyRefiner *topology_refiner, + eOpenSubdivEvaluator evaluator_type, + OpenSubdiv_EvaluatorCache *evaluator_cache) { OpenSubdiv_Evaluator *evaluator = MEM_new<OpenSubdiv_Evaluator>(__func__); assignFunctionPointers(evaluator); - evaluator->impl = openSubdiv_createEvaluatorInternal(topology_refiner); + evaluator->impl = openSubdiv_createEvaluatorInternal( + topology_refiner, evaluator_type, evaluator_cache ? evaluator_cache->impl : nullptr); + evaluator->type = evaluator->impl ? evaluator_type : static_cast<eOpenSubdivEvaluator>(0); return evaluator; } @@ -167,3 +254,30 @@ void openSubdiv_deleteEvaluator(OpenSubdiv_Evaluator *evaluator) openSubdiv_deleteEvaluatorInternal(evaluator->impl); MEM_delete(evaluator); } + +OpenSubdiv_EvaluatorCache *openSubdiv_createEvaluatorCache(eOpenSubdivEvaluator evaluator_type) +{ + OpenSubdiv_EvaluatorCache *evaluator_cache = MEM_new<OpenSubdiv_EvaluatorCache>(__func__); + evaluator_cache->impl = openSubdiv_createEvaluatorCacheInternal(evaluator_type); + return evaluator_cache; +} + +void openSubdiv_deleteEvaluatorCache(OpenSubdiv_EvaluatorCache *evaluator_cache) +{ + if (!evaluator_cache) { + return; + } + + openSubdiv_deleteEvaluatorCacheInternal(evaluator_cache->impl); + MEM_delete(evaluator_cache); +} + +const char *openSubdiv_getGLSLPatchBasisSource(void) +{ + /* Using a global string to avoid dealing with memory allocation/ownership. */ + static std::string patch_basis_source; + if (patch_basis_source.empty()) { + patch_basis_source = OpenSubdiv::Osd::GLSLPatchShaderSource::GetPatchBasisShaderSource(); + } + return patch_basis_source.c_str(); +} diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc index 4f4f332ff15..755b8bfbc81 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc @@ -28,9 +28,6 @@ #include <opensubdiv/far/patchMap.h> #include <opensubdiv/far/patchTable.h> #include <opensubdiv/far/patchTableFactory.h> -#include <opensubdiv/osd/cpuEvaluator.h> -#include <opensubdiv/osd/cpuPatchTable.h> -#include <opensubdiv/osd/cpuVertexBuffer.h> #include <opensubdiv/osd/mesh.h> #include <opensubdiv/osd/types.h> #include <opensubdiv/version.h> @@ -38,19 +35,20 @@ #include "MEM_guardedalloc.h" #include "internal/base/type.h" +#include "internal/evaluator/eval_output_cpu.h" +#include "internal/evaluator/eval_output_gpu.h" +#include "internal/evaluator/evaluator_cache_impl.h" +#include "internal/evaluator/patch_map.h" #include "internal/topology/topology_refiner_impl.h" +#include "opensubdiv_evaluator_capi.h" #include "opensubdiv_topology_refiner_capi.h" -using OpenSubdiv::Far::PatchMap; using OpenSubdiv::Far::PatchTable; using OpenSubdiv::Far::PatchTableFactory; using OpenSubdiv::Far::StencilTable; using OpenSubdiv::Far::StencilTableFactory; using OpenSubdiv::Far::TopologyRefiner; -using OpenSubdiv::Osd::BufferDescriptor; -using OpenSubdiv::Osd::CpuEvaluator; -using OpenSubdiv::Osd::CpuPatchTable; -using OpenSubdiv::Osd::CpuVertexBuffer; +using OpenSubdiv::Osd::PatchArray; using OpenSubdiv::Osd::PatchCoord; namespace blender { @@ -140,407 +138,9 @@ template<typename T, int kNumMaxElementsOnStack> class StackOrHeapArray { // 32 is a number of inner vertices along the patch size at subdivision level 6. typedef StackOrHeapArray<PatchCoord, 32 * 32> StackOrHeapPatchCoordArray; -// Buffer which implements API required by OpenSubdiv and uses an existing memory as an underlying -// storage. -template<typename T> class RawDataWrapperBuffer { - public: - RawDataWrapperBuffer(T *data) : data_(data) - { - } - - T *BindCpuBuffer() - { - return data_; - } - - // TODO(sergey): Support UpdateData(). - - protected: - T *data_; -}; - -template<typename T> class RawDataWrapperVertexBuffer : public RawDataWrapperBuffer<T> { - public: - RawDataWrapperVertexBuffer(T *data, int num_vertices) - : RawDataWrapperBuffer<T>(data), num_vertices_(num_vertices) - { - } - - int GetNumVertices() - { - return num_vertices_; - } - - protected: - int num_vertices_; -}; - -class ConstPatchCoordWrapperBuffer : public RawDataWrapperVertexBuffer<const PatchCoord> { - public: - ConstPatchCoordWrapperBuffer(const PatchCoord *data, int num_vertices) - : RawDataWrapperVertexBuffer(data, num_vertices) - { - } -}; - -template<typename EVAL_VERTEX_BUFFER, - typename STENCIL_TABLE, - typename PATCH_TABLE, - typename EVALUATOR, - typename DEVICE_CONTEXT = void> -class FaceVaryingVolatileEval { - public: - typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache; - - FaceVaryingVolatileEval(int face_varying_channel, - const StencilTable *face_varying_stencils, - int face_varying_width, - PATCH_TABLE *patch_table, - EvaluatorCache *evaluator_cache = NULL, - DEVICE_CONTEXT *device_context = NULL) - : face_varying_channel_(face_varying_channel), - src_face_varying_desc_(0, face_varying_width, face_varying_width), - patch_table_(patch_table), - evaluator_cache_(evaluator_cache), - device_context_(device_context) - { - using OpenSubdiv::Osd::convertToCompatibleStencilTable; - num_coarse_face_varying_vertices_ = face_varying_stencils->GetNumControlVertices(); - const int num_total_face_varying_vertices = face_varying_stencils->GetNumControlVertices() + - face_varying_stencils->GetNumStencils(); - src_face_varying_data_ = EVAL_VERTEX_BUFFER::Create( - 2, num_total_face_varying_vertices, device_context); - face_varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(face_varying_stencils, - device_context_); - } - - ~FaceVaryingVolatileEval() - { - delete src_face_varying_data_; - delete face_varying_stencils_; - } - - void updateData(const float *src, int start_vertex, int num_vertices) - { - src_face_varying_data_->UpdateData(src, start_vertex, num_vertices, device_context_); - } - - void refine() - { - BufferDescriptor dst_face_varying_desc = src_face_varying_desc_; - dst_face_varying_desc.offset += num_coarse_face_varying_vertices_ * - src_face_varying_desc_.stride; - const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( - evaluator_cache_, src_face_varying_desc_, dst_face_varying_desc, device_context_); - // in and out points to same buffer so output is put directly after coarse vertices, needed in - // adaptive mode - EVALUATOR::EvalStencils(src_face_varying_data_, - src_face_varying_desc_, - src_face_varying_data_, - dst_face_varying_desc, - face_varying_stencils_, - eval_instance, - device_context_); - } - - // NOTE: face_varying must point to a memory of at least float[2]*num_patch_coords. - void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *face_varying) - { - RawDataWrapperBuffer<float> face_varying_data(face_varying); - BufferDescriptor face_varying_desc(0, 2, 2); - ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); - const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( - evaluator_cache_, src_face_varying_desc_, face_varying_desc, device_context_); - - // src_face_varying_data_ always contains coarse vertices at the beginning. - // In adaptive mode they are followed by number of blocks for intermediate - // subdivision levels, and this is what OSD expects in this mode. - // In non-adaptive mode (generateIntermediateLevels == false), - // they are followed by max subdivision level, but they break interpolation as OSD - // expects only one subd level in this buffer. - // So in non-adaptive mode we put offset into buffer descriptor to skip over coarse vertices. - BufferDescriptor src_desc = src_face_varying_desc_; - if (!patch_table_->GetPatchArrayBuffer()[0].GetDescriptor().IsAdaptive()) { - src_desc.offset += num_coarse_face_varying_vertices_ * src_face_varying_desc_.stride; - } - - EVALUATOR::EvalPatchesFaceVarying(src_face_varying_data_, - src_desc, - &face_varying_data, - face_varying_desc, - patch_coord_buffer.GetNumVertices(), - &patch_coord_buffer, - patch_table_, - face_varying_channel_, - eval_instance, - device_context_); - } - - protected: - int face_varying_channel_; - - BufferDescriptor src_face_varying_desc_; - - int num_coarse_face_varying_vertices_; - EVAL_VERTEX_BUFFER *src_face_varying_data_; - const STENCIL_TABLE *face_varying_stencils_; - - // NOTE: We reference this, do not own it. - PATCH_TABLE *patch_table_; - - EvaluatorCache *evaluator_cache_; - DEVICE_CONTEXT *device_context_; -}; - -// Volatile evaluator which can be used from threads. -// -// TODO(sergey): Make it possible to evaluate coordinates in chunks. -// TODO(sergey): Make it possible to evaluate multiple face varying layers. -// (or maybe, it's cheap to create new evaluator for existing -// topology to evaluate all needed face varying layers?) -template<typename SRC_VERTEX_BUFFER, - typename EVAL_VERTEX_BUFFER, - typename STENCIL_TABLE, - typename PATCH_TABLE, - typename EVALUATOR, - typename DEVICE_CONTEXT = void> -class VolatileEvalOutput { - public: - typedef OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR> EvaluatorCache; - typedef FaceVaryingVolatileEval<EVAL_VERTEX_BUFFER, - STENCIL_TABLE, - PATCH_TABLE, - EVALUATOR, - DEVICE_CONTEXT> - FaceVaryingEval; - - VolatileEvalOutput(const StencilTable *vertex_stencils, - const StencilTable *varying_stencils, - const vector<const StencilTable *> &all_face_varying_stencils, - const int face_varying_width, - const PatchTable *patch_table, - EvaluatorCache *evaluator_cache = NULL, - DEVICE_CONTEXT *device_context = NULL) - : src_desc_(0, 3, 3), - src_varying_desc_(0, 3, 3), - face_varying_width_(face_varying_width), - evaluator_cache_(evaluator_cache), - device_context_(device_context) - { - // Total number of vertices = coarse points + refined points + local points. - int num_total_vertices = vertex_stencils->GetNumControlVertices() + - vertex_stencils->GetNumStencils(); - num_coarse_vertices_ = vertex_stencils->GetNumControlVertices(); - using OpenSubdiv::Osd::convertToCompatibleStencilTable; - src_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_); - src_varying_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_); - patch_table_ = PATCH_TABLE::Create(patch_table, device_context_); - vertex_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(vertex_stencils, - device_context_); - varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(varying_stencils, - device_context_); - // Create evaluators for every face varying channel. - face_varying_evaluators.reserve(all_face_varying_stencils.size()); - int face_varying_channel = 0; - for (const StencilTable *face_varying_stencils : all_face_varying_stencils) { - face_varying_evaluators.push_back(new FaceVaryingEval(face_varying_channel, - face_varying_stencils, - face_varying_width, - patch_table_, - evaluator_cache_, - device_context_)); - ++face_varying_channel; - } - } - - ~VolatileEvalOutput() - { - delete src_data_; - delete src_varying_data_; - delete patch_table_; - delete vertex_stencils_; - delete varying_stencils_; - for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) { - delete face_varying_evaluator; - } - } - - // TODO(sergey): Implement binding API. - - 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 updateFaceVaryingData(const int face_varying_channel, - const float *src, - int start_vertex, - int num_vertices) - { - assert(face_varying_channel >= 0); - assert(face_varying_channel < face_varying_evaluators.size()); - face_varying_evaluators[face_varying_channel]->updateData(src, start_vertex, num_vertices); - } - - bool hasVaryingData() const - { - // return varying_stencils_ != NULL; - // TODO(sergey): Check this based on actual topology. - return false; - } - - bool hasFaceVaryingData() const - { - return face_varying_evaluators.size() != 0; - } - - void refine() - { - // Evaluate vertex positions. - BufferDescriptor dst_desc = src_desc_; - dst_desc.offset += num_coarse_vertices_ * src_desc_.stride; - const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( - evaluator_cache_, src_desc_, dst_desc, device_context_); - EVALUATOR::EvalStencils(src_data_, - src_desc_, - src_data_, - dst_desc, - vertex_stencils_, - eval_instance, - device_context_); - // Evaluate varying data. - if (hasVaryingData()) { - BufferDescriptor dst_varying_desc = src_varying_desc_; - dst_varying_desc.offset += num_coarse_vertices_ * src_varying_desc_.stride; - eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( - evaluator_cache_, src_varying_desc_, dst_varying_desc, device_context_); - EVALUATOR::EvalStencils(src_varying_data_, - src_varying_desc_, - src_varying_data_, - dst_varying_desc, - varying_stencils_, - eval_instance, - device_context_); - } - // Evaluate face-varying data. - if (hasFaceVaryingData()) { - for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators) { - face_varying_evaluator->refine(); - } - } - } - - // NOTE: P must point to a memory of at least float[3]*num_patch_coords. - void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *P) - { - RawDataWrapperBuffer<float> P_data(P); - // TODO(sergey): Support interleaved vertex-varying data. - BufferDescriptor P_desc(0, 3, 3); - ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); - const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( - evaluator_cache_, src_desc_, P_desc, device_context_); - EVALUATOR::EvalPatches(src_data_, - src_desc_, - &P_data, - P_desc, - patch_coord_buffer.GetNumVertices(), - &patch_coord_buffer, - patch_table_, - eval_instance, - device_context_); - } - - // NOTE: P, dPdu, dPdv must point to a memory of at least float[3]*num_patch_coords. - void evalPatchesWithDerivatives(const PatchCoord *patch_coord, - const int num_patch_coords, - float *P, - float *dPdu, - float *dPdv) - { - assert(dPdu); - assert(dPdv); - RawDataWrapperBuffer<float> P_data(P); - RawDataWrapperBuffer<float> dPdu_data(dPdu), dPdv_data(dPdv); - // TODO(sergey): Support interleaved vertex-varying data. - BufferDescriptor P_desc(0, 3, 3); - BufferDescriptor dpDu_desc(0, 3, 3), pPdv_desc(0, 3, 3); - ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); - const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( - evaluator_cache_, src_desc_, P_desc, dpDu_desc, pPdv_desc, device_context_); - EVALUATOR::EvalPatches(src_data_, - src_desc_, - &P_data, - P_desc, - &dPdu_data, - dpDu_desc, - &dPdv_data, - pPdv_desc, - patch_coord_buffer.GetNumVertices(), - &patch_coord_buffer, - patch_table_, - eval_instance, - device_context_); - } - - // NOTE: varying must point to a memory of at least float[3]*num_patch_coords. - void evalPatchesVarying(const PatchCoord *patch_coord, - const int num_patch_coords, - float *varying) - { - RawDataWrapperBuffer<float> varying_data(varying); - BufferDescriptor varying_desc(3, 3, 6); - ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); - const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( - evaluator_cache_, src_varying_desc_, varying_desc, device_context_); - EVALUATOR::EvalPatchesVarying(src_varying_data_, - src_varying_desc_, - &varying_data, - varying_desc, - patch_coord_buffer.GetNumVertices(), - &patch_coord_buffer, - patch_table_, - eval_instance, - device_context_); - } - - void evalPatchesFaceVarying(const int face_varying_channel, - const PatchCoord *patch_coord, - const int num_patch_coords, - float face_varying[2]) - { - assert(face_varying_channel >= 0); - assert(face_varying_channel < face_varying_evaluators.size()); - face_varying_evaluators[face_varying_channel]->evalPatches( - patch_coord, num_patch_coords, face_varying); - } - - private: - SRC_VERTEX_BUFFER *src_data_; - SRC_VERTEX_BUFFER *src_varying_data_; - PATCH_TABLE *patch_table_; - BufferDescriptor src_desc_; - BufferDescriptor src_varying_desc_; - - int num_coarse_vertices_; - - const STENCIL_TABLE *vertex_stencils_; - const STENCIL_TABLE *varying_stencils_; - - int face_varying_width_; - vector<FaceVaryingEval *> face_varying_evaluators; - - EvaluatorCache *evaluator_cache_; - DEVICE_CONTEXT *device_context_; -}; - void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords, const int num_patch_coords, - const OpenSubdiv::Far::PatchMap *patch_map, + const PatchMap *patch_map, StackOrHeapPatchCoordArray *array) { array->resize(num_patch_coords); @@ -553,79 +153,50 @@ void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords, } // namespace -// Note: Define as a class instead of typedef to make it possible -// to have anonymous class in opensubdiv_evaluator_internal.h -class CpuEvalOutput : public VolatileEvalOutput<CpuVertexBuffer, - CpuVertexBuffer, - StencilTable, - CpuPatchTable, - CpuEvaluator> { - public: - CpuEvalOutput(const StencilTable *vertex_stencils, - const StencilTable *varying_stencils, - const vector<const StencilTable *> &all_face_varying_stencils, - const int face_varying_width, - const PatchTable *patch_table, - EvaluatorCache *evaluator_cache = NULL) - : VolatileEvalOutput<CpuVertexBuffer, - CpuVertexBuffer, - StencilTable, - CpuPatchTable, - CpuEvaluator>(vertex_stencils, - varying_stencils, - all_face_varying_stencils, - face_varying_width, - patch_table, - evaluator_cache) - { - } -}; - //////////////////////////////////////////////////////////////////////////////// // Evaluator wrapper for anonymous API. -CpuEvalOutputAPI::CpuEvalOutputAPI(CpuEvalOutput *implementation, - OpenSubdiv::Far::PatchMap *patch_map) - : implementation_(implementation), patch_map_(patch_map) +EvalOutputAPI::EvalOutputAPI(EvalOutput *implementation, PatchMap *patch_map) + : patch_map_(patch_map), implementation_(implementation) { } -CpuEvalOutputAPI::~CpuEvalOutputAPI() +EvalOutputAPI::~EvalOutputAPI() { delete implementation_; } -void CpuEvalOutputAPI::setCoarsePositions(const float *positions, - const int start_vertex_index, - const int num_vertices) +void EvalOutputAPI::setCoarsePositions(const float *positions, + const int start_vertex_index, + const int num_vertices) { // TODO(sergey): Add sanity check on indices. implementation_->updateData(positions, start_vertex_index, num_vertices); } -void CpuEvalOutputAPI::setVaryingData(const float *varying_data, - const int start_vertex_index, - const int num_vertices) +void EvalOutputAPI::setVaryingData(const float *varying_data, + const int start_vertex_index, + const int num_vertices) { // TODO(sergey): Add sanity check on indices. implementation_->updateVaryingData(varying_data, start_vertex_index, num_vertices); } -void CpuEvalOutputAPI::setFaceVaryingData(const int face_varying_channel, - const float *face_varying_data, - const int start_vertex_index, - const int num_vertices) +void EvalOutputAPI::setFaceVaryingData(const int face_varying_channel, + const float *face_varying_data, + const int start_vertex_index, + const int num_vertices) { // TODO(sergey): Add sanity check on indices. implementation_->updateFaceVaryingData( face_varying_channel, face_varying_data, start_vertex_index, num_vertices); } -void CpuEvalOutputAPI::setCoarsePositionsFromBuffer(const void *buffer, - const int start_offset, - const int stride, - const int start_vertex_index, - const int num_vertices) +void EvalOutputAPI::setCoarsePositionsFromBuffer(const void *buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices) { // TODO(sergey): Add sanity check on indices. const unsigned char *current_buffer = (unsigned char *)buffer; @@ -638,11 +209,11 @@ void CpuEvalOutputAPI::setCoarsePositionsFromBuffer(const void *buffer, } } -void CpuEvalOutputAPI::setVaryingDataFromBuffer(const void *buffer, - const int start_offset, - const int stride, - const int start_vertex_index, - const int num_vertices) +void EvalOutputAPI::setVaryingDataFromBuffer(const void *buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices) { // TODO(sergey): Add sanity check on indices. const unsigned char *current_buffer = (unsigned char *)buffer; @@ -655,12 +226,12 @@ void CpuEvalOutputAPI::setVaryingDataFromBuffer(const void *buffer, } } -void CpuEvalOutputAPI::setFaceVaryingDataFromBuffer(const int face_varying_channel, - const void *buffer, - const int start_offset, - const int stride, - const int start_vertex_index, - const int num_vertices) +void EvalOutputAPI::setFaceVaryingDataFromBuffer(const int face_varying_channel, + const void *buffer, + const int start_offset, + const int stride, + const int start_vertex_index, + const int num_vertices) { // TODO(sergey): Add sanity check on indices. const unsigned char *current_buffer = (unsigned char *)buffer; @@ -675,17 +246,17 @@ void CpuEvalOutputAPI::setFaceVaryingDataFromBuffer(const int face_varying_chann } } -void CpuEvalOutputAPI::refine() +void EvalOutputAPI::refine() { implementation_->refine(); } -void CpuEvalOutputAPI::evaluateLimit(const int ptex_face_index, - float face_u, - float face_v, - float P[3], - float dPdu[3], - float dPdv[3]) +void EvalOutputAPI::evaluateLimit(const int ptex_face_index, + float face_u, + float face_v, + float P[3], + float dPdu[3], + float dPdv[3]) { assert(face_u >= 0.0f); assert(face_u <= 1.0f); @@ -701,10 +272,10 @@ void CpuEvalOutputAPI::evaluateLimit(const int ptex_face_index, } } -void CpuEvalOutputAPI::evaluateVarying(const int ptex_face_index, - float face_u, - float face_v, - float varying[3]) +void EvalOutputAPI::evaluateVarying(const int ptex_face_index, + float face_u, + float face_v, + float varying[3]) { assert(face_u >= 0.0f); assert(face_u <= 1.0f); @@ -715,11 +286,11 @@ void CpuEvalOutputAPI::evaluateVarying(const int ptex_face_index, implementation_->evalPatchesVarying(&patch_coord, 1, varying); } -void CpuEvalOutputAPI::evaluateFaceVarying(const int face_varying_channel, - const int ptex_face_index, - float face_u, - float face_v, - float face_varying[2]) +void EvalOutputAPI::evaluateFaceVarying(const int face_varying_channel, + const int ptex_face_index, + float face_u, + float face_v, + float face_varying[2]) { assert(face_u >= 0.0f); assert(face_u <= 1.0f); @@ -730,11 +301,11 @@ void CpuEvalOutputAPI::evaluateFaceVarying(const int face_varying_channel, implementation_->evalPatchesFaceVarying(face_varying_channel, &patch_coord, 1, face_varying); } -void CpuEvalOutputAPI::evaluatePatchesLimit(const OpenSubdiv_PatchCoord *patch_coords, - const int num_patch_coords, - float *P, - float *dPdu, - float *dPdv) +void EvalOutputAPI::evaluatePatchesLimit(const OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv) { StackOrHeapPatchCoordArray patch_coords_array; convertPatchCoordsToArray(patch_coords, num_patch_coords, patch_map_, &patch_coords_array); @@ -747,6 +318,73 @@ void CpuEvalOutputAPI::evaluatePatchesLimit(const OpenSubdiv_PatchCoord *patch_c } } +void EvalOutputAPI::getPatchMap(OpenSubdiv_Buffer *patch_map_handles, + OpenSubdiv_Buffer *patch_map_quadtree, + int *min_patch_face, + int *max_patch_face, + int *max_depth, + int *patches_are_triangular) +{ + *min_patch_face = patch_map_->getMinPatchFace(); + *max_patch_face = patch_map_->getMaxPatchFace(); + *max_depth = patch_map_->getMaxDepth(); + *patches_are_triangular = patch_map_->getPatchesAreTriangular(); + + const std::vector<PatchTable::PatchHandle> &handles = patch_map_->getHandles(); + PatchTable::PatchHandle *buffer_handles = static_cast<PatchTable::PatchHandle *>( + patch_map_handles->alloc(patch_map_handles, handles.size())); + memcpy(buffer_handles, &handles[0], sizeof(PatchTable::PatchHandle) * handles.size()); + + const std::vector<PatchMap::QuadNode> &quadtree = patch_map_->nodes(); + PatchMap::QuadNode *buffer_nodes = static_cast<PatchMap::QuadNode *>( + patch_map_quadtree->alloc(patch_map_quadtree, quadtree.size())); + memcpy(buffer_nodes, &quadtree[0], sizeof(PatchMap::QuadNode) * quadtree.size()); +} + +void EvalOutputAPI::fillPatchArraysBuffer(OpenSubdiv_Buffer *patch_arrays_buffer) +{ + implementation_->fillPatchArraysBuffer(patch_arrays_buffer); +} + +void EvalOutputAPI::wrapPatchIndexBuffer(OpenSubdiv_Buffer *patch_index_buffer) +{ + implementation_->wrapPatchIndexBuffer(patch_index_buffer); +} + +void EvalOutputAPI::wrapPatchParamBuffer(OpenSubdiv_Buffer *patch_param_buffer) +{ + implementation_->wrapPatchParamBuffer(patch_param_buffer); +} + +void EvalOutputAPI::wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer) +{ + implementation_->wrapSrcBuffer(src_buffer); +} + +void EvalOutputAPI::fillFVarPatchArraysBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_arrays_buffer) +{ + implementation_->fillFVarPatchArraysBuffer(face_varying_channel, patch_arrays_buffer); +} + +void EvalOutputAPI::wrapFVarPatchIndexBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_index_buffer) +{ + implementation_->wrapFVarPatchIndexBuffer(face_varying_channel, patch_index_buffer); +} + +void EvalOutputAPI::wrapFVarPatchParamBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_param_buffer) +{ + implementation_->wrapFVarPatchParamBuffer(face_varying_channel, patch_param_buffer); +} + +void EvalOutputAPI::wrapFVarSrcBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *src_buffer) +{ + implementation_->wrapFVarSrcBuffer(face_varying_channel, src_buffer); +} + } // namespace opensubdiv } // namespace blender @@ -763,8 +401,15 @@ OpenSubdiv_EvaluatorImpl::~OpenSubdiv_EvaluatorImpl() } OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal( - OpenSubdiv_TopologyRefiner *topology_refiner) + OpenSubdiv_TopologyRefiner *topology_refiner, + eOpenSubdivEvaluator evaluator_type, + OpenSubdiv_EvaluatorCacheImpl *evaluator_cache_descr) { + // Only CPU and GLCompute are implemented at the moment. + if (evaluator_type != OPENSUBDIV_EVALUATOR_CPU && + evaluator_type != OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) { + return NULL; + } using blender::opensubdiv::vector; TopologyRefiner *refiner = topology_refiner->impl->topology_refiner; if (refiner == NULL) { @@ -867,14 +512,34 @@ OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal( } } // Create OpenSubdiv's CPU side evaluator. - // TODO(sergey): Make it possible to use different evaluators. - blender::opensubdiv::CpuEvalOutput *eval_output = new blender::opensubdiv::CpuEvalOutput( - vertex_stencils, varying_stencils, all_face_varying_stencils, 2, patch_table); - OpenSubdiv::Far::PatchMap *patch_map = new PatchMap(*patch_table); + blender::opensubdiv::EvalOutputAPI::EvalOutput *eval_output = nullptr; + + const bool use_gl_evaluator = evaluator_type == OPENSUBDIV_EVALUATOR_GLSL_COMPUTE; + if (use_gl_evaluator) { + blender::opensubdiv::GpuEvalOutput::EvaluatorCache *evaluator_cache = nullptr; + if (evaluator_cache_descr) { + evaluator_cache = static_cast<blender::opensubdiv::GpuEvalOutput::EvaluatorCache *>( + evaluator_cache_descr->eval_cache); + } + + eval_output = new blender::opensubdiv::GpuEvalOutput(vertex_stencils, + varying_stencils, + all_face_varying_stencils, + 2, + patch_table, + evaluator_cache); + } + else { + eval_output = new blender::opensubdiv::CpuEvalOutput( + vertex_stencils, varying_stencils, all_face_varying_stencils, 2, patch_table); + } + + blender::opensubdiv::PatchMap *patch_map = new blender::opensubdiv::PatchMap(*patch_table); // Wrap everything we need into an object which we control from our side. OpenSubdiv_EvaluatorImpl *evaluator_descr; evaluator_descr = new OpenSubdiv_EvaluatorImpl(); - evaluator_descr->eval_output = new blender::opensubdiv::CpuEvalOutputAPI(eval_output, patch_map); + + evaluator_descr->eval_output = new blender::opensubdiv::EvalOutputAPI(eval_output, patch_map); evaluator_descr->patch_map = patch_map; evaluator_descr->patch_table = patch_table; // TOOD(sergey): Look into whether we've got duplicated stencils arrays. diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.h b/intern/opensubdiv/internal/evaluator/evaluator_impl.h index e7ccc9b376a..e24d47cba79 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_impl.h +++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.h @@ -28,14 +28,17 @@ #include "internal/base/memory.h" +#include "opensubdiv_capi_type.h" + +struct OpenSubdiv_Buffer; +struct OpenSubdiv_EvaluatorCacheImpl; struct OpenSubdiv_PatchCoord; struct OpenSubdiv_TopologyRefiner; namespace blender { namespace opensubdiv { -// Anonymous forward declaration of actual evaluator implementation. -class CpuEvalOutput; +class PatchMap; // Wrapper around implementation, which defines API which we are capable to // provide over the implementation. @@ -43,11 +46,15 @@ class CpuEvalOutput; // TODO(sergey): It is almost the same as C-API object, so ideally need to // merge them somehow, but how to do this and keep files with all the templates // and such separate? -class CpuEvalOutputAPI { +class EvalOutputAPI { public: - // NOTE: API object becomes an owner of evaluator. Patch we are referencing. - CpuEvalOutputAPI(CpuEvalOutput *implementation, OpenSubdiv::Far::PatchMap *patch_map); - ~CpuEvalOutputAPI(); + // Anonymous forward declaration of actual evaluator implementation. + class EvalOutput; + + // NOTE: PatchMap is not owned, only referenced. + EvalOutputAPI(EvalOutput *implementation, PatchMap *patch_map); + + ~EvalOutputAPI(); // Set coarse positions from a continuous array of coordinates. void setCoarsePositions(const float *positions, @@ -130,9 +137,47 @@ class CpuEvalOutputAPI { float *dPdu, float *dPdv); + // Fill the output buffers and variables with data from the PatchMap. + void getPatchMap(OpenSubdiv_Buffer *patch_map_handles, + OpenSubdiv_Buffer *patch_map_quadtree, + int *min_patch_face, + int *max_patch_face, + int *max_depth, + int *patches_are_triangular); + + // Copy the patch arrays buffer used by OpenSubDiv for the source data to the given buffer. + void fillPatchArraysBuffer(OpenSubdiv_Buffer *patch_arrays_buffer); + + // Wrap the patch index buffer used by OpenSubDiv for the source data with the given buffer. + void wrapPatchIndexBuffer(OpenSubdiv_Buffer *patch_index_buffer); + + // Wrap the patch param buffer used by OpenSubDiv for the source data with the given buffer. + void wrapPatchParamBuffer(OpenSubdiv_Buffer *patch_param_buffer); + + // Wrap the buffer used by OpenSubDiv for the source data with the given buffer. + void wrapSrcBuffer(OpenSubdiv_Buffer *src_buffer); + + // Copy the patch arrays buffer used by OpenSubDiv for the face varying channel with the given + // buffer. + void fillFVarPatchArraysBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_arrays_buffer); + + // Wrap the patch index buffer used by OpenSubDiv for the face varying channel with the given + // buffer. + void wrapFVarPatchIndexBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_index_buffer); + + // Wrap the patch param buffer used by OpenSubDiv for the face varying channel with the given + // buffer. + void wrapFVarPatchParamBuffer(const int face_varying_channel, + OpenSubdiv_Buffer *patch_param_buffer); + + // Wrap thebuffer used by OpenSubDiv for the face varying channel with the given buffer. + void wrapFVarSrcBuffer(const int face_varying_channel, OpenSubdiv_Buffer *src_buffer); + protected: - CpuEvalOutput *implementation_; - OpenSubdiv::Far::PatchMap *patch_map_; + PatchMap *patch_map_; + EvalOutput *implementation_; }; } // namespace opensubdiv @@ -143,15 +188,17 @@ struct OpenSubdiv_EvaluatorImpl { OpenSubdiv_EvaluatorImpl(); ~OpenSubdiv_EvaluatorImpl(); - blender::opensubdiv::CpuEvalOutputAPI *eval_output; - const OpenSubdiv::Far::PatchMap *patch_map; + blender::opensubdiv::EvalOutputAPI *eval_output; + const blender::opensubdiv::PatchMap *patch_map; const OpenSubdiv::Far::PatchTable *patch_table; MEM_CXX_CLASS_ALLOC_FUNCS("OpenSubdiv_EvaluatorImpl"); }; OpenSubdiv_EvaluatorImpl *openSubdiv_createEvaluatorInternal( - struct OpenSubdiv_TopologyRefiner *topology_refiner); + struct OpenSubdiv_TopologyRefiner *topology_refiner, + eOpenSubdivEvaluator evaluator_type, + OpenSubdiv_EvaluatorCacheImpl *evaluator_cache_descr); void openSubdiv_deleteEvaluatorInternal(OpenSubdiv_EvaluatorImpl *evaluator); diff --git a/intern/opensubdiv/internal/evaluator/patch_map.cc b/intern/opensubdiv/internal/evaluator/patch_map.cc new file mode 100644 index 00000000000..2ebdb922326 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/patch_map.cc @@ -0,0 +1,212 @@ +// Original code 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. +// +// Modifications copyright 2021 Blender Foundation. All rights reserved. + +#include "internal/evaluator/patch_map.h" + +using OpenSubdiv::Far::ConstPatchParamArray; +using OpenSubdiv::Far::Index; +using OpenSubdiv::Far::PatchParam; +using OpenSubdiv::Far::PatchParamTable; +using OpenSubdiv::Far::PatchTable; + +namespace blender { +namespace opensubdiv { + +// +// Inline quadtree assembly methods used by the constructor: +// + +// sets all the children to point to the patch of given index +inline void PatchMap::QuadNode::SetChildren(int index) +{ + + for (int i = 0; i < 4; ++i) { + children[i].isSet = true; + children[i].isLeaf = true; + children[i].index = index; + } +} + +// sets the child in "quadrant" to point to the node or patch of the given index +inline void PatchMap::QuadNode::SetChild(int quadrant, int index, bool isLeaf) +{ + + assert(!children[quadrant].isSet); + children[quadrant].isSet = true; + children[quadrant].isLeaf = isLeaf; + children[quadrant].index = index; +} + +inline void PatchMap::assignRootNode(QuadNode *node, int index) +{ + + // Assign the given index to all children of the node (all leaves) + node->SetChildren(index); +} + +inline PatchMap::QuadNode *PatchMap::assignLeafOrChildNode(QuadNode *node, + bool isLeaf, + int quadrant, + int index) +{ + + // Assign the node given if it is a leaf node, otherwise traverse + // the node -- creating/assigning a new child node if needed + + if (isLeaf) { + node->SetChild(quadrant, index, true); + return node; + } + if (node->children[quadrant].isSet) { + return &_quadtree[node->children[quadrant].index]; + } + else { + int newChildNodeIndex = (int)_quadtree.size(); + _quadtree.push_back(QuadNode()); + node->SetChild(quadrant, newChildNodeIndex, false); + return &_quadtree[newChildNodeIndex]; + } +} + +// +// Constructor and initialization methods for the handles and quadtree: +// +PatchMap::PatchMap(PatchTable const &patchTable) + : _minPatchFace(-1), _maxPatchFace(-1), _maxDepth(0) +{ + + _patchesAreTriangular = patchTable.GetVaryingPatchDescriptor().GetNumControlVertices() == 3; + + if (patchTable.GetNumPatchesTotal() > 0) { + initializeHandles(patchTable); + initializeQuadtree(patchTable); + } +} + +void PatchMap::initializeHandles(PatchTable const &patchTable) +{ + + // + // Populate the vector of patch Handles. Keep track of the min and max + // face indices to allocate resources accordingly and limit queries: + // + _minPatchFace = (int)patchTable.GetPatchParamTable()[0].GetFaceId(); + _maxPatchFace = _minPatchFace; + + int numArrays = (int)patchTable.GetNumPatchArrays(); + int numPatches = (int)patchTable.GetNumPatchesTotal(); + + _handles.resize(numPatches); + + for (int pArray = 0, handleIndex = 0; pArray < numArrays; ++pArray) { + + ConstPatchParamArray params = patchTable.GetPatchParams(pArray); + + int patchSize = patchTable.GetPatchArrayDescriptor(pArray).GetNumControlVertices(); + + for (Index j = 0; j < patchTable.GetNumPatches(pArray); ++j, ++handleIndex) { + + Handle &h = _handles[handleIndex]; + + h.arrayIndex = pArray; + h.patchIndex = handleIndex; + h.vertIndex = j * patchSize; + + int patchFaceId = params[j].GetFaceId(); + _minPatchFace = std::min(_minPatchFace, patchFaceId); + _maxPatchFace = std::max(_maxPatchFace, patchFaceId); + } + } +} + +void PatchMap::initializeQuadtree(PatchTable const &patchTable) +{ + + // + // Reserve quadtree nodes for the worst case and prune later. Set the + // initial size to accomodate the root node of each patch face: + // + int nPatchFaces = (_maxPatchFace - _minPatchFace) + 1; + + int nHandles = (int)_handles.size(); + + _quadtree.reserve(nPatchFaces + nHandles); + _quadtree.resize(nPatchFaces); + + PatchParamTable const ¶ms = patchTable.GetPatchParamTable(); + + for (int handle = 0; handle < nHandles; ++handle) { + + PatchParam const ¶m = params[handle]; + + int depth = param.GetDepth(); + int rootDepth = param.NonQuadRoot(); + + _maxDepth = std::max(_maxDepth, depth); + + QuadNode *node = &_quadtree[param.GetFaceId() - _minPatchFace]; + + if (depth == rootDepth) { + assignRootNode(node, handle); + continue; + } + + if (!_patchesAreTriangular) { + // Use the UV bits of the PatchParam directly for quad patches: + int u = param.GetU(); + int v = param.GetV(); + + for (int j = rootDepth + 1; j <= depth; ++j) { + int uBit = (u >> (depth - j)) & 1; + int vBit = (v >> (depth - j)) & 1; + + int quadrant = (vBit << 1) | uBit; + + node = assignLeafOrChildNode(node, (j == depth), quadrant, handle); + } + } + else { + // Use an interior UV point of triangles to identify quadrants: + double u = 0.25; + double v = 0.25; + param.UnnormalizeTriangle(u, v); + + double median = 0.5; + bool triRotated = false; + + for (int j = rootDepth + 1; j <= depth; ++j, median *= 0.5) { + int quadrant = transformUVToTriQuadrant(median, u, v, triRotated); + + node = assignLeafOrChildNode(node, (j == depth), quadrant, handle); + } + } + } + + // Swap the Node vector with a copy to reduce worst case memory allocation: + QuadTree tmpTree = _quadtree; + _quadtree.swap(tmpTree); +} + +} // namespace opensubdiv +} // namespace blender diff --git a/intern/opensubdiv/internal/evaluator/patch_map.h b/intern/opensubdiv/internal/evaluator/patch_map.h new file mode 100644 index 00000000000..af804d6ca71 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/patch_map.h @@ -0,0 +1,264 @@ +// Original code 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. +// +// Modifications copyright 2021 Blender Foundation. All rights reserved. + +#ifndef OPENSUBDIV_PATCH_MAP_H_ +#define OPENSUBDIV_PATCH_MAP_H_ + +#include <opensubdiv/far/patchTable.h> + +namespace blender { +namespace opensubdiv { + +/// \brief An quadtree-based map connecting coarse faces to their sub-patches +/// +/// PatchTable::PatchArrays contain lists of patches that represent the limit +/// surface of a mesh, sorted by their topological type. These arrays break the +/// connection between coarse faces and their sub-patches. +/// +/// The PatchMap provides a quad-tree based lookup structure that, given a singular +/// parametric location, can efficiently return a handle to the sub-patch that +/// contains this location. +/// +class PatchMap { + public: + // Quadtree node with 4 children, tree is just a vector of nodes + struct QuadNode { + QuadNode() + { + std::memset(this, 0, sizeof(QuadNode)); + } + + struct Child { + unsigned int isSet : 1; // true if the child has been set + unsigned int isLeaf : 1; // true if the child is a QuadNode + unsigned int index : 30; // child index (either QuadNode or Handle) + }; + + // sets all the children to point to the patch of given index + void SetChildren(int index); + + // sets the child in "quadrant" to point to the node or patch of the given index + void SetChild(int quadrant, int index, bool isLeaf); + + Child children[4]; + }; + + typedef OpenSubdiv::Far::PatchTable::PatchHandle Handle; + + /// \brief Constructor + /// + /// @param patchTable A valid PatchTable + /// + PatchMap(OpenSubdiv::Far::PatchTable const &patchTable); + + /// \brief Returns a handle to the sub-patch of the face at the given (u,v). + /// Note that the patch face ID corresponds to potentially quadrangulated + /// face indices and not the base face indices (see Far::PtexIndices for more + /// details). + /// + /// @param patchFaceId The index of the patch (Ptex) face + /// + /// @param u Local u parameter + /// + /// @param v Local v parameter + /// + /// @return A patch handle or 0 if the face is not supported (index + /// out of bounds) or is tagged as a hole + /// + Handle const *FindPatch(int patchFaceId, double u, double v) const; + + int getMinPatchFace() const + { + return _minPatchFace; + } + + int getMaxPatchFace() const + { + return _maxPatchFace; + } + + int getMaxDepth() const + { + return _maxDepth; + } + + bool getPatchesAreTriangular() const + { + return _patchesAreTriangular; + } + + const std::vector<Handle> &getHandles() + { + return _handles; + } + + const std::vector<QuadNode> &nodes() + { + return _quadtree; + } + + private: + void initializeHandles(OpenSubdiv::Far::PatchTable const &patchTable); + void initializeQuadtree(OpenSubdiv::Far::PatchTable const &patchTable); + + typedef std::vector<QuadNode> QuadTree; + + // Internal methods supporting quadtree construction and queries + void assignRootNode(QuadNode *node, int index); + QuadNode *assignLeafOrChildNode(QuadNode *node, bool isLeaf, int quad, int index); + + template<class T> static int transformUVToQuadQuadrant(T const &median, T &u, T &v); + template<class T> + static int transformUVToTriQuadrant(T const &median, T &u, T &v, bool &rotated); + + private: + bool _patchesAreTriangular; // tri and quad assembly and search requirements differ + + int _minPatchFace; // minimum patch face index supported by the map + int _maxPatchFace; // maximum patch face index supported by the map + int _maxDepth; // maximum depth of a patch in the tree + + std::vector<Handle> _handles; // all the patches in the PatchTable + std::vector<QuadNode> _quadtree; // quadtree nodes +}; + +// +// Given a median value for both U and V, these methods transform a (u,v) pair +// into the quadrant that contains them and returns the quadrant index. +// +// Quadrant indexing for tri and quad patches -- consistent with PatchParam's +// usage of UV bits: +// +// (0,1) o-----o-----o (1,1) (0,1) o (1,0) o-----o-----o (0,0) +// | | | |\ \ 1 |\ 0 | +// | 2 | 3 | | \ \ | \ | +// | | | | 2 \ \| 3 \| +// o-----o-----o o-----o o-----o +// | | | |\ 3 |\ \ 2 | +// | 0 | 1 | | \ | \ \ | +// | | | | 0 \| 1 \ \| +// (0,0) o-----o-----o (1,0) (0,0) o-----o-----o (1,0) o (0,1) +// +// The triangular case also takes and returns/affects the rotation of the +// quadrant being searched and identified (quadrant 3 imparts a rotation). +// +template<class T> inline int PatchMap::transformUVToQuadQuadrant(T const &median, T &u, T &v) +{ + + int uHalf = (u >= median); + if (uHalf) + u -= median; + + int vHalf = (v >= median); + if (vHalf) + v -= median; + + return (vHalf << 1) | uHalf; +} + +template<class T> +int inline PatchMap::transformUVToTriQuadrant(T const &median, T &u, T &v, bool &rotated) +{ + + if (!rotated) { + if (u >= median) { + u -= median; + return 1; + } + if (v >= median) { + v -= median; + return 2; + } + if ((u + v) >= median) { + rotated = true; + return 3; + } + return 0; + } + else { + if (u < median) { + v -= median; + return 1; + } + if (v < median) { + u -= median; + return 2; + } + u -= median; + v -= median; + if ((u + v) < median) { + rotated = false; + return 3; + } + return 0; + } +} + +/// Returns a handle to the sub-patch of the face at the given (u,v). +inline PatchMap::Handle const *PatchMap::FindPatch(int faceid, double u, double v) const +{ + + // + // Reject patch faces not supported by this map, or those corresponding + // to holes or otherwise unassigned (the root node for a patch will + // have all or no quadrants set): + // + if ((faceid < _minPatchFace) || (faceid > _maxPatchFace)) + return 0; + + QuadNode const *node = &_quadtree[faceid - _minPatchFace]; + + if (!node->children[0].isSet) + return 0; + + // + // Search the tree for the sub-patch containing the given (u,v) + // + assert((u >= 0.0) && (u <= 1.0) && (v >= 0.0) && (v <= 1.0)); + + double median = 0.5; + bool triRotated = false; + + for (int depth = 0; depth <= _maxDepth; ++depth, median *= 0.5) { + + int quadrant = _patchesAreTriangular ? transformUVToTriQuadrant(median, u, v, triRotated) : + transformUVToQuadQuadrant(median, u, v); + + // holes should have been rejected at the root node of the face + assert(node->children[quadrant].isSet); + + if (node->children[quadrant].isLeaf) { + return &_handles[node->children[quadrant].index]; + } + else { + node = &_quadtree[node->children[quadrant].index]; + } + } + assert(0); + return 0; +} +} // namespace opensubdiv +} // namespace blender + +#endif // OPENSUBDIV_PATCH_MAP_H_ diff --git a/intern/opensubdiv/opensubdiv_evaluator_capi.h b/intern/opensubdiv/opensubdiv_evaluator_capi.h index b860ae8db2e..1afd3e966f6 100644 --- a/intern/opensubdiv/opensubdiv_evaluator_capi.h +++ b/intern/opensubdiv/opensubdiv_evaluator_capi.h @@ -19,6 +19,10 @@ #ifndef OPENSUBDIV_EVALUATOR_CAPI_H_ #define OPENSUBDIV_EVALUATOR_CAPI_H_ +#include <stdint.h> // for uint64_t + +#include "opensubdiv_capi_type.h" + #ifdef __cplusplus extern "C" { #endif @@ -27,6 +31,38 @@ struct OpenSubdiv_EvaluatorInternal; struct OpenSubdiv_PatchCoord; struct OpenSubdiv_TopologyRefiner; +// Callback type for doing input/output operations on buffers. +// Useful to abstract GPU buffers. +typedef struct OpenSubdiv_Buffer { + // Bind the buffer to the GPU. + void (*bind_gpu)(const struct OpenSubdiv_Buffer *buffer); + + // Allocate the buffer directly on the host for the given size in bytes. This has to return + // a pointer to the newly allocated memory. + void *(*alloc)(const struct OpenSubdiv_Buffer *buffer, const unsigned int size); + + // Allocate the buffer directly on the device for the given size in bytes. + void (*device_alloc)(const struct OpenSubdiv_Buffer *buffer, const unsigned int size); + + // Update the given range of the buffer with new data. + void (*device_update)(const struct OpenSubdiv_Buffer *buffer, + unsigned int start, + unsigned int len, + const void *data); + + // Wrap an existing GPU buffer, given its device handle, into the client's buffer type for + // read-only use. + void (*wrap_device_handle)(const struct OpenSubdiv_Buffer *buffer, uint64_t device_ptr); + + // Offset in the buffer where the data starts, if a single buffer is used for multiple data + // channels. + int buffer_offset; + + // Pointer to the client buffer data, which is modified or initialized through the various + // callbacks. + void *data; +} OpenSubdiv_Buffer; + typedef struct OpenSubdiv_Evaluator { // Set coarse positions from a continuous array of coordinates. void (*setCoarsePositions)(struct OpenSubdiv_Evaluator *evaluator, @@ -122,15 +158,78 @@ typedef struct OpenSubdiv_Evaluator { float *dPdu, float *dPdv); + // Copy the patch map to the given buffers, and output some topology information. + void (*getPatchMap)(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *patch_map_handles, + struct OpenSubdiv_Buffer *patch_map_quadtree, + int *min_patch_face, + int *max_patch_face, + int *max_depth, + int *patches_are_triangular); + + // Fill the given buffer with data from the evaluator's patch array buffer. + void (*fillPatchArraysBuffer)(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *patch_array_buffer); + + // Fill the given buffer with data from the evaluator's patch index buffer. + void (*wrapPatchIndexBuffer)(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *patch_index_buffer); + + // Fill the given buffer with data from the evaluator's patch parameter buffer. + void (*wrapPatchParamBuffer)(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *patch_param_buffer); + + // Fill the given buffer with data from the evaluator's source buffer. + void (*wrapSrcBuffer)(struct OpenSubdiv_Evaluator *evaluator, + struct OpenSubdiv_Buffer *src_buffer); + + // Fill the given buffer with data from the evaluator's face varying patch array buffer. + void (*fillFVarPatchArraysBuffer)(struct OpenSubdiv_Evaluator *evaluator, + const int face_varying_channel, + struct OpenSubdiv_Buffer *patch_array_buffer); + + // Fill the given buffer with data from the evaluator's face varying patch index buffer. + void (*wrapFVarPatchIndexBuffer)(struct OpenSubdiv_Evaluator *evaluator, + const int face_varying_channel, + struct OpenSubdiv_Buffer *patch_index_buffer); + + // Fill the given buffer with data from the evaluator's face varying patch parameter buffer. + void (*wrapFVarPatchParamBuffer)(struct OpenSubdiv_Evaluator *evaluator, + const int face_varying_channel, + struct OpenSubdiv_Buffer *patch_param_buffer); + + // Fill the given buffer with data from the evaluator's face varying source buffer. + void (*wrapFVarSrcBuffer)(struct OpenSubdiv_Evaluator *evaluator, + const int face_varying_channel, + struct OpenSubdiv_Buffer *src_buffer); + // Implementation of the evaluator. struct OpenSubdiv_EvaluatorImpl *impl; + + // Type of the evaluator. + eOpenSubdivEvaluator type; } OpenSubdiv_Evaluator; +typedef struct OpenSubdiv_EvaluatorCache { + // Implementation of the evaluator cache. + struct OpenSubdiv_EvaluatorCacheImpl *impl; +} OpenSubdiv_EvaluatorCache; + OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner( - struct OpenSubdiv_TopologyRefiner *topology_refiner); + struct OpenSubdiv_TopologyRefiner *topology_refiner, + eOpenSubdivEvaluator evaluator_type, + OpenSubdiv_EvaluatorCache *evaluator_cache); void openSubdiv_deleteEvaluator(OpenSubdiv_Evaluator *evaluator); +OpenSubdiv_EvaluatorCache *openSubdiv_createEvaluatorCache(eOpenSubdivEvaluator evaluator_type); + +void openSubdiv_deleteEvaluatorCache(OpenSubdiv_EvaluatorCache *evaluator_cache); + +// Return the GLSL source code from the OpenSubDiv library used for patch evaluation. +// This function is not thread-safe. +const char *openSubdiv_getGLSLPatchBasisSource(void); + #ifdef __cplusplus } #endif |