${internal_src_dir}/render/renderers/render-sampler.cpp
${internal_src_dir}/render/renderers/render-texture.cpp
${internal_src_dir}/render/renderers/render-vertex-buffer.cpp
-
${internal_src_dir}/render/renderers/shader-cache.cpp
+ ${internal_src_dir}/render/renderers/uniform-buffer-manager.cpp
+
${internal_src_dir}/render/shaders/program.cpp
${internal_src_dir}/render/shaders/program-controller.cpp
${internal_src_dir}/render/shaders/scene-graph-shader.cpp
#include <dali/internal/render/renderers/render-frame-buffer.h>
#include <dali/internal/render/renderers/render-texture.h>
#include <dali/internal/render/renderers/shader-cache.h>
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
#include <dali/internal/render/shaders/program-controller.h>
namespace Dali
// Create thread pool with just one thread ( there may be a need to create more threads in the future ).
threadPool = std::unique_ptr<Dali::ThreadPool>(new Dali::ThreadPool());
threadPool->Initialize(1u);
+
+ uniformBufferManager.reset(new Render::UniformBufferManager(&graphicsController));
}
~Impl()
ProgramController programController; ///< Owner of the GL programs
Render::ShaderCache shaderCache; ///< The cache for the graphics shaders
+ std::unique_ptr<Render::UniformBufferManager> uniformBufferManager; ///< The uniform buffer manager
+
Integration::DepthBufferAvailable depthBufferAvailable; ///< Whether the depth buffer is available
Integration::StencilBufferAvailable stencilBufferAvailable; ///< Whether the stencil buffer is available
Integration::PartialUpdateAvailable partialUpdateAvailable; ///< Whether the partial update is available
void RenderManager::AddRenderer(OwnerPointer<Render::Renderer>& renderer)
{
// Initialize the renderer as we are now in render thread
- renderer->Initialize(mImpl->context, mImpl->graphicsController, mImpl->programController, mImpl->shaderCache);
+ renderer->Initialize(mImpl->context, mImpl->graphicsController, mImpl->programController, mImpl->shaderCache, *(mImpl->uniformBufferManager.get()));
mImpl->rendererContainer.PushBack(renderer.Release());
}
#include <dali/internal/render/renderers/shader-cache.h>
#include <dali/internal/render/shaders/program.h>
#include <dali/internal/render/shaders/scene-graph-shader.h>
+#include <dali/internal/update/common/uniform-map.h>
namespace Dali
{
{
namespace
{
+// Size of uniform buffer page used when resizing
+constexpr uint32_t UBO_PAGE_SIZE = 8192u;
+
+// UBO allocation threshold below which the UBO will shrink
+constexpr auto UBO_SHRINK_THRESHOLD = 0.75f;
+
/**
* Helper to set view and projection matrices once per program
* @param program to set the matrices to
return Graphics::BlendOp{};
}
+/**
+ * Helper function to calculate the correct alignment of data for uniform buffers
+ * @param dataSize size of uniform buffer
+ * @return aligned offset of data
+ */
+inline uint32_t GetUniformBufferDataAlignment(uint32_t dataSize)
+{
+ return ((dataSize / 256u) + ((dataSize % 256u) ? 1u : 0u)) * 256u;
+}
+
} // namespace
namespace Render
mBlendingOptions.SetBlendColor(blendColor);
}
-void Renderer::Initialize(Context& context, Graphics::Controller& graphicsController, ProgramCache& programCache, Render::ShaderCache& shaderCache)
+void Renderer::Initialize(Context& context, Graphics::Controller& graphicsController, ProgramCache& programCache, Render::ShaderCache& shaderCache, Render::UniformBufferManager& uniformBufferManager)
{
- mContext = &context;
- mGraphicsController = &graphicsController;
- mProgramCache = &programCache;
- mShaderCache = &shaderCache;
+ mContext = &context;
+ mGraphicsController = &graphicsController;
+ mProgramCache = &programCache;
+ mShaderCache = &shaderCache;
+ mUniformBufferManager = &uniformBufferManager;
}
Renderer::~Renderer() = default;
{
}
-void Renderer::SetUniforms(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Vector3& size, Program& program)
+void Renderer::BuildUniformIndexMap(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Vector3& size, Program& program)
{
// Check if the map has changed
DALI_ASSERT_DEBUG(mRenderDataProvider && "No Uniform map data provider available");
uint32_t mapIndex = 0;
for(; mapIndex < uniformMap.Count(); ++mapIndex)
{
- mUniformIndexMap[mapIndex].propertyValue = uniformMap[mapIndex].propertyPtr;
- mUniformIndexMap[mapIndex].uniformIndex = program.RegisterUniform(uniformMap[mapIndex].uniformName);
+ mUniformIndexMap[mapIndex].propertyValue = uniformMap[mapIndex].propertyPtr;
+ mUniformIndexMap[mapIndex].uniformIndex = program.RegisterUniform(uniformMap[mapIndex].uniformName);
+ mUniformIndexMap[mapIndex].uniformName = uniformMap[mapIndex].uniformName;
+ mUniformIndexMap[mapIndex].uniformNameHash = uniformMap[mapIndex].uniformNameHash;
+ mUniformIndexMap[mapIndex].uniformNameHashNoArray = uniformMap[mapIndex].uniformNameHashNoArray;
+ mUniformIndexMap[mapIndex].arrayIndex = uniformMap[mapIndex].arrayIndex;
}
for(uint32_t nodeMapIndex = 0; nodeMapIndex < uniformMapNode.Count(); ++nodeMapIndex)
if(!found)
{
- mUniformIndexMap[mapIndex].propertyValue = uniformMapNode[nodeMapIndex].propertyPtr;
- mUniformIndexMap[mapIndex].uniformIndex = uniformIndex;
+ mUniformIndexMap[mapIndex].propertyValue = uniformMapNode[nodeMapIndex].propertyPtr;
+ mUniformIndexMap[mapIndex].uniformName = uniformMapNode[nodeMapIndex].uniformName;
+ mUniformIndexMap[mapIndex].uniformIndex = uniformIndex;
+ mUniformIndexMap[mapIndex].uniformNameHash = uniformMapNode[nodeMapIndex].uniformNameHash;
+ mUniformIndexMap[mapIndex].uniformNameHashNoArray = uniformMapNode[nodeMapIndex].uniformNameHashNoArray;
+ mUniformIndexMap[mapIndex].arrayIndex = uniformMapNode[nodeMapIndex].arrayIndex;
++mapIndex;
}
}
mUniformIndexMap.Resize(mapIndex);
}
+ // The code below is disabled because the uniforms should now be set in the graphics backend.
+
+ /*
// Set uniforms in local map
for(UniformIndexMappings::Iterator iter = mUniformIndexMap.Begin(),
end = mUniformIndexMap.End();
{
program.SetSizeUniform3f(sizeLoc, size.x, size.y, size.z);
}
+*/
}
void Renderer::SetUniformFromProperty(BufferIndex bufferIndex, Program& program, UniformIndexMap& map)
{
// Only set up and draw if we have textures and they are all valid
+ // The code below is disabled because the uniforms should now be set in the graphics backend.
+
+ /*
// set projection and view matrix if program has not yet received them yet this frame
SetMatrices(*program, modelMatrix, viewMatrix, projectionMatrix, modelViewMatrix);
program->SetUniform4f(loc, color.r, color.g, color.b, color.a * mRenderDataProvider->GetOpacity(bufferIndex));
}
}
+*/
+
+ BuildUniformIndexMap(bufferIndex, node, size, *program);
+
+ // Create the UBO
+ uint32_t uniformBlockAllocationBytes{0u};
+ uint32_t uniformBlockMaxSize{0u};
+ uint32_t uboOffset{0u};
+
+ auto& reflection = mGraphicsController->GetProgramReflection(program->GetGraphicsProgram());
+ for(auto i = 0u; i < reflection.GetUniformBlockCount(); ++i)
+ {
+ auto blockSize = GetUniformBufferDataAlignment(reflection.GetUniformBlockSize(i));
+ if(uniformBlockMaxSize < blockSize)
+ {
+ uniformBlockMaxSize = blockSize;
+ }
+ uniformBlockAllocationBytes += blockSize;
+ }
+
+ auto pagedAllocation = ((uniformBlockAllocationBytes / UBO_PAGE_SIZE + 1u)) * UBO_PAGE_SIZE;
+
+ // Allocate twice memory as required by the uniform buffers
+ // todo: memory usage backlog to use optimal allocation
+ if(uniformBlockAllocationBytes && !mUniformBuffer[bufferIndex])
+ {
+ mUniformBuffer[bufferIndex] = std::move(mUniformBufferManager->AllocateUniformBuffer(pagedAllocation));
+ }
+ else if(uniformBlockAllocationBytes && (mUniformBuffer[bufferIndex]->GetSize() < pagedAllocation ||
+ (pagedAllocation < uint32_t(float(mUniformBuffer[bufferIndex]->GetSize()) * UBO_SHRINK_THRESHOLD))))
+ {
+ mUniformBuffer[bufferIndex]->Reserve(pagedAllocation);
+ }
+
+ // Clear UBO
+ if(mUniformBuffer[bufferIndex])
+ {
+ mUniformBuffer[bufferIndex]->Fill(0, 0u, 0u);
+ }
+
+ // update the uniform buffer
+ // pass shared UBO and offset, return new offset for next item to be used
+ // don't process bindings if there are no uniform buffers allocated
+ auto ubo = mUniformBuffer[bufferIndex].get();
+ if(ubo)
+ {
+ std::vector<Graphics::UniformBufferBinding>* bindings{nullptr};
+ FillUniformBuffers(*program, instruction, *ubo, bindings, uboOffset, bufferIndex);
+
+ Vector4 finalColor;
+ const Vector4& color = node.GetRenderColor(bufferIndex);
+ if(mPremultipledAlphaEnabled)
+ {
+ float alpha = color.a * mRenderDataProvider->GetOpacity(bufferIndex);
+ finalColor = Vector4(color.r * alpha, color.g * alpha, color.b * alpha, alpha);
+ }
+ else
+ {
+ finalColor = Vector4(color.r, color.g, color.b, color.a * mRenderDataProvider->GetOpacity(bufferIndex));
+ }
- SetUniforms(bufferIndex, node, size, *program);
+ // We know bindings for this renderer, so we can use 'offset' and write additional uniforms
+ Matrix modelViewProjectionMatrix(false);
+ Matrix::Multiply(modelViewProjectionMatrix, modelViewMatrix, projectionMatrix);
+
+ WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_MATRIX), *ubo, *bindings, modelMatrix);
+ WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::VIEW_MATRIX), *ubo, *bindings, viewMatrix);
+ WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::PROJECTION_MATRIX), *ubo, *bindings, projectionMatrix);
+ WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MVP_MATRIX), *ubo, *bindings, modelViewProjectionMatrix);
+ WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_VIEW_MATRIX), *ubo, *bindings, modelViewMatrix);
+ WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::SIZE), *ubo, *bindings, size);
+ WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::COLOR), *ubo, *bindings, finalColor);
+
+ // Update normal matrix only when used in the shader
+ Matrix3 normalMatrix(modelViewMatrix);
+ normalMatrix.Invert();
+ normalMatrix.Transpose();
+ WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::NORMAL_MATRIX), *ubo, *bindings, normalMatrix);
+
+ commandBuffer->BindUniformBuffers(*bindings);
+ }
bool drawn = false; // Draw can fail if there are no vertex buffers or they haven't been uploaded yet
// @todo We should detect this case much earlier to prevent unnecessary work
}
}
+template<class T>
+bool Renderer::WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const T& data)
+{
+ if(uniformInfo)
+ {
+ WriteUniform(ubo, bindings, *uniformInfo, data);
+ return true;
+ }
+ return false;
+}
+
+template<class T>
+void Renderer::WriteUniform(Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const Graphics::UniformInfo& uniformInfo, const T& data)
+{
+ WriteUniform(ubo, bindings, uniformInfo, &data, sizeof(T));
+}
+
+void Renderer::WriteUniform(Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const Graphics::UniformInfo& uniformInfo, const void* data, uint32_t size)
+{
+ ubo.Write(data, size, bindings[uniformInfo.bufferIndex].offset + uniformInfo.offset);
+}
+
+void Renderer::FillUniformBuffers(Program& program,
+ const SceneGraph::RenderInstruction& instruction,
+ Render::UniformBuffer& ubo,
+ std::vector<Graphics::UniformBufferBinding>*& outBindings,
+ uint32_t& offset,
+ BufferIndex updateBufferIndex)
+{
+ auto& reflection = mGraphicsController->GetProgramReflection(program.GetGraphicsProgram());
+ auto uboCount = reflection.GetUniformBlockCount();
+
+ mUniformBufferBindings.resize(uboCount);
+
+ // Setup bindings
+ uint32_t dataOffset = offset;
+ for(auto i = 0u; i < uboCount; ++i)
+ {
+ mUniformBufferBindings[i].dataSize = reflection.GetUniformBlockSize(i);
+ mUniformBufferBindings[i].binding = reflection.GetUniformBlockBinding(i);
+ mUniformBufferBindings[i].offset = dataOffset;
+
+ dataOffset += GetUniformBufferDataAlignment(mUniformBufferBindings[i].dataSize);
+ mUniformBufferBindings[i].buffer = ubo.GetBuffer();
+
+ for(UniformIndexMappings::Iterator iter = mUniformIndexMap.Begin(),
+ end = mUniformIndexMap.End();
+ iter != end;
+ ++iter)
+ {
+ // @todo This means parsing the uniform string every frame. Instead, store the array index if present.
+ int arrayIndex = (*iter).arrayIndex;
+
+ auto uniformInfo = Graphics::UniformInfo{};
+ auto uniformFound = program.GetUniform((*iter).uniformName.GetCString(),
+ (*iter).uniformNameHashNoArray ? (*iter).uniformNameHashNoArray : (*iter).uniformNameHash,
+ uniformInfo);
+
+ if(uniformFound)
+ {
+ auto dst = mUniformBufferBindings[uniformInfo.bufferIndex].offset + uniformInfo.offset;
+
+ switch((*iter).propertyValue->GetType())
+ {
+ case Property::Type::FLOAT:
+ case Property::Type::INTEGER:
+ case Property::Type::BOOLEAN:
+ {
+ ubo.Write(&(*iter).propertyValue->GetFloat(updateBufferIndex),
+ sizeof(float),
+ dst + static_cast<uint32_t>(sizeof(Vector4)) * arrayIndex);
+ break;
+ }
+ case Property::Type::VECTOR2:
+ {
+ ubo.Write(&(*iter).propertyValue->GetVector2(updateBufferIndex),
+ sizeof(Vector2),
+ dst + static_cast<uint32_t>(sizeof(Vector4)) * arrayIndex);
+ break;
+ }
+ case Property::Type::VECTOR3:
+ {
+ ubo.Write(&(*iter).propertyValue->GetVector3(updateBufferIndex),
+ sizeof(Vector3),
+ dst + static_cast<uint32_t>(sizeof(Vector4)) * arrayIndex);
+ break;
+ }
+ case Property::Type::VECTOR4:
+ {
+ ubo.Write(&(*iter).propertyValue->GetVector4(updateBufferIndex),
+ sizeof(Vector4),
+ dst + static_cast<uint32_t>(sizeof(Vector4)) * arrayIndex);
+ break;
+ }
+ case Property::Type::MATRIX:
+ {
+ ubo.Write(&(*iter).propertyValue->GetMatrix(updateBufferIndex),
+ sizeof(Matrix),
+ dst + static_cast<uint32_t>(sizeof(Matrix)) * arrayIndex);
+ break;
+ }
+ case Property::Type::MATRIX3:
+ {
+ const auto& matrix = &(*iter).propertyValue->GetMatrix3(updateBufferIndex);
+ for(int i = 0; i < 3; ++i)
+ {
+ ubo.Write(&matrix->AsFloat()[i * 3],
+ sizeof(float) * 3,
+ dst + (i * static_cast<uint32_t>(sizeof(Vector4))));
+ }
+ break;
+ }
+ default:
+ {
+ }
+ }
+ }
+ }
+ }
+ // write output bindings
+ outBindings = &mUniformBufferBindings;
+
+ // Update offset
+ offset = dataOffset;
+}
+
void Renderer::SetSortAttributes(BufferIndex bufferIndex,
SceneGraph::RenderInstructionProcessor::SortAttributes& sortAttributes) const
{
#include <dali/graphics-api/graphics-controller.h>
#include <dali/integration-api/debug.h>
#include <dali/internal/common/blending-options.h>
+#include <dali/internal/common/const-string.h>
#include <dali/internal/common/message.h>
#include <dali/internal/common/type-abstraction-enums.h>
#include <dali/internal/event/common/property-input-impl.h>
#include <dali/internal/render/data-providers/render-data-provider.h>
#include <dali/internal/render/gl-resources/gl-resource-owner.h>
#include <dali/internal/render/renderers/render-geometry.h>
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
+#include <dali/internal/render/shaders/program.h>
#include <dali/internal/update/manager/render-instruction-processor.h>
namespace Dali
{
class Context;
class Texture;
-class Program;
class ProgramCache;
namespace SceneGraph
namespace Render
{
struct ShaderCache;
+class UniformBufferManager;
/**
* Renderers are used to render meshes
* @param[in] graphicsController The graphics controller to use
* @param[in] programCache Cache of program objects
* @param[in] shaderCache Cache of shaders
+ * @param[in] uniformBufferManager Uniform buffer manager
*/
- void Initialize(Context& context, Graphics::Controller& graphicsController, ProgramCache& programCache, Render::ShaderCache& shaderCache);
+ void Initialize(Context& context, Graphics::Controller& graphicsController, ProgramCache& programCache, Render::ShaderCache& shaderCache, Render::UniformBufferManager& uniformBufferManager);
/**
* Destructor
*/
bool Updated(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider* node);
+ template<class T>
+ bool WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const T& data);
+
+ template<class T>
+ void WriteUniform(Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const Graphics::UniformInfo& uniformInfo, const T& data);
+
+ void WriteUniform(Render::UniformBuffer& ubo, const std::vector<Graphics::UniformBufferBinding>& bindings, const Graphics::UniformInfo& uniformInfo, const void* data, uint32_t size);
+
private:
struct UniformIndexMap;
void SetBlending(Context& context, bool blend);
/**
- * Set the uniforms from properties according to the uniform map
+ * Builds a uniform map based on the index of the cached location in the Program.
* @param[in] bufferIndex The index of the previous update buffer.
* @param[in] node The node using the renderer
* @param[in] size The size of the renderer
* @param[in] program The shader program on which to set the uniforms.
*/
- void SetUniforms(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Vector3& size, Program& program);
+ void BuildUniformIndexMap(BufferIndex bufferIndex, const SceneGraph::NodeDataProvider& node, const Vector3& size, Program& program);
/**
* Set the program uniform in the map from the mapped property
Program& program,
const Dali::Internal::SceneGraph::RenderInstruction& instruction,
bool blend,
- Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline);
+ Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline);
+
+ /**
+ * @brief Fill uniform buffer at index. Writes uniforms into given memory address
+ *
+ * @param[in] instruction The render instruction
+ * @param[in,out] ubo Target uniform buffer object
+ * @param[out] outBindings output bindings vector
+ * @param[out] offset output offset of the next uniform buffer memory address
+ * @param[in] updateBufferIndex update buffer index
+ */
+ void FillUniformBuffers(Program& program,
+ const SceneGraph::RenderInstruction& instruction,
+ Render::UniformBuffer& ubo,
+ std::vector<Graphics::UniformBufferBinding>*& outBindings,
+ uint32_t& offset,
+ BufferIndex updateBufferIndex);
private:
Graphics::Controller* mGraphicsController;
ProgramCache* mProgramCache{};
Render::ShaderCache* mShaderCache{};
+ Render::UniformBufferManager* mUniformBufferManager{};
+ std::vector<Graphics::UniformBufferBinding> mUniformBufferBindings{};
+
Graphics::UniquePtr<Graphics::Pipeline> mGraphicsPipeline{}; ///< The graphics pipeline. (Cached implementation)
std::vector<Graphics::ShaderState> mShaderStates{};
+ using Hash = unsigned long;
struct UniformIndexMap
{
- uint32_t uniformIndex; ///< The index of the cached location in the Program
- const PropertyInputImpl* propertyValue;
+ uint32_t uniformIndex; ///< The index of the cached location in the Program
+ ConstString uniformName; ///< The uniform name
+ const PropertyInputImpl* propertyValue; ///< The property value
+ Hash uniformNameHash{0u};
+ Hash uniformNameHashNoArray{0u};
+ int32_t arrayIndex; ///< The array index
};
using UniformIndexMappings = Dali::Vector<UniformIndexMap>;
};
LegacyProgram mLegacyProgram; ///< The structure to pass the program ID into Graphics::PipelineCreateInfo
+
+ using UniformBufferList = std::array<Graphics::UniquePtr<Render::UniformBuffer>, 2u>;
+ UniformBufferList mUniformBuffer{}; ///< The double-buffered uniform buffer
};
} // namespace Render
--- /dev/null
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "uniform-buffer-manager.h"
+#include <dali/graphics-api/graphics-buffer-create-info.h>
+#include <dali/graphics-api/graphics-buffer.h>
+
+#include <cstring>
+#include <memory>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Render
+{
+UniformBuffer::UniformBuffer(Dali::Graphics::Controller* controller,
+ uint32_t sizeInBytes,
+ uint32_t alignment,
+ bool persistentMappedEnabled,
+ Graphics::BufferUsageFlags usageFlags)
+: mController(controller),
+ mSize(0u),
+ mPersistentMappedEnabled(persistentMappedEnabled),
+ mUsageFlags(usageFlags)
+{
+ if(sizeInBytes)
+ {
+ Reserve(sizeInBytes);
+ }
+}
+
+UniformBuffer::~UniformBuffer()
+{
+ if(mBuffer && mMemory)
+ {
+ mController->UnmapMemory(std::move(mMemory));
+ }
+}
+
+void UniformBuffer::Flush()
+{
+ if(mBuffer && mMemory)
+ {
+ mMemory->Flush();
+ }
+}
+
+void UniformBuffer::Reserve(uint32_t size)
+{
+ if(mBuffer && mMemory)
+ {
+ Unmap();
+ mMemory = nullptr;
+ }
+
+ mSize = size;
+
+ Graphics::BufferCreateInfo createInfo;
+ createInfo.SetSize(mSize);
+ createInfo.SetUsage(mUsageFlags);
+ mBuffer = std::move(mController->CreateBuffer(createInfo, std::move(mBuffer)));
+
+ mMapBufferInfo.buffer = mBuffer.get();
+ mMapBufferInfo.usage = 0 | Graphics::MemoryUsageFlagBits::WRITE;
+ mMapBufferInfo.offset = 0;
+ mMapBufferInfo.size = size;
+
+ if(mPersistentMappedEnabled)
+ {
+ Map();
+ }
+}
+
+void UniformBuffer::Fill(char data, uint32_t offset, uint32_t size)
+{
+ bool locallyMapped = (mMemory == nullptr);
+ if(locallyMapped)
+ {
+ Map();
+ }
+
+ if(mMemory)
+ {
+ void* ptr = mMemory->LockRegion(0, mSize);
+
+ auto begin = (reinterpret_cast<char*>(ptr) + offset);
+ if(size == 0)
+ {
+ size = mSize - offset - 1;
+ }
+ auto end = begin + size;
+ std::fill(begin, end, data);
+
+ mMemory->Unlock(true);
+ }
+
+ if(locallyMapped)
+ {
+ Unmap();
+ }
+}
+
+void UniformBuffer::Write(const void* data, uint32_t size, uint32_t dstOffset)
+{
+ bool locallyMapped = (mMemory == nullptr);
+ if(locallyMapped)
+ {
+ Map();
+ }
+
+ if(mMemory)
+ {
+ void* ptr = mMemory->LockRegion(0, size);
+ if(dstOffset + size < mSize)
+ {
+ memcpy(reinterpret_cast<char*>(ptr) + dstOffset, data, size);
+ }
+ mMemory->Unlock(true);
+ }
+
+ if(locallyMapped)
+ {
+ Unmap();
+ }
+}
+
+void UniformBuffer::Map()
+{
+ if(!mMemory)
+ {
+ mMemory = mController->MapBufferRange(mMapBufferInfo);
+ }
+}
+
+void UniformBuffer::Unmap()
+{
+ if(mMemory)
+ {
+ mController->UnmapMemory(std::move(mMemory));
+ }
+}
+
+UniformBufferManager::UniformBufferManager(Dali::Graphics::Controller* controller)
+: mController(controller)
+{
+}
+
+UniformBufferManager::~UniformBufferManager() = default;
+
+Graphics::UniquePtr<UniformBuffer> UniformBufferManager::AllocateUniformBuffer(uint32_t size)
+{
+ return Graphics::UniquePtr<UniformBuffer>(
+ new UniformBuffer(mController, size, 256u, true, Dali::Graphics::BufferUsageFlags{0u} | Dali::Graphics::BufferUsage::TRANSFER_DST | Dali::Graphics::BufferUsage::UNIFORM_BUFFER));
+}
+
+} // namespace Render
+
+} // namespace Internal
+
+} // namespace Dali
--- /dev/null
+#ifndef DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_H
+#define DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_H
+
+/*
+ * Copyright (c) 2021 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/graphics-api/graphics-controller.h>
+
+#include <memory>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Render
+{
+class UniformBuffer
+{
+ friend class UniformBufferManager;
+
+private:
+ /**
+ * Constructor of UniformBuffer
+ *
+ * @param[in] mController Pointer of the graphics controller
+ * @param[in] sizeInBytes initial size of allocated buffer
+ * @param[in] alignment memory alignment in bytes
+ * @param[in] persistentMappingEnabled if true, buffer is mapped persistently
+ * @param[in] usageFlags type of usage ( Graphics::BufferUsage )
+ */
+ UniformBuffer(Dali::Graphics::Controller* mController,
+ uint32_t sizeInBytes,
+ uint32_t alignment,
+ bool persistentMappingEnabled,
+ Graphics::BufferUsageFlags usageFlags);
+
+public:
+ /**
+ * Destructor of UniformBuffer
+ */
+ ~UniformBuffer();
+
+ /**
+ * Writes data into the buffer
+ *
+ * @param[in] data pointer to the source data
+ * @param[in] size size of source data
+ * @param[in] offset destination offset
+ */
+ void Write(const void* data, uint32_t size, uint32_t offset);
+
+ /**
+ * Flushes whole buffer range
+ */
+ void Flush();
+
+ /**
+ * Returns allocated ( requested ) size
+ * @return size of buffer
+ */
+ uint32_t GetSize() const
+ {
+ return mSize;
+ }
+
+ /**
+ * Return Graphics API buffer
+ * @return pointer to the buffer object
+ */
+ Dali::Graphics::Buffer* GetBuffer() const
+ {
+ return mBuffer.get();
+ }
+
+ /**
+ * Returns memory alignment
+ * @return memory alignment
+ */
+ uint32_t GetAlignment() const
+ {
+ return mAlignment;
+ }
+
+ /**
+ * Reserves buffer memory
+ *
+ * @param size requested size
+ */
+ void Reserve(uint32_t size);
+
+ /**
+ * Maps buffer memory
+ */
+ void Map();
+
+ /**
+ * Unmaps buffer memory
+ */
+ void Unmap();
+
+ /**
+ * Fills the buffer from the given offset with given data ( single 8bit value )
+ * @param data char type data
+ * @param offset start offset
+ * @param size size to write, 0 if whole size
+ */
+ void Fill(char data, uint32_t offset, uint32_t size);
+
+private:
+ Graphics::UniquePtr<Graphics::Buffer> mBuffer;
+ Dali::Graphics::Controller* mController;
+ Graphics::UniquePtr<Graphics::Memory> mMemory{nullptr};
+
+ Graphics::MapBufferInfo mMapBufferInfo{};
+
+ uint32_t mCapacity{0}; ///< buffer capacity
+ uint32_t mSize; ///< buffer size
+ uint32_t mAlignment;
+ bool mPersistentMappedEnabled;
+
+ Graphics::BufferUsageFlags mUsageFlags;
+};
+
+class UniformBufferManager
+{
+public:
+ UniformBufferManager(Dali::Graphics::Controller* controller);
+
+ ~UniformBufferManager();
+
+ /**
+ * Allocates uniform buffer with given size
+ * @param size
+ * @return
+ */
+ Graphics::UniquePtr<UniformBuffer> AllocateUniformBuffer(uint32_t size);
+
+private:
+ Dali::Graphics::Controller* mController;
+};
+
+} // namespace Render
+} // namespace Internal
+} // namespace Dali
+
+#endif // DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_H
// EXTERNAL INCLUDES
#include <cstring>
#include <iomanip>
+#include <map>
// INTERNAL INCLUDES
+#include <dali/devel-api/common/hash.h>
#include <dali/graphics-api/graphics-controller.h>
#include <dali/graphics-api/graphics-program.h>
+#include <dali/graphics-api/graphics-reflection.h>
#include <dali/integration-api/debug.h>
#include <dali/integration-api/gl-defines.h>
#include <dali/internal/common/shader-data.h>
#include <dali/public-api/common/dali-common.h>
#include <dali/public-api/common/dali-vector.h>
+namespace
+{
+void LogWithLineNumbers(const char* source)
+{
+ uint32_t lineNumber = 0u;
+ const char* prev = source;
+ const char* ptr = prev;
+
+ while(true)
+ {
+ if(lineNumber > 200u)
+ {
+ break;
+ }
+ // seek the next end of line or end of text
+ while(*ptr != '\n' && *ptr != '\0')
+ {
+ ++ptr;
+ }
+
+ std::string line(prev, ptr - prev);
+ Dali::Integration::Log::LogMessage(Dali::Integration::Log::DebugError, "%4d %s\n", lineNumber, line.c_str());
+
+ if(*ptr == '\0')
+ {
+ break;
+ }
+ prev = ++ptr;
+ ++lineNumber;
+ }
+}
+
+} //namespace
+
namespace Dali
{
namespace Internal
"uSize" // UNIFORM_SIZE
};
+/**
+ * List of all default uniforms used for quicker lookup
+ */
+std::array<size_t, 8> DEFAULT_UNIFORM_HASHTABLE =
+ {
+ CalculateHash(std::string("uModelMatrix")),
+ CalculateHash(std::string("uMvpMatrix")),
+ CalculateHash(std::string("uViewMatrix")),
+ CalculateHash(std::string("uModelView")),
+ CalculateHash(std::string("uNormalMatrix")),
+ CalculateHash(std::string("uProjection")),
+ CalculateHash(std::string("uSize")),
+ CalculateHash(std::string("uColor"))};
+
} // namespace
// IMPLEMENTATION
// in order to maintain current functionality as long as needed
gfxController.GetProgramParameter(*gfxProgram, 1, &programId);
- size_t shaderHash = programId;
- Program* program = cache.GetProgram(shaderHash);
+ size_t shaderHash = programId;
+
+ Program* program = cache.GetProgram(shaderHash);
if(nullptr == program)
{
program->GetActiveSamplerUniforms();
cache.AddProgram(shaderHash, program);
}
+
return program;
}
// Get program id and use it as hash for the cache
// in order to maintain current functionality as long as needed
mGfxController.GetProgramParameter(*mGfxProgram, 1, &mProgramId);
+
+ BuildReflection(controller.GetProgramReflection(*mGfxProgram.get()));
}
Program::~Program()
}
}
+void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
+{
+ mReflectionDefaultUniforms.clear();
+ mReflectionDefaultUniforms.resize(DEFAULT_UNIFORM_HASHTABLE.size());
+
+ auto uniformBlockCount = graphicsReflection.GetUniformBlockCount();
+
+ // add uniform block fields
+ for(auto i = 0u; i < uniformBlockCount; ++i)
+ {
+ Graphics::UniformBlockInfo uboInfo;
+ graphicsReflection.GetUniformBlock(i, uboInfo);
+
+ // for each member store data
+ for(const auto& item : uboInfo.members)
+ {
+ auto hashValue = CalculateHash(item.name);
+ mReflection.emplace_back(ReflectionUniformInfo{hashValue, false, item});
+
+ // update buffer index
+ mReflection.back().uniformInfo.bufferIndex = i;
+
+ // Update default uniforms
+ for(auto i = 0u; i < DEFAULT_UNIFORM_HASHTABLE.size(); ++i)
+ {
+ if(hashValue == DEFAULT_UNIFORM_HASHTABLE[i])
+ {
+ mReflectionDefaultUniforms[i] = mReflection.back();
+ break;
+ }
+ }
+ }
+ }
+
+ // add samplers
+ auto samplers = graphicsReflection.GetSamplers();
+ for(const auto& sampler : samplers)
+ {
+ mReflection.emplace_back(ReflectionUniformInfo{CalculateHash(sampler.name), false, sampler});
+ }
+
+ // check for potential collisions
+ std::map<size_t, bool> hashTest;
+ bool hasCollisions(false);
+ for(auto&& item : mReflection)
+ {
+ if(hashTest.find(item.hashValue) == hashTest.end())
+ {
+ hashTest[item.hashValue] = false;
+ }
+ else
+ {
+ hashTest[item.hashValue] = true;
+ hasCollisions = true;
+ }
+ }
+
+ // update collision flag for further use
+ if(hasCollisions)
+ {
+ for(auto&& item : mReflection)
+ {
+ item.hasCollision = hashTest[item.hashValue];
+ }
+ }
+}
+
+bool Program::GetUniform(const std::string& name, size_t hashedName, Graphics::UniformInfo& out) const
+{
+ if(mReflection.empty())
+ {
+ return false;
+ }
+
+ hashedName = !hashedName ? CalculateHash(name) : hashedName;
+
+ for(const ReflectionUniformInfo& item : mReflection)
+ {
+ if(item.hashValue == hashedName)
+ {
+ if(!item.hasCollision || item.uniformInfo.name == name)
+ {
+ out = item.uniformInfo;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+bool Program::GetDefaultUniform(DefaultUniformIndex defaultUniformIndex, Graphics::UniformInfo& out) const
+{
+ if(mReflectionDefaultUniforms.empty())
+ {
+ return false;
+ }
+
+ auto& value = mReflectionDefaultUniforms[static_cast<uint32_t>(defaultUniformIndex)];
+ out = value.uniformInfo;
+ return true;
+}
+
+const Graphics::UniformInfo* Program::GetDefaultUniform(DefaultUniformIndex defaultUniformIndex) const
+{
+ if(mReflectionDefaultUniforms.empty())
+ {
+ return nullptr;
+ }
+
+ const auto value = &mReflectionDefaultUniforms[static_cast<uint32_t>(defaultUniformIndex)];
+ return &value->uniformInfo;
+}
+
} // namespace Internal
} // namespace Dali
{
class Controller;
class Program;
+class Reflection;
} // namespace Graphics
+
class Matrix;
namespace Integration
};
/**
+ * Indices of default uniforms
+ */
+ enum class DefaultUniformIndex
+ {
+ MODEL_MATRIX = 0,
+ MVP_MATRIX,
+ VIEW_MATRIX,
+ MODEL_VIEW_MATRIX,
+ NORMAL_MATRIX,
+ PROJECTION_MATRIX,
+ SIZE,
+ COLOR
+ };
+
+ /**
* Creates a new program, or returns a copy of an existing program in the program cache
* @param[in] cache where the programs are stored
* @param[in] shaderData A pointer to a data structure containing the program source
return *mGfxProgram;
}
+ /**
+ * Retrieves uniform data.
+ * The lookup tries to minimise string comparisons. Ideally, when the hashedName is known
+ * and there are no hash collisions in the reflection it's the most optimal case.
+ *
+ * @param name Name of uniform
+ * @param hashedName Hash value from name or 0 if unknown
+ * @param out Reference to output structure
+ *
+ * @return False when uniform is not found or due to hash collision the result is ambiguous
+ */
+ bool GetUniform(const std::string& name, size_t hashedName, Graphics::UniformInfo& out) const;
+
+ /**
+ * Retrieves default uniform
+ * @param[in] defaultUniformIndex index of the uniform
+ * @param[out] outputUniformInfo the reference to UniformInfo object
+ * @return True is uniform found, false otherwise
+ */
+ bool GetDefaultUniform(DefaultUniformIndex defaultUniformIndex, Graphics::UniformInfo& outputUniformInfo) const;
+
+ /**
+ * Retrievs default uniform
+ * @param[in] defaultUniformIndex index of the uniform
+ * @return Valid pointer to the UniformInfo object or nullptr
+ */
+ const Graphics::UniformInfo* GetDefaultUniform(DefaultUniformIndex defaultUniformIndex) const;
+
private: // Implementation
/**
* Constructor, private so no direct instantiation
*/
void ResetAttribsUniformCache();
+ /**
+ * Struct ReflectionUniformInfo
+ * Contains details of a single uniform buffer field and/or sampler.
+ */
+ struct ReflectionUniformInfo
+ {
+ size_t hashValue{0};
+ bool hasCollision{false};
+ Graphics::UniformInfo uniformInfo{};
+ };
+
+ /**
+ * Build optimized shader reflection of uniforms
+ * @param graphicsReflection The graphics reflection
+ */
+ void BuildReflection(const Graphics::Reflection& graphicsReflection);
+
private: // Data
ProgramCache& mCache; ///< The program cache
Integration::GlAbstraction& mGlAbstraction; ///< The OpenGL Abstraction layer
GLfloat mUniformCacheFloat4[MAX_UNIFORM_CACHE_SIZE][4]; ///< Value cache for uniforms of four floats
Vector3 mSizeUniformCache; ///< Cache value for size uniform
bool mModifiesGeometry; ///< True if the program changes geometry
+
+ using UniformReflectionContainer = std::vector<ReflectionUniformInfo>;
+
+ UniformReflectionContainer mReflection{}; ///< Contains reflection build per program
+ UniformReflectionContainer mReflectionDefaultUniforms{}; ///< Contains default uniforms
};
} // namespace Internal
class UniformPropertyMapping
{
public:
+ using Hash = unsigned long;
+
/**
* Constructor
*/
UniformPropertyMapping(ConstString theUniformName, const PropertyInputImpl* thePropertyPtr)
: propertyPtr(thePropertyPtr),
- uniformName(theUniformName)
+ uniformName(theUniformName),
+ uniformNameHash(0u),
+ uniformNameHashNoArray(0u),
+ arrayIndex(0u)
{
+ // Look for array index closing bracket
+ auto pos = theUniformName.GetStringView().rfind("]");
+
+ // If found, extract the array index and store it
+ if(pos != std::string::npos)
+ {
+ auto pos0 = theUniformName.GetStringView().rfind("[", pos);
+ arrayIndex = atoi(theUniformName.GetCString() + pos0 + 1);
+ // Calculate hash from name without array index
+ uniformNameHashNoArray = Dali::CalculateHash(theUniformName.GetStringView().substr(0, pos0).data());
+ }
+ uniformName = theUniformName;
+ uniformNameHash = Dali::CalculateHash(theUniformName.GetCString());
}
UniformPropertyMapping() = default;
const PropertyInputImpl* propertyPtr{nullptr};
- ConstString uniformName;
+ ConstString uniformName{};
+ Hash uniformNameHash{0u};
+ Hash uniformNameHashNoArray{0u};
+ int32_t arrayIndex{0u};
};
/**