Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Author: Sergey Sharybin. */
|
||||
|
||||
#include "internal/evaluator/eval_output.h"
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
bool is_adaptive(const CpuPatchTable *patch_table)
|
||||
{
|
||||
return patch_table->GetPatchArrayBuffer()[0].GetDescriptor().IsAdaptive();
|
||||
}
|
||||
|
||||
#ifndef WITH_WEB
|
||||
bool is_adaptive(const GPUPatchTable *patch_table)
|
||||
{
|
||||
return patch_table->GetPatchArrays()[0].GetDescriptor().IsAdaptive();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
668
blender-5.2.0/intern/opensubdiv/internal/evaluator/eval_output.h
Normal file
668
blender-5.2.0/intern/opensubdiv/internal/evaluator/eval_output.h
Normal file
@@ -0,0 +1,668 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Author: Sergey Sharybin. */
|
||||
|
||||
#ifndef OPENSUBDIV_EVAL_OUTPUT_H_
|
||||
#define OPENSUBDIV_EVAL_OUTPUT_H_
|
||||
|
||||
#include <opensubdiv/osd/cpuPatchTable.h>
|
||||
#include <opensubdiv/osd/mesh.h>
|
||||
#include <opensubdiv/osd/types.h>
|
||||
|
||||
#include "opensubdiv_evaluator.hh"
|
||||
#include "opensubdiv_evaluator_capi.hh"
|
||||
|
||||
#ifndef WITH_WEB
|
||||
# include "gpu_patch_table.hh"
|
||||
#endif
|
||||
|
||||
using OpenSubdiv::Far::PatchTable;
|
||||
using OpenSubdiv::Far::StencilTable;
|
||||
using OpenSubdiv::Osd::BufferDescriptor;
|
||||
using OpenSubdiv::Osd::CpuPatchTable;
|
||||
using OpenSubdiv::Osd::PatchCoord;
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
|
||||
// Base class for the implementation of the evaluators.
|
||||
class EvalOutputAPI::EvalOutput {
|
||||
public:
|
||||
virtual ~EvalOutput() = default;
|
||||
|
||||
virtual void updateSettings(const OpenSubdiv_EvaluatorSettings *settings) = 0;
|
||||
|
||||
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 updateVertexData(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;
|
||||
|
||||
// NOTE: vertex_data must point to a memory of at least float*num_vertex_data.
|
||||
virtual void evalPatchesVertexData(const PatchCoord *patch_coord,
|
||||
const int num_patch_coords,
|
||||
float *vertex_data) = 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 overridden through VolatileEvalOutput.
|
||||
|
||||
virtual gpu::StorageBuf *create_patch_arrays_buf()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual gpu::StorageBuf *get_patch_index_buf()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual gpu::StorageBuf *get_patch_param_buf()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual gpu::VertBuf *get_source_buf()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual gpu::VertBuf *get_source_data_buf()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual gpu::StorageBuf *create_face_varying_patch_array_buf(const int /*face_varying_channel*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual gpu::StorageBuf *get_face_varying_patch_index_buf(const int /*face_varying_channel*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual gpu::StorageBuf *get_face_varying_patch_param_buf(const int /*face_varying_channel*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
virtual gpu::VertBuf *get_face_varying_source_buf(const int /*face_varying_channel*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual int get_face_varying_source_offset(const int /*face_varying_channel*/) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool hasVertexData() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 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_;
|
||||
}
|
||||
|
||||
gpu::VertBuf *get_vertex_buffer()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// 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(const CpuPatchTable *patch_table);
|
||||
#ifndef WITH_WEB
|
||||
bool is_adaptive(const GPUPatchTable *patch_table);
|
||||
#endif
|
||||
|
||||
template<typename EVAL_VERTEX_BUFFER,
|
||||
typename STENCIL_TABLE,
|
||||
typename PATCH_TABLE,
|
||||
typename EVALUATOR,
|
||||
typename DEVICE_CONTEXT = void>
|
||||
class FaceVaryingVolatileEval {
|
||||
public:
|
||||
using EvaluatorCache = OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR>;
|
||||
|
||||
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;
|
||||
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);
|
||||
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 get_face_varying_source_offset() 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:
|
||||
using EvaluatorCache = OpenSubdiv::Osd::EvaluatorCacheT<EVALUATOR>;
|
||||
using FaceVaryingEval = FaceVaryingVolatileEval<EVAL_VERTEX_BUFFER,
|
||||
STENCIL_TABLE,
|
||||
PATCH_TABLE,
|
||||
EVALUATOR,
|
||||
DEVICE_CONTEXT>;
|
||||
|
||||
VolatileEvalOutput(const StencilTable *vertex_stencils,
|
||||
const StencilTable *varying_stencils,
|
||||
const std::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_vertex_data_(NULL),
|
||||
src_desc_(0, 3, 3),
|
||||
src_varying_desc_(0, 3, 3),
|
||||
src_vertex_data_desc_(0, 0, 0),
|
||||
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 src_vertex_data_;
|
||||
delete patch_table_;
|
||||
delete vertex_stencils_;
|
||||
delete varying_stencils_;
|
||||
for (FaceVaryingEval *face_varying_evaluator : face_varying_evaluators_) {
|
||||
delete face_varying_evaluator;
|
||||
}
|
||||
}
|
||||
|
||||
void updateSettings(const OpenSubdiv_EvaluatorSettings *settings) override
|
||||
{
|
||||
// Optionally allocate additional data to be subdivided like vertex coordinates.
|
||||
if (settings->num_vertex_data != src_vertex_data_desc_.length) {
|
||||
delete src_vertex_data_;
|
||||
if (settings->num_vertex_data > 0) {
|
||||
src_vertex_data_ = SRC_VERTEX_BUFFER::Create(
|
||||
settings->num_vertex_data, src_data_->GetNumVertices(), device_context_);
|
||||
}
|
||||
else {
|
||||
src_vertex_data_ = NULL;
|
||||
}
|
||||
src_vertex_data_desc_ = BufferDescriptor(
|
||||
0, settings->num_vertex_data, settings->num_vertex_data);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 updateVertexData(const float *src, int start_vertex, int num_vertices) override
|
||||
{
|
||||
src_vertex_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;
|
||||
}
|
||||
|
||||
bool hasVertexData() const override
|
||||
{
|
||||
return src_vertex_data_ != nullptr;
|
||||
}
|
||||
|
||||
void refine() override
|
||||
{
|
||||
// Evaluate vertex positions.
|
||||
BufferDescriptor dst_desc = src_desc_;
|
||||
dst_desc.offset += num_coarse_vertices_ * src_desc_.stride;
|
||||
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 smoothly interpolated vertex data.
|
||||
if (src_vertex_data_) {
|
||||
BufferDescriptor dst_vertex_data_desc = src_vertex_data_desc_;
|
||||
dst_vertex_data_desc.offset += num_coarse_vertices_ * src_vertex_data_desc_.stride;
|
||||
EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
|
||||
evaluator_cache_, src_vertex_data_desc_, dst_vertex_data_desc, device_context_);
|
||||
EVALUATOR::EvalStencils(src_vertex_data_,
|
||||
src_vertex_data_desc_,
|
||||
src_vertex_data_,
|
||||
dst_vertex_data_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);
|
||||
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);
|
||||
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);
|
||||
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_);
|
||||
}
|
||||
|
||||
// NOTE: data must point to a memory of at least float*num_vertex_data.
|
||||
void evalPatchesVertexData(const PatchCoord *patch_coord,
|
||||
const int num_patch_coords,
|
||||
float *data) override
|
||||
{
|
||||
RawDataWrapperBuffer<float> vertex_data(data);
|
||||
BufferDescriptor vertex_desc(0, src_vertex_data_desc_.length, src_vertex_data_desc_.length);
|
||||
ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords);
|
||||
EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>(
|
||||
evaluator_cache_, src_vertex_data_desc_, vertex_desc, device_context_);
|
||||
EVALUATOR::EvalPatches(src_vertex_data_,
|
||||
src_vertex_data_desc_,
|
||||
&vertex_data,
|
||||
vertex_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_;
|
||||
}
|
||||
|
||||
SRC_VERTEX_BUFFER *getSrcVertexDataBuffer() const
|
||||
{
|
||||
return src_vertex_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 get_face_varying_source_offset(const int face_varying_channel) const override
|
||||
{
|
||||
return face_varying_evaluators_[face_varying_channel]->get_face_varying_source_offset();
|
||||
}
|
||||
|
||||
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_;
|
||||
SRC_VERTEX_BUFFER *src_vertex_data_;
|
||||
PATCH_TABLE *patch_table_;
|
||||
BufferDescriptor src_desc_;
|
||||
BufferDescriptor src_varying_desc_;
|
||||
BufferDescriptor src_vertex_data_desc_;
|
||||
|
||||
int num_coarse_vertices_;
|
||||
|
||||
const STENCIL_TABLE *vertex_stencils_;
|
||||
const STENCIL_TABLE *varying_stencils_;
|
||||
|
||||
int face_varying_width_;
|
||||
std::vector<FaceVaryingEval *> face_varying_evaluators_;
|
||||
|
||||
EvaluatorCache *evaluator_cache_;
|
||||
DEVICE_CONTEXT *device_context_;
|
||||
};
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
|
||||
#endif // OPENSUBDIV_EVAL_OUTPUT_H_
|
||||
@@ -0,0 +1,9 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Author: Sergey Sharybin. */
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
@@ -0,0 +1,52 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* 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::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 std::vector<const StencilTable *> &all_face_varying_stencils,
|
||||
const int face_varying_width,
|
||||
const PatchTable *patch_table,
|
||||
EvaluatorCache *evaluator_cache = nullptr)
|
||||
: VolatileEvalOutput<CpuVertexBuffer,
|
||||
CpuVertexBuffer,
|
||||
StencilTable,
|
||||
CpuPatchTable,
|
||||
CpuEvaluator>(vertex_stencils,
|
||||
varying_stencils,
|
||||
all_face_varying_stencils,
|
||||
face_varying_width,
|
||||
patch_table,
|
||||
evaluator_cache)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
|
||||
#endif // OPENSUBDIV_EVAL_OUTPUT_CPU_H_
|
||||
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Author: Sergey Sharybin. */
|
||||
|
||||
#include "internal/evaluator/eval_output_gpu.h"
|
||||
|
||||
#include "opensubdiv_evaluator.hh"
|
||||
|
||||
#include "gpu_patch_table.hh"
|
||||
|
||||
using OpenSubdiv::Osd::PatchArray;
|
||||
using OpenSubdiv::Osd::PatchArrayVector;
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
|
||||
static gpu::StorageBuf *create_patch_array_buffer(const PatchArrayVector &patch_arrays)
|
||||
{
|
||||
const size_t patch_array_size = sizeof(PatchArray);
|
||||
const size_t patch_array_byte_size = patch_array_size * patch_arrays.size();
|
||||
gpu::StorageBuf *storage_buf = GPU_storagebuf_create_ex(
|
||||
patch_array_byte_size, patch_arrays.data(), GPU_USAGE_STATIC, "osd_patch_array");
|
||||
return storage_buf;
|
||||
}
|
||||
|
||||
GpuEvalOutput::GpuEvalOutput(const StencilTable *vertex_stencils,
|
||||
const StencilTable *varying_stencils,
|
||||
const std::vector<const StencilTable *> &all_face_varying_stencils,
|
||||
const int face_varying_width,
|
||||
const PatchTable *patch_table,
|
||||
VolatileEvalOutput::EvaluatorCache *evaluator_cache)
|
||||
: VolatileEvalOutput<GPUVertexBuffer,
|
||||
GPUVertexBuffer,
|
||||
GPUStencilTableSSBO,
|
||||
GPUPatchTable,
|
||||
GPUComputeEvaluator>(vertex_stencils,
|
||||
varying_stencils,
|
||||
all_face_varying_stencils,
|
||||
face_varying_width,
|
||||
patch_table,
|
||||
evaluator_cache)
|
||||
{
|
||||
}
|
||||
|
||||
gpu::StorageBuf *GpuEvalOutput::create_patch_arrays_buf()
|
||||
{
|
||||
GPUPatchTable *patch_table = getPatchTable();
|
||||
return create_patch_array_buffer(patch_table->GetPatchArrays());
|
||||
}
|
||||
|
||||
gpu::StorageBuf *GpuEvalOutput::create_face_varying_patch_array_buf(const int face_varying_channel)
|
||||
{
|
||||
GPUPatchTable *patch_table = getFVarPatchTable(face_varying_channel);
|
||||
return create_patch_array_buffer(patch_table->GetFVarPatchArrays(face_varying_channel));
|
||||
}
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Author: Sergey Sharybin. */
|
||||
|
||||
#ifndef OPENSUBDIV_EVAL_OUTPUT_GPU_H_
|
||||
#define OPENSUBDIV_EVAL_OUTPUT_GPU_H_
|
||||
|
||||
#include "internal/evaluator/eval_output.h"
|
||||
#include "internal/evaluator/gpu_compute_evaluator.h"
|
||||
#include "internal/evaluator/gpu_patch_table.hh"
|
||||
|
||||
#include <opensubdiv/osd/glPatchTable.h>
|
||||
#include <opensubdiv/osd/glVertexBuffer.h>
|
||||
|
||||
#include "gpu_vertex_buffer_wrapper.hh"
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
|
||||
class GpuEvalOutput : public VolatileEvalOutput<GPUVertexBuffer,
|
||||
GPUVertexBuffer,
|
||||
GPUStencilTableSSBO,
|
||||
GPUPatchTable,
|
||||
GPUComputeEvaluator> {
|
||||
public:
|
||||
GpuEvalOutput(const StencilTable *vertex_stencils,
|
||||
const StencilTable *varying_stencils,
|
||||
const std::vector<const StencilTable *> &all_face_varying_stencils,
|
||||
const int face_varying_width,
|
||||
const PatchTable *patch_table,
|
||||
EvaluatorCache *evaluator_cache = nullptr);
|
||||
|
||||
gpu::StorageBuf *create_patch_arrays_buf() override;
|
||||
|
||||
gpu::StorageBuf *get_patch_index_buf() override
|
||||
{
|
||||
return getPatchTable()->GetPatchIndexBuffer();
|
||||
}
|
||||
|
||||
gpu::StorageBuf *get_patch_param_buf() override
|
||||
{
|
||||
return getPatchTable()->GetPatchParamBuffer();
|
||||
}
|
||||
|
||||
gpu::VertBuf *get_source_buf() override
|
||||
{
|
||||
return getSrcBuffer()->get_vertex_buffer();
|
||||
}
|
||||
|
||||
gpu::VertBuf *get_source_data_buf() override
|
||||
{
|
||||
return getSrcVertexDataBuffer()->get_vertex_buffer();
|
||||
}
|
||||
|
||||
gpu::StorageBuf *create_face_varying_patch_array_buf(const int face_varying_channel) override;
|
||||
|
||||
gpu::StorageBuf *get_face_varying_patch_index_buf(const int face_varying_channel) override
|
||||
{
|
||||
GPUPatchTable *patch_table = getFVarPatchTable(face_varying_channel);
|
||||
return patch_table->GetFVarPatchIndexBuffer(face_varying_channel);
|
||||
}
|
||||
|
||||
gpu::StorageBuf *get_face_varying_patch_param_buf(const int face_varying_channel) override
|
||||
{
|
||||
GPUPatchTable *patch_table = getFVarPatchTable(face_varying_channel);
|
||||
return patch_table->GetFVarPatchParamBuffer(face_varying_channel);
|
||||
}
|
||||
|
||||
gpu::VertBuf *get_face_varying_source_buf(const int face_varying_channel) override
|
||||
{
|
||||
GPUVertexBuffer *vertex_buffer = getFVarSrcBuffer(face_varying_channel);
|
||||
return vertex_buffer->get_vertex_buffer();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
|
||||
#endif // OPENSUBDIV_EVAL_OUTPUT_GPU_H_
|
||||
@@ -0,0 +1,42 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "internal/evaluator/evaluator_cache_impl.h"
|
||||
|
||||
#ifndef WITH_WEB
|
||||
# include "internal/evaluator/eval_output_gpu.h"
|
||||
#endif
|
||||
|
||||
OpenSubdiv_EvaluatorCacheImpl::OpenSubdiv_EvaluatorCacheImpl() = default;
|
||||
|
||||
OpenSubdiv_EvaluatorCacheImpl::~OpenSubdiv_EvaluatorCacheImpl()
|
||||
{
|
||||
#ifndef WITH_WEB
|
||||
delete static_cast<blender::opensubdiv::GpuEvalOutput::EvaluatorCache *>(eval_cache);
|
||||
#endif
|
||||
}
|
||||
|
||||
OpenSubdiv_EvaluatorCacheImpl *openSubdiv_createEvaluatorCacheInternal(
|
||||
eOpenSubdivEvaluator evaluator_type)
|
||||
{
|
||||
#ifdef WITH_WEB
|
||||
(void)evaluator_type;
|
||||
return nullptr;
|
||||
#else
|
||||
if (evaluator_type != eOpenSubdivEvaluator::OPENSUBDIV_EVALUATOR_GPU) {
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
|
||||
void openSubdiv_deleteEvaluatorCacheInternal(OpenSubdiv_EvaluatorCacheImpl *evaluator_cache)
|
||||
{
|
||||
delete evaluator_cache;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef OPENSUBDIV_EVALUATOR_CACHE_IMPL_H_
|
||||
#define OPENSUBDIV_EVALUATOR_CACHE_IMPL_H_
|
||||
|
||||
#include "internal/base/memory.h"
|
||||
|
||||
#include "opensubdiv_capi_type.hh"
|
||||
|
||||
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_
|
||||
@@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2015 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Author: Sergey Sharybin. */
|
||||
|
||||
#include "opensubdiv_evaluator_capi.hh"
|
||||
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
# include <opensubdiv/osd/mtlPatchShaderSource.h>
|
||||
#endif
|
||||
#if defined(WITH_VULKAN_BACKEND) || defined(WITH_OPENGL_BACKEND)
|
||||
# include <opensubdiv/osd/glslPatchShaderSource.h>
|
||||
#endif
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
|
||||
#include "internal/evaluator/evaluator_cache_impl.h"
|
||||
|
||||
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()
|
||||
{
|
||||
/* 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 =
|
||||
"#define OsdPatchParam_host_shared_ OsdPatchParam\n"
|
||||
"#define OsdPatchArray_host_shared_ OsdPatchArray\n"
|
||||
"#define OsdPatchCoord_host_shared_ OsdPatchCoord\n";
|
||||
|
||||
#ifdef WITH_METAL_BACKEND
|
||||
patch_basis_source += OpenSubdiv::Osd::MTLPatchShaderSource::GetPatchBasisShaderSource();
|
||||
#endif
|
||||
#if defined(WITH_OPENGL_BACKEND) || defined(WITH_VULKAN_BACKEND)
|
||||
patch_basis_source += OpenSubdiv::Osd::GLSLPatchShaderSource::GetPatchBasisShaderSource();
|
||||
#endif
|
||||
}
|
||||
return patch_basis_source.c_str();
|
||||
}
|
||||
@@ -0,0 +1,589 @@
|
||||
/* SPDX-FileCopyrightText: 2018 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Author: Sergey Sharybin. */
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# include <iso646.h>
|
||||
#endif
|
||||
|
||||
#include <opensubdiv/far/patchMap.h>
|
||||
#include <opensubdiv/far/patchTable.h>
|
||||
#include <opensubdiv/far/patchTableFactory.h>
|
||||
#include <opensubdiv/osd/mesh.h>
|
||||
#include <opensubdiv/osd/types.h>
|
||||
#include <opensubdiv/version.h>
|
||||
|
||||
#include "internal/evaluator/eval_output_cpu.h"
|
||||
#ifndef WITH_WEB
|
||||
# include "internal/evaluator/eval_output_gpu.h"
|
||||
#endif
|
||||
#include "internal/evaluator/evaluator_cache_impl.h"
|
||||
#include "internal/evaluator/patch_map.h"
|
||||
#include "opensubdiv_evaluator.hh"
|
||||
#include "opensubdiv_evaluator_capi.hh"
|
||||
#include "opensubdiv_topology_refiner.hh"
|
||||
|
||||
using OpenSubdiv::Far::PatchTable;
|
||||
using OpenSubdiv::Far::PatchTableFactory;
|
||||
using OpenSubdiv::Far::StencilTable;
|
||||
using OpenSubdiv::Far::StencilTableFactory;
|
||||
using OpenSubdiv::Far::StencilTableReal;
|
||||
using OpenSubdiv::Far::TopologyRefiner;
|
||||
using OpenSubdiv::Osd::PatchArray;
|
||||
using OpenSubdiv::Osd::PatchCoord;
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
|
||||
// Array implementation which stores small data on stack (or, rather, in the class itself).
|
||||
template<typename T, int kNumMaxElementsOnStack> class StackOrHeapArray {
|
||||
public:
|
||||
StackOrHeapArray()
|
||||
: num_elements_(0),
|
||||
heap_elements_(nullptr),
|
||||
num_heap_elements_(0),
|
||||
effective_elements_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
explicit StackOrHeapArray(int size) : StackOrHeapArray()
|
||||
{
|
||||
resize(size);
|
||||
}
|
||||
|
||||
~StackOrHeapArray()
|
||||
{
|
||||
delete[] heap_elements_;
|
||||
}
|
||||
|
||||
int size() const
|
||||
{
|
||||
return num_elements_;
|
||||
};
|
||||
|
||||
T *data()
|
||||
{
|
||||
return effective_elements_;
|
||||
}
|
||||
|
||||
void resize(int num_elements)
|
||||
{
|
||||
const int old_num_elements = num_elements_;
|
||||
num_elements_ = num_elements;
|
||||
// Early output if allcoation size did not change, or allocation size is smaller.
|
||||
// We never re-allocate, sacrificing some memory over performance.
|
||||
if (old_num_elements >= num_elements) {
|
||||
return;
|
||||
}
|
||||
// Simple case: no previously allocated buffer, can simply do one allocation.
|
||||
if (effective_elements_ == nullptr) {
|
||||
effective_elements_ = allocate(num_elements);
|
||||
return;
|
||||
}
|
||||
// Make new allocation, and copy elements if needed.
|
||||
T *old_buffer = effective_elements_;
|
||||
effective_elements_ = allocate(num_elements);
|
||||
if (old_buffer != effective_elements_) {
|
||||
memcpy(
|
||||
effective_elements_, old_buffer, sizeof(T) * std::min(old_num_elements, num_elements));
|
||||
}
|
||||
if (old_buffer != stack_elements_) {
|
||||
delete[] old_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
T *allocate(int num_elements)
|
||||
{
|
||||
if (num_elements < kNumMaxElementsOnStack) {
|
||||
return stack_elements_;
|
||||
}
|
||||
heap_elements_ = new T[num_elements];
|
||||
return heap_elements_;
|
||||
}
|
||||
|
||||
// Number of elements in the buffer.
|
||||
int num_elements_;
|
||||
|
||||
// Elements which are allocated on a stack (or, rather, in the same allocation as the buffer
|
||||
// itself).
|
||||
// Is used as long as buffer is smaller than kNumMaxElementsOnStack.
|
||||
T stack_elements_[kNumMaxElementsOnStack];
|
||||
|
||||
// Heap storage for buffer larger than kNumMaxElementsOnStack.
|
||||
T *heap_elements_;
|
||||
int num_heap_elements_;
|
||||
|
||||
// Depending on the current buffer size points to rither stack_elements_ or heap_elements_.
|
||||
T *effective_elements_;
|
||||
};
|
||||
|
||||
// 32 is a number of inner vertices along the patch size at subdivision level 6.
|
||||
using StackOrHeapPatchCoordArray = StackOrHeapArray<PatchCoord, 32 * 32>;
|
||||
|
||||
static void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords,
|
||||
const int num_patch_coords,
|
||||
const PatchMap *patch_map,
|
||||
StackOrHeapPatchCoordArray *array)
|
||||
{
|
||||
array->resize(num_patch_coords);
|
||||
for (int i = 0; i < num_patch_coords; ++i) {
|
||||
const PatchTable::PatchHandle *handle = patch_map->FindPatch(
|
||||
patch_coords[i].ptex_face, patch_coords[i].u, patch_coords[i].v);
|
||||
(array->data())[i] = PatchCoord(*handle, patch_coords[i].u, patch_coords[i].v);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Evaluator wrapper for anonymous API.
|
||||
|
||||
EvalOutputAPI::EvalOutputAPI(EvalOutput *implementation, PatchMap *patch_map)
|
||||
: patch_map_(patch_map), implementation_(implementation)
|
||||
{
|
||||
}
|
||||
|
||||
EvalOutputAPI::~EvalOutputAPI()
|
||||
{
|
||||
delete implementation_;
|
||||
}
|
||||
|
||||
void EvalOutputAPI::setSettings(const OpenSubdiv_EvaluatorSettings *settings)
|
||||
{
|
||||
implementation_->updateSettings(settings);
|
||||
}
|
||||
|
||||
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 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 EvalOutputAPI::setVertexData(const float *vertex_data,
|
||||
const int start_vertex_index,
|
||||
const int num_vertices)
|
||||
{
|
||||
// TODO(sergey): Add sanity check on indices.
|
||||
implementation_->updateVertexData(vertex_data, start_vertex_index, 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 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;
|
||||
current_buffer += start_offset;
|
||||
for (int i = 0; i < num_vertices; ++i) {
|
||||
const int current_vertex_index = start_vertex_index + i;
|
||||
implementation_->updateData(
|
||||
reinterpret_cast<const float *>(current_buffer), current_vertex_index, 1);
|
||||
current_buffer += stride;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
current_buffer += start_offset;
|
||||
for (int i = 0; i < num_vertices; ++i) {
|
||||
const int current_vertex_index = start_vertex_index + i;
|
||||
implementation_->updateVaryingData(
|
||||
reinterpret_cast<const float *>(current_buffer), current_vertex_index, 1);
|
||||
current_buffer += stride;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
current_buffer += start_offset;
|
||||
for (int i = 0; i < num_vertices; ++i) {
|
||||
const int current_vertex_index = start_vertex_index + i;
|
||||
implementation_->updateFaceVaryingData(face_varying_channel,
|
||||
reinterpret_cast<const float *>(current_buffer),
|
||||
current_vertex_index,
|
||||
1);
|
||||
current_buffer += stride;
|
||||
}
|
||||
}
|
||||
|
||||
void EvalOutputAPI::refine()
|
||||
{
|
||||
implementation_->refine();
|
||||
}
|
||||
|
||||
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);
|
||||
assert(face_v >= 0.0f);
|
||||
assert(face_v <= 1.0f);
|
||||
const PatchTable::PatchHandle *handle = patch_map_->FindPatch(ptex_face_index, face_u, face_v);
|
||||
PatchCoord patch_coord(*handle, face_u, face_v);
|
||||
if (dPdu != nullptr || dPdv != nullptr) {
|
||||
implementation_->evalPatchesWithDerivatives(&patch_coord, 1, P, dPdu, dPdv);
|
||||
}
|
||||
else {
|
||||
implementation_->evalPatches(&patch_coord, 1, P);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
assert(face_v >= 0.0f);
|
||||
assert(face_v <= 1.0f);
|
||||
const PatchTable::PatchHandle *handle = patch_map_->FindPatch(ptex_face_index, face_u, face_v);
|
||||
PatchCoord patch_coord(*handle, face_u, face_v);
|
||||
implementation_->evalPatchesVarying(&patch_coord, 1, varying);
|
||||
}
|
||||
|
||||
void EvalOutputAPI::evaluateVertexData(const int ptex_face_index,
|
||||
float face_u,
|
||||
float face_v,
|
||||
float vertex_data[])
|
||||
{
|
||||
assert(face_u >= 0.0f);
|
||||
assert(face_u <= 1.0f);
|
||||
assert(face_v >= 0.0f);
|
||||
assert(face_v <= 1.0f);
|
||||
const PatchTable::PatchHandle *handle = patch_map_->FindPatch(ptex_face_index, face_u, face_v);
|
||||
PatchCoord patch_coord(*handle, face_u, face_v);
|
||||
implementation_->evalPatchesVertexData(&patch_coord, 1, vertex_data);
|
||||
}
|
||||
|
||||
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);
|
||||
assert(face_v >= 0.0f);
|
||||
assert(face_v <= 1.0f);
|
||||
const PatchTable::PatchHandle *handle = patch_map_->FindPatch(ptex_face_index, face_u, face_v);
|
||||
PatchCoord patch_coord(*handle, face_u, face_v);
|
||||
implementation_->evalPatchesFaceVarying(face_varying_channel, &patch_coord, 1, face_varying);
|
||||
}
|
||||
|
||||
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);
|
||||
if (dPdu != nullptr || dPdv != nullptr) {
|
||||
implementation_->evalPatchesWithDerivatives(
|
||||
patch_coords_array.data(), num_patch_coords, P, dPdu, dPdv);
|
||||
}
|
||||
else {
|
||||
implementation_->evalPatches(patch_coords_array.data(), num_patch_coords, P);
|
||||
}
|
||||
}
|
||||
|
||||
void EvalOutputAPI::getPatchMap(blender::gpu::VertBuf *patch_map_handles,
|
||||
blender::gpu::VertBuf *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();
|
||||
// TODO(jbakker): should these be SSBO's they are never bound as vertex buffers.
|
||||
GPU_vertbuf_data_alloc(*patch_map_handles, handles.size());
|
||||
MutableSpan<PatchTable::PatchHandle> buffer_handles =
|
||||
patch_map_handles->data<PatchTable::PatchHandle>();
|
||||
memcpy(buffer_handles.data(), handles.data(), sizeof(PatchTable::PatchHandle) * handles.size());
|
||||
|
||||
const std::vector<PatchMap::QuadNode> &quadtree = patch_map_->nodes();
|
||||
GPU_vertbuf_data_alloc(*patch_map_quadtree, quadtree.size());
|
||||
MutableSpan<PatchMap::QuadNode> buffer_nodes = patch_map_quadtree->data<PatchMap::QuadNode>();
|
||||
memcpy(buffer_nodes.data(), quadtree.data(), sizeof(PatchMap::QuadNode) * quadtree.size());
|
||||
}
|
||||
|
||||
gpu::StorageBuf *EvalOutputAPI::create_patch_arrays_buf()
|
||||
{
|
||||
return implementation_->create_patch_arrays_buf();
|
||||
}
|
||||
|
||||
gpu::StorageBuf *EvalOutputAPI::get_patch_index_buf()
|
||||
{
|
||||
return implementation_->get_patch_index_buf();
|
||||
}
|
||||
|
||||
gpu::StorageBuf *EvalOutputAPI::get_patch_param_buf()
|
||||
{
|
||||
return implementation_->get_patch_param_buf();
|
||||
}
|
||||
|
||||
gpu::VertBuf *EvalOutputAPI::get_source_buf()
|
||||
{
|
||||
return implementation_->get_source_buf();
|
||||
}
|
||||
|
||||
gpu::VertBuf *EvalOutputAPI::get_source_data_buf()
|
||||
{
|
||||
return implementation_->get_source_data_buf();
|
||||
}
|
||||
|
||||
gpu::StorageBuf *EvalOutputAPI::create_face_varying_patch_array_buf(const int face_varying_channel)
|
||||
{
|
||||
return implementation_->create_face_varying_patch_array_buf(face_varying_channel);
|
||||
}
|
||||
|
||||
gpu::StorageBuf *EvalOutputAPI::get_face_varying_patch_index_buf(const int face_varying_channel)
|
||||
{
|
||||
return implementation_->get_face_varying_patch_index_buf(face_varying_channel);
|
||||
}
|
||||
|
||||
gpu::StorageBuf *EvalOutputAPI::get_face_varying_patch_param_buf(const int face_varying_channel)
|
||||
{
|
||||
return implementation_->get_face_varying_patch_param_buf(face_varying_channel);
|
||||
}
|
||||
|
||||
gpu::VertBuf *EvalOutputAPI::get_face_varying_source_buf(const int face_varying_channel)
|
||||
{
|
||||
return implementation_->get_face_varying_source_buf(face_varying_channel);
|
||||
}
|
||||
|
||||
int EvalOutputAPI::get_face_varying_source_offset(const int face_varying_channel) const
|
||||
{
|
||||
return implementation_->get_face_varying_source_offset(face_varying_channel);
|
||||
}
|
||||
|
||||
bool EvalOutputAPI::hasVertexData() const
|
||||
{
|
||||
return implementation_->hasVertexData();
|
||||
}
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
|
||||
OpenSubdiv_Evaluator::OpenSubdiv_Evaluator()
|
||||
: eval_output(nullptr), patch_map(nullptr), patch_table(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
OpenSubdiv_Evaluator::~OpenSubdiv_Evaluator()
|
||||
{
|
||||
delete eval_output;
|
||||
delete patch_map;
|
||||
delete patch_table;
|
||||
}
|
||||
|
||||
OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner(
|
||||
blender::opensubdiv::TopologyRefinerImpl *topology_refiner,
|
||||
eOpenSubdivEvaluator evaluator_type,
|
||||
OpenSubdiv_EvaluatorCache *evaluator_cache_descr)
|
||||
{
|
||||
TopologyRefiner *refiner = topology_refiner->topology_refiner;
|
||||
if (refiner == nullptr) {
|
||||
// Happens on bad topology.
|
||||
return nullptr;
|
||||
}
|
||||
// TODO(sergey): Base this on actual topology.
|
||||
const bool has_varying_data = false;
|
||||
const int num_face_varying_channels = refiner->GetNumFVarChannels();
|
||||
const bool has_face_varying_data = (num_face_varying_channels != 0);
|
||||
const int level = topology_refiner->settings.level;
|
||||
const bool is_adaptive = topology_refiner->settings.is_adaptive;
|
||||
// Common settings for stencils and patches.
|
||||
const bool stencil_generate_intermediate_levels = is_adaptive;
|
||||
const bool stencil_generate_offsets = true;
|
||||
const bool use_inf_sharp_patch = true;
|
||||
// Refine the topology with given settings.
|
||||
// TODO(sergey): What if topology is already refined?
|
||||
if (is_adaptive) {
|
||||
TopologyRefiner::AdaptiveOptions options(level);
|
||||
options.considerFVarChannels = has_face_varying_data;
|
||||
options.useInfSharpPatch = use_inf_sharp_patch;
|
||||
refiner->RefineAdaptive(options);
|
||||
}
|
||||
else {
|
||||
TopologyRefiner::UniformOptions options(level);
|
||||
refiner->RefineUniform(options);
|
||||
}
|
||||
|
||||
// Work around ASAN warnings, due to OpenSubdiv pretending to have an actual StencilTable
|
||||
// instance while it's really its base class.
|
||||
auto delete_stencil_table = [](const StencilTable *table) {
|
||||
static_assert(std::is_base_of_v<StencilTableReal<float>, StencilTable>);
|
||||
delete reinterpret_cast<const StencilTableReal<float> *>(table);
|
||||
};
|
||||
|
||||
// Generate stencil table to update the bi-cubic patches control vertices
|
||||
// after they have been re-posed (both for vertex & varying interpolation).
|
||||
//
|
||||
// Vertex stencils.
|
||||
StencilTableFactory::Options vertex_stencil_options;
|
||||
vertex_stencil_options.generateOffsets = stencil_generate_offsets;
|
||||
vertex_stencil_options.generateIntermediateLevels = stencil_generate_intermediate_levels;
|
||||
const StencilTable *vertex_stencils = StencilTableFactory::Create(*refiner,
|
||||
vertex_stencil_options);
|
||||
// Varying stencils.
|
||||
//
|
||||
// TODO(sergey): Seems currently varying stencils are always required in
|
||||
// OpenSubdiv itself.
|
||||
const StencilTable *varying_stencils = nullptr;
|
||||
if (has_varying_data) {
|
||||
StencilTableFactory::Options varying_stencil_options;
|
||||
varying_stencil_options.generateOffsets = stencil_generate_offsets;
|
||||
varying_stencil_options.generateIntermediateLevels = stencil_generate_intermediate_levels;
|
||||
varying_stencil_options.interpolationMode = StencilTableFactory::INTERPOLATE_VARYING;
|
||||
varying_stencils = StencilTableFactory::Create(*refiner, varying_stencil_options);
|
||||
}
|
||||
// Face warying stencil.
|
||||
std::vector<const StencilTable *> all_face_varying_stencils;
|
||||
all_face_varying_stencils.reserve(num_face_varying_channels);
|
||||
for (int face_varying_channel = 0; face_varying_channel < num_face_varying_channels;
|
||||
++face_varying_channel)
|
||||
{
|
||||
StencilTableFactory::Options face_varying_stencil_options;
|
||||
face_varying_stencil_options.generateOffsets = stencil_generate_offsets;
|
||||
face_varying_stencil_options.generateIntermediateLevels = stencil_generate_intermediate_levels;
|
||||
face_varying_stencil_options.interpolationMode = StencilTableFactory::INTERPOLATE_FACE_VARYING;
|
||||
face_varying_stencil_options.fvarChannel = face_varying_channel;
|
||||
all_face_varying_stencils.push_back(
|
||||
StencilTableFactory::Create(*refiner, face_varying_stencil_options));
|
||||
}
|
||||
// Generate bi-cubic patch table for the limit surface.
|
||||
PatchTableFactory::Options patch_options(level);
|
||||
patch_options.SetEndCapType(PatchTableFactory::Options::ENDCAP_GREGORY_BASIS);
|
||||
patch_options.useInfSharpPatch = use_inf_sharp_patch;
|
||||
patch_options.generateFVarTables = has_face_varying_data;
|
||||
patch_options.generateFVarLegacyLinearPatches = false;
|
||||
const PatchTable *patch_table = PatchTableFactory::Create(*refiner, patch_options);
|
||||
// Append local points stencils.
|
||||
// Point stencils.
|
||||
const StencilTable *local_point_stencil_table = patch_table->GetLocalPointStencilTable();
|
||||
if (local_point_stencil_table != nullptr) {
|
||||
const StencilTable *table = StencilTableFactory::AppendLocalPointStencilTable(
|
||||
*refiner, vertex_stencils, local_point_stencil_table);
|
||||
delete_stencil_table(vertex_stencils);
|
||||
if (table == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
vertex_stencils = table;
|
||||
}
|
||||
// Varying stencils.
|
||||
if (has_varying_data) {
|
||||
const StencilTable *local_point_varying_stencil_table =
|
||||
patch_table->GetLocalPointVaryingStencilTable();
|
||||
if (local_point_varying_stencil_table != nullptr) {
|
||||
const StencilTable *table = StencilTableFactory::AppendLocalPointStencilTable(
|
||||
*refiner, varying_stencils, local_point_varying_stencil_table);
|
||||
delete_stencil_table(varying_stencils);
|
||||
varying_stencils = table;
|
||||
}
|
||||
}
|
||||
for (int face_varying_channel = 0; face_varying_channel < num_face_varying_channels;
|
||||
++face_varying_channel)
|
||||
{
|
||||
const StencilTable *table = StencilTableFactory::AppendLocalPointStencilTableFaceVarying(
|
||||
*refiner,
|
||||
all_face_varying_stencils[face_varying_channel],
|
||||
patch_table->GetLocalPointFaceVaryingStencilTable(face_varying_channel),
|
||||
face_varying_channel);
|
||||
if (table != nullptr) {
|
||||
delete_stencil_table(all_face_varying_stencils[face_varying_channel]);
|
||||
all_face_varying_stencils[face_varying_channel] = table;
|
||||
}
|
||||
}
|
||||
// Create OpenSubdiv's CPU side evaluator.
|
||||
blender::opensubdiv::EvalOutputAPI::EvalOutput *eval_output = nullptr;
|
||||
|
||||
const bool use_gpu_evaluator = evaluator_type == OPENSUBDIV_EVALUATOR_GPU;
|
||||
#ifdef WITH_WEB
|
||||
(void)use_gpu_evaluator;
|
||||
eval_output = new blender::opensubdiv::CpuEvalOutput(
|
||||
vertex_stencils, varying_stencils, all_face_varying_stencils, 2, patch_table);
|
||||
#else
|
||||
if (use_gpu_evaluator) {
|
||||
blender::opensubdiv::GpuEvalOutput::EvaluatorCache *evaluator_cache = nullptr;
|
||||
if (evaluator_cache_descr) {
|
||||
evaluator_cache = static_cast<blender::opensubdiv::GpuEvalOutput::EvaluatorCache *>(
|
||||
evaluator_cache_descr->impl->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);
|
||||
}
|
||||
#endif
|
||||
|
||||
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_Evaluator *evaluator = new OpenSubdiv_Evaluator();
|
||||
evaluator->type = evaluator_type;
|
||||
|
||||
evaluator->eval_output = new blender::opensubdiv::EvalOutputAPI(eval_output, patch_map);
|
||||
evaluator->patch_map = patch_map;
|
||||
evaluator->patch_table = patch_table;
|
||||
// TODO(sergey): Look into whether we've got duplicated stencils arrays.
|
||||
delete_stencil_table(vertex_stencils);
|
||||
delete_stencil_table(varying_stencils);
|
||||
for (const StencilTable *table : all_face_varying_stencils) {
|
||||
delete_stencil_table(table);
|
||||
}
|
||||
|
||||
return evaluator;
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#include "gpu_compute_evaluator.h"
|
||||
|
||||
#include <opensubdiv/far/error.h>
|
||||
#include <opensubdiv/far/patchDescriptor.h>
|
||||
#include <opensubdiv/far/stencilTable.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_compute.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_debug.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
using OpenSubdiv::Far::LimitStencilTable;
|
||||
using OpenSubdiv::Far::StencilTable;
|
||||
using OpenSubdiv::Osd::BufferDescriptor;
|
||||
using OpenSubdiv::Osd::PatchArray;
|
||||
using OpenSubdiv::Osd::PatchArrayVector;
|
||||
|
||||
extern "C" char datatoc_osd_eval_patches_comp_glsl[];
|
||||
extern "C" char datatoc_osd_eval_stencils_comp_glsl[];
|
||||
|
||||
#define SHADER_SRC_VERTEX_BUFFER_BUF_SLOT 0
|
||||
#define SHADER_DST_VERTEX_BUFFER_BUF_SLOT 1
|
||||
#define SHADER_DU_BUFFER_BUF_SLOT 2
|
||||
#define SHADER_DV_BUFFER_BUF_SLOT 3
|
||||
#define SHADER_SIZES_BUF_SLOT 4
|
||||
#define SHADER_OFFSETS_BUF_SLOT 5
|
||||
#define SHADER_INDICES_BUF_SLOT 6
|
||||
#define SHADER_WEIGHTS_BUF_SLOT 7
|
||||
#define SHADER_DU_WEIGHTS_BUF_SLOT 8
|
||||
#define SHADER_DV_WEIGHTS_BUF_SLOT 9
|
||||
|
||||
#define SHADER_PATCH_ARRAY_BUFFER_BUF_SLOT 4
|
||||
#define SHADER_PATCH_COORDS_BUF_SLOT 5
|
||||
#define SHADER_PATCH_INDEX_BUFFER_BUF_SLOT 6
|
||||
#define SHADER_PATCH_PARAM_BUFFER_BUF_SLOT 7
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
|
||||
template<class T> gpu::StorageBuf *create_buffer(std::vector<T> const &src, const char *name)
|
||||
{
|
||||
if (src.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const size_t buffer_size = src.size() * sizeof(T);
|
||||
gpu::StorageBuf *storage_buffer = GPU_storagebuf_create_ex(
|
||||
buffer_size, &src.at(0), GPU_USAGE_STATIC, name);
|
||||
|
||||
return storage_buffer;
|
||||
}
|
||||
|
||||
GPUStencilTableSSBO::GPUStencilTableSSBO(StencilTable const *stencilTable)
|
||||
{
|
||||
_numStencils = stencilTable->GetNumStencils();
|
||||
if (_numStencils > 0) {
|
||||
sizes_buf = create_buffer(stencilTable->GetSizes(), "osd_sized");
|
||||
offsets_buf = create_buffer(stencilTable->GetOffsets(), "osd_offsets");
|
||||
indices_buf = create_buffer(stencilTable->GetControlIndices(), "osd_control_indices");
|
||||
weights_buf = create_buffer(stencilTable->GetWeights(), "osd_weights");
|
||||
}
|
||||
}
|
||||
|
||||
GPUStencilTableSSBO::GPUStencilTableSSBO(LimitStencilTable const *limitStencilTable)
|
||||
{
|
||||
_numStencils = limitStencilTable->GetNumStencils();
|
||||
if (_numStencils > 0) {
|
||||
sizes_buf = create_buffer(limitStencilTable->GetSizes(), "osd_sized");
|
||||
offsets_buf = create_buffer(limitStencilTable->GetOffsets(), "osd_offsets");
|
||||
indices_buf = create_buffer(limitStencilTable->GetControlIndices(), "osd_control_indices");
|
||||
weights_buf = create_buffer(limitStencilTable->GetWeights(), "osd_weights");
|
||||
du_weights_buf = create_buffer(limitStencilTable->GetDuWeights(), "osd_du_weights");
|
||||
dv_weights_buf = create_buffer(limitStencilTable->GetDvWeights(), "osd_dv_weights");
|
||||
duu_weights_buf = create_buffer(limitStencilTable->GetDuuWeights(), "osd_duu_weights");
|
||||
duv_weights_buf = create_buffer(limitStencilTable->GetDuvWeights(), "osd_duv_weights");
|
||||
dvv_weights_buf = create_buffer(limitStencilTable->GetDvvWeights(), "osd_dvv_weights");
|
||||
}
|
||||
}
|
||||
|
||||
static void storage_buffer_free(gpu::StorageBuf **buffer)
|
||||
{
|
||||
if (*buffer) {
|
||||
GPU_storagebuf_free(*buffer);
|
||||
*buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
GPUStencilTableSSBO::~GPUStencilTableSSBO()
|
||||
{
|
||||
storage_buffer_free(&sizes_buf);
|
||||
storage_buffer_free(&offsets_buf);
|
||||
storage_buffer_free(&indices_buf);
|
||||
storage_buffer_free(&weights_buf);
|
||||
storage_buffer_free(&du_weights_buf);
|
||||
storage_buffer_free(&dv_weights_buf);
|
||||
storage_buffer_free(&duu_weights_buf);
|
||||
storage_buffer_free(&duv_weights_buf);
|
||||
storage_buffer_free(&dvv_weights_buf);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
GPUComputeEvaluator::GPUComputeEvaluator() : _workGroupSize(64), _patchArraysSSBO(nullptr)
|
||||
{
|
||||
memset((void *)&_stencilKernel, 0, sizeof(_stencilKernel));
|
||||
memset((void *)&_patchKernel, 0, sizeof(_patchKernel));
|
||||
}
|
||||
|
||||
GPUComputeEvaluator::~GPUComputeEvaluator()
|
||||
{
|
||||
if (_patchArraysSSBO) {
|
||||
GPU_storagebuf_free(_patchArraysSSBO);
|
||||
_patchArraysSSBO = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool GPUComputeEvaluator::Compile(BufferDescriptor const &srcDesc,
|
||||
BufferDescriptor const &dstDesc,
|
||||
BufferDescriptor const &duDesc,
|
||||
BufferDescriptor const &dvDesc)
|
||||
{
|
||||
|
||||
if (!_stencilKernel.Compile(srcDesc, dstDesc, duDesc, dvDesc, _workGroupSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_patchKernel.Compile(srcDesc, dstDesc, duDesc, dvDesc, _workGroupSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void GPUComputeEvaluator::Synchronize(void * /*kernel*/)
|
||||
{
|
||||
// XXX: this is currently just for the performance measuring purpose.
|
||||
// need to be reimplemented by fence and sync.
|
||||
GPU_finish();
|
||||
}
|
||||
|
||||
int GPUComputeEvaluator::GetDispatchSize(int count) const
|
||||
{
|
||||
return (count + _workGroupSize - 1) / _workGroupSize;
|
||||
}
|
||||
|
||||
void GPUComputeEvaluator::DispatchCompute(blender::gpu::Shader *shader,
|
||||
int totalDispatchSize) const
|
||||
{
|
||||
const int dispatchSize = GetDispatchSize(totalDispatchSize);
|
||||
int dispatchRX = dispatchSize;
|
||||
int dispatchRY = 1u;
|
||||
if (dispatchRX > GPU_max_work_group_count(0)) {
|
||||
/* Since there are some limitations with regards to the maximum work group size (could be as
|
||||
* low as 64k elements per call), we split the number elements into a "2d" number, with the
|
||||
* final index being computed as `res_x + res_y * max_work_group_size`. Even with a maximum
|
||||
* work group size of 64k, that still leaves us with roughly `64k * 64k = 4` billion elements
|
||||
* total, which should be enough. If not, we could also use the 3rd dimension. */
|
||||
/* TODO(fclem): We could dispatch fewer groups if we compute the prime factorization and
|
||||
* get the smallest rect fitting the requirements. */
|
||||
dispatchRX = dispatchRY = std::ceil(std::sqrt(dispatchSize));
|
||||
/* Avoid a completely empty dispatch line caused by rounding. */
|
||||
if ((dispatchRX * (dispatchRY - 1)) >= dispatchSize) {
|
||||
dispatchRY -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* X and Y dimensions may have different limits so the above computation may not be right, but
|
||||
* even with the standard 64k minimum on all dimensions we still have a lot of room. Therefore,
|
||||
* we presume it all fits. */
|
||||
assert(dispatchRY < GPU_max_work_group_count(1));
|
||||
GPU_compute_dispatch(shader, dispatchRX, dispatchRY, 1);
|
||||
|
||||
/* Next usage of the src/dst buffers will always be a shader storage. Vertices/normals/attributes
|
||||
* are copied over to the final buffers using compute shaders. */
|
||||
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
|
||||
bool GPUComputeEvaluator::EvalStencils(gpu::VertBuf *srcBuffer,
|
||||
BufferDescriptor const &srcDesc,
|
||||
gpu::VertBuf *dstBuffer,
|
||||
BufferDescriptor const &dstDesc,
|
||||
gpu::VertBuf *duBuffer,
|
||||
BufferDescriptor const &duDesc,
|
||||
gpu::VertBuf *dvBuffer,
|
||||
BufferDescriptor const &dvDesc,
|
||||
gpu::StorageBuf *sizesBuffer,
|
||||
gpu::StorageBuf *offsetsBuffer,
|
||||
gpu::StorageBuf *indicesBuffer,
|
||||
gpu::StorageBuf *weightsBuffer,
|
||||
gpu::StorageBuf *duWeightsBuffer,
|
||||
gpu::StorageBuf *dvWeightsBuffer,
|
||||
int start,
|
||||
int end) const
|
||||
{
|
||||
if (_stencilKernel.shader == nullptr) {
|
||||
return false;
|
||||
}
|
||||
int count = end - start;
|
||||
if (count <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
GPU_shader_bind(_stencilKernel.shader);
|
||||
GPU_vertbuf_bind_as_ssbo(srcBuffer, SHADER_SRC_VERTEX_BUFFER_BUF_SLOT);
|
||||
GPU_vertbuf_bind_as_ssbo(dstBuffer, SHADER_DST_VERTEX_BUFFER_BUF_SLOT);
|
||||
if (duBuffer) {
|
||||
GPU_vertbuf_bind_as_ssbo(duBuffer, SHADER_DU_BUFFER_BUF_SLOT);
|
||||
}
|
||||
if (dvBuffer) {
|
||||
GPU_vertbuf_bind_as_ssbo(dvBuffer, SHADER_DV_BUFFER_BUF_SLOT);
|
||||
}
|
||||
GPU_storagebuf_bind(sizesBuffer, SHADER_SIZES_BUF_SLOT);
|
||||
GPU_storagebuf_bind(offsetsBuffer, SHADER_OFFSETS_BUF_SLOT);
|
||||
GPU_storagebuf_bind(indicesBuffer, SHADER_INDICES_BUF_SLOT);
|
||||
GPU_storagebuf_bind(weightsBuffer, SHADER_WEIGHTS_BUF_SLOT);
|
||||
if (duWeightsBuffer) {
|
||||
GPU_storagebuf_bind(duWeightsBuffer, SHADER_DU_WEIGHTS_BUF_SLOT);
|
||||
}
|
||||
if (dvWeightsBuffer) {
|
||||
GPU_storagebuf_bind(dvWeightsBuffer, SHADER_DV_WEIGHTS_BUF_SLOT);
|
||||
}
|
||||
|
||||
GPU_shader_uniform_int_ex(_stencilKernel.shader, _stencilKernel.uniformStart, 1, 1, &start);
|
||||
GPU_shader_uniform_int_ex(_stencilKernel.shader, _stencilKernel.uniformEnd, 1, 1, &end);
|
||||
GPU_shader_uniform_int_ex(
|
||||
_stencilKernel.shader, _stencilKernel.uniformSrcOffset, 1, 1, &srcDesc.offset);
|
||||
GPU_shader_uniform_int_ex(
|
||||
_stencilKernel.shader, _stencilKernel.uniformDstOffset, 1, 1, &dstDesc.offset);
|
||||
|
||||
// TODO init to -1 and check >= 0 to align with GPU module. Currently we assume that the uniform
|
||||
// location is not zero as there are other uniforms defined as well.
|
||||
#define BIND_BUF_DESC(uniform, desc) \
|
||||
if (_stencilKernel.uniform > 0) { \
|
||||
int value[] = {desc.offset, desc.length, desc.stride}; \
|
||||
GPU_shader_uniform_int_ex(_stencilKernel.shader, _stencilKernel.uniform, 3, 1, value); \
|
||||
}
|
||||
BIND_BUF_DESC(uniformDuDesc, duDesc)
|
||||
BIND_BUF_DESC(uniformDvDesc, dvDesc)
|
||||
#undef BIND_BUF_DESC
|
||||
DispatchCompute(_stencilKernel.shader, count);
|
||||
// GPU_storagebuf_unbind_all();
|
||||
GPU_shader_unbind();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPUComputeEvaluator::EvalPatches(gpu::VertBuf *srcBuffer,
|
||||
BufferDescriptor const &srcDesc,
|
||||
gpu::VertBuf *dstBuffer,
|
||||
BufferDescriptor const &dstDesc,
|
||||
gpu::VertBuf *duBuffer,
|
||||
BufferDescriptor const &duDesc,
|
||||
gpu::VertBuf *dvBuffer,
|
||||
BufferDescriptor const &dvDesc,
|
||||
int numPatchCoords,
|
||||
gpu::VertBuf *patchCoordsBuffer,
|
||||
const PatchArrayVector &patchArrays,
|
||||
gpu::StorageBuf *patchIndexBuffer,
|
||||
gpu::StorageBuf *patchParamsBuffer)
|
||||
{
|
||||
if (_patchKernel.shader == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GPU_shader_bind(_patchKernel.shader);
|
||||
GPU_vertbuf_bind_as_ssbo(srcBuffer, SHADER_SRC_VERTEX_BUFFER_BUF_SLOT);
|
||||
GPU_vertbuf_bind_as_ssbo(dstBuffer, SHADER_DST_VERTEX_BUFFER_BUF_SLOT);
|
||||
if (duBuffer) {
|
||||
GPU_vertbuf_bind_as_ssbo(duBuffer, SHADER_DU_BUFFER_BUF_SLOT);
|
||||
}
|
||||
if (dvBuffer) {
|
||||
GPU_vertbuf_bind_as_ssbo(dvBuffer, SHADER_DV_BUFFER_BUF_SLOT);
|
||||
}
|
||||
GPU_vertbuf_bind_as_ssbo(patchCoordsBuffer, SHADER_PATCH_COORDS_BUF_SLOT);
|
||||
GPU_storagebuf_bind(patchIndexBuffer, SHADER_PATCH_INDEX_BUFFER_BUF_SLOT);
|
||||
GPU_storagebuf_bind(patchParamsBuffer, SHADER_PATCH_PARAM_BUFFER_BUF_SLOT);
|
||||
int patchArraySize = sizeof(PatchArray);
|
||||
if (_patchArraysSSBO) {
|
||||
GPU_storagebuf_free(_patchArraysSSBO);
|
||||
_patchArraysSSBO = nullptr;
|
||||
}
|
||||
_patchArraysSSBO = GPU_storagebuf_create_ex(patchArrays.size() * patchArraySize,
|
||||
static_cast<const void *>(&patchArrays[0]),
|
||||
GPU_USAGE_STATIC,
|
||||
"osd_patch_array");
|
||||
GPU_storagebuf_bind(_patchArraysSSBO, SHADER_PATCH_ARRAY_BUFFER_BUF_SLOT);
|
||||
|
||||
GPU_shader_uniform_int_ex(
|
||||
_patchKernel.shader, _patchKernel.uniformSrcOffset, 1, 1, &srcDesc.offset);
|
||||
GPU_shader_uniform_int_ex(
|
||||
_patchKernel.shader, _patchKernel.uniformDstOffset, 1, 1, &dstDesc.offset);
|
||||
|
||||
// TODO init to -1 and check >= 0 to align with GPU module.
|
||||
#define BIND_BUF_DESC(uniform, desc) \
|
||||
if (_stencilKernel.uniform > 0) { \
|
||||
int value[] = {desc.offset, desc.length, desc.stride}; \
|
||||
GPU_shader_uniform_int_ex(_patchKernel.shader, _patchKernel.uniform, 3, 1, value); \
|
||||
}
|
||||
BIND_BUF_DESC(uniformDuDesc, duDesc)
|
||||
BIND_BUF_DESC(uniformDvDesc, dvDesc)
|
||||
#undef BIND_BUF_DESC
|
||||
|
||||
DispatchCompute(_patchKernel.shader, numPatchCoords);
|
||||
GPU_shader_unbind();
|
||||
|
||||
return true;
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
GPUComputeEvaluator::_StencilKernel::_StencilKernel() {}
|
||||
GPUComputeEvaluator::_StencilKernel::~_StencilKernel()
|
||||
{
|
||||
if (shader) {
|
||||
GPU_shader_free(shader);
|
||||
shader = nullptr;
|
||||
}
|
||||
}
|
||||
static blender::gpu::Shader *compile_eval_stencil_shader(BufferDescriptor const &srcDesc,
|
||||
BufferDescriptor const &dstDesc,
|
||||
BufferDescriptor const &duDesc,
|
||||
BufferDescriptor const &dvDesc,
|
||||
int workGroupSize)
|
||||
{
|
||||
using namespace blender::gpu::shader;
|
||||
ShaderCreateInfo info("osd_eval_stencils_comp");
|
||||
info.local_group_size(workGroupSize, 1, 1);
|
||||
info.builtins(BuiltinBits::GLOBAL_INVOCATION_ID);
|
||||
info.builtins(BuiltinBits::NUM_WORK_GROUP);
|
||||
|
||||
// TODO: use specialization constants for src_stride, dst_stride. Not sure we can use
|
||||
// work group size as that requires extensions. This allows us to compile less shaders and
|
||||
// improve overall performance. Adding length as specialization constant will not work as it is
|
||||
// used to define an array length. This is not supported by Metal.
|
||||
std::string length = std::to_string(srcDesc.length);
|
||||
std::string src_stride = std::to_string(srcDesc.stride);
|
||||
std::string dst_stride = std::to_string(dstDesc.stride);
|
||||
std::string work_group_size = std::to_string(workGroupSize);
|
||||
info.define("LENGTH", length);
|
||||
info.define("SRC_STRIDE", src_stride);
|
||||
info.define("DST_STRIDE", dst_stride);
|
||||
info.define("WORK_GROUP_SIZE", work_group_size);
|
||||
info.typedef_source("osd_patch_defines.glsl");
|
||||
info.typedef_source("osd_patch_basis.glsl");
|
||||
info.storage_buf(
|
||||
SHADER_SRC_VERTEX_BUFFER_BUF_SLOT, Qualifier::read, "float", "srcVertexBuffer[]");
|
||||
info.storage_buf(
|
||||
SHADER_DST_VERTEX_BUFFER_BUF_SLOT, Qualifier::write, "float", "dstVertexBuffer[]");
|
||||
info.push_constant(Type::int_t, "srcOffset");
|
||||
info.push_constant(Type::int_t, "dstOffset");
|
||||
|
||||
bool deriv1 = (duDesc.length > 0 || dvDesc.length > 0);
|
||||
if (deriv1) {
|
||||
info.define("OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES");
|
||||
info.storage_buf(SHADER_DU_BUFFER_BUF_SLOT, Qualifier::read_write, "float", "duBuffer[]");
|
||||
info.storage_buf(SHADER_DV_BUFFER_BUF_SLOT, Qualifier::read_write, "float", "dvBuffer[]");
|
||||
info.push_constant(Type::int3_t, "duDesc");
|
||||
info.push_constant(Type::int3_t, "dvDesc");
|
||||
}
|
||||
|
||||
info.storage_buf(SHADER_SIZES_BUF_SLOT, Qualifier::read, "int", "sizes_buf[]");
|
||||
info.storage_buf(SHADER_OFFSETS_BUF_SLOT, Qualifier::read, "int", "offsets_buf[]");
|
||||
info.storage_buf(SHADER_INDICES_BUF_SLOT, Qualifier::read, "int", "indices_buf[]");
|
||||
info.storage_buf(SHADER_WEIGHTS_BUF_SLOT, Qualifier::read, "float", "weights_buf[]");
|
||||
if (deriv1) {
|
||||
info.storage_buf(
|
||||
SHADER_DU_WEIGHTS_BUF_SLOT, Qualifier::read_write, "float", "du_weights_buf[]");
|
||||
info.storage_buf(
|
||||
SHADER_DV_WEIGHTS_BUF_SLOT, Qualifier::read_write, "float", "dv_weights_buf[]");
|
||||
}
|
||||
info.push_constant(Type::int_t, "batchStart");
|
||||
info.push_constant(Type::int_t, "batchEnd");
|
||||
|
||||
info.compute_source("osd_eval_stencils_comp.glsl");
|
||||
blender::gpu::Shader *shader = GPU_shader_create_from_info(
|
||||
reinterpret_cast<const GPUShaderCreateInfo *>(&info));
|
||||
return shader;
|
||||
}
|
||||
|
||||
bool GPUComputeEvaluator::_StencilKernel::Compile(BufferDescriptor const &srcDesc,
|
||||
BufferDescriptor const &dstDesc,
|
||||
BufferDescriptor const &duDesc,
|
||||
BufferDescriptor const &dvDesc,
|
||||
int workGroupSize)
|
||||
{
|
||||
if (shader) {
|
||||
GPU_shader_free(shader);
|
||||
shader = nullptr;
|
||||
}
|
||||
|
||||
shader = compile_eval_stencil_shader(srcDesc, dstDesc, duDesc, dvDesc, workGroupSize);
|
||||
if (shader == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// cache uniform locations (TODO: use uniform block)
|
||||
uniformStart = GPU_shader_get_uniform(shader, "batchStart");
|
||||
uniformEnd = GPU_shader_get_uniform(shader, "batchEnd");
|
||||
uniformSrcOffset = GPU_shader_get_uniform(shader, "srcOffset");
|
||||
uniformDstOffset = GPU_shader_get_uniform(shader, "dstOffset");
|
||||
uniformDuDesc = GPU_shader_get_uniform(shader, "duDesc");
|
||||
uniformDvDesc = GPU_shader_get_uniform(shader, "dvDesc");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
GPUComputeEvaluator::_PatchKernel::_PatchKernel() {}
|
||||
GPUComputeEvaluator::_PatchKernel::~_PatchKernel()
|
||||
{
|
||||
if (shader) {
|
||||
GPU_shader_free(shader);
|
||||
shader = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static blender::gpu::Shader *compile_eval_patches_shader(BufferDescriptor const &srcDesc,
|
||||
BufferDescriptor const &dstDesc,
|
||||
BufferDescriptor const &duDesc,
|
||||
BufferDescriptor const &dvDesc,
|
||||
int workGroupSize)
|
||||
{
|
||||
using namespace blender::gpu::shader;
|
||||
ShaderCreateInfo info("osd_eval_patches_comp");
|
||||
info.local_group_size(workGroupSize, 1, 1);
|
||||
info.builtins(BuiltinBits::GLOBAL_INVOCATION_ID);
|
||||
info.builtins(BuiltinBits::NUM_WORK_GROUP);
|
||||
info.builtins(BuiltinBits::NO_BUFFER_TYPE_LINTING);
|
||||
|
||||
// TODO: use specialization constants for src_stride, dst_stride. Not sure we can use
|
||||
// work group size as that requires extensions. This allows us to compile less shaders and
|
||||
// improve overall performance. Adding length as specialization constant will not work as it is
|
||||
// used to define an array length. This is not supported by Metal.
|
||||
std::string length = std::to_string(srcDesc.length);
|
||||
std::string src_stride = std::to_string(srcDesc.stride);
|
||||
std::string dst_stride = std::to_string(dstDesc.stride);
|
||||
std::string work_group_size = std::to_string(workGroupSize);
|
||||
info.define("LENGTH", length);
|
||||
info.define("SRC_STRIDE", src_stride);
|
||||
info.define("DST_STRIDE", dst_stride);
|
||||
info.define("WORK_GROUP_SIZE", work_group_size);
|
||||
info.typedef_source("osd_patch_defines.glsl");
|
||||
info.typedef_source("osd_patch_basis.glsl");
|
||||
info.storage_buf(
|
||||
SHADER_SRC_VERTEX_BUFFER_BUF_SLOT, Qualifier::read, "float", "srcVertexBuffer[]");
|
||||
info.storage_buf(
|
||||
SHADER_DST_VERTEX_BUFFER_BUF_SLOT, Qualifier::write, "float", "dstVertexBuffer[]");
|
||||
info.push_constant(Type::int_t, "srcOffset");
|
||||
info.push_constant(Type::int_t, "dstOffset");
|
||||
|
||||
bool deriv1 = (duDesc.length > 0 || dvDesc.length > 0);
|
||||
if (deriv1) {
|
||||
info.define("OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES");
|
||||
info.storage_buf(SHADER_DU_BUFFER_BUF_SLOT, Qualifier::read_write, "float", "duBuffer[]");
|
||||
info.storage_buf(SHADER_DV_BUFFER_BUF_SLOT, Qualifier::read_write, "float", "dvBuffer[]");
|
||||
info.push_constant(Type::int3_t, "duDesc");
|
||||
info.push_constant(Type::int3_t, "dvDesc");
|
||||
}
|
||||
|
||||
info.storage_buf(
|
||||
SHADER_PATCH_ARRAY_BUFFER_BUF_SLOT, Qualifier::read, "OsdPatchArray", "patchArrayBuffer[]");
|
||||
info.storage_buf(
|
||||
SHADER_PATCH_COORDS_BUF_SLOT, Qualifier::read, "OsdPatchCoord", "patchCoords[]");
|
||||
info.storage_buf(
|
||||
SHADER_PATCH_INDEX_BUFFER_BUF_SLOT, Qualifier::read, "int", "patchIndexBuffer[]");
|
||||
info.storage_buf(
|
||||
SHADER_PATCH_PARAM_BUFFER_BUF_SLOT, Qualifier::read, "OsdPatchParam", "patchParamBuffer[]");
|
||||
|
||||
info.compute_source("osd_eval_patches_comp.glsl");
|
||||
blender::gpu::Shader *shader = GPU_shader_create_from_info(
|
||||
reinterpret_cast<const GPUShaderCreateInfo *>(&info));
|
||||
return shader;
|
||||
}
|
||||
|
||||
bool GPUComputeEvaluator::_PatchKernel::Compile(BufferDescriptor const &srcDesc,
|
||||
BufferDescriptor const &dstDesc,
|
||||
BufferDescriptor const &duDesc,
|
||||
BufferDescriptor const &dvDesc,
|
||||
int workGroupSize)
|
||||
{
|
||||
if (shader) {
|
||||
GPU_shader_free(shader);
|
||||
shader = nullptr;
|
||||
}
|
||||
|
||||
shader = compile_eval_patches_shader(srcDesc, dstDesc, duDesc, dvDesc, workGroupSize);
|
||||
if (shader == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// cache uniform locations
|
||||
uniformSrcOffset = GPU_shader_get_uniform(shader, "srcOffset");
|
||||
uniformDstOffset = GPU_shader_get_uniform(shader, "dstOffset");
|
||||
uniformDuDesc = GPU_shader_get_uniform(shader, "duDesc");
|
||||
uniformDvDesc = GPU_shader_get_uniform(shader, "dvDesc");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,113 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_patch_table.hh"
|
||||
|
||||
#include "opensubdiv/far/patchTable.h"
|
||||
#include "opensubdiv/osd/cpuPatchTable.h"
|
||||
|
||||
using namespace OpenSubdiv::Osd;
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
|
||||
GPUPatchTable *GPUPatchTable::Create(PatchTable const *far_patch_table, void * /*deviceContext*/)
|
||||
{
|
||||
GPUPatchTable *instance = new GPUPatchTable();
|
||||
if (instance->allocate(far_patch_table)) {
|
||||
return instance;
|
||||
}
|
||||
delete instance;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void discard_buffer(gpu::StorageBuf **buffer)
|
||||
{
|
||||
if (*buffer != nullptr) {
|
||||
GPU_storagebuf_free(*buffer);
|
||||
*buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void discard_list(std::vector<gpu::StorageBuf *> &buffers)
|
||||
{
|
||||
while (!buffers.empty()) {
|
||||
gpu::StorageBuf *buffer = buffers.back();
|
||||
buffers.pop_back();
|
||||
GPU_storagebuf_free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
GPUPatchTable::~GPUPatchTable()
|
||||
{
|
||||
discard_buffer(&_patchIndexBuffer);
|
||||
discard_buffer(&_patchParamBuffer);
|
||||
discard_buffer(&_varyingIndexBuffer);
|
||||
discard_list(_fvarIndexBuffers);
|
||||
discard_list(_fvarParamBuffers);
|
||||
}
|
||||
|
||||
bool GPUPatchTable::allocate(PatchTable const *far_patch_table)
|
||||
{
|
||||
CpuPatchTable patch_table(far_patch_table);
|
||||
|
||||
/* Patch array */
|
||||
size_t num_patch_arrays = patch_table.GetNumPatchArrays();
|
||||
_patchArrays.assign(patch_table.GetPatchArrayBuffer(),
|
||||
patch_table.GetPatchArrayBuffer() + num_patch_arrays);
|
||||
|
||||
/* Patch index buffer */
|
||||
const size_t index_size = patch_table.GetPatchIndexSize();
|
||||
_patchIndexBuffer = GPU_storagebuf_create_ex(
|
||||
index_size * sizeof(int32_t),
|
||||
static_cast<const void *>(patch_table.GetPatchIndexBuffer()),
|
||||
GPU_USAGE_STATIC,
|
||||
"osd_patch_index");
|
||||
|
||||
/* Patch param buffer */
|
||||
const size_t patch_param_size = patch_table.GetPatchParamSize();
|
||||
_patchParamBuffer = GPU_storagebuf_create_ex(patch_param_size * sizeof(PatchParam),
|
||||
patch_table.GetPatchParamBuffer(),
|
||||
GPU_USAGE_STATIC,
|
||||
"osd_patch_param");
|
||||
|
||||
/* Varying patch array */
|
||||
_varyingPatchArrays.assign(patch_table.GetVaryingPatchArrayBuffer(),
|
||||
patch_table.GetVaryingPatchArrayBuffer() + num_patch_arrays);
|
||||
|
||||
/* Varying index buffer */
|
||||
_varyingIndexBuffer = GPU_storagebuf_create_ex(patch_table.GetVaryingPatchIndexSize() *
|
||||
sizeof(uint32_t),
|
||||
patch_table.GetVaryingPatchIndexBuffer(),
|
||||
GPU_USAGE_STATIC,
|
||||
"osd_varying_index");
|
||||
|
||||
/* Face varying */
|
||||
const int num_face_varying_channels = patch_table.GetNumFVarChannels();
|
||||
_fvarPatchArrays.resize(num_face_varying_channels);
|
||||
_fvarIndexBuffers.resize(num_face_varying_channels);
|
||||
_fvarParamBuffers.resize(num_face_varying_channels);
|
||||
for (int index = 0; index < num_face_varying_channels; index++) {
|
||||
/* Face varying patch arrays */
|
||||
_fvarPatchArrays[index].assign(patch_table.GetFVarPatchArrayBuffer(),
|
||||
patch_table.GetFVarPatchArrayBuffer() + num_patch_arrays);
|
||||
|
||||
/* Face varying patch index buffer */
|
||||
_fvarIndexBuffers[index] = GPU_storagebuf_create_ex(patch_table.GetFVarPatchIndexSize(index) *
|
||||
sizeof(int32_t),
|
||||
patch_table.GetFVarPatchIndexBuffer(index),
|
||||
GPU_USAGE_STATIC,
|
||||
"osd_face_varying_index");
|
||||
|
||||
/* Face varying patch param buffer */
|
||||
_fvarParamBuffers[index] = GPU_storagebuf_create_ex(patch_table.GetFVarPatchParamSize(index) *
|
||||
sizeof(PatchParam),
|
||||
patch_table.GetFVarPatchParamBuffer(index),
|
||||
GPU_USAGE_STATIC,
|
||||
"osd_face_varying_params");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
@@ -0,0 +1,100 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_storage_buffer.hh"
|
||||
|
||||
#include <opensubdiv/version.h>
|
||||
|
||||
#include <opensubdiv/osd/nonCopyable.h>
|
||||
#include <opensubdiv/osd/types.h>
|
||||
|
||||
using OpenSubdiv::Far::PatchTable;
|
||||
using OpenSubdiv::Osd::NonCopyable;
|
||||
using OpenSubdiv::Osd::PatchArrayVector;
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
|
||||
// TODO: use Blenlib NonCopyable.
|
||||
class GPUPatchTable : private OpenSubdiv::Osd::NonCopyable<GPUPatchTable> {
|
||||
public:
|
||||
~GPUPatchTable();
|
||||
|
||||
static GPUPatchTable *Create(PatchTable const *farPatchTable, void *deviceContext = nullptr);
|
||||
|
||||
/// Returns the patch arrays for vertex index buffer data
|
||||
PatchArrayVector const &GetPatchArrays() const
|
||||
{
|
||||
return _patchArrays;
|
||||
}
|
||||
|
||||
/// Returns the GL index buffer containing the patch control vertices
|
||||
gpu::StorageBuf *GetPatchIndexBuffer() const
|
||||
{
|
||||
return _patchIndexBuffer;
|
||||
}
|
||||
|
||||
/// Returns the GL index buffer containing the patch parameter
|
||||
gpu::StorageBuf *GetPatchParamBuffer() const
|
||||
{
|
||||
return _patchParamBuffer;
|
||||
}
|
||||
|
||||
/// Returns the patch arrays for varying index buffer data
|
||||
PatchArrayVector const &GetVaryingPatchArrays() const
|
||||
{
|
||||
return _varyingPatchArrays;
|
||||
}
|
||||
|
||||
/// Returns the GL index buffer containing the varying control vertices
|
||||
gpu::StorageBuf *GetVaryingPatchIndexBuffer() const
|
||||
{
|
||||
return _varyingIndexBuffer;
|
||||
}
|
||||
|
||||
/// Returns the number of face-varying channel buffers
|
||||
int GetNumFVarChannels() const
|
||||
{
|
||||
return (int)_fvarPatchArrays.size();
|
||||
}
|
||||
|
||||
/// Returns the patch arrays for face-varying index buffer data
|
||||
PatchArrayVector const &GetFVarPatchArrays(int fvarChannel = 0) const
|
||||
{
|
||||
return _fvarPatchArrays[fvarChannel];
|
||||
}
|
||||
|
||||
/// Returns the GL index buffer containing face-varying control vertices
|
||||
gpu::StorageBuf *GetFVarPatchIndexBuffer(int fvarChannel = 0) const
|
||||
{
|
||||
return _fvarIndexBuffers[fvarChannel];
|
||||
}
|
||||
|
||||
/// Returns the GL index buffer containing face-varying patch params
|
||||
gpu::StorageBuf *GetFVarPatchParamBuffer(int fvarChannel = 0) const
|
||||
{
|
||||
return _fvarParamBuffers[fvarChannel];
|
||||
}
|
||||
|
||||
protected:
|
||||
GPUPatchTable() {}
|
||||
|
||||
// allocate buffers from patchTable
|
||||
bool allocate(PatchTable const *farPatchTable);
|
||||
|
||||
PatchArrayVector _patchArrays;
|
||||
|
||||
gpu::StorageBuf *_patchIndexBuffer = nullptr;
|
||||
gpu::StorageBuf *_patchParamBuffer = nullptr;
|
||||
|
||||
PatchArrayVector _varyingPatchArrays;
|
||||
gpu::StorageBuf *_varyingIndexBuffer = nullptr;
|
||||
|
||||
std::vector<PatchArrayVector> _fvarPatchArrays;
|
||||
std::vector<gpu::StorageBuf *> _fvarIndexBuffers;
|
||||
std::vector<gpu::StorageBuf *> _fvarParamBuffers;
|
||||
};
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
namespace blender::opensubdiv {
|
||||
|
||||
/**
|
||||
* GLVertexBuffer compatible API wrapped around a blender::gpu::VertBuf
|
||||
*
|
||||
* The blender::gpu::VertBuf is owned by the wrapper.
|
||||
* Vertex buffer is used as its API is able to wrap around SSBOs as well.
|
||||
*/
|
||||
class GPUVertexBuffer {
|
||||
gpu::VertBuf &gpu_vertex_buffer_;
|
||||
|
||||
/** Number of float elements/components does a single vertex have. */
|
||||
int element_count_;
|
||||
|
||||
/** Should we upload data directly to the GPU, or should we use a staging buffer. */
|
||||
bool use_update_sub_;
|
||||
|
||||
public:
|
||||
GPUVertexBuffer(gpu::VertBuf &gpu_vertex_buffer, int element_count, bool use_update_sub)
|
||||
: gpu_vertex_buffer_(gpu_vertex_buffer),
|
||||
element_count_(element_count),
|
||||
use_update_sub_(use_update_sub)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new gpu::VertBuf wrapped in a GPUVertexBuffer.
|
||||
*
|
||||
* @param element_count: Number of elements per vertex
|
||||
* @param vertex_len: Number of vertices
|
||||
* @param device_context: Unused.
|
||||
*/
|
||||
|
||||
static GPUVertexBuffer *Create(int element_count, int vertex_len, void *device_context = nullptr)
|
||||
{
|
||||
using namespace blender::gpu;
|
||||
(void)device_context;
|
||||
GPUVertFormat format;
|
||||
GPU_vertformat_clear(&format);
|
||||
switch (element_count) {
|
||||
case 4:
|
||||
GPU_vertformat_attr_add(&format, "elements", VertAttrType::SFLOAT_32_32_32_32);
|
||||
break;
|
||||
case 3:
|
||||
GPU_vertformat_attr_add(&format, "elements", VertAttrType::SFLOAT_32_32_32);
|
||||
break;
|
||||
case 2:
|
||||
GPU_vertformat_attr_add(&format, "elements", VertAttrType::SFLOAT_32_32);
|
||||
break;
|
||||
case 1:
|
||||
GPU_vertformat_attr_add(&format, "elements", VertAttrType::SFLOAT_32);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
const bool use_update_sub = GPU_backend_get_type() != GPU_BACKEND_VULKAN;
|
||||
gpu::VertBuf *vertex_buffer = nullptr;
|
||||
if (use_update_sub) {
|
||||
vertex_buffer = GPU_vertbuf_calloc();
|
||||
GPU_vertbuf_init_build_on_device(*vertex_buffer, format, vertex_len);
|
||||
}
|
||||
else {
|
||||
vertex_buffer = GPU_vertbuf_create_with_format_ex(format, GPU_USAGE_DYNAMIC);
|
||||
GPU_vertbuf_data_alloc(*vertex_buffer, vertex_len);
|
||||
}
|
||||
return new GPUVertexBuffer(*vertex_buffer, element_count, use_update_sub);
|
||||
}
|
||||
|
||||
/// Destructor.
|
||||
~GPUVertexBuffer()
|
||||
{
|
||||
GPU_vertbuf_discard(&gpu_vertex_buffer_);
|
||||
}
|
||||
|
||||
/// This method is meant to be used in client code in order to provide coarse
|
||||
/// vertices data to Osd.
|
||||
void UpdateData(const float *src,
|
||||
int start_vertex,
|
||||
int num_vertices,
|
||||
void *device_context = NULL)
|
||||
{
|
||||
(void)device_context;
|
||||
if (use_update_sub_) {
|
||||
GPU_vertbuf_use(&gpu_vertex_buffer_);
|
||||
size_t offset = start_vertex * element_count_ * sizeof(float);
|
||||
size_t data_len = num_vertices * element_count_ * sizeof(float);
|
||||
GPU_vertbuf_update_sub(&gpu_vertex_buffer_, offset, data_len, src);
|
||||
}
|
||||
else {
|
||||
MutableSpan<float> buffer_nodes = gpu_vertex_buffer_.data<float>();
|
||||
buffer_nodes = buffer_nodes.drop_front(start_vertex * element_count_);
|
||||
memcpy(buffer_nodes.data(), src, sizeof(float) * element_count_ * num_vertices);
|
||||
GPU_vertbuf_tag_dirty(&gpu_vertex_buffer_);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns how many vertices allocated in this vertex buffer.
|
||||
int GetNumVertices() const
|
||||
{
|
||||
return GPU_vertbuf_get_vertex_len(&gpu_vertex_buffer_);
|
||||
}
|
||||
|
||||
gpu::VertBuf *get_vertex_buffer()
|
||||
{
|
||||
return &gpu_vertex_buffer_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
193
blender-5.2.0/intern/opensubdiv/internal/evaluator/patch_map.cc
Normal file
193
blender-5.2.0/intern/opensubdiv/internal/evaluator/patch_map.cc
Normal file
@@ -0,0 +1,193 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Pixar
|
||||
* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Original code by Pixar with modifications by the Blender foundation. */
|
||||
|
||||
#include "internal/evaluator/patch_map.h"
|
||||
#include <algorithm>
|
||||
|
||||
using OpenSubdiv::Far::ConstPatchParamArray;
|
||||
using OpenSubdiv::Far::Index;
|
||||
using OpenSubdiv::Far::PatchParam;
|
||||
using OpenSubdiv::Far::PatchParamTable;
|
||||
using OpenSubdiv::Far::PatchTable;
|
||||
|
||||
namespace blender::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];
|
||||
}
|
||||
|
||||
int newChildNodeIndex = (int)_quadtree.size();
|
||||
_quadtree.emplace_back();
|
||||
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 = patchTable.GetNumPatchArrays();
|
||||
int numPatches = 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 blender::opensubdiv
|
||||
246
blender-5.2.0/intern/opensubdiv/internal/evaluator/patch_map.h
Normal file
246
blender-5.2.0/intern/opensubdiv/internal/evaluator/patch_map.h
Normal file
@@ -0,0 +1,246 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Pixar
|
||||
* SPDX-FileCopyrightText: 2021 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Original code by Pixar with modifications by the Blender foundation. */
|
||||
|
||||
#ifndef OPENSUBDIV_PATCH_MAP_H_
|
||||
#define OPENSUBDIV_PATCH_MAP_H_
|
||||
|
||||
#include <opensubdiv/far/patchTable.h>
|
||||
|
||||
namespace blender::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];
|
||||
};
|
||||
|
||||
using Handle = OpenSubdiv::Far::PatchTable::PatchHandle;
|
||||
|
||||
/// \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);
|
||||
|
||||
using QuadTree = std::vector<QuadNode>;
|
||||
|
||||
// Internal methods supporting quadtree construction and queries
|
||||
void assignRootNode(QuadNode *node, int index);
|
||||
QuadNode *assignLeafOrChildNode(QuadNode *node, bool isLeaf, int quadrant, 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
QuadNode const *node = &_quadtree[faceid - _minPatchFace];
|
||||
|
||||
if (!node->children[0].isSet) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//
|
||||
// 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];
|
||||
}
|
||||
node = &_quadtree[node->children[quadrant].index];
|
||||
}
|
||||
assert(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::opensubdiv
|
||||
|
||||
#endif // OPENSUBDIV_PATCH_MAP_H_
|
||||
@@ -0,0 +1,150 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* `osd_patch_defines.glsl` must be included before `osd_patch_basis.glsl` */
|
||||
#include "osd_patch_defines.glsl"
|
||||
|
||||
#include "osd_patch_basis.glsl"
|
||||
|
||||
/* Runtime create info. */
|
||||
GPU_SHADER_CREATE_INFO(osd_eval_patches_comp)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
OsdPatchCoord GetPatchCoord(int coordIndex)
|
||||
{
|
||||
return patchCoords[coordIndex];
|
||||
}
|
||||
|
||||
OsdPatchArray GetPatchArray(int arrayIndex)
|
||||
{
|
||||
return patchArrayBuffer[arrayIndex];
|
||||
}
|
||||
|
||||
OsdPatchParam GetPatchParam(int patchIndex)
|
||||
{
|
||||
return patchParamBuffer[patchIndex];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct Vertex {
|
||||
float vertexData[LENGTH];
|
||||
};
|
||||
|
||||
void clear(out Vertex v)
|
||||
{
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Vertex readVertex(int index)
|
||||
{
|
||||
Vertex v;
|
||||
int vertexIndex = srcOffset + index * SRC_STRIDE;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] = srcVertexBuffer[vertexIndex + i];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void writeVertex(int index, Vertex v)
|
||||
{
|
||||
int vertexIndex = dstOffset + index * DST_STRIDE;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dstVertexBuffer[vertexIndex + i] = v.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
void addWithWeight(Vertex &v, const Vertex src, float weight)
|
||||
{
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] += weight * src.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
void writeDu(int index, Vertex du)
|
||||
{
|
||||
int duIndex = duDesc.x + index * duDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
duBuffer[duIndex + i] = du.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
void writeDv(int index, Vertex dv)
|
||||
{
|
||||
int dvIndex = dvDesc.x + index * dvDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dvBuffer[dvIndex + i] = dv.vertexData[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PERFORMANCE: stride could be constant, but not as significant as length
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
int current = int(gl_GlobalInvocationID.x);
|
||||
|
||||
OsdPatchCoord coord = GetPatchCoord(current);
|
||||
OsdPatchArray array = GetPatchArray(coord.arrayIndex);
|
||||
OsdPatchParam param = GetPatchParam(coord.patchIndex);
|
||||
|
||||
int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
|
||||
|
||||
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
|
||||
int nPoints = OsdEvaluatePatchBasis(
|
||||
patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
|
||||
|
||||
Vertex dst, du, dv, duu, duv, dvv;
|
||||
clear(dst);
|
||||
clear(du);
|
||||
clear(dv);
|
||||
|
||||
int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase);
|
||||
|
||||
for (int cv = 0; cv < nPoints; ++cv) {
|
||||
int index = patchIndexBuffer[indexBase + cv];
|
||||
addWithWeight(dst, readVertex(index), wP[cv]);
|
||||
addWithWeight(du, readVertex(index), wDu[cv]);
|
||||
addWithWeight(dv, readVertex(index), wDv[cv]);
|
||||
}
|
||||
writeVertex(current, dst);
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
if (duDesc.y > 0) { // length
|
||||
writeDu(current, du);
|
||||
}
|
||||
if (dvDesc.y > 0) {
|
||||
writeDv(current, dv);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#include "gpu_shader_compat.hh"
|
||||
|
||||
/* `osd_patch_defines.glsl` must be included before `osd_patch_basis.glsl` */
|
||||
#include "osd_patch_defines.glsl"
|
||||
|
||||
#include "osd_patch_basis.glsl"
|
||||
|
||||
/* Runtime create info. */
|
||||
GPU_SHADER_CREATE_INFO(osd_eval_stencils_comp)
|
||||
GPU_SHADER_CREATE_END()
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint getGlobalInvocationIndex()
|
||||
{
|
||||
uint invocations_per_row = gl_WorkGroupSize.x * gl_NumWorkGroups.x;
|
||||
return gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * invocations_per_row;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct Vertex {
|
||||
float vertexData[LENGTH];
|
||||
};
|
||||
|
||||
void clear(out Vertex v)
|
||||
{
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Vertex readVertex(int index)
|
||||
{
|
||||
Vertex v;
|
||||
int vertexIndex = srcOffset + index * SRC_STRIDE;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] = srcVertexBuffer[vertexIndex + i];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void writeVertex(int index, Vertex v)
|
||||
{
|
||||
int vertexIndex = dstOffset + index * DST_STRIDE;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dstVertexBuffer[vertexIndex + i] = v.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
void addWithWeight(Vertex &v, const Vertex src, float weight)
|
||||
{
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] += weight * src.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
void writeDu(int index, Vertex du)
|
||||
{
|
||||
int duIndex = duDesc.x + index * duDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
duBuffer[duIndex + i] = du.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
void writeDv(int index, Vertex dv)
|
||||
{
|
||||
int dvIndex = dvDesc.x + index * dvDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dvBuffer[dvIndex + i] = dv.vertexData[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void main()
|
||||
{
|
||||
int current = int(getGlobalInvocationIndex()) + batchStart;
|
||||
|
||||
if (current >= batchEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vertex dst;
|
||||
clear(dst);
|
||||
|
||||
int offset = offsets_buf[current], size = sizes_buf[current];
|
||||
|
||||
for (int stencil = 0; stencil < size; ++stencil) {
|
||||
int vindex = offset + stencil;
|
||||
addWithWeight(dst, readVertex(indices_buf[vindex]), weights_buf[vindex]);
|
||||
}
|
||||
|
||||
writeVertex(current, dst);
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
Vertex du, dv;
|
||||
clear(du);
|
||||
clear(dv);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
// expects the compiler optimizes readVertex out here.
|
||||
Vertex src = readVertex(indices_buf[offset + i]);
|
||||
addWithWeight(du, src, du_weights_buf[offset + i]);
|
||||
addWithWeight(dv, src, dv_weights_buf[offset + i]);
|
||||
}
|
||||
|
||||
if (duDesc.y > 0) { // length
|
||||
writeDu(current, du);
|
||||
}
|
||||
if (dvDesc.y > 0) {
|
||||
writeDv(current, dv);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user