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.  # 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
103 lines
3.1 KiB
C++
103 lines
3.1 KiB
C++
/* 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)
|
|
{
|
|
(void)device_context;
|
|
GPUVertFormat format;
|
|
GPU_vertformat_clear(&format);
|
|
GPU_vertformat_attr_add(&format, "elements", GPU_COMP_F32, element_count, GPU_FETCH_FLOAT);
|
|
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
|