Files
test/intern/opensubdiv/opensubdiv_evaluator.hh
Jeroen Bakker 40696ca452 SubDiv: Migrate GPU subdivision to use GPU module
Blender already had its own copy of OpenSubDiv containing some local fixes
and code-style. This code still used gl-calls. This PR updates the calls
to use GPU module. This allows us to use OpenSubDiv to be usable on other
backends as well.

This PR was tested on OpenGL, Vulkan and Metal. Metal can be enabled,
but Vulkan requires some API changes to work with loose geometry.

![metal.png](/attachments/bb042c3a-1a87-4140-9958-a80da10d417b)

# Considerations

**ShaderCreateInfo**

intern/opensubdiv now requires access to GPU module. This to create buffers
in the correct context and trigger correct dispatches. ShaderCreateInfo is used
to construct the shader for cross compilation to Metal/Vulkan. However opensubdiv
shader caching structures are still used.

**Vertex buffers vs storage buffers**

Implementation tries to keep as close to the original OSD implementation. If
they used storage buffers for data, we will use GPUStorageBuf. If it uses vertex
buffers, we will use gpu::VertBuf.

**Evaluator const**

The evaluator cannot be const anymore as the GPU module API only allows
updating SSBOs when constructing. API could be improved to support updating
SSBOs.

Current implementation has a change to use reads out of bounds when constructing
SSBOs. An API change is in the planning to remove this issue. This will be fixed in
an upcoming PR. We wanted to land this PR as the visibility of the issue is not
common and multiple other changes rely on this PR to land.

Pull Request: https://projects.blender.org/blender/blender/pulls/135296
2025-03-10 07:31:59 +01:00

200 lines
7.8 KiB
C++

