Added VertexBufferUpdateCallback 61/290561/8
authorAdam Bialogonski <adam.b@samsung.com>
Tue, 28 Mar 2023 20:52:18 +0000 (21:52 +0100)
committerAdam Bialogonski <adam.b@samsung.com>
Fri, 12 May 2023 13:34:47 +0000 (14:34 +0100)
Added the callback invoked when the vertex buffer needs to be updated.
The callback passes pointer and maximum size that should be written.

The callback returns number of valid elements to draw.

In case there are more than 1 vertex buffers attached, the render geometry iterates and looks for lowest number of elements to render (so we won't go out of boundaries of any of attached buffers).

The update of VertexBuffer through the callback takes place on the update/render thread and it's up to the user to ensure explicit synchronization. Event side DALi objects must not be accessed (most likely, that will fail) so alternative way of
passing data should be added by developers.

Change-Id: I4db7812cf6d6579c8cd05bcc5c25d966f6ae1aad

14 files changed:
automated-tests/src/dali/utc-Dali-VertexBuffer.cpp
dali/internal/event/rendering/vertex-buffer-impl.cpp
dali/internal/event/rendering/vertex-buffer-impl.h
dali/internal/render/common/render-manager.cpp
dali/internal/render/common/render-manager.h
dali/internal/render/renderers/gpu-buffer.cpp
dali/internal/render/renderers/gpu-buffer.h
dali/internal/render/renderers/render-geometry.cpp
dali/internal/render/renderers/render-vertex-buffer.cpp
dali/internal/render/renderers/render-vertex-buffer.h
dali/internal/update/manager/update-manager.cpp
dali/internal/update/manager/update-manager.h
dali/public-api/rendering/vertex-buffer.cpp
dali/public-api/rendering/vertex-buffer.h

index b03d938..079a3fe 100644 (file)
 
 #include <dali-test-suite-utils.h>
 #include <dali/public-api/dali-core.h>
-
+#include <chrono>
+using namespace std::chrono_literals;
 using namespace Dali;
 
 #include <mesh-builder.h>
+#include <future>
+
+struct VertexBufferUpdater
+{
+  struct Diagnostics
+  {
+    uint32_t counter{0};
+    void*    lastPtr{nullptr};
+    size_t   lastSize{0};
+    size_t   lastReturned{0};
+  };
+
+  VertexBufferUpdater() = default;
+
+  uint32_t UpdateVertices(void* ptr, size_t size)
+  {
+    diagnostics.lastPtr      = ptr;
+    diagnostics.lastSize     = size;
+    diagnostics.lastReturned = returnSize;
+    diagnostics.counter++;
+
+    promise.set_value(diagnostics);
+    return returnSize;
+  }
+
+  void SetCallbackReturnValue(size_t size)
+  {
+    returnSize = size;
+  }
+
+  void Reset()
+  {
+    promise = std::promise<Diagnostics>();
+  }
+
+  std::unique_ptr<VertexBufferUpdateCallback> CreateCallback()
+  {
+    return VertexBufferUpdateCallback::New(this, &VertexBufferUpdater::UpdateVertices);
+  }
+
+  Diagnostics GetValue()
+  {
+    auto value = promise.get_future().get();
+
+    // reset promise automatically
+    promise = {};
+    return value;
+  }
+
+  bool IsValueReady()
+  {
+    // fake-wait for two frames
+    auto status = promise.get_future().wait_for(32ms);
+    return status == std::future_status::ready;
+  }
+
+  Diagnostics               diagnostics;
+  size_t                    returnSize{0u};
+  std::promise<Diagnostics> promise;
+};
 
 void vertexBuffer_test_startup(void)
 {
@@ -446,3 +507,112 @@ int UtcDaliVertexBufferSetDivisor(void)
   DALI_TEST_EQUALS(params2["instanceCount"].str(), oss.str(), TEST_LOCATION);
   END_TEST;
 }
+
+int UtcDaliVertexBufferUpdateCallback(void)
+{
+  TestApplication application;
+
+  // Create vertex buffer
+  VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map() = {
+                                                  {"aPosition", Property::Type::VECTOR2},
+                                                  {"aTexCoord", Property::Type::VECTOR2}});
+
+  // set callback
+  auto callback = std::make_unique<VertexBufferUpdater>();
+  vertexBuffer.SetVertexBufferUpdateCallback(callback->CreateCallback());
+
+  struct Vertex
+  {
+    Vector2 pos;
+    Vector2 uv;
+  };
+
+  std::vector<Vertex> vertices;
+  vertices.resize(16);
+  vertexBuffer.SetData(vertices.data(), 16);
+
+  Geometry geometry = Geometry::New();
+  geometry.AddVertexBuffer(vertexBuffer);
+  Shader   shader   = CreateShader();
+  Renderer renderer = Renderer::New(geometry, shader);
+  Actor    actor    = Actor::New();
+  actor.SetProperty(Actor::Property::SIZE, Vector3::ONE * 100.f);
+  actor.AddRenderer(renderer);
+  application.GetScene().Add(actor);
+
+  auto& gl    = application.GetGlAbstraction();
+  auto& trace = gl.GetDrawTrace();
+  trace.Enable(true);
+  trace.EnableLogging(true);
+
+  callback->SetCallbackReturnValue(16 * sizeof(Vertex));
+
+  application.SendNotification();
+  application.Render();
+
+  auto value = callback->GetValue();
+
+  // Test whether callback ran
+  DALI_TEST_EQUALS(value.counter, 1, TEST_LOCATION);
+  DALI_TEST_EQUALS(value.lastSize, 16 * sizeof(Vertex), TEST_LOCATION);
+  DALI_TEST_EQUALS(value.lastReturned, 16 * sizeof(Vertex), TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(value.lastPtr, (void*)nullptr, 0, TEST_LOCATION);
+
+  // test whether draw call has been issued (return value indicates end of array to be drawn)
+  auto result = trace.FindMethod("DrawArrays");
+  DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+  result = trace.FindMethodAndParams("DrawArrays", "4, 0, 16");
+  DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+
+  // Test 2. Update and render only half of vertex buffer
+  callback->SetCallbackReturnValue(8 * sizeof(Vertex));
+  trace.Reset();
+
+  application.SendNotification();
+  application.Render();
+
+  value = callback->GetValue();
+  // Test whether callback ran
+  DALI_TEST_EQUALS(value.counter, 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(value.lastSize, 16 * sizeof(Vertex), TEST_LOCATION);
+  DALI_TEST_EQUALS(value.lastReturned, 8 * sizeof(Vertex), TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(value.lastPtr, (void*)nullptr, 0, TEST_LOCATION);
+  result = trace.FindMethod("DrawArrays");
+  DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+  result = trace.FindMethodAndParams("DrawArrays", "4, 0, 8");
+  DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+
+  // Test 3. callback returns 0 elements to render, the draw call shouldn't happen.
+  callback->SetCallbackReturnValue(0);
+  trace.Reset();
+
+  application.SendNotification();
+  application.Render();
+
+  value = callback->GetValue();
+  // Test whether callback ran
+  DALI_TEST_EQUALS(value.counter, 3, TEST_LOCATION);
+  DALI_TEST_EQUALS(value.lastSize, 16 * sizeof(Vertex), TEST_LOCATION);
+  DALI_TEST_EQUALS(value.lastReturned, 0, TEST_LOCATION);
+  DALI_TEST_NOT_EQUALS(value.lastPtr, (void*)nullptr, 0, TEST_LOCATION);
+  result = trace.FindMethod("DrawArrays");
+  DALI_TEST_EQUALS(result, false, TEST_LOCATION);
+
+  // Test 4. removing callback, original behaviour should kick in
+  vertexBuffer.SetVertexBufferUpdateCallback(nullptr);
+  trace.Reset();
+  callback->Reset();
+
+  application.SendNotification();
+  application.Render();
+
+  auto valueReady = callback->IsValueReady();
+  DALI_TEST_EQUALS(valueReady, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(callback->diagnostics.counter, 3, TEST_LOCATION);
+  result = trace.FindMethod("DrawArrays");
+  DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+  result = trace.FindMethodAndParams("DrawArrays", "4, 0, 16");
+  DALI_TEST_EQUALS(result, true, TEST_LOCATION);
+
+  END_TEST;
+}
index 547d60d..b1b58a0 100644 (file)
@@ -182,6 +182,11 @@ uint32_t VertexBuffer::GetDivisor() const
   return mDivisor;
 }
 
+void VertexBuffer::SetVertexBufferUpdateCallback(VertexBufferUpdateCallback& callback)
+{
+  SceneGraph::SetVertexBufferUpdateCallback(mEventThreadServices.GetUpdateManager(), *mRenderObject, &callback);
+}
+
 const Render::VertexBuffer* VertexBuffer::GetRenderObject() const
 {
   return mRenderObject;
index a10a886..17f3b62 100644 (file)
@@ -68,6 +68,11 @@ public:
    */
   uint32_t GetDivisor() const;
 
+  /**
+   * @copydoc Dali::VertexBuffer::SetVertexBufferUpdateCallback()
+   */
+  void SetVertexBufferUpdateCallback(VertexBufferUpdateCallback& callback);
+
 public: // Default property extensions from Object
   /**
    * @brief Get the render thread side of the VertexBuffer
index 6b58571..2dff2e5 100644 (file)
@@ -384,6 +384,11 @@ void RenderManager::SetVertexBufferData(Render::VertexBuffer* vertexBuffer, Owne
   vertexBuffer->SetData(data.Release(), size);
 }
 
+void RenderManager::SetVertexBufferUpdateCallback(Render::VertexBuffer* vertexBuffer, Dali::VertexBufferUpdateCallback* callback)
+{
+  vertexBuffer->SetVertexBufferUpdateCallback(callback);
+}
+
 void RenderManager::SetIndexBuffer(Render::Geometry* geometry, Render::Geometry::Uint16ContainerType& indices)
 {
   geometry->SetIndexBuffer(indices);
index 6bd9a3d..eb23b29 100644 (file)
@@ -207,6 +207,13 @@ public:
   void SetVertexBufferData(Render::VertexBuffer* vertexBuffer, OwnerPointer<Vector<uint8_t>>& data, uint32_t size);
 
   /**
+   * Sets vertex buffer update callback
+   * @param vertexBuffer
+   * @param callback
+   */
+  void SetVertexBufferUpdateCallback(Render::VertexBuffer* vertexBuffer, Dali::VertexBufferUpdateCallback* callback);
+
+  /**
    * Sets the data for the index buffer of an existing geometry
    * @param[in] geometry The geometry
    * @param[in] data A vector containing the indices
index 11cc2a9..9b2e42a 100644 (file)
@@ -17,6 +17,7 @@
 
 // CLASS HEADER
 #include <dali/internal/render/renderers/gpu-buffer.h>
+#include <dali/public-api/rendering/vertex-buffer.h>
 
 // INTERNAL INCLUDES
 #include <dali/graphics-api/graphics-types.h>
@@ -74,6 +75,32 @@ void GpuBuffer::UpdateDataBuffer(Graphics::Controller& graphicsController, uint3
   graphicsController.UnmapMemory(std::move(memory));
 }
 
+void GpuBuffer::UpdateDataBufferWithCallback(Graphics::Controller& graphicsController, Dali::VertexBufferUpdateCallback* callback, uint32_t& bytesUpdatedCount)
+{
+  // create or orphan object so mapping can be instant
+  if(!mGraphicsObject || mWritePolicy == WritePolicy::DISCARD)
+  {
+    Graphics::BufferCreateInfo createInfo{};
+    createInfo.SetUsage(mUsage).SetSize(mSize);
+    mGraphicsObject = graphicsController.CreateBuffer(createInfo, std::move(mGraphicsObject));
+    mCapacity       = mSize;
+  }
+
+  Graphics::MapBufferInfo info{};
+  info.buffer = mGraphicsObject.get();
+  info.usage  = 0 | Graphics::MemoryUsageFlagBits::WRITE;
+  info.offset = 0;
+  info.size   = mSize; // map entire buffer
+
+  auto  memory = graphicsController.MapBufferRange(info);
+  void* ptr    = memory->LockRegion(0, mSize);
+
+  bytesUpdatedCount = callback->Invoke(ptr, mSize); // passing low level binary size, not element count!
+
+  memory->Unlock(true);
+  graphicsController.UnmapMemory(std::move(memory));
+}
+
 void GpuBuffer::Destroy()
 {
   mCapacity = 0;
index 645480a..45ac785 100644 (file)
@@ -23,6 +23,8 @@
 
 namespace Dali
 {
+class VertexBufferUpdateCallback;
+
 namespace Internal
 {
 /**
@@ -70,7 +72,6 @@ public:
   ~GpuBuffer() = default;
 
   /**
-   *
    * Creates or updates a buffer object and binds it to the target.
    * @param graphicsController The graphics controller
    * @param size Specifies the size in bytes of the buffer object's new data store.
@@ -79,6 +80,17 @@ public:
   void UpdateDataBuffer(Graphics::Controller& graphicsController, uint32_t size, const void* data);
 
   /**
+   * Updates existing buffer by calling associated VertexBufferUpdateCallback
+   *
+   * bytesUpdatedCount limits next draw call to that amount of data.
+   *
+   * @param[in] graphicsController Valid controller
+   * @param[in] callback  Valid pointer to the VertexBufferUpdateCallback
+   * @param[out] bytesUpdatedCount Number of bytes updated
+   */
+  void UpdateDataBufferWithCallback(Graphics::Controller& graphicsController, Dali::VertexBufferUpdateCallback* callback, uint32_t& bytesUpdatedCount);
+
+  /**
    * Get the size of the buffer
    * @return size Size of the buffer in bytes
    */
index 03afcd7..ed21d36 100644 (file)
@@ -179,7 +179,7 @@ bool Geometry::BindVertexAttributes(Graphics::CommandBuffer& commandBuffer)
     }
     //@todo Figure out why this is being drawn without geometry having been uploaded
   }
-  if(buffers.empty())
+  if(buffers.empty() || buffers.size() != vertexBufferCount)
   {
     return false;
   }
@@ -233,13 +233,32 @@ bool Geometry::Draw(
     // Un-indexed draw call
     uint32_t numVertices(0u);
 
-    if(mVertexBuffers.Count() > 0)
+    // Use element buffer count for drawing arrays (needs changing API, for workaround)
+    if(elementBufferCount)
+    {
+      numVertices = elementBufferCount;
+    }
+    else if(mVertexBuffers.Count() > 0)
     {
       // truncated, no value loss happening in practice
-      numVertices = static_cast<uint32_t>(mVertexBuffers[0]->GetElementCount());
+      numVertices = static_cast<uint32_t>(mVertexBuffers[0]->GetRenderableElementCount());
+    }
+    // In case we have more buffers, we select buffer with less elements to render
+    // TODO: we may eventually support wrapping around buffers????
+    else if(mVertexBuffers.Count() > 1)
+    {
+      auto elementsCount = mVertexBuffers[0]->GetRenderableElementCount();
+      for(auto& vertexBuffer : mVertexBuffers)
+      {
+        elementsCount = std::min(elementsCount, vertexBuffer->GetRenderableElementCount());
+      }
+      numVertices = elementsCount;
+    }
+    // Issue draw call only if there's non-zero numVertices
+    if(numVertices)
+    {
+      commandBuffer.Draw(numVertices, mInstanceCount, 0, 0);
     }
-
-    commandBuffer.Draw(numVertices, mInstanceCount, 0, 0);
   }
   return true;
 }
index a383551..0ccf583 100644 (file)
@@ -32,6 +32,7 @@ VertexBuffer::VertexBuffer()
   mData(nullptr),
   mGpuBuffer(nullptr),
   mSize(0),
+  mElementCount(0),
   mDataChanged(true)
 {
 }
@@ -51,9 +52,19 @@ void VertexBuffer::SetData(Dali::Vector<uint8_t>* data, uint32_t size)
   mDataChanged = true;
 }
 
