Uniform blocks in GLES3 shaders are now supported by GPU buffer.
Fixed up test harness to handle CPU buffers properly
Fixed up test cases with wrong buffer counts
Added method to get uniform block alignment from the
backend - this is slightly more complicated than it
needs to be, because of the Vulkan-like API we have.
Enhanced test harness to enable testing of UBOs.
Adding reflection & test cases for uniform blocks
Ensured that multi-pass of draw commands doesn't
generate multiple blocks.
Ensured GPU uniform buffers are double-buffered
Ensured size of CPU/GPU for offscreen vs onscreen
buffers is correct.
Added test cases to ensure that large enough buffers
are allocated, and that properties are written
into the relevant uniform block areas within the buffers.
Change-Id: Id551d56ecea228320af3896afef49790d5ceec6d
#include "test-gl-abstraction.h"
#include "test-trace-call-stack.h"
-static const bool TRACE{
- false};
+static const bool TRACE{false};
+
+uint32_t GetGLDataTypeSize(GLenum type)
+{
+ // There are many more types than what are covered here, but
+ // they are not supported in dali.
+ switch(type)
+ {
+ case GL_FLOAT: // "float", 1 float, 4 bytes
+ return 4;
+ case GL_FLOAT_VEC2: // "vec2", 2 floats, 8 bytes
+ return 8;
+ case GL_FLOAT_VEC3: // "vec3", 3 floats, 12 bytes
+ return 12;
+ case GL_FLOAT_VEC4: // "vec4", 4 floats, 16 bytes
+ return 16;
+ case GL_INT: // "int", 1 integer, 4 bytes
+ return 4;
+ case GL_FLOAT_MAT2: // "mat2", 4 floats, 16 bytes
+ return 16;
+ case GL_FLOAT_MAT3: // "mat3", 3 vec3, 36 bytes
+ return 36;
+ case GL_FLOAT_MAT4: // "mat4", 4 vec4, 64 bytes
+ return 64;
+ default:
+ return 0;
+ }
+}
namespace Dali
{
{"uLightCameraProjectionMatrix", GL_FLOAT_MAT4, 1},
{"uLightCameraViewMatrix", GL_FLOAT_MAT4, 1}};
+ int offset = 0;
+ for(uint32_t i = 0; i < mActiveUniforms.size(); ++i)
+ {
+ mActiveUniforms[i].offset = offset;
+ offset += mActiveUniforms[i].size * GetGLDataTypeSize(mActiveUniforms[i].type);
+ }
// WARNING: IF YOU CHANGE THIS LIST, ALSO CHANGE UNIFORMS IN test-graphics-reflection.cpp
}
return ((imageGlFormat == GL_RGB) && (textureGlFormat == GL_RGBA));
}
+void TestGlAbstraction::SetActiveUniforms(const std::vector<ActiveUniform>& uniforms)
+{
+ mActiveUniforms = uniforms;
+ int offset = 0;
+ for(uint32_t i = 0; i < uniforms.size(); ++i)
+ {
+ mActiveUniforms[i].offset = offset;
+ offset += mActiveUniforms[i].size * GetGLDataTypeSize(mActiveUniforms[i].type);
+ }
+}
+
} // namespace Dali
bool BlendEnabled(const Dali::TraceCallStack& callStack)
struct ActiveUniform
{
- std::string name;
- GLenum type;
- GLint size;
+ std::string name{};
+ GLenum type{GL_FLOAT};
+ GLint size{0};
+ GLint offset{0};
};
class DALI_CORE_API TestGlAbstraction : public Dali::Integration::GlAbstraction
inline void DeleteBuffers(GLsizei n, const GLuint* buffers) override
{
+ TraceCallStack::NamedParams namedParams;
+ namedParams["n"] << n;
+ namedParams["id"] << buffers[0];
+ mBufferTrace.PushCall("DeleteBuffers", namedParams.str(), namedParams);
}
inline void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) override
inline void GenBuffers(GLsizei n, GLuint* buffers) override
{
// avoids an assert in GpuBuffers
- *buffers = 1u;
+ static GLuint id = 1;
- std::ostringstream o;
- o << n;
TraceCallStack::NamedParams namedParams;
- namedParams["n"] << o.str();
- mBufferTrace.PushCall("GenBuffers", o.str(), namedParams);
+ namedParams["n"] << n;
+
+ // Allocate some buffer names
+ bool first = true;
+ while(n)
+ {
+ namedParams["buffers"] << (first ? "" : ", ") << id;
+ first = false;
+ *buffers++ = id++;
+ --n;
+ }
+ mBufferTrace.PushCall("GenBuffers", namedParams.str(), namedParams);
}
inline void GenerateMipmap(GLenum target) override
*type = mAttribTypes[index];
}
- inline void SetActiveUniforms(const std::vector<ActiveUniform>& uniforms)
- {
- mActiveUniforms = uniforms;
- }
+ void SetActiveUniforms(const std::vector<ActiveUniform>& uniforms);
inline void GetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) override
{
case GL_PROGRAM_BINARY_FORMATS_OES:
*params = mBinaryFormats;
break;
+ case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
+ *params = mUniformBufferOffsetAlignment;
+ break;
}
}
inline void GetActiveUniformsiv(GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params) override
{
+ for(int i = 0; i < uniformCount; ++i)
+ {
+ if(i < int(mActiveUniforms.size()))
+ {
+ switch(pname)
+ {
+ case GL_UNIFORM_TYPE:
+ {
+ params[i] = mActiveUniforms[i].type;
+ break;
+ }
+ case GL_UNIFORM_SIZE:
+ {
+ params[i] = mActiveUniforms[i].size;
+ break;
+ }
+ case GL_UNIFORM_NAME_LENGTH:
+ {
+ params[i] = mActiveUniforms[i].name.length();
+ break;
+ }
+ case GL_UNIFORM_BLOCK_INDEX:
+ {
+ params[i] = -1;
+ break;
+ }
+ case GL_UNIFORM_OFFSET:
+ {
+ params[i] = mActiveUniforms[i].offset;
+ break;
+ }
+ case GL_UNIFORM_MATRIX_STRIDE:
+ {
+ break;
+ }
+ }
+ }
+ }
}
inline GLuint GetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName) override
{
mProgramBinaryLength = length;
}
-
+ inline void SetUniformBufferOffsetAlignment(GLint align)
+ {
+ mUniformBufferOffsetAlignment = align;
+ }
inline bool GetVertexAttribArrayState(GLuint index)
{
if(index >= MAX_ATTRIBUTE_CACHE_SIZE)
GLint mNumBinaryFormats;
GLint mBinaryFormats;
GLint mProgramBinaryLength;
+ GLint mUniformBufferOffsetAlignment{1};
bool mVertexAttribArrayState[MAX_ATTRIBUTE_CACHE_SIZE];
bool mVertexAttribArrayChanged; // whether the vertex attrib array has been changed
bool mGetProgramBinaryCalled;
namespace Dali
{
-TestGraphicsBuffer::TestGraphicsBuffer(TraceCallStack& callStack, TestGlAbstraction& glAbstraction, uint32_t size, Graphics::BufferUsageFlags usage)
+TestGraphicsBuffer::TestGraphicsBuffer(const Graphics::BufferCreateInfo& createInfo, TestGraphicsController& controller, TestGlAbstraction& glAbstraction, TraceCallStack& callStack)
: mCallStack(callStack),
+ mController(controller),
mGl(glAbstraction),
- mUsage(usage)
+ mCreateInfo(createInfo),
+ mUsage(createInfo.usage)
{
- memory.resize(size);
- mGl.GetBufferTrace().EnableLogging(false);
+ if(createInfo.propertiesFlags & int(Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED))
+ {
+ mCpuOnly = true;
+ }
+ else
+ {
+ mGl.GenBuffers(1, &mId);
+ }
+ memory.resize(createInfo.size);
+}
+
+TestGraphicsBuffer::~TestGraphicsBuffer()
+{
+ // Not strictly parameters, but useful for testing
+ TraceCallStack::NamedParams namedParams;
+ namedParams["usage"] << "0x" << std::hex << mCreateInfo.usage;
+ namedParams["propertiesFlags"] << mCreateInfo.propertiesFlags;
+
+ mCallStack.PushCall("Buffer::~Buffer", namedParams.str(), namedParams);
+ if(!mCpuOnly && mId)
+ {
+ mGl.DeleteBuffers(1, &mId);
+ }
+}
+
+void TestGraphicsBuffer::DiscardResource()
+{
+ mController.DiscardBuffer(this);
}
void TestGraphicsBuffer::Bind()
{
mCallStack.PushCall("Buffer::Bind", "");
- if(!mId)
+ if(!mCpuOnly && mId > 0)
{
- mGl.GenBuffers(1, &mId);
+ mGl.BindBuffer(GetTarget(), mId);
}
- mGl.BindBuffer(GetTarget(), mId);
}
void TestGraphicsBuffer::Unbind()
{
mCallStack.PushCall("Buffer::Unbind", "");
- if(mId)
+ if(!mCpuOnly)
{
- mGl.BindBuffer(GetTarget(), 0);
+ if(mId)
+ {
+ mGl.BindBuffer(GetTarget(), 0);
+ }
}
}
namedParams["offset"] << offset;
namedParams["size"] << size;
mCallStack.PushCall("Buffer::Upload", o.str(), namedParams);
- if(size <= memory.size() && mCreated)
- {
- // Use subData to avoid re-allocation
- mGl.BufferSubData(GetTarget(), static_cast<GLintptr>(static_cast<unsigned long>(offset)), static_cast<GLsizeiptr>(static_cast<unsigned long>(size)), &memory[offset]);
- }
- else
+
+ if(!mCpuOnly)
{
- mGl.BufferData(GetTarget(), static_cast<GLsizeiptr>(static_cast<unsigned long>(size)), &memory[0], GL_STATIC_DRAW); //@todo Query - do we need other usages?
- mCreated = true;
+ if(size <= memory.size() && mCreated)
+ {
+ // Use subData to avoid re-allocation
+ mGl.BufferSubData(GetTarget(), static_cast<GLintptr>(static_cast<unsigned long>(offset)), static_cast<GLsizeiptr>(static_cast<unsigned long>(size)), &memory[offset]);
+ }
+ else
+ {
+ mGl.BufferData(GetTarget(), static_cast<GLsizeiptr>(static_cast<unsigned long>(size)), &memory[0], GL_STATIC_DRAW); //@todo Query - do we need other usages?
+ mCreated = true;
+ }
}
}
#define DALI_TEST_GRAPHICS_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.
* limitations under the License.
*/
+#include <dali/graphics-api/graphics-buffer-create-info.h>
#include <dali/graphics-api/graphics-buffer.h>
#include <dali/graphics-api/graphics-types.h>
namespace Dali
{
class TestGraphicsProgram;
+class TestGraphicsController;
class UniformBufferBindingDescriptor;
+
class TestGraphicsBuffer : public Graphics::Buffer
{
public:
- TestGraphicsBuffer(TraceCallStack& callStack, TestGlAbstraction& glAbstraction, uint32_t size, Graphics::BufferUsageFlags usage);
+ TestGraphicsBuffer(const Graphics::BufferCreateInfo& createInfo, TestGraphicsController& controller, TestGlAbstraction& glAbstraction, TraceCallStack& callStack);
+ ~TestGraphicsBuffer();
+ void DiscardResource();
+
void Bind();
void Unbind();
void Upload(uint32_t offset, uint32_t size);
bool IsCPUAllocated() const
{
- return true;
+ return mCpuOnly;
}
void BindAsUniformBuffer(const TestGraphicsProgram* program, const Dali::UniformBufferBindingDescriptor& uboBinding) const;
- TraceCallStack& mCallStack;
- TestGlAbstraction& mGl;
- std::vector<uint8_t> memory;
+ TraceCallStack& mCallStack;
+ TestGraphicsController& mController;
+ TestGlAbstraction& mGl;
+ std::vector<uint8_t> memory;
+
+ Graphics::BufferCreateInfo mCreateInfo;
Graphics::BufferUsageFlags mUsage;
GLuint mId{0};
bool mCreated{false};
+ bool mCpuOnly{false};
};
} // namespace Dali
#include "test-graphics-controller.h"
+#include <dali/graphics-api/graphics-types.h>
#include "test-graphics-buffer.h"
#include "test-graphics-command-buffer.h"
#include "test-graphics-framebuffer.h"
#include "test-graphics-texture.h"
#include <dali/integration-api/gl-defines.h>
+#include <any>
#include <cstdio>
#include <iostream>
+#include <memory>
#include <sstream>
-#include <any>
-
namespace Dali
{
+namespace
+{
+template<class T>
+struct TestGraphicsDeleter
+{
+ TestGraphicsDeleter() = default;
+ void operator()(T* object)
+ {
+ // Discard resource
+ object->DiscardResource();
+ }
+};
+
+} //namespace
+
std::ostream& operator<<(std::ostream& o, const Graphics::BufferCreateInfo& bufferCreateInfo)
{
return o << "usage:" << std::hex << bufferCreateInfo.usage << ", size:" << std::dec << bufferCreateInfo.size;
Graphics::UniquePtr<Graphics::Buffer> TestGraphicsController::CreateBuffer(const Graphics::BufferCreateInfo& createInfo, Graphics::UniquePtr<Graphics::Buffer>&& oldBuffer)
{
- std::ostringstream oss;
- oss << "bufferCreateInfo:" << createInfo;
- mCallStack.PushCall("CreateBuffer", oss.str());
- return Graphics::MakeUnique<TestGraphicsBuffer>(mCallStack, mGl, createInfo.size, createInfo.usage);
+ TraceCallStack::NamedParams namedParams;
+ namedParams["usage"] << "0x" << std::hex << createInfo.usage;
+ namedParams["propertiesFlags"] << createInfo.propertiesFlags;
+ namedParams["size"] << createInfo.size;
+ mCallStack.PushCall("CreateBuffer", namedParams.str(), namedParams);
+
+ auto ptr = Graphics::MakeUnique<TestGraphicsBuffer, TestGraphicsDeleter<TestGraphicsBuffer>>(createInfo, *this, mGl, mCallStack);
+ mAllocatedBuffers.push_back(ptr.get());
+ return ptr;
+}
+
+void TestGraphicsController::DiscardBuffer(TestGraphicsBuffer* buffer)
+{
+ auto iter = std::find(mAllocatedBuffers.begin(), mAllocatedBuffers.end(), buffer);
+ if(iter != mAllocatedBuffers.end())
+ {
+ mAllocatedBuffers.erase(iter);
+ }
+ delete buffer;
}
Graphics::UniquePtr<Graphics::CommandBuffer> TestGraphicsController::CreateCommandBuffer(const Graphics::CommandBufferCreateInfo& commandBufferCreateInfo, Graphics::UniquePtr<Graphics::CommandBuffer>&& oldCommandBuffer)
}
mProgramCache.emplace_back();
- mProgramCache.back().programImpl = new TestGraphicsProgramImpl(mGl, programCreateInfo, mVertexFormats, mCustomUniforms);
+ mProgramCache.back().programImpl = new TestGraphicsProgramImpl(mGl, programCreateInfo, mVertexFormats, mCustomUniforms, mCustomUniformBlocks);
+
for(auto& shader : *(programCreateInfo.shaderState))
{
auto graphicsShader = Uncast<TestGraphicsShader>(shader.shader);
Graphics::MemoryRequirements TestGraphicsController::GetBufferMemoryRequirements(Graphics::Buffer& buffer) const
{
+ static GLint uniformAlign{0};
+
+ Graphics::MemoryRequirements reqs{};
mCallStack.PushCall("GetBufferMemoryRequirements", "");
- return Graphics::MemoryRequirements{};
+
+ auto gfxBuffer = Uncast<TestGraphicsBuffer>(&buffer);
+ if(gfxBuffer->mCreateInfo.usage & (0 | Graphics::BufferUsage::UNIFORM_BUFFER))
+ {
+ if(!uniformAlign)
+ {
+ // Throw off the shackles of constness
+ auto& gl = *const_cast<TestGlAbstraction*>(&mGl);
+ gl.GetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniformAlign);
+ }
+ reqs.size = gfxBuffer->mCreateInfo.size;
+ reqs.alignment = uint32_t(uniformAlign);
+ }
+ return reqs;
}
Graphics::TextureProperties TestGraphicsController::GetTextureProperties(const Graphics::Texture& texture)
#define TEST_GRAPHICS_CONTROLLER_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.
*/
Graphics::UniquePtr<Graphics::Buffer> CreateBuffer(const Graphics::BufferCreateInfo& bufferCreateInfo, Graphics::UniquePtr<Graphics::Buffer>&& oldBuffer) override;
+ void DiscardBuffer(TestGraphicsBuffer* buffer);
+
/**
* @brief Creates new CommandBuffer object
*
mCustomUniforms = customUniforms;
}
+ void AddCustomUniformBlock(const TestGraphicsReflection::TestUniformBlockInfo& blockInfo)
+ {
+ mCustomUniformBlocks.push_back(blockInfo);
+ }
+
void ClearSubmitStack()
{
mSubmitStack.clear();
};
std::vector<ProgramCache> mProgramCache;
+ std::vector<TestGraphicsBuffer*> mAllocatedBuffers;
+
struct PipelineCache
{
};
- std::vector<UniformData> mCustomUniforms;
+ std::vector<UniformData> mCustomUniforms;
+ std::vector<TestGraphicsReflection::TestUniformBlockInfo> mCustomUniformBlocks;
};
} // namespace Dali
/*
- * 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.
namespace Dali
{
-TestGraphicsProgramImpl::TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms)
+TestGraphicsProgramImpl::TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks)
: mGl(gl),
mId(gl.CreateProgram()),
mCreateInfo(createInfo),
- mReflection(gl, mId, vertexFormats, createInfo, customUniforms)
+ mReflection(gl, mId, vertexFormats, createInfo, customUniforms, customUniformBlocks)
{
// Ensure active sampler uniforms are set
mGl.SetCustomUniforms(customUniforms);
#define DALI_TEST_GRAPHICS_PROGRAM_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.
class TestGraphicsProgramImpl
{
public:
- TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms);
+ TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks);
// For API
const TestGraphicsReflection& GetReflection() const
/*
- * 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.
#include <dali/public-api/object/property-map.h>
#include <string>
#include <vector>
+
+extern "C"
+{
+ void tet_infoline(const char* str);
+ void tet_printf(const char* format, ...);
+}
+
namespace Dali
{
namespace
} // namespace
-TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t programId, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms)
+TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t programId, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms, std::vector<TestGraphicsReflection::TestUniformBlockInfo>& customUniformBlocks)
: mGl(gl),
mCustomUniforms(customUniforms)
{
mDefaultUniformBlock.size = offset;
mUniformBlocks.push_back(mDefaultUniformBlock);
+ for(auto& element : customUniformBlocks)
+ {
+ mUniformBlocks.push_back(element);
+ }
}
uint32_t TestGraphicsReflection::GetVertexAttributeLocation(const std::string& name) const
Dali::Graphics::VertexInputAttributeFormat TestGraphicsReflection::GetVertexAttributeFormat(uint32_t location) const
{
+ tet_infoline("Warning, TestGraphicsReflection::GetVertexAttributeFormat is unimplemented\n");
return Dali::Graphics::VertexInputAttributeFormat{};
}
std::string TestGraphicsReflection::GetVertexAttributeName(uint32_t location) const
{
+ tet_infoline("Warning, TestGraphicsReflection::GetVertexAttributeName is unimplemented\n");
return 0u;
}
uint32_t TestGraphicsReflection::GetUniformBlockBinding(uint32_t index) const
{
- return 0u;
+ if(index >= mUniformBlocks.size())
+ {
+ return 0;
+ }
+ return mUniformBlocks[index].binding;
}
uint32_t TestGraphicsReflection::GetUniformBlockSize(uint32_t index) const
std::vector<uint32_t> TestGraphicsReflection::GetUniformBlockLocations() const
{
+ tet_infoline("Warning, TestGraphicsReflection::GetUniformBlockLocations is unimplemented\n");
return std::vector<uint32_t>{};
}
std::string TestGraphicsReflection::GetUniformBlockName(uint32_t blockIndex) const
{
+ tet_infoline("Warning, TestGraphicsReflection::GetUniformBlockName is unimplemented\n");
return std::string{};
}
bool TestGraphicsReflection::GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const
{
+ tet_infoline("Warning, TestGraphicsReflection::GetNamedUniform is unimplemented\n");
return true;
}
const std::vector<Dali::Graphics::UniformInfo>& TestGraphicsReflection::GetSamplers() const
{
+ tet_infoline("Warning, TestGraphicsReflection::GetSamplers is unimplemented\n");
static std::vector<Dali::Graphics::UniformInfo> samplers{};
return samplers;
}
#define DALI_TEST_GRAPHICS_REFLECTION_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.
class TestGraphicsReflection : public Graphics::Reflection
{
public:
- TestGraphicsReflection(TestGlAbstraction& gl, uint32_t program_id, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms);
+ class TestUniformBlockInfo;
+
+ TestGraphicsReflection(TestGlAbstraction& gl, uint32_t program_id, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector<UniformData>& customUniforms, std::vector<TestUniformBlockInfo>& customUniformBlocks);
uint32_t GetVertexAttributeLocation(const std::string& name) const override;
Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeFormat(uint32_t location) const override;
public:
TestGraphicsTexture(TestGlAbstraction& glAbstraction, const Graphics::TextureCreateInfo& createInfo);
- ~TestGraphicsTexture();
+ ~TestGraphicsTexture() override;
/**
* Initialize the texture: allocate gl mem, apply default samplers
/*
- * 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.
for(auto iter = params.mParams.begin(); iter != params.mParams.end(); ++iter)
{
- auto paramIter = mCallStack[i].namedParams.find(iter->parameterName);
- std::string value = paramIter->value.str();
- std::string iValue = iter->value.str();
-
- if(paramIter == mCallStack[i].namedParams.end() || value.compare(iValue))
+ auto paramIter = mCallStack[i].namedParams.find(iter->parameterName);
+ if(paramIter == mCallStack[i].namedParams.end())
+ {
+ match = false;
+ break;
+ }
+ std::string value = paramIter->value.str();
+ std::string iValue = iter->value.str();
+ if(value.compare(iValue))
{
match = false;
break;
/*
- * 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.
{
const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
application.GetGlAbstraction().GetBufferDataCalls();
-
- DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
+ DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
DALI_TEST_EQUALS(bufferDataCalls[0], 4 * sizeof(TexturedQuadVertex), TEST_LOCATION);
}
const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
application.GetGlAbstraction().GetBufferDataCalls();
- DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
+ DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
DALI_TEST_EQUALS(bufferDataCalls[0], 4 * sizeof(TexturedQuadVertex), TEST_LOCATION);
}
const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
application.GetGlAbstraction().GetBufferDataCalls();
- DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
+ DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
DALI_TEST_EQUALS(bufferDataCalls[0], 4 * sizeof(TexturedQuadVertex), TEST_LOCATION);
}
* FinishedSignal 1+ve
*/
-namespace // unnamed namespace
+namespace // unnamed namespace
{
const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS)
Geometry geometry = Geometry::New();
Property::Map map[2];
- map[0]["vertex"] = SHADER_COLOR_TEST_SHADER_VERT1.data();
- map[0]["fragment"] = SHADER_COLOR_TEST_SHADER_FRAG.data();
+ map[0]["vertex"] = SHADER_COLOR_TEST_SHADER_VERT1.data();
+ map[0]["fragment"] = SHADER_COLOR_TEST_SHADER_FRAG.data();
map[0]["renderPassTag"] = 0;
- map[1]["vertex"] = SHADER_COLOR_TEST_SHADER_VERT2.data();
- map[1]["fragment"] = SHADER_COLOR_TEST_SHADER_FRAG.data();
+ map[1]["vertex"] = SHADER_COLOR_TEST_SHADER_VERT2.data();
+ map[1]["fragment"] = SHADER_COLOR_TEST_SHADER_FRAG.data();
map[1]["renderPassTag"] = 1;
Property::Array array;
stage.Add(blue);
auto& gfx = application.GetGraphicsController();
+ gfx.mCallStack.EnableLogging(true);
RenderTaskList renderTaskList = stage.GetRenderTaskList();
DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPassTag(), TEST_LOCATION);
DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPassTag(), TEST_LOCATION);
END_TEST;
-}
\ No newline at end of file
+}
#include <dali/devel-api/common/capabilities.h>
#include <dali/devel-api/common/stage.h>
#include <dali/devel-api/rendering/renderer-devel.h>
+#include <dali/integration-api/debug.h>
#include <dali/integration-api/render-task-list-integ.h>
#include <dali/public-api/dali-core.h>
#include <cstdio>
#include <dali-test-suite-utils.h>
#include <mesh-builder.h>
#include <test-trace-call-stack.h>
+#include "test-actor-utils.h"
#include "test-graphics-command-buffer.h"
using namespace Dali;
DALI_TEST_EQUALS(drawTrace.CountMethod("DrawArrays"), 10, TEST_LOCATION);
END_TEST;
}
+
+TestGraphicsBuffer* FindUniformBuffer(int bufferIndex, TestGraphicsController& graphics)
+{
+ int counter = 0;
+ for(auto bufferPtr : graphics.mAllocatedBuffers)
+ {
+ if(((bufferPtr->mCreateInfo.usage & (0 | Graphics::BufferUsage::UNIFORM_BUFFER)) > 0) &&
+ !(bufferPtr->mCpuOnly))
+ {
+ if(counter == bufferIndex)
+ {
+ return bufferPtr;
+ }
+ ++counter;
+ }
+ }
+ return nullptr;
+}
+
+void CreateRendererProperties(Renderer renderer, const Matrix& m, const Matrix& n)
+{
+ for(int i = 0; i < 300; ++i)
+ {
+ std::ostringstream property;
+ property << "uBone[" << i << "]";
+ if(i < 299)
+ renderer.RegisterProperty(property.str(), m);
+ else
+ renderer.RegisterProperty(property.str(), n);
+ }
+ renderer.RegisterProperty("uNumberOfBlendShapes", 55.0f);
+ float weight = 0.5f;
+ for(int i = 0; i < 128; ++i)
+ {
+ std::ostringstream property;
+ property << "uBlendShapeWeight[" << i << "]";
+ renderer.RegisterProperty(property.str(), weight);
+ }
+ float w1 = 0.01f;
+ float w2 = 0.5f;
+ float w3 = 0.79f;
+ renderer["uBlendShapeWeight[0]"] = w1;
+ renderer["uBlendShapeWeight[55]"] = w2;
+ renderer["uBlendShapeWeight[127]"] = w3;
+}
+
+int UtcDaliRendererUniformBlocks01(void)
+{
+ setenv("LOG_UNIFORM_BUFFER", "5f", 1); // Turns on buffer logging
+ TestApplication application;
+
+ tet_infoline("Test that uniforms in blocks are written to a gpu buffer");
+ auto& graphics = application.GetGraphicsController();
+ auto& gl = application.GetGlAbstraction();
+ gl.mBufferTrace.EnableLogging(true);
+
+ gl.SetUniformBufferOffsetAlignment(1024); // Arbitrarily big to easily see it work in debug
+
+ const int MAX_BONE_COUNT{300};
+ const int skinningBlockSize = MAX_BONE_COUNT * sizeof(Matrix);
+
+ graphics.AddCustomUniformBlock(TestGraphicsReflection::TestUniformBlockInfo{"Skinning Block", 0, 0, skinningBlockSize, {{"uBone", Graphics::UniformClass::UNIFORM, 0, 0, {0}, {1}, MAX_BONE_COUNT, Property::Type::MATRIX}}});
+
+ const int MAX_MORPH_COUNT{128};
+ const int morphBlockSize = MAX_MORPH_COUNT * sizeof(float) + sizeof(float);
+ graphics.AddCustomUniformBlock(
+ TestGraphicsReflection::TestUniformBlockInfo{"MorphBlock", 0, 1, morphBlockSize, {{"uNumberOfBlendShapes", Graphics::UniformClass::UNIFORM, 0, 2, {0}, {2}, 0, Property::Type::FLOAT}, {"uBlendShapeWeight", Graphics::UniformClass::UNIFORM, 0, 2, {4}, {3}, MAX_MORPH_COUNT, Property::Type::FLOAT}}});
+
+ Actor actor = CreateActor(application.GetScene().GetRootLayer(), 0, TEST_LOCATION);
+ Shader shader = CreateShader(); // Don't care about src content
+ Geometry geometry = CreateQuadGeometry();
+ Renderer renderer = CreateRenderer(actor, geometry, shader, 0);
+ Matrix m, n;
+ m.SetIdentity();
+ n.SetIdentity();
+ n.SetTransformComponents(Vector3(2.f, 2.f, 2.f), Quaternion(Radian(0.3f), Vector3::YAXIS), Vector3(200.0f, 1.0f, 20.0f));
+
+ CreateRendererProperties(renderer, m, n);
+
+ TraceCallStack& graphicsTrace = graphics.mCallStack;
+ TraceCallStack& cmdTrace = graphics.mCommandBufferCallStack;
+ graphicsTrace.EnableLogging(true);
+ cmdTrace.EnableLogging(true);
+
+ application.SendNotification();
+ application.Render();
+
+ // We expect 1 vertex buffer, 1 index buffer and 1 uniform buffer (representing 2 blocks)
+ DALI_TEST_EQUALS(cmdTrace.CountMethod("BindUniformBuffers"), 1, TEST_LOCATION);
+
+ tet_infoline("Test that uBone[299] is written correctly");
+
+ bool found = false;
+ for(auto bufferPtr : graphics.mAllocatedBuffers)
+ {
+ if(((bufferPtr->mCreateInfo.usage & (0 | Graphics::BufferUsage::UNIFORM_BUFFER)) > 0) &&
+ !(bufferPtr->mCpuOnly))
+ {
+ // We have a GPU uniform buffer. Probably the right one.
+ // The custom uniform block above should point us to the right spot...
+ DALI_TEST_CHECK(bufferPtr->memory.size() >= skinningBlockSize);
+ found = true;
+ Matrix* mPtr = reinterpret_cast<Dali::Matrix*>(&bufferPtr->memory[0] + sizeof(Dali::Matrix) * 299);
+ DALI_TEST_EQUALS(*mPtr, n, 0.0001, TEST_LOCATION);
+ break;
+ }
+ }
+ DALI_TEST_CHECK(found);
+
+ END_TEST;
+}
+
+int UtcDaliRendererUniformBlocks02(void)
+{
+ setenv("LOG_UNIFORM_BUFFER", "5f", 1); // Turns on buffer logging
+ TestApplication application;
+
+ tet_infoline("Test that repeated update/render cycles write into alternative buffers");
+ auto& graphics = application.GetGraphicsController();
+ auto& gl = application.GetGlAbstraction();
+ gl.mBufferTrace.EnableLogging(true);
+
+ const uint32_t UNIFORM_BLOCK_ALIGNMENT(512);
+ gl.SetUniformBufferOffsetAlignment(UNIFORM_BLOCK_ALIGNMENT);
+
+ const int MAX_BONE_COUNT{300};
+ const int skinningBlockSize = MAX_BONE_COUNT * sizeof(Matrix);
+
+ graphics.AddCustomUniformBlock(TestGraphicsReflection::TestUniformBlockInfo{"Skinning Block", 0, 0, skinningBlockSize, {{"uBone", Graphics::UniformClass::UNIFORM, 0, 0, {0}, {1}, MAX_BONE_COUNT, Property::Type::MATRIX}}});
+
+ const int MAX_MORPH_COUNT{128};
+ const int morphBlockSize = MAX_MORPH_COUNT * sizeof(float) + sizeof(float);
+ graphics.AddCustomUniformBlock(
+ TestGraphicsReflection::TestUniformBlockInfo{"MorphBlock", 0, 1, morphBlockSize, {{"uNumberOfBlendShapes", Graphics::UniformClass::UNIFORM, 0, 2, {0}, {2}, 0, Property::Type::FLOAT}, {"uBlendShapeWeight", Graphics::UniformClass::UNIFORM, 0, 2, {4}, {3}, MAX_MORPH_COUNT, Property::Type::FLOAT}}});
+
+ Actor actor = CreateActor(application.GetScene().GetRootLayer(), 0, TEST_LOCATION);
+ Shader shader = CreateShader(); // Don't care about src content
+ Geometry geometry = CreateQuadGeometry();
+ Renderer renderer = CreateRenderer(actor, geometry, shader, 0);
+ Matrix m, n;
+ m.SetIdentity();
+ n.SetIdentity();
+ n.SetTransformComponents(Vector3(2.f, 2.f, 2.f), Quaternion(Radian(0.3f), Vector3::YAXIS), Vector3(200.0f, 1.0f, 20.0f));
+
+ CreateRendererProperties(renderer, m, n);
+ float w1 = 0.01f;
+ float w2 = 0.5f;
+ float w3 = 0.79f;
+ renderer["uBlendShapeWeight[0]"] = w1;
+ renderer["uBlendShapeWeight[55]"] = w2;
+ renderer["uBlendShapeWeight[127]"] = w3;
+
+ TraceCallStack& graphicsTrace = graphics.mCallStack;
+ TraceCallStack& cmdTrace = graphics.mCommandBufferCallStack;
+ graphicsTrace.EnableLogging(true);
+ cmdTrace.EnableLogging(true);
+
+ application.SendNotification();
+ application.Render();
+
+ // We expect 1 vertex buffer, 1 index buffer and 1 uniform buffer (representing 2 blocks)
+ DALI_TEST_EQUALS(cmdTrace.CountMethod("BindUniformBuffers"), 1, TEST_LOCATION);
+
+ const uint32_t MORPH_BLOCK_OFFSET = (skinningBlockSize % UNIFORM_BLOCK_ALIGNMENT == 0) ? skinningBlockSize : ((skinningBlockSize / UNIFORM_BLOCK_ALIGNMENT) + 1) * UNIFORM_BLOCK_ALIGNMENT;
+
+ for(int i = 0; i < 50; ++i)
+ {
+ tet_infoline("\nTest that uBone[299] is written correctly");
+ TestGraphicsBuffer* bufferPtr = FindUniformBuffer(i % 2, graphics);
+ DALI_TEST_CHECK(graphics.mAllocatedBuffers.size() == (i == 0 ? 4 : 5));
+ DALI_TEST_CHECK(bufferPtr != nullptr);
+ Matrix* mPtr = reinterpret_cast<Dali::Matrix*>(&bufferPtr->memory[0] + sizeof(Dali::Matrix) * 299);
+ DALI_TEST_EQUALS(*mPtr, n, 0.0001, TEST_LOCATION);
+
+ float* wPtr1 = reinterpret_cast<float*>(&bufferPtr->memory[MORPH_BLOCK_OFFSET] + sizeof(float) * 1);
+ float* wPtr2 = reinterpret_cast<float*>(&bufferPtr->memory[MORPH_BLOCK_OFFSET] + sizeof(float) * 56);
+ float* wPtr3 = reinterpret_cast<float*>(&bufferPtr->memory[MORPH_BLOCK_OFFSET] + sizeof(float) * 128);
+
+ tet_printf("Test that uBlendShapeWeight[0] is written correctly as %4.2f\n", w1);
+ tet_printf("Test that uBlendShapeWeight[55] is written correctly as %4.2f\n", w2);
+ tet_printf("Test that uBlendShapeWeight[127] is written correctly as %4.2f\n", w3);
+
+ DALI_TEST_EQUALS(*wPtr1, w1, 0.0001f, TEST_LOCATION);
+ DALI_TEST_EQUALS(*wPtr2, w2, 0.0001f, TEST_LOCATION);
+ DALI_TEST_EQUALS(*wPtr3, w3, 0.0001f, TEST_LOCATION);
+
+ n.SetTransformComponents(Vector3(2.f, 2.f, 2.f), Quaternion(Radian(i * 0.3f), Vector3::YAXIS), Vector3(200.0f + i * 10.0f, -i, 20.0f));
+ renderer["uBone[299]"] = n;
+
+ w1 += 0.005f;
+ w2 += 0.005f;
+ w3 -= 0.01f;
+ renderer["uBlendShapeWeight[0]"] = w1;
+ renderer["uBlendShapeWeight[55]"] = w2;
+ renderer["uBlendShapeWeight[127]"] = w3;
+
+ application.SendNotification();
+ application.Render();
+ }
+
+ END_TEST;
+}
+
+int AlignSize(int size, int align)
+{
+ return (size % align == 0) ? size : ((size / align) + 1) * align;
+}
+
+int UtcDaliRendererUniformBlocks03(void)
+{
+ setenv("LOG_UNIFORM_BUFFER", "5f", 1); // Turns on buffer logging
+ TestApplication application;
+
+ tet_infoline("Test that adding actors grows the uniform buffer");
+ auto& graphics = application.GetGraphicsController();
+ auto& gl = application.GetGlAbstraction();
+ gl.mBufferTrace.EnableLogging(true);
+
+ const uint32_t UNIFORM_BLOCK_ALIGNMENT(512);
+ gl.SetUniformBufferOffsetAlignment(UNIFORM_BLOCK_ALIGNMENT);
+
+ const int MAX_BONE_COUNT{300};
+ const int skinningBlockSize = MAX_BONE_COUNT * sizeof(Matrix);
+
+ graphics.AddCustomUniformBlock(TestGraphicsReflection::TestUniformBlockInfo{"Skinning Block", 0, 0, skinningBlockSize, {{"uBone", Graphics::UniformClass::UNIFORM, 0, 0, {0}, {1}, MAX_BONE_COUNT, Property::Type::MATRIX}}});
+
+ const int MAX_MORPH_COUNT{128};
+ const int morphBlockSize = MAX_MORPH_COUNT * sizeof(float) + sizeof(float);
+ graphics.AddCustomUniformBlock(
+ TestGraphicsReflection::TestUniformBlockInfo{"MorphBlock", 0, 1, morphBlockSize, {{"uNumberOfBlendShapes", Graphics::UniformClass::UNIFORM, 0, 2, {0}, {2}, 0, Property::Type::FLOAT}, {"uBlendShapeWeight", Graphics::UniformClass::UNIFORM, 0, 2, {4}, {3}, MAX_MORPH_COUNT, Property::Type::FLOAT}}});
+
+ Actor actor = CreateActor(application.GetScene().GetRootLayer(), 0, TEST_LOCATION);
+ Shader shader = CreateShader(); // Don't care about src content
+ Geometry geometry = CreateQuadGeometry();
+ Renderer renderer = CreateRenderer(actor, geometry, shader, 0);
+ Matrix m, n;
+ m.SetIdentity();
+ n.SetIdentity();
+ n.SetTransformComponents(Vector3(2.f, 2.f, 2.f), Quaternion(Radian(0.3f), Vector3::YAXIS), Vector3(200.0f, 1.0f, 20.0f));
+
+ CreateRendererProperties(renderer, m, n);
+
+ TraceCallStack& graphicsTrace = graphics.mCallStack;
+ TraceCallStack& cmdTrace = graphics.mCommandBufferCallStack;
+ graphicsTrace.EnableLogging(true);
+ cmdTrace.EnableLogging(true);
+
+ application.SendNotification();
+ application.Render();
+
+ // We expect 1 vertex buffer, 1 index buffer and 1 uniform buffer (representing 2 blocks)
+ DALI_TEST_EQUALS(cmdTrace.CountMethod("BindUniformBuffers"), 1, TEST_LOCATION);
+
+ unsigned int overallSize = 0;
+
+ for(int i = 0; i < 10; ++i)
+ {
+ overallSize += AlignSize(skinningBlockSize, UNIFORM_BLOCK_ALIGNMENT) + AlignSize(morphBlockSize, UNIFORM_BLOCK_ALIGNMENT);
+
+ DALI_TEST_CHECK(graphics.mAllocatedBuffers.size() == (i == 0 ? 4 : 5));
+
+ TestGraphicsBuffer* bufferPtr = graphics.mAllocatedBuffers.back();
+ tet_printf("\nTest that latest buffer is big enough(%d)>%d\n", bufferPtr->memory.size(), overallSize);
+
+ DALI_TEST_CHECK(bufferPtr->memory.size() >= overallSize);
+
+ Actor actor = CreateActor(application.GetScene().GetRootLayer(), 0, TEST_LOCATION);
+ actor.AddRenderer(renderer);
+ application.GetScene().Add(actor);
+
+ application.SendNotification();
+ application.Render();
+ }
+
+ END_TEST;
+}
+
+int UtcDaliRendererUniformBlocksUnregisterScene01(void)
+{
+ TestApplication application;
+
+ tet_infoline("Test that uniform buffers are unregistered after a scene is destroyed\n");
+
+ auto& graphics = application.GetGraphicsController();
+ auto& gl = application.GetGlAbstraction();
+ graphics.mCallStack.EnableLogging(true);
+ graphics.mCommandBufferCallStack.EnableLogging(true);
+ gl.mBufferTrace.EnableLogging(true);
+ gl.mBufferTrace.Enable(true);
+
+ Actor dummyActor = CreateRenderableActor(CreateTexture(TextureType::TEXTURE_2D, Pixel::RGB888, 45, 45));
+ application.GetScene().Add(dummyActor);
+ application.SendNotification();
+ application.Render();
+
+ Dali::Integration::Scene scene = Dali::Integration::Scene::New(Size(480.0f, 800.0f));
+ DALI_TEST_CHECK(scene);
+ application.AddScene(scene);
+
+ Actor actor = CreateActor(scene.GetRootLayer(), 0, TEST_LOCATION);
+ Shader shader = CreateShader(); // Don't really care...
+ Geometry geometry = CreateQuadGeometry();
+ Renderer renderer = CreateRenderer(actor, geometry, shader, 0);
+
+ const int MAX_BONE_COUNT{300};
+ const int skinningBlockSize = MAX_BONE_COUNT * sizeof(Matrix);
+
+ graphics.AddCustomUniformBlock(TestGraphicsReflection::TestUniformBlockInfo{"Skinning Block", 0, 0, skinningBlockSize, {{"uBone", Graphics::UniformClass::UNIFORM, 0, 0, {0}, {1}, MAX_BONE_COUNT, Property::Type::MATRIX}}});
+ Matrix m;
+ m.SetIdentity();
+ for(int i = 0; i < MAX_BONE_COUNT; ++i)
+ {
+ std::ostringstream property;
+ property << "uBone[" << i << "]";
+ renderer.RegisterProperty(property.str(), m);
+ }
+ tet_infoline("--Expect new scene's buffers to be created here");
+ application.SendNotification();
+ application.Render();
+
+ scene.RemoveSceneObject(); // Scene's scene graph lifecycle is NOT managed by scene handle
+ scene.Discard();
+ scene.Reset();
+
+ gl.mBufferTrace.Reset();
+
+ tet_infoline("--Expect UnregisterScene to happen during this render cycle");
+ dummyActor[Actor::Property::SIZE] = Vector3(100, 100, 0);
+ application.SendNotification();
+ application.Render();
+
+ TraceCallStack::NamedParams namedParams;
+ namedParams["id"] << 6;
+ DALI_TEST_CHECK(gl.mBufferTrace.FindMethodAndParams("DeleteBuffers", namedParams));
+
+ END_TEST;
+}
DALI_TEST_CHECK(drawTrace.FindMethod("DrawArrays"));
- DALI_TEST_EQUALS(bufferDataCalls.size(), 3u, TEST_LOCATION);
+ DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
DALI_TEST_EQUALS(bufferDataCalls[0], sizeof(texturedQuadVertexData), TEST_LOCATION);
}
application.SendNotification();
application.Render();
- {
- const TestGlAbstraction::BufferSubDataCalls& bufferSubDataCalls =
- application.GetGlAbstraction().GetBufferSubDataCalls();
+ auto& gl = application.GetGlAbstraction();
+ const TestGlAbstraction::BufferSubDataCalls& bufferSubDataCalls = gl.GetBufferSubDataCalls();
+ const TestGlAbstraction::BufferDataCalls& bufferDataCalls = gl.GetBufferDataCalls();
- const TestGlAbstraction::BufferDataCalls& bufferDataCalls =
- application.GetGlAbstraction().GetBufferDataCalls();
+ DALI_TEST_EQUALS(bufferSubDataCalls.size(), 0u, TEST_LOCATION);
+ DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
+ DALI_TEST_EQUALS(bufferDataCalls[0], sizeof(texturedQuadVertexData), TEST_LOCATION);
- // Should be 1 (Flush standalone uniform buffer per each RenderScene)
- DALI_TEST_EQUALS(bufferSubDataCalls.size(), 1u, TEST_LOCATION);
- DALI_TEST_EQUALS(bufferDataCalls.size(), 2u, TEST_LOCATION);
-
- DALI_TEST_EQUALS(bufferDataCalls[0], sizeof(texturedQuadVertexData), TEST_LOCATION);
- }
+ gl.ResetBufferDataCalls();
+ gl.ResetBufferSubDataCalls();
// Re-upload the data on the vertexBuffer
vertexBuffer.SetData(texturedQuadVertexData, 4);
application.SendNotification();
application.Render(0);
+ DALI_TEST_EQUALS(bufferSubDataCalls.size(), 0u, TEST_LOCATION);
+ DALI_TEST_EQUALS(bufferDataCalls.size(), 1u, TEST_LOCATION);
+ DALI_TEST_EQUALS(bufferDataCalls[0], sizeof(texturedQuadVertexData), TEST_LOCATION);
+
END_TEST;
}
*/
struct MemoryRequirements
{
- size_t size;
- size_t alignment;
+ size_t size{0u};
+ size_t alignment{1u};
};
using TextureUpdateFlags = uint32_t;
${internal_src_dir}/render/renderers/uniform-buffer.cpp
${internal_src_dir}/render/renderers/uniform-buffer-manager.cpp
${internal_src_dir}/render/renderers/uniform-buffer-view.cpp
- ${internal_src_dir}/render/renderers/uniform-buffer-view-pool.cpp
${internal_src_dir}/render/shaders/program.cpp
${internal_src_dir}/render/shaders/program-controller.cpp
#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/renderers/uniform-buffer-view-pool.h>
+#include <dali/internal/render/renderers/uniform-buffer.h>
#include <dali/internal/render/shaders/program-controller.h>
#include <memory>
Vector<Render::TextureKey> updatedTextures{}; ///< The updated texture list
uint32_t frameCount{0u}; ///< The current frame count
- BufferIndex renderBufferIndex{SceneGraphBuffers::INITIAL_UPDATE_BUFFER_INDEX}; ///< The index of the buffer to read from; this is opposite of the "update" buffer
+ BufferIndex renderBufferIndex{SceneGraphBuffers::INITIAL_UPDATE_BUFFER_INDEX}; ///< The index of the buffer to read from;
bool lastFrameWasRendered{false}; ///< Keeps track of the last frame being rendered due to having render instructions
bool commandBufferSubmitted{false};
{
scene->Initialize(mImpl->graphicsController, mImpl->depthBufferAvailable, mImpl->stencilBufferAvailable);
mImpl->sceneContainer.push_back(scene);
+ mImpl->uniformBufferManager->RegisterScene(scene);
}
void RenderManager::UninitializeScene(SceneGraph::Scene* scene)
{
+ mImpl->uniformBufferManager->UnregisterScene(scene);
auto iter = std::find(mImpl->sceneContainer.begin(), mImpl->sceneContainer.end(), scene);
if(iter != mImpl->sceneContainer.end())
{
void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear)
{
DALI_PRINT_RENDER_START(mImpl->renderBufferIndex);
-
- // Rollback
- mImpl->uniformBufferManager->GetUniformBufferViewPool(mImpl->renderBufferIndex)->Rollback();
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "\n\nNewFrame %d\n", mImpl->frameCount);
// Increment the frame count at the beginning of each frame
++mImpl->frameCount;
// Process messages queued during previous update
mImpl->renderQueue.ProcessMessages(mImpl->renderBufferIndex);
- uint32_t count = 0u;
+ uint32_t totalInstructionCount = 0u;
for(auto& i : mImpl->sceneContainer)
{
- count += i->GetRenderInstructions().Count(mImpl->renderBufferIndex);
+ totalInstructionCount += i->GetRenderInstructions().Count(mImpl->renderBufferIndex);
}
- const bool haveInstructions = count > 0u;
+ const bool haveInstructions = totalInstructionCount > 0u;
DALI_LOG_INFO(gLogFilter, Debug::General, "Render: haveInstructions(%s) || mImpl->lastFrameWasRendered(%s) || forceClear(%s)\n", haveInstructions ? "true" : "false", mImpl->lastFrameWasRendered ? "true" : "false", forceClear ? "true" : "false");
return;
}
- uint32_t count = sceneObject->GetRenderInstructions().Count(mImpl->renderBufferIndex);
+ uint32_t instructionCount = sceneObject->GetRenderInstructions().Count(mImpl->renderBufferIndex);
std::vector<Graphics::RenderTarget*> targetstoPresent;
clippingRect = Rect<int>();
}
- // Prepare to lock and map standalone uniform buffer.
- mImpl->uniformBufferManager->ReadyToLockUniformBuffer(mImpl->renderBufferIndex);
+ // Prefetch programs before we start rendering so reflection is
+ // ready, and we can pull exact size of UBO needed (no need to resize during drawing)
+ auto totalSizeCPU = 0u;
+ auto totalSizeGPU = 0u;
+
+ for(uint32_t i = 0; i < instructionCount; ++i)
+ {
+ RenderInstruction& instruction = sceneObject->GetRenderInstructions().At(mImpl->renderBufferIndex, i);
+
+ if((instruction.mFrameBuffer != nullptr && renderToFbo) ||
+ (instruction.mFrameBuffer == nullptr && !renderToFbo))
+ {
+ for(auto j = 0u; j < instruction.RenderListCount(); ++j)
+ {
+ const auto& renderList = instruction.GetRenderList(j);
+ for(auto k = 0u; k < renderList->Count(); ++k)
+ {
+ auto& item = renderList->GetItem(k);
+ if(item.mRenderer && item.mRenderer->NeedsProgram())
+ {
+ // Prepare and store used programs for further processing
+ auto program = item.mRenderer->PrepareProgram(instruction);
+ if(program)
+ {
+ auto memoryRequirements = program->GetUniformBlocksMemoryRequirements();
+
+ totalSizeCPU += memoryRequirements.totalCpuSizeRequired;
+ totalSizeGPU += memoryRequirements.totalGpuSizeRequired;
+ }
+ }
+ }
+ }
+ }
+ }
- for(uint32_t i = 0; i < count; ++i)
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Render scene (%s), CPU:%d GPU:%d\n", renderToFbo ? "Offscreen" : "Onscreen", totalSizeCPU, totalSizeGPU);
+
+ auto& uboManager = mImpl->uniformBufferManager;
+
+ uboManager->SetCurrentSceneRenderInfo(sceneObject, renderToFbo);
+ uboManager->Rollback(sceneObject, renderToFbo);
+
+ // Respec UBOs for this frame (orphan buffers or double buffer in the GPU)
+ if(instructionCount)
+ {
+ uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, true)->ReSpecify(totalSizeCPU);
+ uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, false)->ReSpecify(totalSizeGPU);
+ }
+
+#if defined(DEBUG_ENABLED)
+ auto uniformBuffer1 = uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, true);
+ auto uniformBuffer2 = uboManager->GetUniformBufferForScene(sceneObject, renderToFbo, false);
+ if(uniformBuffer1)
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "CPU buffer: Offset(%d), Cap(%d)\n", uniformBuffer1->GetCurrentOffset(), uniformBuffer1->GetCurrentCapacity());
+ }
+ else
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "CPU buffer: nil\n");
+ }
+ if(uniformBuffer2)
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GPU buffer: Offset(%d), Cap(%d)\n", uniformBuffer2->GetCurrentOffset(), uniformBuffer2->GetCurrentCapacity());
+ }
+ else
+ {
+ DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GPU buffer: nil\n");
+ }
+#endif
+
+ for(uint32_t i = 0; i < instructionCount; ++i)
{
RenderInstruction& instruction = sceneObject->GetRenderInstructions().At(mImpl->renderBufferIndex, i);
mainCommandBuffer->EndRenderPass(syncObject);
}
- // Unlock standalone uniform buffer.
- mImpl->uniformBufferManager->UnlockUniformBuffer(mImpl->renderBufferIndex);
+ // Flush UBOs
+ mImpl->uniformBufferManager->Flush(sceneObject, renderToFbo);
mImpl->renderAlgorithms.SubmitCommandBuffer();
mImpl->commandBufferSubmitted = true;
#include <dali/internal/render/renderers/render-texture.h>
#include <dali/internal/render/renderers/render-vertex-buffer.h>
#include <dali/internal/render/renderers/shader-cache.h>
-#include <dali/internal/render/renderers/uniform-buffer-view-pool.h>
#include <dali/internal/render/renderers/uniform-buffer-view.h>
+#include <dali/internal/render/renderers/uniform-buffer.h>
#include <dali/internal/render/shaders/program.h>
#include <dali/internal/render/shaders/render-shader.h>
#include <dali/internal/update/common/uniform-map.h>
namespace Dali::Internal
{
+Dali::Matrix* testMVP;
+uint32_t mvpBufferIndex;
+
namespace
{
// Helper to get the property value getter by type
mGeometry->Upload(*mGraphicsController);
}
+bool Renderer::NeedsProgram() const
+{
+ // Our access to shader is currently through the RenderDataProvider, which
+ // returns a reference to the shader (as at the time, we couldn't have empty
+ // renderers). We prefer to keep this as a reference. So, we don't use it
+ // here. Instead, we use the mGeometry pointer to decide if this is an empty
+ // renderer.
+ return (!mRenderCallback && mGeometry != nullptr);
+}
+
+Program* Renderer::PrepareProgram(const SceneGraph::RenderInstruction& instruction)
+{
+ // Create Program
+ const SceneGraph::Shader& shader = mRenderDataProvider->GetShader();
+
+ ShaderDataPtr shaderData = shader.GetShaderData(instruction.mRenderPassTag);
+ if(!shaderData)
+ {
+ DALI_LOG_ERROR("Failed to get shader data.\n");
+ mCurrentProgram = nullptr;
+ return nullptr;
+ }
+
+ Program* program = Program::New(*mProgramCache,
+ shaderData,
+ *mGraphicsController);
+ if(!program)
+ {
+ DALI_LOG_ERROR("Failed to create program for shader at address %p.\n", reinterpret_cast<const void*>(&shader));
+ mCurrentProgram = nullptr;
+ return nullptr;
+ }
+
+ // If program doesn't have Gfx program object assigned yet, prepare it.
+ if(!program->GetGraphicsProgramPtr())
+ {
+ const std::vector<char>& vertShader = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::VERTEX_SHADER);
+ const std::vector<char>& fragShader = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER);
+ Dali::Graphics::Shader& vertexShader = mShaderCache->GetShader(
+ vertShader,
+ Graphics::PipelineStage::VERTEX_SHADER,
+ shaderData->GetSourceMode());
+
+ Dali::Graphics::Shader& fragmentShader = mShaderCache->GetShader(
+ fragShader,
+ Graphics::PipelineStage::FRAGMENT_SHADER,
+ shaderData->GetSourceMode());
+
+ std::vector<Graphics::ShaderState> shaderStates{
+ Graphics::ShaderState()
+ .SetShader(vertexShader)
+ .SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER),
+ Graphics::ShaderState()
+ .SetShader(fragmentShader)
+ .SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER)};
+
+ auto createInfo = Graphics::ProgramCreateInfo();
+ createInfo.SetShaderState(shaderStates);
+ auto graphicsProgram = mGraphicsController->CreateProgram(createInfo, nullptr);
+ program->SetGraphicsProgram(std::move(graphicsProgram), *mUniformBufferManager); // generates reflection
+ }
+
+ // Set prefetched program to be used during rendering
+ mCurrentProgram = program;
+ return mCurrentProgram;
+}
+
bool Renderer::Render(Graphics::CommandBuffer& commandBuffer,
BufferIndex bufferIndex,
const SceneGraph::NodeDataProvider& node,
blend = (commands[0]->queue != DevelRenderer::RENDER_QUEUE_OPAQUE) && blend;
}
- // Create Program
- ShaderDataPtr shaderData = mRenderDataProvider->GetShader().GetShaderData(instruction.mRenderPassTag);
- if(!shaderData)
- {
- DALI_LOG_ERROR("Failed to get shader data.\n");
- return false;
- }
+ bool drawn = false;
- Program* program = Program::New(*mProgramCache,
- shaderData,
- *mGraphicsController);
- if(!program)
+ // We should have a shader here (as only RenderCallback has no shader, and that's been early out)
+ Program* program = PrepareProgram(instruction);
+ if(program)
{
- DALI_LOG_ERROR("Failed to get program for shader at address %p.\n", reinterpret_cast<const void*>(&mRenderDataProvider->GetShader()));
- return false;
- }
+ // Prepare the graphics pipeline. This may either re-use an existing pipeline or create a new one.
+ auto& pipeline = PrepareGraphicsPipeline(*program, instruction, node, blend);
- // If program doesn't have Gfx program object assigned yet, prepare it.
- if(!program->GetGraphicsProgramPtr())
- {
- const std::vector<char>& vertShader = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::VERTEX_SHADER);
- const std::vector<char>& fragShader = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER);
- Dali::Graphics::Shader& vertexShader = mShaderCache->GetShader(
- vertShader,
- Graphics::PipelineStage::VERTEX_SHADER,
- shaderData->GetSourceMode());
+ commandBuffer.BindPipeline(pipeline);
+ BindTextures(commandBuffer, boundTextures);
- Dali::Graphics::Shader& fragmentShader = mShaderCache->GetShader(
- fragShader,
- Graphics::PipelineStage::FRAGMENT_SHADER,
- shaderData->GetSourceMode());
-
- std::vector<Graphics::ShaderState> shaderStates{
- Graphics::ShaderState()
- .SetShader(vertexShader)
- .SetPipelineStage(Graphics::PipelineStage::VERTEX_SHADER),
- Graphics::ShaderState()
- .SetShader(fragmentShader)
- .SetPipelineStage(Graphics::PipelineStage::FRAGMENT_SHADER)};
-
- auto createInfo = Graphics::ProgramCreateInfo();
- createInfo.SetShaderState(shaderStates);
- auto graphicsProgram = mGraphicsController->CreateProgram(createInfo, nullptr);
- program->SetGraphicsProgram(std::move(graphicsProgram));
- }
-
- // Prepare the graphics pipeline. This may either re-use an existing pipeline or create a new one.
- auto& pipeline = PrepareGraphicsPipeline(*program, instruction, node, blend);
-
- commandBuffer.BindPipeline(pipeline);
-
- BindTextures(commandBuffer, boundTextures);
-
- std::size_t nodeIndex = BuildUniformIndexMap(bufferIndex, node, size, *program);
-
- WriteUniformBuffer(bufferIndex, commandBuffer, program, instruction, node, modelMatrix, modelViewMatrix, viewMatrix, projectionMatrix, size, nodeIndex);
-
- 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
-
- // Reuse latest bound vertex attributes location, or Bind buffers to attribute locations.
- if(ReuseLatestBoundVertexAttributes(mGeometry) || mGeometry->BindVertexAttributes(commandBuffer))
- {
- uint32_t instanceCount = mRenderDataProvider->GetInstanceCount();
-
- if(mDrawCommands.empty())
+ if(queueIndex == 0)
{
- drawn = mGeometry->Draw(*mGraphicsController, commandBuffer, mIndexedDrawFirstElement, mIndexedDrawElementsCount, instanceCount);
+ std::size_t nodeIndex = BuildUniformIndexMap(bufferIndex, node, size, *program);
+ WriteUniformBuffer(bufferIndex, commandBuffer, program, instruction, node, modelMatrix, modelViewMatrix, viewMatrix, projectionMatrix, size, nodeIndex);
}
- else
+ // @todo We should detect this case much earlier to prevent unnecessary work
+ // Reuse latest bound vertex attributes location, or Bind buffers to attribute locations.
+ if(ReuseLatestBoundVertexAttributes(mGeometry) || mGeometry->BindVertexAttributes(commandBuffer))
{
- for(auto& cmd : commands)
+ uint32_t instanceCount = mRenderDataProvider->GetInstanceCount();
+
+ if(mDrawCommands.empty())
{
- drawn |= mGeometry->Draw(*mGraphicsController, commandBuffer, cmd->firstIndex, cmd->elementCount, instanceCount);
+ drawn = mGeometry->Draw(*mGraphicsController, commandBuffer, mIndexedDrawFirstElement, mIndexedDrawElementsCount, instanceCount);
+ }
+ else
+ {
+ for(auto& cmd : commands)
+ {
+ drawn |= mGeometry->Draw(*mGraphicsController, commandBuffer, cmd->firstIndex, cmd->elementCount, instanceCount);
+ }
}
}
- }
- else
- {
- // BindVertexAttributes failed. Reset cached geometry.
- ReuseLatestBoundVertexAttributes(nullptr);
+ else
+ {
+ // BindVertexAttributes failed. Reset cached geometry.
+ ReuseLatestBoundVertexAttributes(nullptr);
+ }
}
return drawn;
const Vector3& size,
std::size_t nodeIndex)
{
- // Create the UBO
- uint32_t uboOffset{0u};
-
auto& reflection = mGraphicsController->GetProgramReflection(program->GetGraphicsProgram());
- uint32_t uniformBlockAllocationBytes = program->GetUniformBlocksMemoryRequirements().totalSizeRequired;
+ const auto& programRequirements = program->GetUniformBlocksMemoryRequirements();
+
+ // Allocate UBO view per each block (include standalone block)
+ auto blockCount = programRequirements.blockCount;
- // Create uniform buffer view from uniform buffer
- Graphics::UniquePtr<Render::UniformBufferView> uboView{nullptr};
- if(uniformBlockAllocationBytes)
+ std::vector<std::unique_ptr<UniformBufferView>> uboViews;
+ uboViews.resize(blockCount);
+
+ // Prepare bindings
+ auto uboCount = reflection.GetUniformBlockCount();
+ mUniformBufferBindings.resize(uboCount);
+
+ for(auto i = 0u; i < blockCount; ++i)
{
- auto uboPoolView = mUniformBufferManager->GetUniformBufferViewPool(bufferIndex);
- uboView = uboPoolView->CreateUniformBufferView(uniformBlockAllocationBytes);
+ bool standaloneUniforms = (i == 0);
+ if(programRequirements.blockSize[i])
+ {
+ auto uniformBufferView = mUniformBufferManager->CreateUniformBufferView(programRequirements.blockSize[i], standaloneUniforms);
+ mUniformBufferBindings[i].buffer = uniformBufferView->GetBuffer();
+ mUniformBufferBindings[i].offset = uniformBufferView->GetOffset();
+ mUniformBufferBindings[i].binding = standaloneUniforms ? 0 : i - 1;
+ mUniformBufferBindings[i].dataSize = reflection.GetUniformBlockSize(i);
+ uboViews[i].reset(uniformBufferView.release());
+ }
}
// 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
- if(uboView)
+ if(!uboViews.empty())
{
- auto uboCount = reflection.GetUniformBlockCount();
- mUniformBufferBindings.resize(uboCount);
-
- std::vector<Graphics::UniformBufferBinding>* bindings{&mUniformBufferBindings};
-
- mUniformBufferBindings[0].buffer = uboView->GetBuffer(&mUniformBufferBindings[0].offset);
-
// Write default uniforms
- WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_MATRIX), *uboView, modelMatrix);
- WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::VIEW_MATRIX), *uboView, viewMatrix);
- WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::PROJECTION_MATRIX), *uboView, projectionMatrix);
- WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_VIEW_MATRIX), *uboView, modelViewMatrix);
+ WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_MATRIX), uboViews, modelMatrix);
+ WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::VIEW_MATRIX), uboViews, viewMatrix);
+ WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::PROJECTION_MATRIX), uboViews, projectionMatrix);
+ WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::MODEL_VIEW_MATRIX), uboViews, modelViewMatrix);
auto mvpUniformInfo = program->GetDefaultUniform(Program::DefaultUniformIndex::MVP_MATRIX);
if(mvpUniformInfo && !mvpUniformInfo->name.empty())
{
Matrix modelViewProjectionMatrix(false);
MatrixUtils::MultiplyProjectionMatrix(modelViewProjectionMatrix, modelViewMatrix, projectionMatrix);
- WriteDefaultUniform(mvpUniformInfo, *uboView, modelViewProjectionMatrix);
+ WriteDefaultUniformV2(mvpUniformInfo, uboViews, modelViewProjectionMatrix);
}
auto normalUniformInfo = program->GetDefaultUniform(Program::DefaultUniformIndex::NORMAL_MATRIX);
Matrix3 normalMatrix(modelViewMatrix);
normalMatrix.Invert();
normalMatrix.Transpose();
- WriteDefaultUniform(normalUniformInfo, *uboView, normalMatrix);
+ WriteDefaultUniformV2(normalUniformInfo, uboViews, normalMatrix);
}
Vector4 finalColor; ///< Applied renderer's opacity color
{
finalColor = Vector4(color.r, color.g, color.b, color.a * mRenderDataProvider->GetOpacity(bufferIndex));
}
- WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::COLOR), *uboView, finalColor);
- WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::ACTOR_COLOR), *uboView, color);
+
+ WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::COLOR), uboViews, finalColor);
+ WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::ACTOR_COLOR), uboViews, color);
// Write uniforms from the uniform map
- FillUniformBuffer(*program, instruction, *uboView, bindings, uboOffset, bufferIndex, nodeIndex);
+ FillUniformBuffer(*program, instruction, uboViews, bufferIndex, nodeIndex);
// Write uSize in the end, as it shouldn't be overridable by dynamic properties.
- WriteDefaultUniform(program->GetDefaultUniform(Program::DefaultUniformIndex::SIZE), *uboView, size);
+ WriteDefaultUniformV2(program->GetDefaultUniform(Program::DefaultUniformIndex::SIZE), uboViews, size);
- commandBuffer.BindUniformBuffers(*bindings);
+ commandBuffer.BindUniformBuffers(mUniformBufferBindings);
}
}
}
template<class T>
+bool Renderer::WriteDefaultUniformV2(const Graphics::UniformInfo* uniformInfo, const std::vector<std::unique_ptr<Render::UniformBufferView>>& uboViews, const T& data)
+{
+ if(uniformInfo && !uniformInfo->name.empty())
+ {
+ WriteUniform(*uboViews[uniformInfo->bufferIndex], *uniformInfo, data);
+ return true;
+ }
+ return false;
+}
+
+template<class T>
void Renderer::WriteUniform(Render::UniformBufferView& ubo, const Graphics::UniformInfo& uniformInfo, const T& data)
{
WriteUniform(ubo, uniformInfo, &data, sizeof(T));
ubo.Write(data, size, ubo.GetOffset() + uniformInfo.offset);
}
-void Renderer::FillUniformBuffer(Program& program,
- const SceneGraph::RenderInstruction& instruction,
- Render::UniformBufferView& ubo,
- std::vector<Graphics::UniformBufferBinding>*& outBindings,
- uint32_t& offset,
- BufferIndex updateBufferIndex,
- std::size_t nodeIndex)
+void Renderer::FillUniformBuffer(Program& program,
+ const SceneGraph::RenderInstruction& instruction,
+ const std::vector<std::unique_ptr<Render::UniformBufferView>>& uboViews,
+ BufferIndex updateBufferIndex,
+ std::size_t nodeIndex)
{
- auto& reflection = mGraphicsController->GetProgramReflection(program.GetGraphicsProgram());
- auto uboCount = reflection.GetUniformBlockCount();
-
- // Setup bindings
- uint32_t dataOffset = offset;
- for(auto i = 0u; i < uboCount; ++i)
+ for(auto& iter : mUniformIndexMaps[nodeIndex])
{
- mUniformBufferBindings[i].dataSize = reflection.GetUniformBlockSize(i);
- mUniformBufferBindings[i].binding = reflection.GetUniformBlockBinding(i);
-
- dataOffset += GetUniformBufferDataAlignment(mUniformBufferBindings[i].dataSize);
- mUniformBufferBindings[i].buffer = ubo.GetBuffer(&mUniformBufferBindings[i].offset);
-
- for(auto iter = mUniformIndexMaps[nodeIndex].begin(),
- end = mUniformIndexMaps[nodeIndex].end();
- iter != end;
- ++iter)
+ auto& uniform = iter;
+ int arrayIndex = uniform.arrayIndex;
+ if(!uniform.uniformFunc)
{
- auto& uniform = *iter;
- int arrayIndex = uniform.arrayIndex;
-
- if(!uniform.uniformFunc)
+ auto uniformInfo = Graphics::UniformInfo{};
+ auto uniformFound = program.GetUniform(uniform.uniformName.GetStringView(),
+ uniform.uniformNameHash,
+ uniform.uniformNameHashNoArray,
+ uniformInfo);
+
+ UniformBufferView* ubo = nullptr;
+ if(uniformFound)
{
- auto uniformInfo = Graphics::UniformInfo{};
- auto uniformFound = program.GetUniform(uniform.uniformName.GetStringView(),
- uniform.uniformNameHash,
- uniform.uniformNameHashNoArray,
- uniformInfo);
-
- uniform.uniformOffset = uniformInfo.offset;
- uniform.uniformLocation = uniformInfo.location;
-
- if(uniformFound)
- {
- auto dst = ubo.GetOffset() + uniformInfo.offset;
- const auto typeSize = GetPropertyValueSizeForUniform((*iter).propertyValue->GetType());
- const auto dest = dst + static_cast<uint32_t>(typeSize) * arrayIndex;
- const auto func = GetPropertyValueGetter((*iter).propertyValue->GetType());
-
- ubo.Write(&((*iter).propertyValue->*func)(updateBufferIndex),
- typeSize,
- dest);
-
- uniform.uniformSize = typeSize;
- uniform.uniformFunc = func;
- }
+ ubo = uboViews[uniformInfo.bufferIndex].get();
}
else
{
- auto dst = ubo.GetOffset() + uniform.uniformOffset;
- const auto typeSize = uniform.uniformSize;
- const auto dest = dst + static_cast<uint32_t>(typeSize) * arrayIndex;
- const auto func = uniform.uniformFunc;
-
- ubo.Write(&((*iter).propertyValue->*func)(updateBufferIndex),
- typeSize,
- dest);
+ continue;
}
+
+ uniform.uniformOffset = uniformInfo.offset;
+ uniform.uniformLocation = uniformInfo.location;
+ uniform.uniformBlockIndex = uniformInfo.bufferIndex;
+
+ auto dst = ubo->GetOffset() + uniformInfo.offset;
+ const auto typeSize = GetPropertyValueSizeForUniform(iter.propertyValue->GetType());
+ const auto dest = dst + static_cast<uint32_t>(typeSize) * arrayIndex;
+ const auto func = GetPropertyValueGetter(iter.propertyValue->GetType());
+ uniform.uniformSize = typeSize;
+ uniform.uniformFunc = func;
+
+ ubo->Write(&(iter.propertyValue->*func)(updateBufferIndex),
+ typeSize,
+ dest);
}
- }
- // write output bindings
- outBindings = &mUniformBufferBindings;
+ else
+ {
+ UniformBufferView* ubo = uboViews[uniform.uniformBlockIndex].get();
+
+ auto dst = ubo->GetOffset() + uniform.uniformOffset;
+ const auto typeSize = uniform.uniformSize;
+ const auto dest = dst + static_cast<uint32_t>(typeSize) * arrayIndex;
+ const auto func = uniform.uniformFunc;
- // Update offset
- offset = dataOffset;
+ ubo->Write(&(iter.propertyValue->*func)(updateBufferIndex),
+ typeSize,
+ dest);
+ }
+ }
}
void Renderer::SetSortAttributes(SceneGraph::RenderInstructionProcessor::SortAttributes& sortAttributes) const
{
- sortAttributes.shader = &(mRenderDataProvider->GetShader());
+ sortAttributes.shader = &mRenderDataProvider->GetShader();
sortAttributes.geometry = mGeometry;
}
class PipelineCache;
class PipelineCacheL2;
class UniformBufferManager;
+class UniformBufferV2;
class Renderer;
using PipelineCacheL2Container = std::list<PipelineCacheL2>;
uint32_t queueIndex);
/**
+ * Returns true if this will create a draw command with it's own geometry
+ * and shader. Some renderers don't have a shader/geometry, e.g. drawable-actor
+ * creates an empty renderer.
+ */
+ bool NeedsProgram() const;
+
+ /**
+ * If we need a program, prepare it and return it.
+ * @param[in] instruction The render instruction
+ * @return the prepared program, or nullptr.
+ */
+ Program* PrepareProgram(const SceneGraph::RenderInstruction& instruction);
+
+ /**
* Sets RenderCallback object
*
* @param[in] callback Valid pointer to RenderCallback object
const T& data);
template<class T>
+ bool WriteDefaultUniformV2(const Graphics::UniformInfo* uniformInfo,
+ const std::vector<std::unique_ptr<Render::UniformBufferView>>& uboViews,
+ const T& data);
+
+ template<class T>
void WriteUniform(Render::UniformBufferView& ubo,
const Graphics::UniformInfo& uniformInfo,
const T& data);
- void WriteUniform(Render::UniformBufferView& ubo,
- const Graphics::UniformInfo& uniformInfo,
- const void* data,
- uint32_t size);
+ static void WriteUniform(Render::UniformBufferView& ubo,
+ const Graphics::UniformInfo& uniformInfo,
+ const void* data,
+ uint32_t size);
[[nodiscard]] FaceCullingMode::Type GetFaceCullMode() const
{
* @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] uboViews Target uniform buffer object
* @param[in] updateBufferIndex update buffer index
+ * @param[in] nodeIndex Index of node/renderer pair in mUniformIndexMaps
*/
- void FillUniformBuffer(Program& program,
- const SceneGraph::RenderInstruction& instruction,
- Render::UniformBufferView& ubo,
- std::vector<Graphics::UniformBufferBinding>*& outBindings,
- uint32_t& offset,
- BufferIndex updateBufferIndex,
- std::size_t nodeIndex);
+ void FillUniformBuffer(Program& program,
+ const SceneGraph::RenderInstruction& instruction,
+ const std::vector<std::unique_ptr<Render::UniformBufferView>>& uboViews,
+ BufferIndex updateBufferIndex,
+ std::size_t nodeIndex);
private:
Graphics::Controller* mGraphicsController;
int16_t uniformLocation{0u};
uint16_t uniformOffset{0u};
uint16_t uniformSize{0u};
+ uint16_t uniformBlockIndex{0u};
FuncGetter uniformFunc{0};
};
RenderCallback* mRenderCallback{nullptr};
std::unique_ptr<RenderCallbackInput> mRenderCallbackInput{nullptr};
std::vector<Graphics::Texture*> mRenderCallbackTextureBindings{};
+
+ Program* mCurrentProgram{nullptr}; ///< Prefetched program
};
} // namespace Render
/*
- * 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.
#include <dali/internal/render/renderers/uniform-buffer-manager.h>
// INTERNAL INCLUDES
-#include <dali/internal/render/renderers/uniform-buffer-view-pool.h>
#include <dali/internal/render/renderers/uniform-buffer-view.h>
#include <dali/internal/render/renderers/uniform-buffer.h>
+#include <dali/internal/update/common/scene-graph-scene.h>
+
#include <dali/graphics-api/graphics-buffer-create-info.h>
#include <dali/graphics-api/graphics-buffer.h>
#include <cstring>
#include <memory>
+namespace
+{
+uint32_t CPU_MEMORY_ALIGNMENT{256};
+}
+
namespace Dali::Internal::Render
{
UniformBufferManager::UniformBufferManager(Dali::Graphics::Controller* controller)
UniformBufferManager::~UniformBufferManager() = default;
-Graphics::UniquePtr<UniformBuffer> UniformBufferManager::AllocateUniformBuffer(uint32_t size, uint32_t alignment)
+void UniformBufferManager::SetCurrentSceneRenderInfo(SceneGraph::Scene* scene, bool offscreen)
{
- // TODO : Current code only assume CPU_ALLOCATED uniform buffer now
- return Graphics::UniquePtr<UniformBuffer>(
- new UniformBuffer(mController,
- size,
- alignment,
- Dali::Graphics::BufferUsageFlags{0u} | Dali::Graphics::BufferUsage::TRANSFER_DST | Dali::Graphics::BufferUsage::UNIFORM_BUFFER,
- Dali::Graphics::BufferPropertiesFlags{0u} | Dali::Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED));
+ mCurrentSceneOffscreen = offscreen;
+ mCurrentScene = scene;
+
+ // Only works if RegisterScene has been called...
+ mCurrentUBOSet = FindSetForScene(scene);
}
-Graphics::UniquePtr<UniformBufferView> UniformBufferManager::CreateUniformBufferView(UniformBuffer* uniformBuffer, uint32_t offset, uint32_t size)
+Graphics::UniquePtr<UniformBufferView> UniformBufferManager::CreateUniformBufferView(uint32_t size, bool emulated)
{
// Allocate offset of given UBO (allocation strategy may reuse memory)
- return Graphics::UniquePtr<UniformBufferView>(new UniformBufferView(*uniformBuffer, offset, size));
+
+ DALI_ASSERT_DEBUG(mCurrentUBOSet && "UBO set should exist when creating view");
+ if(!mCurrentUBOSet)
+ {
+ return Graphics::UniquePtr<UniformBufferView>(nullptr);
+ }
+
+ UBOSet::BufferType bufferType = UBOSet::GetBufferType(mCurrentSceneOffscreen, emulated);
+ Graphics::UniquePtr<UniformBufferV2>& ubo = mCurrentUBOSet->GetBuffer(bufferType);
+
+ // Use current offset and increment it after
+ auto offset = ubo->GetCurrentOffset();
+ auto retval = Graphics::UniquePtr<UniformBufferView>(new UniformBufferView(*ubo.get(), offset, size));
+
+ // make sure new offset will meet alignment requirements
+ uint32_t alignedSize = ubo->AlignSize(size);
+ ubo->IncrementOffsetBy(alignedSize);
+ return retval;
+}
+
+void UniformBufferManager::RegisterScene(SceneGraph::Scene* scene)
+{
+ auto iter = mUBOMap.find(scene);
+ if(iter == mUBOMap.end())
+ {
+ // Create new UBO set
+ UBOSet uboSet;
+
+ uint32_t cpuAlignment = GetUniformBlockAlignment(true);
+ uint32_t gpuAlignment = GetUniformBlockAlignment(false);
+
+ // Create all buffers per scene
+ uboSet.cpuBufferOnScreen = Render::UniformBufferV2::New(mController, true, cpuAlignment);
+ uboSet.gpuBufferOnScreen = Render::UniformBufferV2::New(mController, false, gpuAlignment);
+ uboSet.cpuBufferOffScreen = Render::UniformBufferV2::New(mController, true, cpuAlignment);
+ uboSet.gpuBufferOffScreen = Render::UniformBufferV2::New(mController, false, gpuAlignment);
+
+ mUBOMap.emplace(std::pair(scene, std::move(uboSet)));
+ }
+}
+
+void UniformBufferManager::UnregisterScene(SceneGraph::Scene* scene)
+{
+ auto iter = mUBOMap.find(scene);
+ if(iter != mUBOMap.end())
+ {
+ mUBOMap.erase(iter);
+ }
+}
+
+UniformBufferV2* UniformBufferManager::GetUniformBufferForScene(SceneGraph::Scene* scene, bool offscreen, bool emulated)
+{
+ auto* uboSet = GetUBOSetForScene(scene);
+ if(!uboSet)
+ {
+ return nullptr;
+ }
+ DALI_ASSERT_DEBUG(mCurrentScene == scene && "Scene should match current cache");
+ return uboSet->GetBuffer(UBOSet::GetBufferType(offscreen, emulated)).get();
+}
+
+void UniformBufferManager::Rollback(SceneGraph::Scene* scene, bool offscreen)
+{
+ auto* uboSet = GetUBOSetForScene(scene);
+ if(uboSet)
+ {
+ if(offscreen)
+ {
+ uboSet->cpuBufferOffScreen->Rollback();
+ uboSet->gpuBufferOffScreen->Rollback();
+ }
+ else
+ {
+ uboSet->cpuBufferOnScreen->Rollback();
+ uboSet->gpuBufferOnScreen->Rollback();
+ }
+ }
+}
+
+void UniformBufferManager::Flush(SceneGraph::Scene* scene, bool offscreen)
+{
+ auto* uboSet = GetUBOSetForScene(scene);
+ if(uboSet)
+ {
+ if(offscreen)
+ {
+ uboSet->cpuBufferOffScreen->Flush();
+ uboSet->gpuBufferOffScreen->Flush();
+ }
+ else
+ {
+ uboSet->cpuBufferOnScreen->Flush();
+ uboSet->gpuBufferOnScreen->Flush();
+ }
+ }
}
-Graphics::UniquePtr<UniformBufferViewPool> UniformBufferManager::CreateUniformBufferViewPool()
+uint32_t UniformBufferManager::GetUniformBlockAlignment(bool emulated)
{
- return Graphics::UniquePtr<UniformBufferViewPool>(
- new UniformBufferViewPool(*this, 1));
+ if(emulated)
+ {
+ return CPU_MEMORY_ALIGNMENT;
+ }
+
+ if(mCachedUniformBlockAlignment == 0)
+ {
+ /* Getting the block alignment. This is a little complicated for the following reasons:
+ *
+ * On GL, this is as simple as calling glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT).
+ *
+ * On Vulkan, this needs a test buffer to be allocated with the right usage flags,
+ * and GetBufferMemoryRequirements will return alignment that would then be common
+ * to all similar buffers.
+ *
+ * We have generally copied the Vulkan api into our GraphicsAPI, with some changes.
+ * So, we have to use Graphics::GetBufferMemoryRequirements() which we call on a test buffer.
+ *
+ * Note, this doesn't change during the graphics context lifetime, so can be cached.
+ */
+
+ Graphics::BufferPropertiesFlags flags = 0u;
+
+ const uint32_t TEST_BUFFER_ALLOCATION{256};
+ auto createInfo = Graphics::BufferCreateInfo()
+ .SetSize(TEST_BUFFER_ALLOCATION)
+ .SetBufferPropertiesFlags(flags)
+ .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
+
+ Graphics::UniquePtr<Graphics::Buffer> graphicsBuffer = mController->CreateBuffer(createInfo, nullptr);
+ Graphics::MemoryRequirements requirements = mController->GetBufferMemoryRequirements(*graphicsBuffer.get());
+ mCachedUniformBlockAlignment = requirements.alignment;
+ }
+
+ return mCachedUniformBlockAlignment;
}
-[[nodiscard]] UniformBufferViewPool* UniformBufferManager::GetUniformBufferViewPool(uint32_t bufferIndex)
+UniformBufferManager::UBOSet* UniformBufferManager::GetUBOSetForScene(SceneGraph::Scene* scene)
{
- if(!mUniformBufferPoolStorage[bufferIndex])
+ if(mCurrentScene == scene && mCurrentScene == scene)
{
- // create new uniform buffer view pool with default (initial) capacity
- mUniformBufferPoolStorage[bufferIndex] = CreateUniformBufferViewPool();
+ return mCurrentUBOSet;
}
- return mUniformBufferPoolStorage[bufferIndex].get();
+ mCurrentUBOSet = FindSetForScene(scene);
+ return mCurrentUBOSet;
}
-void UniformBufferManager::ReadyToLockUniformBuffer(uint32_t bufferIndex)
+// Find efficiently without caching
+UniformBufferManager::UBOSet* UniformBufferManager::FindSetForScene(SceneGraph::Scene* scene)
{
- GetUniformBufferViewPool(bufferIndex)->ReadyToLockUniformBuffer();
+ if(mUBOMap.size() == 1 && mUBOMap.begin()->first == scene)
+ {
+ return &mUBOMap.begin()->second;
+ }
+ else
+ {
+ auto iter = mUBOMap.find(scene);
+ if(iter != mUBOMap.end())
+ {
+ return &iter->second;
+ }
+ }
+ return nullptr;
}
-void UniformBufferManager::UnlockUniformBuffer(uint32_t bufferIndex)
+UniformBufferManager::UBOSet::UBOSet(UniformBufferManager::UBOSet&& rhs)
{
- GetUniformBufferViewPool(bufferIndex)->UnlockUniformBuffer();
+ cpuBufferOnScreen.swap(rhs.cpuBufferOnScreen);
+ gpuBufferOnScreen.swap(rhs.gpuBufferOnScreen);
+ cpuBufferOffScreen.swap(rhs.cpuBufferOffScreen);
+ gpuBufferOffScreen.swap(rhs.gpuBufferOffScreen);
}
} // namespace Dali::Internal::Render
#define DALI_INTERNAL_UNIFORM_BUFFER_MANAGER_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.
*/
// INTERNAL INCLUDES
+#include <dali/devel-api/common/map-wrapper.h>
#include <dali/graphics-api/graphics-controller.h>
+namespace Dali::Internal::SceneGraph
+{
+class Scene;
+}
+
namespace Dali::Internal::Render
{
-class UniformBuffer;
+class UniformBufferV2;
class UniformBufferView;
-class UniformBufferViewPool;
/**
* Class UniformBufferManager
~UniformBufferManager();
+ Graphics::UniquePtr<UniformBufferView> CreateUniformBufferView(uint32_t size, bool emulated = true);
+
/**
- * @brief Allocates uniform buffer with given size and alignment
- * @param size Size of uniform buffer
- * @param alignment Alignment
- * @return new UniformBuffer
+ * @brief Registers scene with the manager
+ * The manager creates a set of UBOs per scene.
+ *
+ * @param[in] scene Valid pointer to the scene
*/
- Graphics::UniquePtr<UniformBuffer> AllocateUniformBuffer(uint32_t size, uint32_t alignment = 256);
+ void RegisterScene(SceneGraph::Scene* scene);
/**
- * @brief Creates a view on UniformBuffer
+ * @brief Removes association with a scene
*
- * @param uniformBuffer
- * @param size
- * @return Uniform buffer view
+ * @param[in] scene Valid pointer to a scene object
*/
- Graphics::UniquePtr<UniformBufferView> CreateUniformBufferView(UniformBuffer* uniformBuffer, uint32_t offset, uint32_t size);
+ void UnregisterScene(SceneGraph::Scene* scene);
/**
- * @brief Creates uniform buffer pool view
- * @param size
- * @return
+ * @brief Get the uniform buffer for the given scene, if current.
*/
- Graphics::UniquePtr<UniformBufferViewPool> CreateUniformBufferViewPool();
+ UniformBufferV2* GetUniformBufferForScene(SceneGraph::Scene* scene, bool offscreen, bool emulated);
/**
- * @brief Returns Controller object
- * @return controller object
+ * @brief must be called when rendering scene starts, this way the manager knows
+ * which UBO set we are going to use.
*/
- [[nodiscard]] Graphics::Controller& GetController() const
- {
- return *mController;
- }
+ void SetCurrentSceneRenderInfo(SceneGraph::Scene* scene, bool offscreen);
/**
- * @brief Returns embedded uniform buffer pool view for specified DAli buffer index
- * @return Pointer to valid uniform buffer pool view
+ * @brief Rolls back UBO set matching conditions
+ * @param[in] scene Valid scene pointer
+ * @param[in] offscreen Offscreen or onscreen
*/
- [[nodiscard]] UniformBufferViewPool* GetUniformBufferViewPool(uint32_t bufferIndex);
+ void Rollback(SceneGraph::Scene* scene, bool offscreen);
/**
- * @brief Prepare to lock the uniform buffer so we can write to the standalone uniform map directly.
- * Uniform buffer will be locked at the first call of UniformBuffer::Write after call this API.
- * @note After all write done, We should call UnlockUniformBuffer.
- *
- * @param bufferIndex current update/render buffer index
+ * @brief Flushes current UBO set
*/
- void ReadyToLockUniformBuffer(uint32_t bufferIndex);
+ void Flush(SceneGraph::Scene* scene, bool offscreen);
/**
- * @brief Unlock the uniform buffer.
- * @note We should call ReadyToLockUniformBuffer before call this.
- *
- * @param bufferIndex current update/render buffer index
+ * Gets the uniform block alignment.
+ * It will cache it locally, as this doesn't change during a graphics context's lifetime
+ * @param[in] emulated - True if this is to query CPU alignment, or false to query GPU
+ * alignment.
+ * @return the alignment
*/
- void UnlockUniformBuffer(uint32_t bufferIndex);
+ uint32_t GetUniformBlockAlignment(bool emulated);
private:
Dali::Graphics::Controller* mController;
- Graphics::UniquePtr<UniformBufferViewPool> mUniformBufferPoolStorage[2u]; ///< The pool view into UniformBuffer (double buffered)
+ /**
+ * Stores pointers to uniform buffers associated with the scene
+ * There may be up to 4 UBOs created per scene:
+ * - Emulated on-screen
+ * - GPU on-screen
+ * - Emulated off-screen
+ * - GPU off-screen
+ */
+ struct UBOSet
+ {
+ UBOSet() = default;
+ ~UBOSet() = default;
+
+ UBOSet(UBOSet&& rhs);
+
+ enum class BufferType
+ {
+ CPU_ONSCREEN = 0,
+ GPU_ONSCREEN = 1,
+ CPU_OFFSCREEN = 2,
+ GPU_OFFSCREEN = 3,
+ };
+
+ static BufferType GetBufferType(bool offscreen, bool emulated)
+ {
+ if(offscreen)
+ {
+ return (emulated ? BufferType::CPU_OFFSCREEN : BufferType::GPU_OFFSCREEN);
+ }
+ return (emulated ? BufferType::CPU_ONSCREEN : BufferType::GPU_ONSCREEN);
+ }
+
+ Graphics::UniquePtr<UniformBufferV2> cpuBufferOnScreen;
+ Graphics::UniquePtr<UniformBufferV2> gpuBufferOnScreen;
+ Graphics::UniquePtr<UniformBufferV2> cpuBufferOffScreen;
+ Graphics::UniquePtr<UniformBufferV2> gpuBufferOffScreen;
+
+ Graphics::UniquePtr<UniformBufferV2>& GetBuffer(BufferType bufferType)
+ {
+ switch(bufferType)
+ {
+ case BufferType::CPU_ONSCREEN:
+ {
+ return cpuBufferOnScreen;
+ }
+ case BufferType::CPU_OFFSCREEN:
+ {
+ return cpuBufferOffScreen;
+ }
+ case BufferType::GPU_ONSCREEN:
+ {
+ return gpuBufferOnScreen;
+ }
+ case BufferType::GPU_OFFSCREEN:
+ {
+ return gpuBufferOffScreen;
+ }
+ }
+ // Appease the compiler
+ return cpuBufferOnScreen;
+ }
+ };
+
+ /**
+ * Find with caching, updates current cached set
+ */
+ UBOSet* GetUBOSetForScene(SceneGraph::Scene* scene);
+
+ /**
+ * Find without caching
+ */
+ UBOSet* FindSetForScene(SceneGraph::Scene* scene);
+
+ std::map<SceneGraph::Scene*, UBOSet> mUBOMap;
+ SceneGraph::Scene* mCurrentScene{nullptr};
+ UBOSet* mCurrentUBOSet{nullptr};
+ uint32_t mCachedUniformBlockAlignment{0u};
+ bool mCurrentSceneOffscreen{false};
};
} // namespace Dali::Internal::Render
+++ /dev/null
-/*
- * Copyright (c) 2022 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.
- *
- */
-
-// CLASS HEADER
-#include <dali/internal/render/renderers/uniform-buffer-view-pool.h>
-
-// INTERNAL INCLUDES
-#include <dali/graphics-api/graphics-buffer-create-info.h>
-#include <dali/graphics-api/graphics-buffer.h>
-#include <dali/internal/render/renderers/uniform-buffer-manager.h>
-#include <dali/internal/render/renderers/uniform-buffer-view.h>
-#include <dali/internal/render/renderers/uniform-buffer.h>
-
-namespace Dali::Internal::Render
-{
-namespace
-{
-// Default UBO page size set to 32kb
-const uint32_t DEFAULT_UBO_PAGE_SIZE = 32768;
-} // namespace
-
-UniformBufferViewPool::UniformBufferViewPool(UniformBufferManager& manager, uint32_t alignment)
-: mUboManager(manager)
-{
- // Create initial UBO
- mUniformBufferStorage = mUboManager.AllocateUniformBuffer(DEFAULT_UBO_PAGE_SIZE, alignment);
- mAlignment = alignment;
- mCurrentOffset = 0;
-}
-
-UniformBufferViewPool::~UniformBufferViewPool() = default;
-
-void UniformBufferViewPool::Rollback()
-{
- // Rollback offset
- mCurrentOffset = 0;
-
- // turn buffer into single allocation by resizing it
- // to current size with invalidation
- auto currentSize = mUniformBufferStorage->GetSize();
- mUniformBufferStorage->Resize(currentSize ? currentSize : DEFAULT_UBO_PAGE_SIZE, true);
-}
-
-Graphics::UniquePtr<UniformBufferView> UniformBufferViewPool::CreateUniformBufferView(size_t size)
-{
- // find new offset
- auto newOffset = ((mCurrentOffset + size) / mAlignment) * mAlignment;
- if(mAlignment > 1 && newOffset < mCurrentOffset + size)
- {
- newOffset += mAlignment;
- }
-
- // resize Ubo if needed
- if(newOffset >= mUniformBufferStorage->GetSize())
- {
- // move offset to the new buffer
- mCurrentOffset = mUniformBufferStorage->GetSize();
- newOffset = ((mCurrentOffset + size) / mAlignment) * mAlignment;
- if(mAlignment > 1 && newOffset < mCurrentOffset + size)
- {
- newOffset += mAlignment;
- }
-
- size_t increaseBufferStorageSize = DALI_LIKELY(size > 0) ? ((size - 1) / DEFAULT_UBO_PAGE_SIZE + 1) * DEFAULT_UBO_PAGE_SIZE : DEFAULT_UBO_PAGE_SIZE;
-
- mUniformBufferStorage->Resize(mUniformBufferStorage->GetSize() + increaseBufferStorageSize, false);
- }
-
- // create buffer view from
- Graphics::UniquePtr<UniformBufferView> uboView = mUboManager.CreateUniformBufferView(mUniformBufferStorage.get(), mCurrentOffset, size);
-
- // adjust offset
- mCurrentOffset = newOffset;
-
- return uboView;
-}
-
-void UniformBufferViewPool::ReadyToLockUniformBuffer()
-{
- mUniformBufferStorage->ReadyToLockUniformBuffer();
-}
-
-void UniformBufferViewPool::UnlockUniformBuffer()
-{
- mUniformBufferStorage->UnlockUniformBuffer();
-}
-
-} // namespace Dali::Internal::Render
\ No newline at end of file
+++ /dev/null
-#ifndef DALI_INTERNAL_UNIFORM_BUFFER_VIEW_POOL_H
-#define DALI_INTERNAL_UNIFORM_BUFFER_VIEW_POOL_H
-
-/*
- * Copyright (c) 2022 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.
- *
- */
-
-// INTERNAL INCLUDES
-#include <dali/graphics-api/graphics-types.h>
-
-// EXTERNAL INCLUDES
-#include <stdint.h>
-#include <stdlib.h>
-
-namespace Dali::Internal::Render
-{
-class UniformBufferManager;
-class UniformBufferView;
-class UniformBuffer;
-
-/**
- * Class UniformBufferPoolView
- *
- * The class is responsible for managing the UniformBuffer memory.
- * It is stack-based pool allocator. It doesn't own the UniformBuffer but
- * it's mere view into the memory.
- *
- * The view may however request UniformBuffer to resize if there is a need
- * to allocate memory beyond its current size.
- *
- * The UniformBuffer may be trimmed on Rollback().
- *
- * Rollback() operation moves allocation pointer to the very beginning
- * of the UniformBuffer. The data stored in the UniformBuffer after
- * Rollback() is considered invalid and shouldn't be used by the client side.
- */
-class UniformBufferViewPool
-{
- friend class UniformBufferManager;
-
-private:
- UniformBufferViewPool(UniformBufferManager& manager, uint32_t alignment);
-
-public:
- ~UniformBufferViewPool();
-
- /**
- * @brief Rolls back allocation to the beginning of pool
- */
- void Rollback();
-
- /**
- * @brief Creates view for next free chunk of UBO memory of specified size.
- */
- Graphics::UniquePtr<UniformBufferView> CreateUniformBufferView(size_t size);
-
- /**
- * @copydoc Dali::Internal::Render::UniformBufferManager::ReadyToLockUniformBuffer
- */
- void ReadyToLockUniformBuffer();
-
- /**
- * @copydoc Dali::Internal::Render::UniformBufferManager::UnlockUniformBuffer
- */
- void UnlockUniformBuffer();
-
-private:
- UniformBufferManager& mUboManager;
- Graphics::UniquePtr<UniformBuffer> mUniformBufferStorage;
-
- uint32_t mAlignment; // 1 for tightly packed emulated UBO
- uint32_t mCurrentOffset;
-};
-
-} // namespace Dali::Internal::Render
-#endif //DALI_INTERNAL_UNIFORM_BUFFER_VIEW_POOL_H
\ No newline at end of file
/*
- * 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.
namespace Dali::Internal::Render
{
-UniformBufferView::UniformBufferView( UniformBuffer& ubo, uint32_t offset, size_t size ) :
- mUniformBuffer(&ubo),
+UniformBufferView::UniformBufferView(UniformBufferV2& ubo, uint32_t offset, size_t size)
+: mUniformBuffer(&ubo),
mOffset(offset),
- mSize (size)
+ mSize(size)
{
}
void UniformBufferView::Write(const void* data, uint32_t size, uint32_t offset)
{
// Write into mapped buffer
- mUniformBuffer->Write( data, size, offset );
+ mUniformBuffer->Write(data, size, offset);
}
-Graphics::Buffer* UniformBufferView::GetBuffer( uint32_t* relativeOffset )
+Graphics::Buffer* UniformBufferView::GetBuffer() const
{
- auto buffer = mUniformBuffer->GetBufferByOffset( mOffset, relativeOffset, nullptr );
- return buffer ? buffer->buffer.get() : nullptr;
+ return mUniformBuffer->GetGraphicsBuffer();
}
-} // Namespace Dali::Internal::Render
\ No newline at end of file
+
+} // Namespace Dali::Internal::Render
#define DALI_INTERNAL_UNIFORM_BUFFER_VIEW_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.
// EXTERNAL INCLUDES
#include <stdint.h>
#include <stdlib.h>
+#include <memory>
namespace Dali
{
}
namespace Internal::Render
{
-class UniformBuffer;
+class UniformBufferV2;
/**
* Class UniformBufferView
class UniformBufferView
{
public:
- UniformBufferView(UniformBuffer& ubo, uint32_t offset, size_t size);
+ UniformBufferView(UniformBufferV2& ubo, uint32_t offset, size_t size);
~UniformBufferView();
/**
* @brief Returns Graphics buffer associated with this View
*
- * If 'relativeOffset' isn't nullptr then the offset into the individual
- * Graphics::Buffer is written.
- *
- * @param[out] relativeOffset the offset into individual Graphics::Buffer
- *
* @return Pointer to a valid Graphics::Buffer object
*/
- [[nodiscard]] Graphics::Buffer* GetBuffer(uint32_t* relativeOffset = nullptr);
+ [[nodiscard]] Graphics::Buffer* GetBuffer() const;
private:
- UniformBuffer* mUniformBuffer{nullptr}; ///< UniformBuffer that the view views
- uint32_t mOffset{0u}; ///< Offset within the buffer
- size_t mSize{0u}; ///< Size of view
+ UniformBufferV2* mUniformBuffer{nullptr}; ///< UniformBuffer that the view views
+ uint32_t mOffset{0u}; ///< Offset within the buffer
+ size_t mSize{0u}; ///< Size of view
};
} // Namespace Internal::Render
} // Namespace Dali
-#endif //DALI_INTERNAL_UNIFORM_BUFFER_VIEW_H
\ No newline at end of file
+#endif //DALI_INTERNAL_UNIFORM_BUFFER_VIEW_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.
namespace Dali::Internal::Render
{
-namespace
+// GPU UBOs need to be double-buffered in order to avoid stalling the CPU during mapping/unmapping
+constexpr uint32_t INTERNAL_UBO_BUFFER_COUNT = 2u;
+
+Graphics::UniquePtr<UniformBufferV2> UniformBufferV2::New(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment)
{
-static constexpr uint32_t INVALID_BUFFER_INDEX = std::numeric_limits<uint32_t>::max();
+ return Graphics::UniquePtr<UniformBufferV2>(new UniformBufferV2(controller, emulated, alignment));
}
-UniformBuffer::UniformBuffer(Dali::Graphics::Controller* controller,
- uint32_t sizeInBytes,
- uint32_t alignment,
- Graphics::BufferUsageFlags usageFlags,
- Graphics::BufferPropertiesFlags propertiesFlags)
+
+UniformBufferV2::UniformBufferV2(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment)
: mController(controller),
- mSize(0u),
- mUsageFlags(usageFlags),
- mPropertiesFlags(propertiesFlags),
- mLockedBufferIndex(INVALID_BUFFER_INDEX),
- mLockedPtr(nullptr),
- mReadyToBeLocked(false)
+ mBlockAlignment(alignment),
+ mCurrentGraphicsBufferIndex(0),
+ mEmulated(emulated)
{
- mAlignment = alignment;
- if(sizeInBytes)
- {
- Resize(sizeInBytes, true);
- }
+ mBufferList.resize(emulated ? 1 : INTERNAL_UBO_BUFFER_COUNT);
}
-UniformBuffer::~UniformBuffer()
+void UniformBufferV2::ReSpecify(uint32_t sizeInBytes)
{
- // Unmap and flush all allocated buffers
- for(auto i = 0u; i < mBuffers.size(); ++i)
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Respec(%p) [%d] BufferType:%s newSize:%d\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU", sizeInBytes);
+ if(mEmulated)
{
- Flush(i);
- Unmap(i);
+ ReSpecifyCPU(sizeInBytes);
}
-}
-
-void UniformBuffer::Flush(uint32_t bufferIndex)
-{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Flush (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
- const auto& buffer = mBuffers[bufferIndex];
- if(buffer.buffer && buffer.memory)
+ else
{
- buffer.memory->Flush();
+ ReSpecifyGPU(sizeInBytes);
}
}
-void UniformBuffer::Resize(uint32_t newSize, bool invalidate)
+void UniformBufferV2::Write(const void* data, uint32_t size, uint32_t offset)
{
- // Adjust alignment, the alignment is needed for
- // real UBOs (it should be given by the buffer requirements)
- if(DALI_LIKELY(mAlignment && newSize > 0))
+ // Very verbose logging!
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::LogLevel(4), "Write(%p) [%d] BufferType:%s offset:%d size:%d\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU", offset, size);
+ if(mEmulated)
+ {
+ WriteCPU(data, size, offset);
+ }
+ else
{
- newSize = (((newSize - 1) / mAlignment) + 1) * mAlignment;
+ WriteGPU(data, size, offset);
}
+}
- // The buffer is already optimal
- if(newSize == mSize && !invalidate)
+void UniformBufferV2::Map()
+{
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Map(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
+ if(mEmulated)
{
- return;
+ MapCPU();
}
- if(invalidate && newSize == mSize && mBuffers.size() == 1)
+ else
{
- return;
+ MapGPU();
}
+}
- if(DALI_UNLIKELY(mReadyToBeLocked))
+void UniformBufferV2::Unmap()
+{
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Unmap(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
+ if(mEmulated)
{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize %d --> %d with %s [mBuffers : %d] during lock (lockedBufferIndex : %d, lockedPtr : %p)\n", mSize, newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size(), mLockedBufferIndex, mLockedPtr);
+ UnmapCPU();
}
else
{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize %d --> %d with %s [mBuffers : %d]\n", mSize, newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size());
+ UnmapGPU();
}
+}
- // Throw away content
- if(invalidate)
+void UniformBufferV2::Flush()
+{
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Flush(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
+
+ // Flush only for GPU buffertype by unmapping
+ if(!mEmulated && mMappedPtr)
{
- if(mReadyToBeLocked)
- {
- UnlockUniformBuffer();
- mReadyToBeLocked = true;
- }
- // Flush and unmap all allocated buffers
- for(auto i = 0u; i < mBuffers.size(); ++i)
+ auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
+ if(buffer.graphicsMemory)
{
- Flush(i);
- Unmap(i);
+ UnmapGPU();
+ // flush range?
}
- mBuffers.clear();
- mSize = 0;
+
+ // Swap buffers for GPU UBOs
+ auto s = mBufferList.size();
+ mCurrentGraphicsBufferIndex = ((mCurrentGraphicsBufferIndex + 1) % s);
}
+}
- if(newSize > mSize)
+void UniformBufferV2::Rollback()
+{
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Rollback(%p) [%d]\n", this, mCurrentGraphicsBufferIndex);
+ if(!mBufferList.empty())
{
- auto createInfo = Graphics::BufferCreateInfo()
- .SetSize(newSize - mSize)
- .SetBufferPropertiesFlags(mPropertiesFlags)
- .SetUsage(mUsageFlags);
-
- auto buffer = mController->CreateBuffer(createInfo, nullptr);
-
- mBuffers.emplace_back(GfxBuffer(std::move(buffer), createInfo));
-
- mSize = newSize;
+ mBufferList[mCurrentGraphicsBufferIndex].currentOffset = 0; // reset offset
}
+}
- // If invalidate during locked, begin lock again.
- if(DALI_UNLIKELY(invalidate && mReadyToBeLocked))
+uint32_t UniformBufferV2::AlignSize(uint32_t size)
+{
+ if(size % mBlockAlignment != 0)
{
- mReadyToBeLocked = false;
- ReadyToLockUniformBuffer();
+ size = ((size / mBlockAlignment) + 1) * mBlockAlignment;
}
+ return size;
+}
- if(DALI_UNLIKELY(mReadyToBeLocked))
+uint32_t UniformBufferV2::IncrementOffsetBy(uint32_t value)
+{
+ if(mEmulated && !mBufferList.empty())
{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize done as %d with %s [mBuffers : %d] during lock (lockedBufferIndex : %d, lockedPtr : %p)\n", newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size(), mLockedBufferIndex, mLockedPtr);
- }
- else
+ mBufferList[mCurrentGraphicsBufferIndex].currentOffset += value; // reset offset
+ return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
+ } // GPU
+ else if(!mBufferList.empty())
{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize done as %d with %s [mBuffers : %d]\n", newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size());
+ mBufferList[mCurrentGraphicsBufferIndex].currentOffset += value; // reset offset
+ return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
}
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Buffer should be allocated before incrementing offset\n");
+ return 0;
}
-const UniformBuffer::GfxBuffer* UniformBuffer::GetBufferByOffset(uint32_t offset, uint32_t* newOffset, uint32_t* outBufferIndex) const
+bool UniformBufferV2::MemoryCompare(void* data, uint32_t offset, uint32_t size)
{
- uint32_t bufferOffset = offset;
- uint32_t bufferIndex = 0u;
-
- // Find buffer if UBO is fragmented
- if(mBuffers.size() > 1)
- {
- for(const auto& buffer : mBuffers)
- {
- if(bufferOffset >= buffer.createInfo.size)
- {
- bufferOffset -= buffer.createInfo.size;
- }
- else
- {
- break;
- }
- bufferIndex++;
- }
- }
+ return !memcmp(data, reinterpret_cast<uint8_t*>(mMappedPtr) + offset, size);
+}
- auto& bufferDesc = mBuffers[bufferIndex];
+uint32_t UniformBufferV2::GetBlockAlignment() const
+{
+ return mBlockAlignment;
+}
- if(outBufferIndex)
+uint32_t UniformBufferV2::GetCurrentOffset() const
+{
+ if(!mBufferList.empty())
{
- *outBufferIndex = bufferIndex;
+ return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
}
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Buffer should be allocated before getting offset\n");
+ return 0;
+}
- if(newOffset)
+uint32_t UniformBufferV2::GetCurrentCapacity() const
+{
+ uint32_t capacity = 0;
+ if(!mBufferList.empty())
{
- *newOffset = bufferOffset;
+ DALI_ASSERT_DEBUG(mBufferList.size() > mCurrentGraphicsBufferIndex);
+ capacity = mBufferList[mCurrentGraphicsBufferIndex].capacity;
}
+ return capacity;
+}
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GetBufferByOffset (bufferIndex : %d / %d [%d], bufferOffset : %d, Graphics::BufferPtr : %p)\n", bufferIndex, mBuffers.size(), mSize, bufferOffset, bufferDesc.buffer.get());
-
- return &bufferDesc;
+Dali::Graphics::Buffer* UniformBufferV2::GetGraphicsBuffer() const
+{
+ return mBufferList[mCurrentGraphicsBufferIndex].graphicsBuffer.get();
}
-void UniformBuffer::Write(const void* data, uint32_t size, uint32_t dstOffset)
+void UniformBufferV2::ReSpecifyCPU(uint32_t sizeInBytes)
{
- // find which buffer we want to write into
- uint32_t bufferOffset = dstOffset;
- uint32_t bufferIndex = 0u;
+ GfxBuffer gfxBuffer;
+
+ uint32_t currentCapacity = GetCurrentCapacity();
- // Find buffer if UBO is fragmented
- if(mBuffers.size() > 1)
+ if(sizeInBytes > currentCapacity)
{
- for(const auto& buffer : mBuffers)
+ Graphics::UniquePtr<Graphics::Buffer> oldBuffer{nullptr};
+
+ // If the CPU buffer already exist use it when applying respec
+ if(!mBufferList.empty())
{
- if(bufferOffset >= buffer.createInfo.size)
- {
- bufferOffset -= buffer.createInfo.size;
- }
- else
- {
- break;
- }
- bufferIndex++;
+ mBufferList[0].graphicsMemory = nullptr; // Discard mapped memory if exists
+ oldBuffer = std::move(mBufferList[0].graphicsBuffer); // Store old buffer for re-using
}
- }
+ Graphics::BufferPropertiesFlags flags = 0u | Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED;
+
+ auto createInfo = Graphics::BufferCreateInfo()
+ .SetSize(sizeInBytes)
+ .SetBufferPropertiesFlags(flags)
+ .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
- auto& bufferDesc = mBuffers[bufferIndex];
+ gfxBuffer.graphicsBuffer = mController->CreateBuffer(createInfo, std::move(oldBuffer));
+ gfxBuffer.capacity = sizeInBytes;
+ gfxBuffer.currentOffset = 0;
- if(bufferDesc.needsUpdate)
- {
+ mBufferList[0] = std::move(gfxBuffer);
+ // make sure buffer is created (move creation to run in parallel in the backed
+ // as this may be a major slowdown)
mController->WaitIdle();
- bufferDesc.needsUpdate = false;
}
- DALI_ASSERT_ALWAYS(mBuffers.size() > bufferIndex);
- DALI_ASSERT_ALWAYS(mBuffers[bufferIndex].buffer);
- DALI_ASSERT_ALWAYS(mBuffers[bufferIndex].createInfo.size > bufferOffset + size);
+ mMappedPtr = nullptr;
- const bool locallyMapped = (bufferDesc.mappedPtr != nullptr);
- if(!locallyMapped)
+ if(sizeInBytes)
{
- // Map once and keep it
- Map(bufferIndex);
+ // After respecifying the buffer, we can map it persistently as it already exists
+ // in the CPU memory
+ MapCPU();
}
+}
+
+void UniformBufferV2::ReSpecifyGPU(uint32_t sizeInBytes)
+{
+ uint32_t currentCapacity = GetCurrentCapacity();
- if(bufferDesc.memory)
+ if(sizeInBytes > currentCapacity)
{
- // Rarely happened that we use over the locked memory
- // Unlock previous buffer, and lock as current bufferIndex again
- if(DALI_UNLIKELY(mLockedBufferIndex != bufferIndex))
+ GfxBuffer gfxBuffer;
+ Graphics::UniquePtr<Graphics::Buffer> oldBuffer{nullptr};
+
+ // If the GPU buffer already exist use it when applying respec
+ if(!mBufferList.empty())
{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Unlock (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
-
- // mLockedBufferIndex == INVALID_BUFFER_INDEX only first time of current RenderScene.
- if(DALI_LIKELY(mLockedBufferIndex != INVALID_BUFFER_INDEX))
- {
- // Unlock previous memory
- if(mBuffers[mLockedBufferIndex].memory)
- {
- mBuffers[mLockedBufferIndex].memory->Unlock(true);
- }
- }
- mLockedBufferIndex = bufferIndex;
- mLockedPtr = nullptr;
-
- // Initial mapping done previously. Just lock and roll now.
- if(mBuffers[mLockedBufferIndex].memory)
- {
- mLockedPtr = reinterpret_cast<uint8_t*>(mBuffers[mLockedBufferIndex].memory->LockRegion(0, mBuffers[mLockedBufferIndex].createInfo.size));
- }
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Lock (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
+ mBufferList[mCurrentGraphicsBufferIndex].graphicsMemory = nullptr; // Discard mapped memory if exists
+ oldBuffer = std::move(mBufferList[mCurrentGraphicsBufferIndex].graphicsBuffer); // Store old buffer for re-using
}
+ Graphics::BufferPropertiesFlags flags = 0u;
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "memcpy (lockedBufferIndex : %d / %d [%d], lockedPtr : %p, offset : %d, size : %d, lockedBufferSize : %d)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr, bufferOffset, size, mBuffers[mLockedBufferIndex].createInfo.size);
+ auto createInfo = Graphics::BufferCreateInfo()
+ .SetSize(sizeInBytes)
+ .SetBufferPropertiesFlags(flags)
+ .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
- // We already check validation of buffer range. We can assume that bufferOffset + size <= mBuffers[mLockedBufferIndex].createInfo.size
- if(mLockedPtr)
- {
- memcpy(mLockedPtr + bufferOffset, data, size);
- }
+ gfxBuffer.graphicsBuffer = mController->CreateBuffer(createInfo, std::move(oldBuffer));
+ gfxBuffer.capacity = sizeInBytes;
+ gfxBuffer.currentOffset = 0;
+
+ mBufferList[mCurrentGraphicsBufferIndex] = std::move(gfxBuffer);
+ // make sure buffer is created (move creation to run in parallel in the backend
+ // as this may be a major slowdown)
+ mController->WaitIdle();
+ DALI_ASSERT_ALWAYS(mBufferList[mCurrentGraphicsBufferIndex].capacity == sizeInBytes && "std::move failed");
+ }
+
+ mMappedPtr = nullptr;
+
+ if(sizeInBytes)
+ {
+ MapGPU(); // Note, this will flush the creation buffer queues in the backend and initialize buffer.
}
}
-void UniformBuffer::Map(uint32_t bufferIndex)
+void UniformBufferV2::WriteCPU(const void* data, uint32_t size, uint32_t offset)
{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Map (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
- auto& buffer = mBuffers[bufferIndex];
-
- if(buffer.needsUpdate)
+ // If not mapped
+ if(!mMappedPtr)
{
- mController->WaitIdle();
- buffer.needsUpdate = false;
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Warning: CPU buffer should already be mapped!\n");
+ MapCPU();
}
- if(!buffer.memory)
+ DALI_ASSERT_DEBUG(offset + size <= mBufferList[mCurrentGraphicsBufferIndex].capacity);
+
+ // just copy whatever comes here
+ memcpy(reinterpret_cast<uint8_t*>(mMappedPtr) + offset, data, size);
+}
+
+void UniformBufferV2::WriteGPU(const void* data, uint32_t size, uint32_t offset)
+{
+ if(!mMappedPtr)
{
- Graphics::MapBufferInfo info{};
- info.buffer = buffer.buffer.get();
- info.usage = 0 | Graphics::MemoryUsageFlagBits::WRITE;
- info.offset = 0;
- info.size = buffer.createInfo.size;
- buffer.memory = mController->MapBufferRange(info);
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GraphicsMemoryMapped (bufferIndex : %d / %d [%d], size : %d)\n", bufferIndex, mBuffers.size(), mSize, info.size);
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Warning: GPU buffer should already be mapped!\n");
+ MapGPU();
}
+
+ DALI_ASSERT_DEBUG(offset + size <= mBufferList[mCurrentGraphicsBufferIndex].capacity);
+
+ memcpy(reinterpret_cast<uint8_t*>(mMappedPtr) + offset, data, size);
}
-void UniformBuffer::Unmap(uint32_t bufferIndex)
+void UniformBufferV2::MapCPU()
{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Unmap (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
- auto& buffer = mBuffers[bufferIndex];
- if(buffer.memory)
+ auto& buffer = mBufferList[0]; // CPU contains always one buffer
+ if(!buffer.graphicsMemory)
{
- mController->UnmapMemory(std::move(buffer.memory));
+ Graphics::MapBufferInfo info{};
+ info.buffer = buffer.graphicsBuffer.get();
+ info.usage = 0 | Graphics::MemoryUsageFlagBits::WRITE;
+ info.offset = 0;
+ info.size = buffer.capacity;
+ buffer.graphicsMemory = mController->MapBufferRange(info);
+ }
+
+ // obtain pointer instantly
+ if(buffer.graphicsMemory)
+ {
+ mMappedPtr = buffer.graphicsMemory->LockRegion(0, buffer.capacity);
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "CPU buffer is mapped to %p\n", mMappedPtr);
}
}
-void UniformBuffer::ReadyToLockUniformBuffer()
+void UniformBufferV2::MapGPU()
{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "LockUniformBuffer\n");
- if(DALI_UNLIKELY(mReadyToBeLocked && mLockedBufferIndex != INVALID_BUFFER_INDEX))
+ auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
+ if(!buffer.graphicsMemory)
{
- // Unlock previous locked buffer first
- DALI_LOG_ERROR("Warning! : called LockUniformBuffer() before called UnlockUniformBuffer()!\n");
- UnlockUniformBuffer();
+ Graphics::MapBufferInfo info{};
+ info.buffer = buffer.graphicsBuffer.get();
+ info.usage = 0 | Graphics::MemoryUsageFlagBits::WRITE;
+ info.offset = 0;
+ info.size = buffer.capacity;
+ buffer.graphicsMemory = mController->MapBufferRange(info);
}
- mReadyToBeLocked = true;
- mLockedBufferIndex = INVALID_BUFFER_INDEX;
- mLockedPtr = nullptr;
+ // obtain pointer instantly
+ if(buffer.graphicsMemory)
+ {
+ mMappedPtr = buffer.graphicsMemory->LockRegion(0, buffer.capacity);
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GPU buffer is mapped to %p\n", mMappedPtr);
+ }
}
-void UniformBuffer::UnlockUniformBuffer()
+void UniformBufferV2::UnmapCPU()
{
- DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "UnlockUniformBuffer (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
- if(mReadyToBeLocked && mLockedBufferIndex != INVALID_BUFFER_INDEX)
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "CPU buffer is unmapped\n");
+}
+
+void UniformBufferV2::UnmapGPU()
+{
+ auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
+ if(buffer.graphicsMemory)
{
- auto& bufferDesc = mBuffers[mLockedBufferIndex];
- if(bufferDesc.memory)
- {
- bufferDesc.memory->Unlock(true);
- }
- // Flush all allocated buffers
- for(auto i = 0u; i < mBuffers.size(); ++i)
- {
- Flush(i);
- }
+ mController->UnmapMemory(std::move(buffer.graphicsMemory));
+ buffer.graphicsMemory = nullptr;
}
- mLockedPtr = nullptr;
- mLockedBufferIndex = INVALID_BUFFER_INDEX;
- mReadyToBeLocked = false;
+ mMappedPtr = nullptr;
+ DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GPU buffer is unmapped\n");
}
-} // namespace Dali::Internal::Render
\ No newline at end of file
+} // namespace Dali::Internal::Render
#define DALI_INTERNAL_UNIFORM_BUFFER_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.
* From the client side, the UBO memory is continuous and individual
* Graphics::Buffer objects are not visible.
*/
-class UniformBuffer
+class UniformBufferV2
{
- friend class UniformBufferManager;
- friend class UniformBufferView;
- friend class UniformBufferViewPool;
-
-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] usageFlags type of usage ( Graphics::BufferUsage )
- * @param[in] propertiesFlags buffer properties (Gracphis::BufferPropertiesFlags)
- */
- UniformBuffer(Dali::Graphics::Controller* mController,
- uint32_t sizeInBytes,
- uint32_t alignment,
- Graphics::BufferUsageFlags usageFlags,
- Graphics::BufferPropertiesFlags propertiesFlags);
-
public:
/**
- * Destructor of UniformBuffer
+ * factory constructor method.
+ * @param[in] mController Pointer to the graphics controller
+ * @param[in] emulated True if this buffer is for standalone uniforms,
+ * false if for uniform blocks
+ * @param[in] alignment The block alignment to use
*/
- ~UniformBuffer();
+ static Graphics::UniquePtr<UniformBufferV2> New(Dali::Graphics::Controller* mController, bool emulated, uint32_t alignment);
/**
- * @brief Writes data into the buffer
- * @note We prefer to call ReadyToLockUniformBuffer before call Write API.
- * And also, prefer to call UnlockUniformBuffer if current frame's all Write API action done.
- *
- * @param[in] data pointer to the source data
- * @param[in] size size of source data
- * @param[in] offset destination offset
+ * Create the memory backing this buffer and map it.
*/
+ void ReSpecify(uint32_t sizeInBytes);
+
void Write(const void* data, uint32_t size, uint32_t offset);
- /**
- * @brief Flushes whole buffer range
- *
- * @param[in] bufferIndex Index of Graphics::Buffer
- */
- void Flush(uint32_t bufferIndex = 0);
+ void Map();
- /**
- * @brief Returns allocated ( requested ) size
- * @return size of buffer
- */
- [[nodiscard]] uint32_t GetSize() const
- {
- return mSize;
- }
+ void Unmap();
- /**
- * @brief Return Graphics::Buffer object at specified array index
- *
- * @param[in] bufferIndex index of Graphics buffer
- *
- * @return pointer to the buffer object
- */
- [[nodiscard]] Dali::Graphics::Buffer* GetBuffer(uint32_t bufferIndex) const
- {
- return mBuffers[bufferIndex].buffer.get();
- }
+ void Flush(); // We aren't guaranteeing creation with this call.
- /**
- * @brief Maps individual Graphics buffer memory
- *
- * @param[in] bufferIndex index of Graphics buffer
- */
- void Map(uint32_t bufferIndex = 0);
+ void Rollback();
/**
- * Unmaps individual Graphics buffer memory
- *
- * @param[in] bufferIndex index of Graphics buffer
+ * Align size to the current block size
*/
- void Unmap(uint32_t bufferIndex = 0);
+ uint32_t AlignSize(uint32_t size);
- /**
- * @brief Resizes the buffer
- *
- * The resize strategy depends on 'invalidate' parameter.
- *
- * If 'invalidate' is true, all the content if the buffer
- * is discarded, the individual Graphics::Buffers are deleted
- * and a single Graphics::Buffer is allocated.
- *
- * If 'invalidate' is false, additional Graphics::Buffer
- * is created and all recorded content is kept unchanged.
- *
- * @param[in] newSize new size of UniformBuffer
- * @param[in] invalidate specifies whether the content should be discarded
- *
- */
- void Resize(uint32_t newSize, bool invalidate);
+ uint32_t IncrementOffsetBy(uint32_t value);
- /**
- * @copydoc Dali::Internal::Render::UniformBufferViewPool::ReadyToLockUniformBuffer
- */
- void ReadyToLockUniformBuffer();
+ bool MemoryCompare(void* data, uint32_t offset, uint32_t size);
- /**
- * @copydoc Dali::Internal::Render::UniformBufferViewPool::UnlockUniformBuffer
- */
- void UnlockUniformBuffer();
+ [[nodiscard]] uint32_t GetBlockAlignment() const;
+
+ [[nodiscard]] uint32_t GetCurrentOffset() const;
+
+ [[nodiscard]] uint32_t GetCurrentCapacity() const;
+
+ [[nodiscard]] Dali::Graphics::Buffer* GetGraphicsBuffer() const;
private:
- /**
- * @brief GfxBuffer wraps single GPU buffer and encapsulates individual
- * buffer mapping and create info details.
- *
- * The array of GfxBuffers makes a single UniformBuffer.
- */
- struct GfxBuffer
- {
- GfxBuffer() = default;
- ~GfxBuffer() = default;
- GfxBuffer(GfxBuffer&&) = default;
- GfxBuffer(Graphics::UniquePtr<Graphics::Buffer>&& b, const Graphics::BufferCreateInfo& i)
- : buffer(std::move(b)),
- createInfo(i)
- {
- }
-
- Graphics::UniquePtr<Graphics::Buffer> buffer{}; ///< Graphics buffer
- Graphics::UniquePtr<Graphics::Memory> memory{}; ///< Mapped memory associated with buffer
- Graphics::BufferCreateInfo createInfo{}; ///< create info describing the buffer
- void* mappedPtr{}; ///< Mapped pointer (if mapped)
- bool needsUpdate{true}; ///< Indicates whether the buffer needs flushing the queue
- };
+ UniformBufferV2(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment);
- /**
- * @brief Returns GfxBuffer object by offset
- *
- * The memory of UniformBuffer is considered to be continuous, however,
- * it may contain multiple graphics buffers.
- *
- */
- const GfxBuffer* GetBufferByOffset(uint32_t offset, uint32_t* newOffset, uint32_t* bufferIndex) const;
+ void ReSpecifyCPU(uint32_t sizeInBytes);
- std::vector<GfxBuffer> mBuffers; ///< List of GfxBuffer objects
+ void ReSpecifyGPU(uint32_t sizeInBytes);
- Dali::Graphics::Controller* mController; ///< Pointer to the controller
+ void WriteCPU(const void* data, uint32_t size, uint32_t offset);
- uint32_t mSize; ///< Current size of buffer
- uint32_t mAlignment{0}; ///< Buffer alignment
+ void WriteGPU(const void* data, uint32_t size, uint32_t offset);
- Graphics::BufferUsageFlags mUsageFlags;
- Graphics::BufferPropertiesFlags mPropertiesFlags;
+ void MapCPU();
- uint32_t mLockedBufferIndex; ///< Current locked buffer region index.
- uint8_t* mLockedPtr; ///< Current locked buffer pointer.
- bool mReadyToBeLocked : 1; ///< True if current uniform buffer is ready to be locked.
-};
+ void MapGPU();
-} // namespace Dali::Internal::Render
+ void UnmapCPU();
+
+ void UnmapGPU();
+
+private:
+ Graphics::Controller* mController{nullptr};
+
+ uint32_t mBlockAlignment{0u};
+ struct GfxBuffer
+ {
+ Graphics::UniquePtr<Graphics::Buffer> graphicsBuffer;
+ Graphics::UniquePtr<Graphics::Memory> graphicsMemory;
+ uint32_t capacity;
+ uint32_t currentOffset;
+ };
+
+ // List of buffers, in case of CPU one buffer will be sufficient
+ std::vector<GfxBuffer> mBufferList;
+ void* mMappedPtr{nullptr};
+ uint32_t mCurrentGraphicsBufferIndex{0u};
+ bool mEmulated;
+};
+} // namespace Dali::Internal::Render
#endif //DALI_INTERNAL_UNIFORM_BUFFER_H
#include <dali/integration-api/debug.h>
#include <dali/internal/common/shader-data.h>
#include <dali/internal/render/common/performance-monitor.h>
+#include <dali/internal/render/renderers/uniform-buffer-manager.h>
#include <dali/internal/render/shaders/program-cache.h>
#include <dali/public-api/common/constants.h>
#include <dali/public-api/common/dali-common.h>
/**
* Helper function to calculate the correct alignment of data for uniform buffers
* @param dataSize size of uniform buffer
- * @return aligned offset of data
+ * @return size of data aligned to given size
*/
-inline uint32_t GetUniformBufferDataAlignment(uint32_t dataSize)
+inline uint32_t AlignSize(uint32_t dataSize, uint32_t alignSize)
{
- return ((dataSize / 256u) + ((dataSize % 256u) ? 1u : 0u)) * 256u;
+ return ((dataSize / alignSize) + ((dataSize % alignSize) ? 1u : 0u)) * alignSize;
}
} // namespace
{
// program not found so create it
program = new Program(cache, shaderData, gfxController);
+ DALI_LOG_RELEASE_INFO("Program::New() created a unique program\n");
cache.AddProgram(shaderHash, program);
}
Program::~Program() = default;
-void Program::BuildReflection(const Graphics::Reflection& graphicsReflection)
+void Program::BuildReflection(const Graphics::Reflection& graphicsReflection, Render::UniformBufferManager& uniformBufferManager)
{
mReflectionDefaultUniforms.clear();
mReflectionDefaultUniforms.resize(NUMBER_OF_DEFAULT_UNIFORMS);
}
}
- // Calculate size of memory for uniform blocks
- mUniformBlockRequirements.totalSizeRequired = 0;
- mUniformBlockRequirements.blockCount = graphicsReflection.GetUniformBlockCount();
- for(auto i = 0u; i < mUniformBlockRequirements.blockCount; ++i)
+ mUniformBlockMemoryRequirements.blockSize.resize(uniformBlockCount);
+ mUniformBlockMemoryRequirements.blockSizeAligned.resize(uniformBlockCount);
+ mUniformBlockMemoryRequirements.blockCount = uniformBlockCount;
+ mUniformBlockMemoryRequirements.totalSizeRequired = 0u;
+ mUniformBlockMemoryRequirements.totalCpuSizeRequired = 0u;
+ mUniformBlockMemoryRequirements.totalGpuSizeRequired = 0u;
+
+ for(auto i = 0u; i < uniformBlockCount; ++i)
{
- auto blockSize = GetUniformBufferDataAlignment(graphicsReflection.GetUniformBlockSize(i));
- mUniformBlockRequirements.totalSizeRequired += blockSize;
+ Graphics::UniformBlockInfo uboInfo;
+ graphicsReflection.GetUniformBlock(i, uboInfo);
+ bool standaloneUniformBlock = (i == 0);
+
+ auto blockSize = graphicsReflection.GetUniformBlockSize(i);
+ uint32_t blockAlignment = uniformBufferManager.GetUniformBlockAlignment(standaloneUniformBlock);
+ auto alignedBlockSize = AlignSize(blockSize, blockAlignment);
+
+ mUniformBlockMemoryRequirements.blockSize[i] = blockSize;
+ mUniformBlockMemoryRequirements.blockSizeAligned[i] = alignedBlockSize;
+
+ mUniformBlockMemoryRequirements.totalSizeRequired += alignedBlockSize;
+ mUniformBlockMemoryRequirements.totalCpuSizeRequired += (standaloneUniformBlock) ? alignedBlockSize : 0;
+ mUniformBlockMemoryRequirements.totalGpuSizeRequired += (standaloneUniformBlock) ? 0 : alignedBlockSize;
}
}
-void Program::SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program)
+void Program::SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program, Render::UniformBufferManager& uniformBufferManager)
{
mGfxProgram = std::move(program);
- BuildReflection(mGfxController.GetProgramReflection(*mGfxProgram.get()));
+ BuildReflection(mGfxController.GetProgramReflection(*mGfxProgram.get()), uniformBufferManager);
}
bool Program::GetUniform(const std::string_view& name, Hash hashedName, Hash hashedNameNoArray, Graphics::UniformInfo& out) const
#define DALI_INTERNAL_PROGRAM_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.
{
class ProgramCache;
+namespace Render
+{
+class UniformBufferManager;
+}
+
/**
* A program contains a vertex & fragment shader.
* It interfaces to the implementation program and it's reflection.
return mGfxProgram.get();
}
- void SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program);
+ /**
+ * Setup the actual program, and ensure that it's reflection is generated.
+ */
+ void SetGraphicsProgram(Graphics::UniquePtr<Graphics::Program>&& program, Render::UniformBufferManager& uniformBufferManager);
/**
* Retrieves uniform data.
* Build optimized shader reflection of uniforms
* @param graphicsReflection The graphics reflection
*/
- void BuildReflection(const Graphics::Reflection& graphicsReflection);
+ void BuildReflection(const Graphics::Reflection& graphicsReflection, Render::UniformBufferManager& uniformBufferManager);
/**
* Struct UniformBlockMemoryRequirements
*/
struct UniformBlockMemoryRequirements
{
- uint32_t blockCount;
- uint32_t totalSizeRequired;
+ uint32_t blockCount{0u};
+ uint32_t totalSizeRequired{0u};
+ uint32_t totalCpuSizeRequired{0u}; ///< requirements for CPU memory
+ uint32_t totalGpuSizeRequired{0u}; ///< requirements of hardware buffer
+
+ // Per block
+ std::vector<uint32_t> blockSize{};
+ std::vector<uint32_t> blockSizeAligned{};
};
-
/**
* Retrieves uniform blocks requirements
*
*/
[[nodiscard]] const UniformBlockMemoryRequirements& GetUniformBlocksMemoryRequirements() const
{
- return mUniformBlockRequirements;
+ return mUniformBlockMemoryRequirements;
}
private: // Data
using UniformReflectionContainer = std::vector<ReflectionUniformInfo>;
- UniformReflectionContainer mReflection{}; ///< Contains reflection build per program
- UniformReflectionContainer mReflectionDefaultUniforms{}; ///< Contains default uniforms
- UniformBlockMemoryRequirements mUniformBlockRequirements{}; ///< Memory requirements for uniform blocks
+ UniformReflectionContainer mReflection{}; ///< Contains reflection build per program
+ UniformReflectionContainer mReflectionDefaultUniforms{}; ///< Contains default uniforms
+
+ UniformBlockMemoryRequirements mUniformBlockMemoryRequirements; ///< Memory requirements per each block, block 0 = standalone/emulated
};
} // namespace Internal