/* SPDX-FileCopyrightText: 2018 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Author: Sergey Sharybin. */
#ifndef OPENSUBDIV_EVALUATOR_IMPL_H_
#define OPENSUBDIV_EVALUATOR_IMPL_H_
#ifdef _MSC_VER
# include <iso646.h>
#endif
#include <opensubdiv/far/patchMap.h>
#include <opensubdiv/far/patchTable.h>
#include "opensubdiv_capi_type.hh"
#include "GPU_storage_buffer.hh"
struct OpenSubdiv_EvaluatorCache;
struct OpenSubdiv_EvaluatorSettings;
struct OpenSubdiv_PatchCoord;
namespace blender::gpu {
class VertBuf;
}
namespace blender::opensubdiv {
class TopologyRefinerImpl;
class PatchMap;
// Wrapper around implementation, which defines API which we are capable to
// provide over the implementation.
class EvalOutputAPI {
public:
// Anonymous forward declaration of actual evaluator implementation.
class EvalOutput;
// NOTE: PatchMap is not owned, only referenced.
EvalOutputAPI(EvalOutput *implementation, PatchMap *patch_map);
~EvalOutputAPI();
// Set settings for data buffers.
void setSettings(const OpenSubdiv_EvaluatorSettings *settings);
// Set coarse positions from a continuous array of coordinates.
void setCoarsePositions(const float *positions,
const int start_vertex_index,
const int num_vertices);
// Set vertex data from a continuous array of data.
void setVertexData(const float *data, const int start_vertex_index, const int num_vertices);
// Set varying data from a continuous array of data.
void setVaryingData(const float *varying_data,
const int start_vertex_index,
const int num_vertices);
// Set face varying data from a continuous array of data.
//
// TODO(sergey): Find a better name for vertex here. It is not the vertex of
// geometry, but a vertex of UV map.
void setFaceVaryingData(const int face_varying_channel,
const float *varying_data,
const int start_vertex_index,
const int num_vertices);
// Set coarse vertex position from a continuous memory buffer where
// first coordinate starts at offset of `start_offset` and there is `stride`
// bytes between adjacent vertex coordinates.
void setCoarsePositionsFromBuffer(const void *buffer,
const int start_offset,
const int stride,
const int start_vertex_index,
const int num_vertices);
// Set varying data from a continuous memory buffer where
// first coordinate starts at offset of `start_offset` and there is `stride`
// bytes between adjacent vertex coordinates.
void setVaryingDataFromBuffer(const void *buffer,
const int start_offset,
const int stride,
const int start_vertex_index,
const int num_vertices);
// Set face varying data from a continuous memory buffer where
// first coordinate starts at offset of `start_offset` and there is `stride`
// bytes between adjacent vertex coordinates.
//
// TODO(sergey): Find a better name for vertex here. It is not the vertex of
// geometry, but a vertex of UV map.
void 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);
// Refine after coarse positions update.
void refine();
// Evaluate given ptex face at given bilinear coordinate.
// If derivatives are NULL, they will not be evaluated.
void evaluateLimit(const int ptex_face_index,
float face_u,
float face_v,
float P[3],
float dPdu[3],
float dPdv[3]);
// Evaluate varying data at a given bilinear coordinate of given ptex face.
void evaluateVertexData(const int ptex_face_index, float face_u, float face_v, float data[]);
// Evaluate varying data at a given bilinear coordinate of given ptex face.
void evaluateVarying(const int ptex_face_index, float face_u, float face_v, float varying[3]);
// Evaluate facee-varying data at a given bilinear coordinate of given
// ptex face.
void evaluateFaceVarying(const int face_varying_channel,
const int ptex_face_index,
float face_u,
float face_v,
float face_varying[2]);
// Batched evaluation of multiple input coordinates.
// Evaluate given ptex face at given bilinear coordinate.
// If derivatives are NULL, they will not be evaluated.
//
// NOTE: Output arrays must point to a memory of size float[3]*num_patch_coords.
void evaluatePatchesLimit(const OpenSubdiv_PatchCoord *patch_coords,
const int num_patch_coords,
float *P,
float *dPdu,
float *dPdv);
// Fill the output buffers and variables with data from the PatchMap.
void 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);
// Copy the patch arrays buffer used by OpenSubDiv for the source data to the given buffer.
GPUStorageBuf *create_patch_arrays_buf();
// Wrap the patch index buffer used by OpenSubDiv for the source data with the given buffer.
GPUStorageBuf *get_patch_index_buf();
// Wrap the patch param buffer used by OpenSubDiv for the source data with the given buffer.
GPUStorageBuf *get_patch_param_buf();
// Wrap the buffer used by OpenSubDiv for the source data with the given buffer.
gpu::VertBuf *get_source_buf();
// Wrap the buffer used by OpenSubDiv for the extra source data with the given buffer.
gpu::VertBuf *get_source_data_buf();
// Copy the patch arrays buffer used by OpenSubDiv for the face varying channel with the given
// buffer.
GPUStorageBuf *create_face_varying_patch_array_buf(const int face_varying_channel);
// Wrap the patch index buffer used by OpenSubDiv for the face varying channel with the given
// buffer.
GPUStorageBuf *get_face_varying_patch_index_buf(const int face_varying_channel);
// Wrap the patch param buffer used by OpenSubDiv for the face varying channel with the given
// buffer.
GPUStorageBuf *get_face_varying_patch_param_buf(const int face_varying_channel);
// Wrap thebuffer used by OpenSubDiv for the face varying channel with the given buffer.
gpu::VertBuf *get_face_varying_source_buf(const int face_varying_channel);
/** Get the source buffer offset for the given channel. */
int get_face_varying_source_offset(const int face_varying_channel) const;
// Return true if source vertex data has been set.
bool hasVertexData() const;
protected:
PatchMap *patch_map_;
EvalOutput *implementation_;
};
} // namespace blender::opensubdiv
struct OpenSubdiv_Evaluator {
blender::opensubdiv::EvalOutputAPI *eval_output;
const blender::opensubdiv::PatchMap *patch_map;
const OpenSubdiv::Far::PatchTable *patch_table;
eOpenSubdivEvaluator type;
OpenSubdiv_Evaluator();
~OpenSubdiv_Evaluator();
};
OpenSubdiv_Evaluator *openSubdiv_createEvaluatorFromTopologyRefiner(
blender::opensubdiv::TopologyRefinerImpl *topology_refiner,
eOpenSubdivEvaluator evaluator_type,
OpenSubdiv_EvaluatorCache *evaluator_cache_descr);
#endif // OPENSUBDIV_EVALUATOR_IMPL_H_