+void VertexBuffer::SetVertexBufferUpdateCallback(Dali::VertexBufferUpdateCallback* callback)
+{
+  mVertexBufferUpdateCallback.reset(callback);
+}
+
 bool VertexBuffer::Update(Graphics::Controller& graphicsController)
 {
-  if(!mData || !mFormat || !mSize)
+  if(!mFormat || !mSize)
+  {
+    return false;
+  }
+
+  if(!mVertexBufferUpdateCallback && !mData)
   {
     return false;
   }
@@ -66,15 +77,27 @@ bool VertexBuffer::Update(Graphics::Controller& graphicsController)
     }
 
     // Update the GpuBuffer
-    if(mGpuBuffer)
+    if(mGpuBuffer && mData)
     {
       DALI_ASSERT_DEBUG(mSize && "No data in the property buffer!");
       mGpuBuffer->UpdateDataBuffer(graphicsController, GetDataSize(), &((*mData)[0]));
     }
 
+    mElementCount = mSize;
+
     mDataChanged = false;
   }
 
+  // To execute the callback the buffer must be already initialized.
+  if(mVertexBufferUpdateCallback && mGpuBuffer)
+  {
+    // If running callback, we may end up with less elements in the buffer
+    // of the same capacity
+    uint32_t updatedSize = mSize * mFormat->size;
+    mGpuBuffer->UpdateDataBufferWithCallback(graphicsController, mVertexBufferUpdateCallback.get(), updatedSize);
+    mElementCount = updatedSize / mFormat->size;
+  }
+
   return true;
 }
 
