@@ -18,20 +18,18 @@ namespace render {
1818class 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 */
3634template <typename T>
3735class 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 {
247233protected:
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
0 commit comments