Added a SetDivisor() API to Vertex buffer.
This will enable instancing on that buffer - when this is
drawn, it will use DrawArraysInstanced or DrawElementsInstanced,
with all the attributes in this vertex buffer having the same
input rate.
Currently, only an input rate of 1 is supported - this will be changed
in a future patch.
Requires an update to dali-adaptor too.
Change-Id: I5c7461cc3e2cb72c6257d3093afa95116a6dc6c1
#define TEST_GL_ABSTRACTION_H
/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
inline void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount) override
{
+ std::stringstream out;
+ out << mode << ", " << first << ", " << count << ", " << instanceCount;
+ TraceCallStack::NamedParams namedParams;
+ namedParams["mode"] << std::hex << mode;
+ namedParams["first"] << first;
+ namedParams["count"] << count;
+ namedParams["instanceCount"] << instanceCount;
+ mDrawTrace.PushCall("DrawArraysInstanced", out.str(), namedParams);
}
inline void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLsizei instanceCount) override
{
+ std::stringstream out;
+ out << mode << ", " << count << ", " << type << ", " << instanceCount;
+ TraceCallStack::NamedParams namedParams;
+ namedParams["mode"] << std::hex << mode;
+ namedParams["count"] << count;
+ namedParams["type"] << std::hex << type;
+ namedParams["indexCount"] << instanceCount;
+ mDrawTrace.PushCall("DrawElementsInstanced", out.str(), namedParams);
}
inline GLsync FenceSync(GLenum condition, GLbitfield flags) override
inline void VertexAttribDivisor(GLuint index, GLuint divisor) override
{
+ std::stringstream out;
+ out << index << ", " << divisor;
+ TraceCallStack::NamedParams namedParams;
+ namedParams["index"] << index;
+ namedParams["divisor"] << divisor;
+ mBufferTrace.PushCall("VertexAttribDivisor", out.str(), namedParams);
}
inline void BindTransformFeedback(GLenum target, GLuint id) override
{
if(currentPipeline)
{
- mGl.DrawArrays(GetTopology(currentPipeline->inputAssemblyState.topology),
- 0,
- cmd.data.draw.draw.vertexCount);
+ if(cmd.data.draw.draw.instanceCount == 0)
+ {
+ mGl.DrawArrays(GetTopology(currentPipeline->inputAssemblyState.topology),
+ 0,
+ cmd.data.draw.draw.vertexCount);
+ }
+ else
+ {
+ mGl.DrawArraysInstanced(GetTopology(currentPipeline->inputAssemblyState.topology),
+ 0,
+ cmd.data.draw.draw.vertexCount,
+ cmd.data.draw.draw.instanceCount);
+ }
}
break;
}
{
if(currentPipeline)
{
- mGl.DrawElements(GetTopology(currentPipeline->inputAssemblyState.topology),
- static_cast<GLsizei>(cmd.data.draw.drawIndexed.indexCount),
- GL_UNSIGNED_SHORT,
- reinterpret_cast<void*>(cmd.data.draw.drawIndexed.firstIndex));
+ if(cmd.data.draw.draw.instanceCount == 0)
+ {
+ mGl.DrawElements(GetTopology(currentPipeline->inputAssemblyState.topology),
+ static_cast<GLsizei>(cmd.data.draw.drawIndexed.indexCount),
+ GL_UNSIGNED_SHORT,
+ reinterpret_cast<void*>(cmd.data.draw.drawIndexed.firstIndex));
+ }
+ else
+ {
+ mGl.DrawElementsInstanced(GetTopology(currentPipeline->inputAssemblyState.topology),
+ static_cast<GLsizei>(cmd.data.draw.drawIndexed.indexCount),
+ GL_UNSIGNED_SHORT,
+ reinterpret_cast<void*>(cmd.data.draw.drawIndexed.firstIndex),
+ cmd.data.draw.drawIndexed.instanceCount);
+ }
}
break;
}
uint32_t attributeOffset = attribute.offset;
GLsizei stride = vi.bufferBindings[attribute.binding].stride;
+ auto rate = vi.bufferBindings[attribute.binding].inputRate;
+
mGl.VertexAttribPointer(attribute.location,
GetNumComponents(attribute.format),
GetGlType(attribute.format),
GL_FALSE, // Not normalized
stride,
reinterpret_cast<void*>(attributeOffset));
+ if(rate == Graphics::VertexInputRate::PER_VERTEX)
+ {
+ mGl.VertexAttribDivisor(attribute.location, 0);
+ }
+ else if(rate == Graphics::VertexInputRate::PER_INSTANCE)
+ {
+ mGl.VertexAttribDivisor(attribute.location, 1);
+ }
}
// Cull face setup
actor.AddRenderer(renderer);
application.GetScene().Add(actor);
+ auto& drawTrace = application.GetGlAbstraction().GetDrawTrace();
+ drawTrace.Enable(true);
+
application.SendNotification();
application.Render(0);
application.Render();
const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
application.GetGlAbstraction().GetBufferDataCalls();
+ DALI_TEST_CHECK(drawTrace.FindMethod("DrawArrays"));
+
DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
DALI_TEST_EQUALS(bufferDataCalls[0], sizeof(texturedQuadVertexData), TEST_LOCATION);
application.SendNotification();
application.Render(0);
- {
- const TestGlAbstraction::BufferSubDataCalls& bufferSubDataCalls =
- application.GetGlAbstraction().GetBufferSubDataCalls();
-
- const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
- application.GetGlAbstraction().GetBufferDataCalls();
-
- // Should be 3 (2 Render + 1 vertexBuffer reload)
- DALI_TEST_EQUALS(bufferSubDataCalls.size(), 3u, TEST_LOCATION);
- DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
-
- if(bufferSubDataCalls.size() >= 2)
- {
- DALI_TEST_EQUALS(bufferSubDataCalls[1], sizeof(texturedQuadVertexData), TEST_LOCATION);
- }
- }
-
END_TEST;
}
}
END_TEST;
}
+
+int UtcDaliVertexBufferSetDivisor(void)
+{
+ TestApplication application;
+
+ Property::Map texturedQuadVertexFormat;
+ texturedQuadVertexFormat["aPosition"] = Property::VECTOR2;
+ texturedQuadVertexFormat["aTexCoord"] = Property::VECTOR2;
+
+ Property::Map instanceFormat{{"aTranslate", Property::VECTOR2}, {"aColor", Property::VECTOR4}};
+
+ VertexBuffer vertexBuffer = VertexBuffer::New(texturedQuadVertexFormat);
+ DALI_TEST_EQUALS((bool)vertexBuffer, true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(0, vertexBuffer.GetDivisor(), TEST_LOCATION);
+
+ VertexBuffer instanceBuffer = VertexBuffer::New(instanceFormat);
+ DALI_TEST_EQUALS((bool)instanceBuffer, true, TEST_LOCATION);
+
+ const float halfQuadSize = .5f;
+ struct TexturedQuadVertex
+ {
+ Vector2 position;
+ Vector2 textureCoordinates;
+ };
+ TexturedQuadVertex texturedQuadVertexData[4] = {
+ {Vector2(-halfQuadSize, -halfQuadSize), Vector2(0.f, 0.f)},
+ {Vector2(halfQuadSize, -halfQuadSize), Vector2(1.f, 0.f)},
+ {Vector2(-halfQuadSize, halfQuadSize), Vector2(0.f, 1.f)},
+ {Vector2(halfQuadSize, halfQuadSize), Vector2(1.f, 1.f)}};
+
+ vertexBuffer.SetData(texturedQuadVertexData, 4);
+
+ struct InstanceData
+ {
+ Vector2 translate;
+ Vector4 color;
+ };
+
+ InstanceData instanceData[] = {{Vector2(12, 33), Color::WHITE},
+ {Vector2(-2000, 43), Color::BLUE},
+ {Vector2(200, 43), Color::GREEN},
+ {Vector2(-243, 43), Color::TURQUOISE},
+ {Vector2(192, 43), Color::CYAN},
+ {Vector2(-2000, 43), Color::MAGENTA},
+ {Vector2(-292, 393), Color::BLUE},
+ {Vector2(-499, 128), Color::BLUE},
+ {Vector2(328, 43), Color::BLUE},
+ {Vector2(726, 43), Color::BLUE}};
+ instanceBuffer.SetData(instanceData, sizeof(instanceData) / sizeof(InstanceData));
+ instanceBuffer.SetDivisor(1);
+ DALI_TEST_EQUALS(1, instanceBuffer.GetDivisor(), TEST_LOCATION);
+
+ Geometry geometry = Geometry::New();
+ geometry.AddVertexBuffer(vertexBuffer);
+ geometry.AddVertexBuffer(instanceBuffer);
+
+ 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);
+
+ TestGlAbstraction& gl = application.GetGlAbstraction();
+ auto& bufferTrace = gl.GetBufferTrace();
+ auto& drawTrace = gl.GetDrawTrace();
+ bufferTrace.Enable(true);
+ drawTrace.Enable(true);
+
+ application.SendNotification();
+ application.Render();
+
+ TraceCallStack::NamedParams params;
+ params["divisor"] << "1";
+ DALI_TEST_CHECK(bufferTrace.FindMethodAndParams("VertexAttribDivisor", params));
+
+ TraceCallStack::NamedParams params2;
+ DALI_TEST_CHECK(drawTrace.FindMethodAndGetParameters("DrawArraysInstanced", params2));
+ std::ostringstream oss;
+ oss << sizeof(instanceData) / sizeof(InstanceData);
+ DALI_TEST_EQUALS(params2["instanceCount"].str(), oss.str(), TEST_LOCATION);
+ END_TEST;
+}
}
uint32_t stride;
VertexInputRate inputRate;
+ //@todo Add actual rate...
};
/**
return mSize;
}
+void VertexBuffer::SetDivisor(uint32_t divisor)
+{
+ SceneGraph::SetVertexBufferDivisorMessage(mEventThreadServices.GetUpdateManager(), *mRenderObject, divisor);
+ mDivisor = divisor;
+}
+
+uint32_t VertexBuffer::GetDivisor() const
+{
+ return mDivisor;
+}
+
const Render::VertexBuffer* VertexBuffer::GetRenderObject() const
{
return mRenderObject;
#define DALI_INTERNAL_VERTEX_BUFFER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
public:
/**
- * @copydoc PropertBuffer::New()
+ * @copydoc Dali::VertexBuffer::New()
*/
static VertexBufferPtr New(Dali::Property::Map& format);
/**
- * @copydoc PropertBuffer::SetData()
+ * @copydoc Dali::VertexBuffer::SetData()
*/
void SetData(const void* data, uint32_t size);
/**
- * @copydoc PropertBuffer::GetSize()
+ * @copydoc Dali::VertexBuffer::GetSize()
*/
uint32_t GetSize() const;
+ /**
+ * @copydoc Dali::VertexBuffer::SetDivisor()
+ */
+ void SetDivisor(uint32_t divisor);
+
+ /**
+ * @copydoc Dali::VertexBuffer::GetDivisor()
+ */
+ uint32_t GetDivisor() const;
+
public: // Default property extensions from Object
/**
* @brief Get the render thread side of the VertexBuffer
VertexBuffer(const VertexBuffer&);
VertexBuffer& operator=(const VertexBuffer&);
-private: // data
- EventThreadServices& mEventThreadServices; ///<Used to send messages to the render thread via update thread
- Render::VertexBuffer* mRenderObject; ///<Render side object
- uint32_t mBufferFormatSize;
- uint32_t mSize; ///< Number of elements in the buffer
+private: // data
+ EventThreadServices& mEventThreadServices; ///<Used to send messages to the render thread via update thread
+ Render::VertexBuffer* mRenderObject{nullptr}; ///<Render side object
+ uint32_t mBufferFormatSize{0};
+ uint32_t mSize{0}; ///< Number of elements in the buffer
+ uint32_t mDivisor{0}; ///< How many elements to skip in instanced draw
};
/**
{
const VertexBuffer::Format& vertexFormat = *vertexBuffer->GetFormat();
+ uint32_t divisor = vertexBuffer->GetDivisor();
+ Graphics::VertexInputRate vertexInputRate = (divisor == 0
+ ? Graphics::VertexInputRate::PER_VERTEX
+ : Graphics::VertexInputRate::PER_INSTANCE);
+
vertexInputState.bufferBindings.emplace_back(vertexFormat.size, // stride
- Graphics::VertexInputRate::PER_VERTEX);
+ vertexInputRate);
+ //@todo Add the actual rate to the graphics struct
const uint32_t attributeCount = vertexBuffer->GetAttributeCount();
uint32_t lastBoundAttributeIndex = 0;
void Geometry::RemoveVertexBuffer(const Render::VertexBuffer* vertexBuffer)
{
const auto&& end = mVertexBuffers.End();
+ // @todo if this buffer is the only instance buffer, reduce instance count to 1.
for(auto&& iter = mVertexBuffers.Begin(); iter != end; ++iter)
{
if(*iter == vertexBuffer)
for(uint32_t i = 0; i < vertexBufferCount; ++i)
{
+ if(mVertexBuffers[i]->GetDivisor() > 0)
+ {
+ mInstanceCount = mVertexBuffers[i]->GetElementCount();
+ }
+
const GpuBuffer* gpuBuffer = mVertexBuffers[i]->GetGpuBuffer();
if(gpuBuffer)
{
commandBuffer.BindIndexBuffer(*ibo, 0, mIndexType);
}
- commandBuffer.DrawIndexed(numIndices, 1, firstIndexOffset, 0, 0);
+ commandBuffer.DrawIndexed(numIndices, mInstanceCount, firstIndexOffset, 0, 0);
}
else
{
numVertices = static_cast<uint32_t>(mVertexBuffers[0]->GetElementCount());
}
- commandBuffer.Draw(numVertices, 1, 0, 0);
+ commandBuffer.Draw(numVertices, mInstanceCount, 0, 0);
}
return true;
}
OwnerPointer<GpuBuffer> mIndexBuffer;
IndexType mIndexType;
Type mGeometryType;
+ uint32_t mInstanceCount{0};
// Booleans
bool mIndicesChanged : 1;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return true;
}
+void VertexBuffer::SetDivisor(uint32_t divisor)
+{
+ mDivisor = divisor;
+}
+
+uint32_t VertexBuffer::GetDivisor()
+{
+ return mDivisor;
+}
+
} // namespace Render
} // namespace Internal
} // namespace Dali
#define DALI_INTERNAL_RENDER_VERTEX_BUFFER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
bool Update(Graphics::Controller& graphicsController);
/**
+ * @brief Set the divisor of the buffer for instanced drawing
+ * @param[in] divisor The divisor (0 = not instanced, >=1 = instanced)
+ */
+ void SetDivisor(uint32_t divisor);
+
+ /**
+ * Get the divisor for the vertex buffer
+ */
+ [[nodiscard]] uint32_t GetDivisor();
+
+ /**
* Get the number of attributes present in the buffer
* @return The number of attributes stored in this buffer
*/
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
};
#include <dali/internal/render/common/render-manager.h>
#include <dali/internal/render/queue/render-queue.h>
+#include <dali/internal/render/renderers/render-vertex-buffer.h>
// Un-comment to enable node tree debug logging
//#define NODE_TREE_LOGGING 1
new(slot) DerivedType(&mImpl->renderManager, &RenderManager::SetVertexBufferData, vertexBuffer, data, size);
}
+void UpdateManager::SetVertexBufferDivisor(Render::VertexBuffer* vertexBuffer, uint32_t divisor)
+{
+ using LocalType = MessageValue1<Render::VertexBuffer, uint32_t>;
+ uint32_t* slot = mImpl->renderQueue.ReserveMessageSlot(mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof(LocalType));
+ new(slot) LocalType(vertexBuffer, &Render::VertexBuffer::SetDivisor, divisor);
+}
+
void UpdateManager::AddGeometry(OwnerPointer<Render::Geometry>& geometry)
{
// Message has ownership of format while in transit from update -> render
void SetVertexBufferData(Render::VertexBuffer* vertexBuffer, OwnerPointer<Vector<uint8_t>>& data, uint32_t size);
/**
+ * Sets the divisor of a vertex buffer. This is used by the GPU to provide
+ * instanced drawing.
+ * @param[in] vertexBuffer The property buffer.
+ * @param[in] divisor The instance divisor. 0 to turn instancing off.
+ */
+ void SetVertexBufferDivisor(Render::VertexBuffer* vertexBuffer, uint32_t divisor);
+
+ /**
* Adds a geometry to the RenderManager
* @param[in] geometry The geometry to add
* @post Sends a message to RenderManager to add the Geometry
new(slot) LocalType(&manager, &UpdateManager::SetVertexBufferData, &vertexBuffer, data, size);
}
+inline void SetVertexBufferDivisorMessage(UpdateManager& manager, Render::VertexBuffer& vertexBuffer, uint32_t divisor)
+{
+ using LocalType = MessageValue2<UpdateManager, Render::VertexBuffer*, uint32_t>;
+ uint32_t* slot = manager.ReserveMessageSlot(sizeof(LocalType));
+ new(slot) LocalType(&manager, &UpdateManager::SetVertexBufferDivisor, &vertexBuffer, divisor);
+}
+
inline void AddGeometry(UpdateManager& manager, OwnerPointer<Render::Geometry>& geometry)
{
// Message has ownership of Geometry while in transit from event -> update
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return GetImplementation(*this).GetSize();
}
+void VertexBuffer::SetDivisor(uint32_t divisor)
+{
+ GetImplementation(*this).SetDivisor(divisor);
+}
+
+uint32_t VertexBuffer::GetDivisor() const
+{
+ return GetImplementation(*this).GetDivisor();
+}
+
VertexBuffer::VertexBuffer(Internal::VertexBuffer* pointer)
: BaseHandle(pointer)
{
#define DALI_VERTEX_BUFFER_H
/*
- * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
std::size_t GetSize() const;
+ /**
+ * @brief Sets vertex divisor for all attributes
+ *
+ * If instancing isn't supported, the function has no effect.
+ * It's responsibility of developer to make sure the feature is supported.
+ * A divisor of 0 will turn off instanced drawing.
+ * Currently, a divisor > 1 will turn on instanced draw, but will have an
+ * actual rate of 1.
+ *
+ * @param[in] divisor Sets vertex buffer divisor for an instanced draw
+ */
+ void SetDivisor(uint32_t divisor);
+
+ /**
+ * @brief Get the divisor for the given attribute. A return value of 0 means that
+ * instancing is turned off.
+ *
+ * @return either 0 (not instanced), or > 0 (instanced)
+ */
+ uint32_t GetDivisor() const;
+
public:
/**
* @brief The constructor.