index a8b86c1..e007977 100644 (file)
@@ -28,6 +28,7 @@
 
 namespace Dali
 {
+class VertexBufferUpdateCallback;
 namespace Internal
 {
 namespace Render
@@ -81,6 +82,16 @@ public:
   void SetData(Dali::Vector<uint8_t>* data, uint32_t size);
 
   /**
+   * @brief Sets vertex buffer update callback
+   *
+   * This function takes ownership over the callback object.
+   *
+   * The callback will run during rendering on the update/render thread.
+   * @param[in] callback Valid update callback
+   */
+  void SetVertexBufferUpdateCallback(Dali::VertexBufferUpdateCallback* callback);
+
+  /**
    * Perform the upload of the buffer only when required
    * @param[in] graphicsController The controller
    */
@@ -147,6 +158,16 @@ public:
     return mSize;
   }
 
+  /**
+   * Retrieves number of renderable elements when vertex update callback
+   * is used. If there is no callback set the total number of elements
+   * is returned.
+   */
+  [[nodiscard]] inline uint32_t GetRenderableElementCount() const
+  {
+    return mVertexBufferUpdateCallback ? mElementCount : mSize;
+  }
+
   [[nodiscard]] inline const VertexBuffer::Format* GetFormat() const
   {
     return mFormat.Get();
@@ -162,9 +183,12 @@ private:
   OwnerPointer<Dali::Vector<uint8_t> > mData;      ///< Data
   OwnerPointer<GpuBuffer>              mGpuBuffer; ///< Pointer to the GpuBuffer associated with this RenderVertexBuffer
 
-  uint32_t mSize;        ///< Number of Elements in the buffer
-  uint32_t mDivisor{0};  ///< The divisor (0:not instanced, >=1:instanced)
-  bool     mDataChanged; ///< Flag to know if data has changed in a frame
+  uint32_t                                          mSize;         ///< Number of Elements in the buffer
+  uint32_t                                          mDivisor{0};   ///< The divisor (0:not instanced, >=1:instanced)
+  uint32_t                                          mElementCount; ///< Number of valid elements in the buffer
+  std::unique_ptr<Dali::VertexBufferUpdateCallback> mVertexBufferUpdateCallback;
+
+  bool mDataChanged; ///< Flag to know if data has changed in a frame
 };
 
 } // namespace Render
