From 9aac60fe14229bab1dbef7a3e11fba09149a7366 Mon Sep 17 00:00:00 2001 From: Adam Bialogonski Date: Tue, 28 Mar 2023 21:52:18 +0100 Subject: [PATCH] Added VertexBufferUpdateCallback 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 --- .../src/dali/utc-Dali-VertexBuffer.cpp | 172 +++++++++++++++++- .../event/rendering/vertex-buffer-impl.cpp | 5 + .../event/rendering/vertex-buffer-impl.h | 5 + .../internal/render/common/render-manager.cpp | 5 + dali/internal/render/common/render-manager.h | 7 + dali/internal/render/renderers/gpu-buffer.cpp | 27 +++ dali/internal/render/renderers/gpu-buffer.h | 14 +- .../render/renderers/render-geometry.cpp | 29 ++- .../render/renderers/render-vertex-buffer.cpp | 27 ++- .../render/renderers/render-vertex-buffer.h | 30 ++- .../update/manager/update-manager.cpp | 12 ++ dali/internal/update/manager/update-manager.h | 18 ++ dali/public-api/rendering/vertex-buffer.cpp | 44 +++++ dali/public-api/rendering/vertex-buffer.h | 75 ++++++++ 14 files changed, 458 insertions(+), 12 deletions(-) diff --git a/automated-tests/src/dali/utc-Dali-VertexBuffer.cpp b/automated-tests/src/dali/utc-Dali-VertexBuffer.cpp index b03d93864..079a3fe90 100644 --- a/automated-tests/src/dali/utc-Dali-VertexBuffer.cpp +++ b/automated-tests/src/dali/utc-Dali-VertexBuffer.cpp @@ -17,10 +17,71 @@ #include #include - +#include +using namespace std::chrono_literals; using namespace Dali; #include +#include + +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(); + } + + std::unique_ptr 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 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(); + vertexBuffer.SetVertexBufferUpdateCallback(callback->CreateCallback()); + + struct Vertex + { + Vector2 pos; + Vector2 uv; + }; + + std::vector 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; +} diff --git a/dali/internal/event/rendering/vertex-buffer-impl.cpp b/dali/internal/event/rendering/vertex-buffer-impl.cpp index 547d60dfc..b1b58a071 100644 --- a/dali/internal/event/rendering/vertex-buffer-impl.cpp +++ b/dali/internal/event/rendering/vertex-buffer-impl.cpp @@ -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; diff --git a/dali/internal/event/rendering/vertex-buffer-impl.h b/dali/internal/event/rendering/vertex-buffer-impl.h index a10a8862b..17f3b62ec 100644 --- a/dali/internal/event/rendering/vertex-buffer-impl.h +++ b/dali/internal/event/rendering/vertex-buffer-impl.h @@ -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 diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp index 6b58571b4..2dff2e549 100644 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -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); diff --git a/dali/internal/render/common/render-manager.h b/dali/internal/render/common/render-manager.h index 6bd9a3d6c..eb23b29e7 100644 --- a/dali/internal/render/common/render-manager.h +++ b/dali/internal/render/common/render-manager.h @@ -206,6 +206,13 @@ public: */ void SetVertexBufferData(Render::VertexBuffer* vertexBuffer, OwnerPointer>& 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 diff --git a/dali/internal/render/renderers/gpu-buffer.cpp b/dali/internal/render/renderers/gpu-buffer.cpp index 11cc2a94e..9b2e42a91 100644 --- a/dali/internal/render/renderers/gpu-buffer.cpp +++ b/dali/internal/render/renderers/gpu-buffer.cpp @@ -17,6 +17,7 @@ // CLASS HEADER #include +#include // INTERNAL INCLUDES #include @@ -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; diff --git a/dali/internal/render/renderers/gpu-buffer.h b/dali/internal/render/renderers/gpu-buffer.h index 645480a78..45ac78542 100644 --- a/dali/internal/render/renderers/gpu-buffer.h +++ b/dali/internal/render/renderers/gpu-buffer.h @@ -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. @@ -78,6 +79,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 diff --git a/dali/internal/render/renderers/render-geometry.cpp b/dali/internal/render/renderers/render-geometry.cpp index 03afcd7e2..ed21d36e9 100644 --- a/dali/internal/render/renderers/render-geometry.cpp +++ b/dali/internal/render/renderers/render-geometry.cpp @@ -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(mVertexBuffers[0]->GetElementCount()); + numVertices = static_cast(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; } diff --git a/dali/internal/render/renderers/render-vertex-buffer.cpp b/dali/internal/render/renderers/render-vertex-buffer.cpp index a383551ab..0ccf583ff 100644 --- a/dali/internal/render/renderers/render-vertex-buffer.cpp +++ b/dali/internal/render/renderers/render-vertex-buffer.cpp @@ -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* 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; } diff --git a/dali/internal/render/renderers/render-vertex-buffer.h b/dali/internal/render/renderers/render-vertex-buffer.h index a8b86c1cc..e00797782 100644 --- a/dali/internal/render/renderers/render-vertex-buffer.h +++ b/dali/internal/render/renderers/render-vertex-buffer.h @@ -28,6 +28,7 @@ namespace Dali { +class VertexBufferUpdateCallback; namespace Internal { namespace Render @@ -80,6 +81,16 @@ public: */ void SetData(Dali::Vector* 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 > mData; ///< Data OwnerPointer 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 mVertexBufferUpdateCallback; + + bool mDataChanged; ///< Flag to know if data has changed in a frame }; } // namespace Render diff --git a/dali/internal/update/manager/update-manager.cpp b/dali/internal/update/manager/update-manager.cpp index d7437f188..03659c3ac 100644 --- a/dali/internal/update/manager/update-manager.cpp +++ b/dali/internal/update/manager/update-manager.cpp @@ -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; + + // 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& geometry) { // Message has ownership of format while in transit from update -> render diff --git a/dali/internal/update/manager/update-manager.h b/dali/internal/update/manager/update-manager.h index 674a603fb..b42bc151a 100644 --- a/dali/internal/update/manager/update-manager.h +++ b/dali/internal/update/manager/update-manager.h @@ -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; + + // 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& geometry) { // Message has ownership of Geometry while in transit from event -> update diff --git a/dali/public-api/rendering/vertex-buffer.cpp b/dali/public-api/rendering/vertex-buffer.cpp index 85b8bb34d..69ac95a80 100644 --- a/dali/public-api/rendering/vertex-buffer.cpp +++ b/dali/public-api/rendering/vertex-buffer.cpp @@ -22,6 +22,45 @@ #include // Dali::Internal::VertexBuffer #include // Dali::Property::Map +namespace Dali +{ +struct VertexBufferUpdateCallback::Impl +{ + Impl(CallbackBase* callback) + : mCallback(callback) + { + } + + uint32_t Invoke(void* data, size_t size) + { + return CallbackBase::ExecuteReturn(*mCallback, data, size); + } + + std::unique_ptr mCallback; +}; + +VertexBufferUpdateCallback::~VertexBufferUpdateCallback() = default; + +std::unique_ptr VertexBufferUpdateCallback::New(CallbackBase* callbackBase) +{ + std::unique_ptr retval; + auto impl = std::make_unique(callbackBase); + retval.reset(new VertexBufferUpdateCallback(std::move(impl))); + return retval; +} + +VertexBufferUpdateCallback::VertexBufferUpdateCallback(std::unique_ptr&& 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) @@ -68,6 +107,11 @@ uint32_t VertexBuffer::GetDivisor() const return GetImplementation(*this).GetDivisor(); } +void VertexBuffer::SetVertexBufferUpdateCallback(std::unique_ptr&& updateCallback) +{ + GetImplementation(*this).SetVertexBufferUpdateCallback(*updateCallback.release()); +} + VertexBuffer::VertexBuffer(Internal::VertexBuffer* pointer) : BaseHandle(pointer) { diff --git a/dali/public-api/rendering/vertex-buffer.h b/dali/public-api/rendering/vertex-buffer.h index 7300a38f0..c8dad495a 100644 --- a/dali/public-api/rendering/vertex-buffer.h +++ b/dali/public-api/rendering/vertex-buffer.h @@ -38,6 +38,69 @@ namespace Internal DALI_INTERNAL 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 + 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 + static std::unique_ptr New(T* object, FuncType 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 New(CallbackBase* callbackBase); + + struct Impl; + explicit VertexBufferUpdateCallback(std::unique_ptr&& impl); + std::unique_ptr mImpl; +}; + /** * @brief VertexBuffer is a handle to an object that contains a buffer of structured data. * @@ -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&& updateCallback); + public: /** * @brief The constructor. -- 2.34.1