Skip to content

Commit e2a8ebc

Browse files
committed
ManagedBuffer now owns data arrays, rather than wrapping
1 parent 735a44c commit e2a8ebc

34 files changed

Lines changed: 504 additions & 370 deletions

include/polyscope/color_quantity.ipp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ template <typename QuantityT>
2727
template <class V>
2828
void ColorQuantity<QuantityT>::updateData(const V& newColors) {
2929
validateSize(newColors, colors.size(), "color quantity");
30-
colors.data = standardizeVectorArray<glm::vec3, 3>(newColors);
30+
auto d = standardizeVectorArray<glm::vec3, 3>(newColors);
31+
colors.resize(d.size());
32+
colors.setDataHost(d);
3133
colors.markHostBufferUpdated();
3234
}
3335

include/polyscope/color_render_image_quantity.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ void ColorRenderImageQuantity::updateBuffers(const T1& depthData, const T2& norm
5757
std::vector<glm::vec3> standardNormal(standardizeVectorArray<glm::vec3, 3>(normalData));
5858
std::vector<glm::vec3> standardColor(standardizeVectorArray<glm::vec3, 3>(colorsData));
5959

60-
colors.data = standardColor;
60+
colors.setDataHost(standardColor);
6161
colors.markHostBufferUpdated();
6262

6363
updateBaseBuffers(standardDepth, standardNormal);

include/polyscope/curve_network.ipp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@ CurveNetwork* registerCurveNetworkLoop2D(std::string name, const P& nodes) {
170170
template <class V>
171171
void CurveNetwork::updateNodePositions(const V& newPositions) {
172172
validateSize(newPositions, nNodes(), "newPositions");
173-
nodePositions.data = standardizeVectorArray<glm::vec3, 3>(newPositions);
173+
auto d = standardizeVectorArray<glm::vec3, 3>(newPositions);
174+
nodePositions.resize(d.size());
175+
nodePositions.setDataHost(d);
174176
nodePositions.markHostBufferUpdated();
175177
recomputeGeometryIfPopulated();
176178
}

include/polyscope/parameterization_quantity.ipp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ template <typename QuantityT>
226226
template <class V>
227227
void ParameterizationQuantity<QuantityT>::updateCoords(const V& newCoords) {
228228
validateSize(newCoords, coords.size(), "parameterization quantity " + quantity.name);
229-
coords.data = standardizeVectorArray<glm::vec2, 2>(newCoords);
229+
auto d = standardizeVectorArray<glm::vec2, 2>(newCoords);
230+
coords.resize(d.size());
231+
coords.setDataHost(d);
230232
coords.markHostBufferUpdated();
231233
}
232234

include/polyscope/point_cloud.ipp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ PointCloud* registerPointCloud2D(std::string name, const T& points) {
3636
template <class V>
3737
void PointCloud::updatePointPositions(const V& newPositions) {
3838
validateSize(newPositions, nPoints(), "point cloud updated positions " + name);
39-
points.data = standardizeVectorArray<glm::vec3, 3>(newPositions);
39+
auto d = standardizeVectorArray<glm::vec3, 3>(newPositions);
40+
points.resize(d.size());
41+
points.setDataHost(d);
4042
points.markHostBufferUpdated();
4143
}
4244

include/polyscope/raw_color_alpha_render_image_quantity.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ void RawColorAlphaRenderImageQuantity::updateBuffers(const T1& depthData, const
5656
std::vector<glm::vec3> standardNormal;
5757
std::vector<glm::vec4> standardColor(standardizeVectorArray<glm::vec4, 4>(colorsData));
5858

59-
colors.data = standardColor;
59+
colors.setDataHost(standardColor);
6060
colors.markHostBufferUpdated();
6161

6262
updateBaseBuffers(standardDepth, standardNormal);

include/polyscope/raw_color_render_image_quantity.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ void RawColorRenderImageQuantity::updateBuffers(const T1& depthData, const T2& c
5353
std::vector<glm::vec3> standardNormal;
5454
std::vector<glm::vec3> standardColor(standardizeVectorArray<glm::vec3, 3>(colorsData));
5555

56-
colors.data = standardColor;
56+
colors.setDataHost(standardColor);
5757
colors.markHostBufferUpdated();
5858

5959
updateBaseBuffers(standardDepth, standardNormal);

include/polyscope/render/managed_buffer.h

Lines changed: 64 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,18 @@ namespace render {
1818
class ManagedBufferRegistry;
1919

2020
/*
21-
* This class is a wrapper which sits on top of data buffers in Polyscope, and handles common data-management concerns
22-
* of:
21+
* This class owns and manages a typed data buffer in Polyscope, handling common data-management concerns of:
2322
*
2423
* (a) mirroring the buffer to the GPU/rendering framework
2524
* (b) allowing external users to update data either on the CPU- or GPU-side, and updating mirrored copies
2625
* appropriately
27-
* (c) managed _indexed_ data, which gts expanded according to some index set for access at rendering time.
26+
* (c) managing _indexed_ data, which gets expanded according to some index set for access at rendering time.
2827
*
29-
* Most often this class is used to wrap structure/quantity data passed in by the user, such as a scalar quantity, but
30-
* it is also sometimes wraps automatically-computed values within Polyscope, such as a vertex normal buffer for
28+
* Most often this class is used to hold structure/quantity data passed in by the user, such as a scalar quantity, but
29+
* it also sometimes holds automatically-computed values within Polyscope, such as a vertex normal buffer for
3130
* rendering.
3231
*
33-
* This class offers functions and accessors which can (and generally, MUST) be used to interact with the underlying
34-
* data buffer.
32+
* Use the public accessor API (getHostValue, setHostValue, setDataHost, begin/end, etc.) to interact with the buffer.
3533
*/
3634
template <typename T>
3735
class ManagedBuffer : public virtual WeakReferrable {
@@ -63,31 +61,6 @@ class ManagedBuffer : public virtual WeakReferrable {
6361
ManagedBufferRegistry* registry;
6462

6563

66-
// The raw underlying buffer which this class owns and holds the data.
67-
//
68-
// It is possible that data.size() == 0 if the data is lazily computed and has not been computed yet, or if this
69-
// host-side buffer is invalidated because it is being updated externally directly on the render device.
70-
//
71-
// External users can write directly for this buffer. The required order of operations for writing to this buffer is:
72-
// buff.ensureHostBufferAllocated();
73-
// buff.data = // fill .data with your values
74-
// buff.markHostBufferUpdated();
75-
//
76-
//
77-
std::vector<T> data;
78-
79-
// == Members for computed data
80-
81-
// This class somewhat weirdly tracks data from two separate sources:
82-
// A) Values directly stored in `data` by an external source
83-
// B) Values that get computed lazily by some function when needed
84-
//
85-
// When `dataGetsComputed = true`, we are in Case B, and computeFunc() must be set to a callback that does the
86-
// lazy computing.
87-
88-
bool dataGetsComputed; // if true, the value gets computed on-demand by calling computeFunc()
89-
std::function<void()> computeFunc; // (optional) callback which populates the `data` buffer
90-
9164
// sanity check helper
9265
void checkInvalidValues();
9366

@@ -108,7 +81,7 @@ class ManagedBuffer : public virtual WeakReferrable {
10881
// But in settings where we e.g. incrementally add elements or change the number of data elements on each frame, we
10982
// may want to allocate a larger capacity to avoid expensive re-allocation each time.
11083

111-
// Resize the buffer to newSize elements.
84+
// Resize the buffer to newSize elements. Sets hostBufferIsPopulated = true.
11285
//
11386
// If newSize <= capacity(), this is a cheap constant-time operation which just updates metadata.
11487
//
@@ -140,33 +113,46 @@ class ManagedBuffer : public virtual WeakReferrable {
140113
// doubling, this sets the logical capacity to a precise value. Error if newCapacity < size().
141114
// Reallocates the GPU buffer in-place (same buffer object, new backing memory) if one exists.
142115
//
143-
// Note: data.capacity() is guaranteed to be >= managedCapacity, but may remain larger than
144-
// newCapacity if the underlying vector already had more space allocated.
145-
//
146116
// Valid for attributes and 1D textures only; multidimensional textures always have capacity
147117
// equal to their size, so use the 2D/3D resize() variants instead.
148118
void setCapacity(size_t newCapacity);
149119

150120

151-
// == Members for indexed data
152-
153121
// == Basic interactions
154122

155-
// Ensure that the `data` member vector reference is populated with the current values. In the common-case where
156-
// the user sets data and it never changes, then this function will do nothing. However, if e.g. the value is
157-
// being updated directly from GPU memory, this will mirror the updates to the cpu-side vector. Also, if the value
158-
// is lazily computed by computeFunc(), it ensures that that function has been called.
123+
// Ensure that the host buffer is populated with the current values. In the common case where the user sets data
124+
// and it never changes, this does nothing. However, if the value is being updated directly from GPU memory, this
125+
// mirrors the updates to the host-side buffer. Also, if the value is lazily computed by computeFunc(), it ensures
126+
// that function has been called.
159127
void ensureHostBufferPopulated();
160128

161-
// Ensure that the `data` member has the proper size. This does _not_ populate the buffer with any particular data,
162-
// just ensures it is allocated. It is useful for when an external wants to fill the buffer with data.
163-
void ensureHostBufferAllocated();
164-
165-
// Combines calling ensureHostBufferPopulated() and returning a reference to the `data` member
166-
std::vector<T>& getPopulatedHostBufferRef();
167-
168-
// If the contents of `data` are updated, this function MUST be called. It internally handles concerns like
169-
// reflecting updates to the render buffer.
129+
// Copy newData into the host buffer. Errors if newData.size() > capacity() — call resize() or
130+
// setCapacity() first if needed. Does not change capacity. Automatically syncs to any allocated
131+
// device buffers and triggers a redraw. Prefer this over setHostValue() for bulk writes.
132+
void setDataHost(const std::vector<T>& newData);
133+
134+
// Iterator support. Caller must call ensureHostBufferPopulated() before using these.
135+
// A debug-mode check will fire if this precondition is violated.
136+
const T* begin() const;
137+
const T* end() const;
138+
139+
// Single-element read. Caller MUST call ensureHostBufferPopulated() before calling this
140+
// (especially in loops — call it once before the loop, not per element).
141+
// A debug-mode check will fire if this precondition is violated.
142+
T getHostValue(size_t ind) const;
143+
T getHostValue(size_t indX, size_t indY) const; // only valid for 2d texture data
144+
T getHostValue(size_t indX, size_t indY, size_t indZ) const; // only valid for 3d texture data
145+
146+
// Single-element write. Caller MUST call ensureHostBufferPopulated() (or resize()) before
147+
// calling this. Automatically syncs to any allocated device buffers and triggers a redraw.
148+
// For bulk writes, prefer setDataHost() to avoid redundant GPU uploads.
149+
// A debug-mode check will fire if the precondition is violated.
150+
void setHostValue(size_t ind, T val);
151+
void setHostValue(size_t indX, size_t indY, T val); // only valid for 2d texture data
152+
void setHostValue(size_t indX, size_t indY, size_t indZ, T val); // only valid for 3d texture data
153+
154+
// If the contents of the host buffer have been updated externally, this function MUST be called.
155+
// It internally handles concerns like reflecting updates to the render buffer.
170156
void markHostBufferUpdated();
171157

172158
// Get the value at index `i`. It may be dynamically fetched from either the cpu-side `data` member or the render
@@ -247,6 +233,17 @@ class ManagedBuffer : public virtual WeakReferrable {
247233
protected:
248234
// == Internal members
249235

236+
// This class tracks data from two separate sources:
237+
// A) Values directly stored in the buffer by an external source (dataGetsComputed == false)
238+
// B) Values that get computed lazily by some function when needed (dataGetsComputed == true)
239+
//
240+
// When dataGetsComputed is true, computeFunc() must be set to a callback that populates the buffer.
241+
// The callback should call resize() then setHostValue() to populate the buffer.
242+
// The callback does NOT need to call markHostBufferUpdated() — that is handled by the ManagedBuffer
243+
// infrastructure after the callback returns.
244+
bool dataGetsComputed; // if true, the value gets computed on-demand by calling computeFunc()
245+
std::function<void()> computeFunc; // (optional) callback which populates the buffer
246+
250247
bool hostBufferIsPopulated; // true if the host buffer contains currently-valid data
251248

252249
// The buffer has capacity for at least this many elements. It is distinct from .size(), which is the actual number of
@@ -278,17 +275,27 @@ class ManagedBuffer : public virtual WeakReferrable {
278275
// == Internal helper functions
279276

280277
void invalidateHostBuffer();
281-
bool deviceBufferTypeIsTexture();
282-
void checkDeviceBufferTypeIs(DeviceBufferType targetType);
283-
void checkDeviceBufferTypeIsTexture();
278+
bool deviceBufferTypeIsTexture() const;
279+
void checkDeviceBufferTypeIs(DeviceBufferType targetType) const;
280+
void checkDeviceBufferTypeIsTexture() const;
284281

285282
enum class CanonicalDataSource { HostData = 0, NeedsCompute, RenderBuffer };
286283
CanonicalDataSource currentCanonicalDataSource();
287284

288-
// Manage the program which copies indexed data from the renderBuffer to the indexed views
289-
void ensureHaveBufferIndexCopyProgram();
290-
void invokeBufferIndexCopyProgram();
285+
// TODO: add direct GPU-side indexed copy support using a transform-feedback/compute shader program.
291286
std::shared_ptr<render::ShaderProgram> bufferIndexCopyProgram;
287+
288+
private:
289+
template <typename U> friend class ManagedBuffer;
290+
291+
// The raw underlying buffer which this class owns and holds the data.
292+
//
293+
// It is possible that data.size() == 0 if the data is lazily computed and has not been computed yet, or if this
294+
// host-side buffer is invalidated because it is being updated externally directly on the render device.
295+
//
296+
// Use the accessor API (getHostValue, setHostValue, setDataHost, begin/end) to interact
297+
// with this buffer rather than accessing it directly.
298+
std::vector<T> data;
292299
};
293300

294301

include/polyscope/scalar_quantity.ipp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace polyscope {
99
template <typename QuantityT>
1010
ScalarQuantity<QuantityT>::ScalarQuantity(QuantityT& quantity_, const std::vector<float>& values_, DataType dataType_)
1111
: quantity(quantity_), values(&quantity, quantity.uniquePrefix() + "values", std::vector<float>(values_)),
12-
dataType(dataType_), dataRange(robustMinMax(values.data, 1e-5)),
12+
dataType(dataType_), dataRange(robustMinMax(values_, 1e-5)),
1313
vizRangeMin(quantity.uniquePrefix() + "vizRangeMin", -777.), // set later,
1414
vizRangeMax(quantity.uniquePrefix() + "vizRangeMax", -777.), // including clearing cache
1515
colorBar(quantity), cMap(quantity.uniquePrefix() + "cmap", defaultColorMap(dataType)),
@@ -23,7 +23,7 @@ ScalarQuantity<QuantityT>::ScalarQuantity(QuantityT& quantity_, const std::vecto
2323
{
2424
values.checkInvalidValues();
2525
colorBar.updateColormap(cMap.get());
26-
colorBar.buildHistogram(values.data, dataType);
26+
colorBar.buildHistogram(values_, dataType);
2727
// TODO: I think we might be building the histogram ^^^ twice for many quantities
2828

2929
if (vizRangeMin.holdsDefaultValue()) { // min and max should always have same cache state
@@ -312,7 +312,9 @@ template <typename QuantityT>
312312
template <class V>
313313
void ScalarQuantity<QuantityT>::updateData(const V& newValues) {
314314
validateSize(newValues, values.size(), "scalar quantity " + quantity.name);
315-
values.data = standardizeArray<float, V>(newValues);
315+
auto d = standardizeArray<float, V>(newValues);
316+
values.resize(d.size());
317+
values.setDataHost(d);
316318
values.markHostBufferUpdated();
317319
}
318320

include/polyscope/scalar_render_image_quantity.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ void ScalarRenderImageQuantity::updateBuffers(const T1& depthData, const T2& nor
5353
std::vector<glm::vec3> standardNormal(standardizeVectorArray<glm::vec3, 3>(normalData));
5454
std::vector<float> standardScalar(standardizeArray<float>(scalarData));
5555

56-
values.data = standardScalar;
56+
values.setDataHost(standardScalar);
5757
values.markHostBufferUpdated();
5858

5959
updateBaseBuffers(standardDepth, standardNormal);

0 commit comments

Comments
 (0)