index d7437f1..03659c3 100644 (file)
@@ -1407,6 +1407,18 @@ void UpdateManager::SetVertexBufferDivisor(Render::VertexBuffer* vertexBuffer, u
   new(slot) LocalType(vertexBuffer, &Render::VertexBuffer::SetDivisor, divisor);
 }
 
+void UpdateManager::SetVertexBufferUpdateCallback(Render::VertexBuffer* vertexBuffer, Dali::VertexBufferUpdateCallback* callback)
+{
+  // Message has ownership of format while in transit from update -> render
+  using DerivedType = MessageValue2<RenderManager, Render::VertexBuffer*, Dali::VertexBufferUpdateCallback*>;
+
+  // Reserve some memory inside the render queue
+  uint32_t* slot = mImpl->renderQueue.ReserveMessageSlot(mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof(DerivedType));
+
+  // Construct message in the render queue memory; note that delete should not be called on the return value
+  new(slot) DerivedType(&mImpl->renderManager, &RenderManager::SetVertexBufferUpdateCallback, vertexBuffer, callback);
+}
+
 void UpdateManager::AddGeometry(OwnerPointer<Render::Geometry>& geometry)
 {
   // Message has ownership of format while in transit from update -> render
index 674a603..b42bc15 100644 (file)
@@ -483,6 +483,12 @@ public:
    * @param[in] divisor The instance divisor. 0 to turn instancing off.
    */
   void SetVertexBufferDivisor(Render::VertexBuffer* vertexBuffer, uint32_t divisor);
+  /**
+   * Sets vertex buffer update callback
+   * @param[in] vertexBuffer
+   * @param[in] callback
+   */
+  void SetVertexBufferUpdateCallback(Render::VertexBuffer* vertexBuffer, Dali::VertexBufferUpdateCallback* callback);
 
   /**
    * Adds a geometry to the RenderManager
@@ -1283,6 +1289,18 @@ inline void SetVertexBufferDivisorMessage(UpdateManager& manager, Render::Vertex
   new(slot) LocalType(&manager, &UpdateManager::SetVertexBufferDivisor, &vertexBuffer, divisor);
 }
 
+inline void SetVertexBufferUpdateCallback(UpdateManager& manager, Render::VertexBuffer& vertexBuffer, Dali::VertexBufferUpdateCallback* callback)
+{
+  // Message has ownership of VertexBuffer data while in transit from event -> update
+  using LocalType = MessageValue2<UpdateManager, Render::VertexBuffer*, Dali::VertexBufferUpdateCallback*>;
+
+  // Reserve some memory inside the message queue
+  uint32_t* slot = manager.ReserveMessageSlot(sizeof(LocalType));
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new(slot) LocalType(&manager, &UpdateManager::SetVertexBufferUpdateCallback, &vertexBuffer, callback);
+}
+
 inline void AddGeometry(UpdateManager& manager, OwnerPointer<Render::Geometry>& geometry)
 {
   // Message has ownership of Geometry while in transit from event -> update
index 85b8bb3..69ac95a 100644 (file)
 
 namespace Dali
 {
+struct VertexBufferUpdateCallback::Impl
+{
+  Impl(CallbackBase* callback)
+  : mCallback(callback)
+  {
+  }
+
+  uint32_t Invoke(void* data, size_t size)
+  {
+    return CallbackBase::ExecuteReturn<uint32_t>(*mCallback, data, size);
+  }
+
+  std::unique_ptr<CallbackBase> mCallback;
+};
+
+VertexBufferUpdateCallback::~VertexBufferUpdateCallback() = default;
+
+std::unique_ptr<VertexBufferUpdateCallback> VertexBufferUpdateCallback::New(CallbackBase* callbackBase)
+{
+  std::unique_ptr<VertexBufferUpdateCallback> retval;
+  auto                                        impl = std::make_unique<Impl>(callbackBase);
+  retval.reset(new VertexBufferUpdateCallback(std::move(impl)));
+  return retval;
+}
+
+VertexBufferUpdateCallback::VertexBufferUpdateCallback(std::unique_ptr<VertexBufferUpdateCallback::Impl>&& impl)
+: mImpl(std::move(impl))
+{
+}
+
+uint32_t VertexBufferUpdateCallback::Invoke(void* data, size_t size)
+{
+  return mImpl->Invoke(data, size);
+}
+
+} // namespace Dali
+
+namespace Dali
+{
 VertexBuffer VertexBuffer::New(Dali::Property::Map& bufferFormat)
 {
   Internal::VertexBufferPtr vertexBuffer = Internal::VertexBuffer::New(bufferFormat);
@@ -68,6 +107,11 @@ uint32_t VertexBuffer::GetDivisor() const
   return GetImplementation(*this).GetDivisor();
 }
 
+void VertexBuffer::SetVertexBufferUpdateCallback(std::unique_ptr<VertexBufferUpdateCallback>&& updateCallback)
+{
+  GetImplementation(*this).SetVertexBufferUpdateCallback(*updateCallback.release());
+}
+
 VertexBuffer::VertexBuffer(Internal::VertexBuffer* pointer)
 : BaseHandle(pointer)
 {
index 7300a38..c8dad49 100644 (file)
@@ -39,6 +39,69 @@ class VertexBuffer;
 }
 
 /**
+ * @class VertexBufferUpdateCallback
+ *
+ * The class defines a callback that VertexBuffer object may call
+ * to obtain new data. The callback runs before draw call is issued
+ * and it will run on update/render thread (developers must synchronize
+ * explicitly).
+ *
+ * Callback returns number of bytes written. This will limit next draw call
+ * to number of elements that have been written by the callback.
+ *
+ * Using callback invalidates current vertex buffer data. Unchanged data
+ * stays undefined.
+ */
+class DALI_CORE_API VertexBufferUpdateCallback
+{
+public:
+  /**
+   * @brief Destructor
+   */
+  ~VertexBufferUpdateCallback();
+
+  /**
+   * @brief Callback functor signature:
+   *
+   * uint32_t T::*(void* pointer, size_t dataSizeInBytes)
+   */
+  template<class T>
+  using FuncType = uint32_t (T::*)(void*, size_t);
+
+  /**
+   * @brief Creates a new instance of VertexBufferUpdateCallback
+   *
+   * @tparam T class the functor is a member of
+   * @param[in] object Object associated with callback
+   * @param[in] functor Member function to be executed
+   *
+   * @return Returns valid VertexBufferUpdateCallback object
+   */
+  template<class T>
+  static std::unique_ptr<VertexBufferUpdateCallback> New(T* object, FuncType<T> functor)
+  {
+    auto callback = Dali::MakeCallback(object, functor);
+    return New(callback);
+  }
+
+  /**
+   * @brief Invokes callback directly
+   * @param[in] data pointer to write into
+   * @param[in] size size of region in bytes
+   *
+   * @return Number of bytes written
+   */
+  uint32_t Invoke(void* data, size_t size);
+
+private:
+  static std::unique_ptr<VertexBufferUpdateCallback> New(CallbackBase* callbackBase);
+
+  struct Impl;
+  explicit VertexBufferUpdateCallback(std::unique_ptr<VertexBufferUpdateCallback::Impl>&& impl);
+  std::unique_ptr<Impl> mImpl;
+};
+
+/**
  * @brief VertexBuffer is a handle to an object that contains a buffer of structured data.
  *
  * VertexBuffers can be used to provide data to Geometry objects.
@@ -192,6 +255,18 @@ public:
    */
   uint32_t GetDivisor() const;
 
+  /**
+   * @brief Sets VertexBufferUpdateCallback
+   *
+   * Function takes over the ownership over the callback.
+   *
+   * Developers must make sure the lifetime of used objects within the callback
+   * will remain valid as long as the callback exists.
+   *
+   * @param[in] updateCallback Valid VertexBufferUpdateCallback object
+   */
+  void SetVertexBufferUpdateCallback(std::unique_ptr<VertexBufferUpdateCallback>&& updateCallback);
+
 public:
   /**
    * @brief The constructor.