From 397e6d997ea235ea953e6f1e005d39dcac93eb59 Mon Sep 17 00:00:00 2001 From: David Steele Date: Wed, 21 Jun 2023 13:07:41 +0100 Subject: [PATCH] Refactored Uniform Buffer support 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 --- .../dali-test-suite-utils/test-gl-abstraction.cpp | 47 ++- .../dali-test-suite-utils/test-gl-abstraction.h | 81 +++- .../dali-test-suite-utils/test-graphics-buffer.cpp | 68 +++- .../dali-test-suite-utils/test-graphics-buffer.h | 22 +- .../test-graphics-controller.cpp | 64 ++- .../test-graphics-controller.h | 14 +- .../test-graphics-program.cpp | 6 +- .../dali-test-suite-utils/test-graphics-program.h | 4 +- .../test-graphics-reflection.cpp | 27 +- .../test-graphics-reflection.h | 6 +- .../dali-test-suite-utils/test-graphics-texture.h | 2 +- .../test-trace-call-stack.cpp | 16 +- automated-tests/src/dali/utc-Dali-Geometry.cpp | 9 +- automated-tests/src/dali/utc-Dali-RenderTask.cpp | 13 +- automated-tests/src/dali/utc-Dali-Renderer.cpp | 339 ++++++++++++++++ automated-tests/src/dali/utc-Dali-VertexBuffer.cpp | 25 +- dali/graphics-api/graphics-types.h | 4 +- dali/internal/file.list | 1 - dali/internal/render/common/render-manager.cpp | 95 ++++- dali/internal/render/renderers/render-renderer.cpp | 359 +++++++++-------- dali/internal/render/renderers/render-renderer.h | 48 ++- .../render/renderers/uniform-buffer-manager.cpp | 195 +++++++-- .../render/renderers/uniform-buffer-manager.h | 151 +++++-- .../render/renderers/uniform-buffer-view-pool.cpp | 102 ----- .../render/renderers/uniform-buffer-view-pool.h | 89 ----- .../render/renderers/uniform-buffer-view.cpp | 18 +- .../render/renderers/uniform-buffer-view.h | 22 +- dali/internal/render/renderers/uniform-buffer.cpp | 442 +++++++++++---------- dali/internal/render/renderers/uniform-buffer.h | 189 +++------ dali/internal/render/shaders/program.cpp | 42 +- dali/internal/render/shaders/program.h | 34 +- 31 files changed, 1611 insertions(+), 923 deletions(-) delete mode 100644 dali/internal/render/renderers/uniform-buffer-view-pool.cpp delete mode 100644 dali/internal/render/renderers/uniform-buffer-view-pool.h diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp index 1d20a6a..38169a2 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.cpp @@ -18,8 +18,34 @@ #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 { @@ -145,6 +171,12 @@ void TestGlAbstraction::Initialize() {"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 } @@ -196,6 +228,17 @@ bool TestGlAbstraction::TextureRequiresConverting(const GLenum imageGlFormat, co return ((imageGlFormat == GL_RGB) && (textureGlFormat == GL_RGBA)); } +void TestGlAbstraction::SetActiveUniforms(const std::vector& 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) diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h index cc27d9e..2ca98dc 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-gl-abstraction.h @@ -51,9 +51,10 @@ struct UniformData 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 @@ -457,6 +458,10 @@ public: 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 @@ -697,13 +702,21 @@ public: 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 @@ -800,10 +813,7 @@ public: *type = mAttribTypes[index]; } - inline void SetActiveUniforms(const std::vector& uniforms) - { - mActiveUniforms = uniforms; - } + void SetActiveUniforms(const std::vector& uniforms); inline void GetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) override { @@ -865,6 +875,9 @@ public: case GL_PROGRAM_BINARY_FORMATS_OES: *params = mBinaryFormats; break; + case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: + *params = mUniformBufferOffsetAlignment; + break; } } @@ -1882,6 +1895,44 @@ public: 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 @@ -2169,7 +2220,10 @@ public: // TEST FUNCTIONS { mProgramBinaryLength = length; } - + inline void SetUniformBufferOffsetAlignment(GLint align) + { + mUniformBufferOffsetAlignment = align; + } inline bool GetVertexAttribArrayState(GLuint index) { if(index >= MAX_ATTRIBUTE_CACHE_SIZE) @@ -2582,6 +2636,7 @@ public: 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; diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.cpp index 42b53b1..456d978 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.cpp @@ -22,31 +22,61 @@ 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); + } } } @@ -58,15 +88,19 @@ void TestGraphicsBuffer::Upload(uint32_t offset, uint32_t size) 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(static_cast(offset)), static_cast(static_cast(size)), &memory[offset]); - } - else + + if(!mCpuOnly) { - mGl.BufferData(GetTarget(), static_cast(static_cast(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(static_cast(offset)), static_cast(static_cast(size)), &memory[offset]); + } + else + { + mGl.BufferData(GetTarget(), static_cast(static_cast(size)), &memory[0], GL_STATIC_DRAW); //@todo Query - do we need other usages? + mCreated = true; + } } } diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.h b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.h index 1dc2715..220ae06 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-buffer.h @@ -2,7 +2,7 @@ #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. @@ -17,6 +17,7 @@ * limitations under the License. */ +#include #include #include @@ -26,11 +27,16 @@ 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); @@ -38,17 +44,21 @@ public: bool IsCPUAllocated() const { - return true; + return mCpuOnly; } void BindAsUniformBuffer(const TestGraphicsProgram* program, const Dali::UniformBufferBindingDescriptor& uboBinding) const; - TraceCallStack& mCallStack; - TestGlAbstraction& mGl; - std::vector memory; + TraceCallStack& mCallStack; + TestGraphicsController& mController; + TestGlAbstraction& mGl; + std::vector memory; + + Graphics::BufferCreateInfo mCreateInfo; Graphics::BufferUsageFlags mUsage; GLuint mId{0}; bool mCreated{false}; + bool mCpuOnly{false}; }; } // namespace Dali diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp index 14c6d50..4bac7a6 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.cpp @@ -16,6 +16,7 @@ #include "test-graphics-controller.h" +#include #include "test-graphics-buffer.h" #include "test-graphics-command-buffer.h" #include "test-graphics-framebuffer.h" @@ -28,14 +29,29 @@ #include "test-graphics-texture.h" #include +#include #include #include +#include #include -#include - namespace Dali { +namespace +{ +template +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; @@ -1185,10 +1201,25 @@ bool TestGraphicsController::IsDrawOnResumeRequired() Graphics::UniquePtr TestGraphicsController::CreateBuffer(const Graphics::BufferCreateInfo& createInfo, Graphics::UniquePtr&& oldBuffer) { - std::ostringstream oss; - oss << "bufferCreateInfo:" << createInfo; - mCallStack.PushCall("CreateBuffer", oss.str()); - return Graphics::MakeUnique(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>(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 TestGraphicsController::CreateCommandBuffer(const Graphics::CommandBufferCreateInfo& commandBufferCreateInfo, Graphics::UniquePtr&& oldCommandBuffer) @@ -1258,7 +1289,8 @@ Graphics::UniquePtr TestGraphicsController::CreateProgram(con } 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(shader.shader); @@ -1329,8 +1361,24 @@ Graphics::MemoryRequirements TestGraphicsController::GetTextureMemoryRequirement Graphics::MemoryRequirements TestGraphicsController::GetBufferMemoryRequirements(Graphics::Buffer& buffer) const { + static GLint uniformAlign{0}; + + Graphics::MemoryRequirements reqs{}; mCallStack.PushCall("GetBufferMemoryRequirements", ""); - return Graphics::MemoryRequirements{}; + + auto gfxBuffer = Uncast(&buffer); + if(gfxBuffer->mCreateInfo.usage & (0 | Graphics::BufferUsage::UNIFORM_BUFFER)) + { + if(!uniformAlign) + { + // Throw off the shackles of constness + auto& gl = *const_cast(&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) diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.h b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.h index 81b93d2..ade48d7 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-controller.h @@ -2,7 +2,7 @@ #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. @@ -207,6 +207,8 @@ public: */ Graphics::UniquePtr CreateBuffer(const Graphics::BufferCreateInfo& bufferCreateInfo, Graphics::UniquePtr&& oldBuffer) override; + void DiscardBuffer(TestGraphicsBuffer* buffer); + /** * @brief Creates new CommandBuffer object * @@ -384,6 +386,11 @@ public: // Test Functions mCustomUniforms = customUniforms; } + void AddCustomUniformBlock(const TestGraphicsReflection::TestUniformBlockInfo& blockInfo) + { + mCustomUniformBlocks.push_back(blockInfo); + } + void ClearSubmitStack() { mSubmitStack.clear(); @@ -427,11 +434,14 @@ public: }; std::vector mProgramCache; + std::vector mAllocatedBuffers; + struct PipelineCache { }; - std::vector mCustomUniforms; + std::vector mCustomUniforms; + std::vector mCustomUniformBlocks; }; } // namespace Dali diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.cpp index 8f2e0c7..2706305 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -18,11 +18,11 @@ namespace Dali { -TestGraphicsProgramImpl::TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector& customUniforms) +TestGraphicsProgramImpl::TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector& customUniforms, std::vector& 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); diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.h b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.h index 3899bec..8641800 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-program.h @@ -2,7 +2,7 @@ #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. @@ -27,7 +27,7 @@ namespace Dali class TestGraphicsProgramImpl { public: - TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector& customUniforms); + TestGraphicsProgramImpl(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats, std::vector& customUniforms, std::vector& customUniformBlocks); // For API const TestGraphicsReflection& GetReflection() const diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.cpp index 6cb90ba..2822c0a 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -20,6 +20,13 @@ #include #include #include + +extern "C" +{ + void tet_infoline(const char* str); + void tet_printf(const char* format, ...); +} + namespace Dali { namespace @@ -103,7 +110,7 @@ constexpr int GetSizeForType(Property::Type type) } // namespace -TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t programId, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector& customUniforms) +TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t programId, Property::Array& vfs, const Graphics::ProgramCreateInfo& createInfo, std::vector& customUniforms, std::vector& customUniformBlocks) : mGl(gl), mCustomUniforms(customUniforms) { @@ -226,6 +233,10 @@ TestGraphicsReflection::TestGraphicsReflection(TestGlAbstraction& gl, uint32_t p mDefaultUniformBlock.size = offset; mUniformBlocks.push_back(mDefaultUniformBlock); + for(auto& element : customUniformBlocks) + { + mUniformBlocks.push_back(element); + } } uint32_t TestGraphicsReflection::GetVertexAttributeLocation(const std::string& name) const @@ -247,11 +258,13 @@ uint32_t TestGraphicsReflection::GetVertexAttributeLocation(const std::string& n 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; } @@ -272,7 +285,11 @@ uint32_t TestGraphicsReflection::GetUniformBlockCount() const 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 @@ -316,11 +333,13 @@ bool TestGraphicsReflection::GetUniformBlock(uint32_t index, Dali::Graphics::Uni std::vector TestGraphicsReflection::GetUniformBlockLocations() const { + tet_infoline("Warning, TestGraphicsReflection::GetUniformBlockLocations is unimplemented\n"); return std::vector{}; } std::string TestGraphicsReflection::GetUniformBlockName(uint32_t blockIndex) const { + tet_infoline("Warning, TestGraphicsReflection::GetUniformBlockName is unimplemented\n"); return std::string{}; } @@ -362,11 +381,13 @@ uint32_t TestGraphicsReflection::GetUniformBlockMemberOffset(uint32_t blockIndex 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& TestGraphicsReflection::GetSamplers() const { + tet_infoline("Warning, TestGraphicsReflection::GetSamplers is unimplemented\n"); static std::vector samplers{}; return samplers; } diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.h b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.h index e968bd5..e701e17 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-reflection.h @@ -2,7 +2,7 @@ #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. @@ -26,7 +26,9 @@ namespace Dali class TestGraphicsReflection : public Graphics::Reflection { public: - TestGraphicsReflection(TestGlAbstraction& gl, uint32_t program_id, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector& customUniforms); + class TestUniformBlockInfo; + + TestGraphicsReflection(TestGlAbstraction& gl, uint32_t program_id, Property::Array& vertexFormats, const Graphics::ProgramCreateInfo& createInfo, std::vector& customUniforms, std::vector& customUniformBlocks); uint32_t GetVertexAttributeLocation(const std::string& name) const override; Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeFormat(uint32_t location) const override; diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-texture.h b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-texture.h index 782904c..4d86364 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-graphics-texture.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-graphics-texture.h @@ -31,7 +31,7 @@ class TestGraphicsTexture : public Graphics::Texture public: TestGraphicsTexture(TestGlAbstraction& glAbstraction, const Graphics::TextureCreateInfo& createInfo); - ~TestGraphicsTexture(); + ~TestGraphicsTexture() override; /** * Initialize the texture: allocate gl mem, apply default samplers diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-trace-call-stack.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-trace-call-stack.cpp index 2316036..1284793 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-trace-call-stack.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-trace-call-stack.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -248,11 +248,15 @@ int TraceCallStack::FindIndexFromMethodAndParams(std::string method, const Trace 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; diff --git a/automated-tests/src/dali/utc-Dali-Geometry.cpp b/automated-tests/src/dali/utc-Dali-Geometry.cpp index 2243872..bb1205d 100644 --- a/automated-tests/src/dali/utc-Dali-Geometry.cpp +++ b/automated-tests/src/dali/utc-Dali-Geometry.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -201,8 +201,7 @@ int UtcDaliGeometryAddVertexBuffer(void) { 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); } @@ -322,7 +321,7 @@ int UtcDaliGeometrySetIndexBuffer(void) 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); } @@ -381,7 +380,7 @@ int UtcDaliGeometrySetIndexBuffer32Bits(void) 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); } diff --git a/automated-tests/src/dali/utc-Dali-RenderTask.cpp b/automated-tests/src/dali/utc-Dali-RenderTask.cpp index 11219df..a74afa9 100644 --- a/automated-tests/src/dali/utc-Dali-RenderTask.cpp +++ b/automated-tests/src/dali/utc-Dali-RenderTask.cpp @@ -78,7 +78,7 @@ void utc_dali_render_task_cleanup(void) * FinishedSignal 1+ve */ -namespace // unnamed namespace +namespace // unnamed namespace { const int RENDER_FRAME_INTERVAL = 16; ///< Duration of each frame in ms. (at approx 60FPS) @@ -4103,12 +4103,12 @@ int UtcDaliRenderTaskRenderPassTag(void) 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; @@ -4122,6 +4122,7 @@ int UtcDaliRenderTaskRenderPassTag(void) 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); @@ -4197,4 +4198,4 @@ int UtcDaliRenderTaskWithWrongShaderData(void) DALI_TEST_EQUALS(0u, renderTaskList.GetTask(0u).GetRenderPassTag(), TEST_LOCATION); END_TEST; -} \ No newline at end of file +} diff --git a/automated-tests/src/dali/utc-Dali-Renderer.cpp b/automated-tests/src/dali/utc-Dali-Renderer.cpp index 9038a62..9a2d967 100644 --- a/automated-tests/src/dali/utc-Dali-Renderer.cpp +++ b/automated-tests/src/dali/utc-Dali-Renderer.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include "test-actor-utils.h" #include "test-graphics-command-buffer.h" using namespace Dali; @@ -4596,3 +4598,340 @@ int UtcDaliRendererVertexRange(void) 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(&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(&bufferPtr->memory[0] + sizeof(Dali::Matrix) * 299); + DALI_TEST_EQUALS(*mPtr, n, 0.0001, TEST_LOCATION); + + float* wPtr1 = reinterpret_cast(&bufferPtr->memory[MORPH_BLOCK_OFFSET] + sizeof(float) * 1); + float* wPtr2 = reinterpret_cast(&bufferPtr->memory[MORPH_BLOCK_OFFSET] + sizeof(float) * 56); + float* wPtr3 = reinterpret_cast(&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; +} diff --git a/automated-tests/src/dali/utc-Dali-VertexBuffer.cpp b/automated-tests/src/dali/utc-Dali-VertexBuffer.cpp index 605cbd5..d407b36 100644 --- a/automated-tests/src/dali/utc-Dali-VertexBuffer.cpp +++ b/automated-tests/src/dali/utc-Dali-VertexBuffer.cpp @@ -276,7 +276,7 @@ int UtcDaliVertexBufferSetData01(void) 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); } @@ -325,19 +325,16 @@ int UtcDaliVertexBufferSetData02(void) 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); @@ -345,6 +342,10 @@ int UtcDaliVertexBufferSetData02(void) 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; } diff --git a/dali/graphics-api/graphics-types.h b/dali/graphics-api/graphics-types.h index 45b8e6f..e271bc2 100644 --- a/dali/graphics-api/graphics-types.h +++ b/dali/graphics-api/graphics-types.h @@ -920,8 +920,8 @@ inline BufferPropertiesFlags operator|(BufferPropertiesFlags flags, BufferProper */ struct MemoryRequirements { - size_t size; - size_t alignment; + size_t size{0u}; + size_t alignment{1u}; }; using TextureUpdateFlags = uint32_t; diff --git a/dali/internal/file.list b/dali/internal/file.list index efa4c8b..ece9186 100644 --- a/dali/internal/file.list +++ b/dali/internal/file.list @@ -126,7 +126,6 @@ SET( internal_src_files ${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 diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp index 8955f07..fdb9a3b 100644 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include #include @@ -181,7 +181,7 @@ struct RenderManager::Impl Vector 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}; @@ -328,10 +328,12 @@ void RenderManager::InitializeScene(SceneGraph::Scene* scene) { 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()) { @@ -437,9 +439,7 @@ void RenderManager::RemoveRenderTracker(Render::RenderTracker* renderTracker) 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; @@ -447,13 +447,13 @@ void RenderManager::PreRender(Integration::RenderStatus& status, bool forceClear // 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"); @@ -761,7 +761,7 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration:: return; } - uint32_t count = sceneObject->GetRenderInstructions().Count(mImpl->renderBufferIndex); + uint32_t instructionCount = sceneObject->GetRenderInstructions().Count(mImpl->renderBufferIndex); std::vector targetstoPresent; @@ -774,10 +774,77 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration:: clippingRect = Rect(); } - // 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); @@ -994,8 +1061,8 @@ void RenderManager::RenderScene(Integration::RenderStatus& status, Integration:: 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; diff --git a/dali/internal/render/renderers/render-renderer.cpp b/dali/internal/render/renderers/render-renderer.cpp index 56dbcc6..3c8e4af 100644 --- a/dali/internal/render/renderers/render-renderer.cpp +++ b/dali/internal/render/renderers/render-renderer.cpp @@ -33,8 +33,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -42,6 +42,9 @@ namespace Dali::Internal { +Dali::Matrix* testMVP; +uint32_t mvpBufferIndex; + namespace { // Helper to get the property value getter by type @@ -464,6 +467,73 @@ void Renderer::Upload() 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(&shader)); + mCurrentProgram = nullptr; + return nullptr; + } + + // If program doesn't have Gfx program object assigned yet, prepare it. + if(!program->GetGraphicsProgramPtr()) + { + const std::vector& vertShader = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::VERTEX_SHADER); + const std::vector& 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 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, @@ -554,87 +624,46 @@ bool Renderer::Render(Graphics::CommandBuffer& comma 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(&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& vertShader = shaderData->GetShaderForPipelineStage(Graphics::PipelineStage::VERTEX_SHADER); - const std::vector& 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 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; @@ -756,45 +785,51 @@ void Renderer::WriteUniformBuffer( 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 uboView{nullptr}; - if(uniformBlockAllocationBytes) + std::vector> 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* 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); @@ -803,7 +838,7 @@ void Renderer::WriteUniformBuffer( Matrix3 normalMatrix(modelViewMatrix); normalMatrix.Invert(); normalMatrix.Transpose(); - WriteDefaultUniform(normalUniformInfo, *uboView, normalMatrix); + WriteDefaultUniformV2(normalUniformInfo, uboViews, normalMatrix); } Vector4 finalColor; ///< Applied renderer's opacity color @@ -817,16 +852,17 @@ void Renderer::WriteUniformBuffer( { 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); } } @@ -842,6 +878,17 @@ bool Renderer::WriteDefaultUniform(const Graphics::UniformInfo* uniformInfo, Ren } template +bool Renderer::WriteDefaultUniformV2(const Graphics::UniformInfo* uniformInfo, const std::vector>& uboViews, const T& data) +{ + if(uniformInfo && !uniformInfo->name.empty()) + { + WriteUniform(*uboViews[uniformInfo->bufferIndex], *uniformInfo, data); + return true; + } + return false; +} + +template void Renderer::WriteUniform(Render::UniformBufferView& ubo, const Graphics::UniformInfo& uniformInfo, const T& data) { WriteUniform(ubo, uniformInfo, &data, sizeof(T)); @@ -852,84 +899,68 @@ void Renderer::WriteUniform(Render::UniformBufferView& ubo, const Graphics::Unif ubo.Write(data, size, ubo.GetOffset() + uniformInfo.offset); } -void Renderer::FillUniformBuffer(Program& program, - const SceneGraph::RenderInstruction& instruction, - Render::UniformBufferView& ubo, - std::vector*& outBindings, - uint32_t& offset, - BufferIndex updateBufferIndex, - std::size_t nodeIndex) +void Renderer::FillUniformBuffer(Program& program, + const SceneGraph::RenderInstruction& instruction, + const std::vector>& 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(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(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(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(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; } diff --git a/dali/internal/render/renderers/render-renderer.h b/dali/internal/render/renderers/render-renderer.h index 1f4aa6e..5af3bb7 100644 --- a/dali/internal/render/renderers/render-renderer.h +++ b/dali/internal/render/renderers/render-renderer.h @@ -60,6 +60,7 @@ struct ShaderCache; class PipelineCache; class PipelineCacheL2; class UniformBufferManager; +class UniformBufferV2; class Renderer; using PipelineCacheL2Container = std::list; @@ -423,6 +424,20 @@ public: 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 @@ -477,14 +492,19 @@ public: const T& data); template + bool WriteDefaultUniformV2(const Graphics::UniformInfo* uniformInfo, + const std::vector>& uboViews, + const T& data); + + template 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 { @@ -585,18 +605,15 @@ private: * @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*& outBindings, - uint32_t& offset, - BufferIndex updateBufferIndex, - std::size_t nodeIndex); + void FillUniformBuffer(Program& program, + const SceneGraph::RenderInstruction& instruction, + const std::vector>& uboViews, + BufferIndex updateBufferIndex, + std::size_t nodeIndex); private: Graphics::Controller* mGraphicsController; @@ -628,6 +645,7 @@ private: int16_t uniformLocation{0u}; uint16_t uniformOffset{0u}; uint16_t uniformSize{0u}; + uint16_t uniformBlockIndex{0u}; FuncGetter uniformFunc{0}; }; @@ -662,6 +680,8 @@ private: RenderCallback* mRenderCallback{nullptr}; std::unique_ptr mRenderCallbackInput{nullptr}; std::vector mRenderCallbackTextureBindings{}; + + Program* mCurrentProgram{nullptr}; ///< Prefetched program }; } // namespace Render diff --git a/dali/internal/render/renderers/uniform-buffer-manager.cpp b/dali/internal/render/renderers/uniform-buffer-manager.cpp index 5e04f82..df8ee47 100644 --- a/dali/internal/render/renderers/uniform-buffer-manager.cpp +++ b/dali/internal/render/renderers/uniform-buffer-manager.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -19,16 +19,22 @@ #include // INTERNAL INCLUDES -#include #include #include +#include + #include #include #include #include +namespace +{ +uint32_t CPU_MEMORY_ALIGNMENT{256}; +} + namespace Dali::Internal::Render { UniformBufferManager::UniformBufferManager(Dali::Graphics::Controller* controller) @@ -38,47 +44,188 @@ UniformBufferManager::UniformBufferManager(Dali::Graphics::Controller* controlle UniformBufferManager::~UniformBufferManager() = default; -Graphics::UniquePtr 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( - 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 UniformBufferManager::CreateUniformBufferView(UniformBuffer* uniformBuffer, uint32_t offset, uint32_t size) +Graphics::UniquePtr UniformBufferManager::CreateUniformBufferView(uint32_t size, bool emulated) { // Allocate offset of given UBO (allocation strategy may reuse memory) - return Graphics::UniquePtr(new UniformBufferView(*uniformBuffer, offset, size)); + + DALI_ASSERT_DEBUG(mCurrentUBOSet && "UBO set should exist when creating view"); + if(!mCurrentUBOSet) + { + return Graphics::UniquePtr(nullptr); + } + + UBOSet::BufferType bufferType = UBOSet::GetBufferType(mCurrentSceneOffscreen, emulated); + Graphics::UniquePtr& ubo = mCurrentUBOSet->GetBuffer(bufferType); + + // Use current offset and increment it after + auto offset = ubo->GetCurrentOffset(); + auto retval = Graphics::UniquePtr(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 UniformBufferManager::CreateUniformBufferViewPool() +uint32_t UniformBufferManager::GetUniformBlockAlignment(bool emulated) { - return Graphics::UniquePtr( - 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 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 diff --git a/dali/internal/render/renderers/uniform-buffer-manager.h b/dali/internal/render/renderers/uniform-buffer-manager.h index 4c651d6..fce57f6 100644 --- a/dali/internal/render/renderers/uniform-buffer-manager.h +++ b/dali/internal/render/renderers/uniform-buffer-manager.h @@ -2,7 +2,7 @@ #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. @@ -19,13 +19,18 @@ */ // INTERNAL INCLUDES +#include #include +namespace Dali::Internal::SceneGraph +{ +class Scene; +} + namespace Dali::Internal::Render { -class UniformBuffer; +class UniformBufferV2; class UniformBufferView; -class UniformBufferViewPool; /** * Class UniformBufferManager @@ -40,66 +45,136 @@ public: ~UniformBufferManager(); + Graphics::UniquePtr 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 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 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 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 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 cpuBufferOnScreen; + Graphics::UniquePtr gpuBufferOnScreen; + Graphics::UniquePtr cpuBufferOffScreen; + Graphics::UniquePtr gpuBufferOffScreen; + + Graphics::UniquePtr& 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 mUBOMap; + SceneGraph::Scene* mCurrentScene{nullptr}; + UBOSet* mCurrentUBOSet{nullptr}; + uint32_t mCachedUniformBlockAlignment{0u}; + bool mCurrentSceneOffscreen{false}; }; } // namespace Dali::Internal::Render diff --git a/dali/internal/render/renderers/uniform-buffer-view-pool.cpp b/dali/internal/render/renderers/uniform-buffer-view-pool.cpp deleted file mode 100644 index 6cc5150..0000000 --- a/dali/internal/render/renderers/uniform-buffer-view-pool.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 - -// INTERNAL INCLUDES -#include -#include -#include -#include -#include - -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 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 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 diff --git a/dali/internal/render/renderers/uniform-buffer-view-pool.h b/dali/internal/render/renderers/uniform-buffer-view-pool.h deleted file mode 100644 index 8f7a1bd..0000000 --- a/dali/internal/render/renderers/uniform-buffer-view-pool.h +++ /dev/null @@ -1,89 +0,0 @@ -#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 - -// EXTERNAL INCLUDES -#include -#include - -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 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 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 diff --git a/dali/internal/render/renderers/uniform-buffer-view.cpp b/dali/internal/render/renderers/uniform-buffer-view.cpp index b8c00d0..1b1bf18 100644 --- a/dali/internal/render/renderers/uniform-buffer-view.cpp +++ b/dali/internal/render/renderers/uniform-buffer-view.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -23,10 +23,10 @@ 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) { } @@ -35,12 +35,12 @@ UniformBufferView::~UniformBufferView() = default; 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 diff --git a/dali/internal/render/renderers/uniform-buffer-view.h b/dali/internal/render/renderers/uniform-buffer-view.h index dfe8c23..028e546 100644 --- a/dali/internal/render/renderers/uniform-buffer-view.h +++ b/dali/internal/render/renderers/uniform-buffer-view.h @@ -2,7 +2,7 @@ #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. @@ -21,6 +21,7 @@ // EXTERNAL INCLUDES #include #include +#include namespace Dali { @@ -30,7 +31,7 @@ class Buffer; } namespace Internal::Render { -class UniformBuffer; +class UniformBufferV2; /** * Class UniformBufferView @@ -47,7 +48,7 @@ class UniformBuffer; class UniformBufferView { public: - UniformBufferView(UniformBuffer& ubo, uint32_t offset, size_t size); + UniformBufferView(UniformBufferV2& ubo, uint32_t offset, size_t size); ~UniformBufferView(); @@ -84,20 +85,15 @@ public: /** * @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 diff --git a/dali/internal/render/renderers/uniform-buffer.cpp b/dali/internal/render/renderers/uniform-buffer.cpp index 5787c68..a6c9502 100644 --- a/dali/internal/render/renderers/uniform-buffer.cpp +++ b/dali/internal/render/renderers/uniform-buffer.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -27,313 +27,335 @@ Debug::Filter* gUniformBufferLogFilter = Debug::Filter::New(Debug::NoLogging, fa 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::New(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment) { -static constexpr uint32_t INVALID_BUFFER_INDEX = std::numeric_limits::max(); + return Graphics::UniquePtr(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(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 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 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(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(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(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 diff --git a/dali/internal/render/renderers/uniform-buffer.h b/dali/internal/render/renderers/uniform-buffer.h index 4f83bbd..0ff24b9 100644 --- a/dali/internal/render/renderers/uniform-buffer.h +++ b/dali/internal/render/renderers/uniform-buffer.h @@ -2,7 +2,7 @@ #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. @@ -36,164 +36,87 @@ namespace Dali::Internal::Render * 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 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&& b, const Graphics::BufferCreateInfo& i) - : buffer(std::move(b)), - createInfo(i) - { - } - - Graphics::UniquePtr buffer{}; ///< Graphics buffer - Graphics::UniquePtr 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 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 graphicsBuffer; + Graphics::UniquePtr graphicsMemory; + uint32_t capacity; + uint32_t currentOffset; + }; + + // List of buffers, in case of CPU one buffer will be sufficient + std::vector mBufferList; + void* mMappedPtr{nullptr}; + uint32_t mCurrentGraphicsBufferIndex{0u}; + bool mEmulated; +}; +} // namespace Dali::Internal::Render #endif //DALI_INTERNAL_UNIFORM_BUFFER_H diff --git a/dali/internal/render/shaders/program.cpp b/dali/internal/render/shaders/program.cpp index f6d9f97..1acafae 100644 --- a/dali/internal/render/shaders/program.cpp +++ b/dali/internal/render/shaders/program.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -62,11 +63,11 @@ size_t DEFAULT_UNIFORM_HASHTABLE[NUMBER_OF_DEFAULT_UNIFORMS] = /** * 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 @@ -83,6 +84,7 @@ Program* Program::New(ProgramCache& cache, Internal::ShaderDataPtr shaderData, G { // 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); } @@ -101,7 +103,7 @@ Program::Program(ProgramCache& cache, Internal::ShaderDataPtr shaderData, Graphi 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); @@ -174,20 +176,36 @@ void Program::BuildReflection(const Graphics::Reflection& graphicsReflection) } } - // 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&& program) +void Program::SetGraphicsProgram(Graphics::UniquePtr&& 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 diff --git a/dali/internal/render/shaders/program.h b/dali/internal/render/shaders/program.h index 09c5ff8..5e46c39 100644 --- a/dali/internal/render/shaders/program.h +++ b/dali/internal/render/shaders/program.h @@ -2,7 +2,7 @@ #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. @@ -43,6 +43,11 @@ namespace Internal { class ProgramCache; +namespace Render +{ +class UniformBufferManager; +} + /** * A program contains a vertex & fragment shader. * It interfaces to the implementation program and it's reflection. @@ -127,7 +132,10 @@ public: return mGfxProgram.get(); } - void SetGraphicsProgram(Graphics::UniquePtr&& program); + /** + * Setup the actual program, and ensure that it's reflection is generated. + */ + void SetGraphicsProgram(Graphics::UniquePtr&& program, Render::UniformBufferManager& uniformBufferManager); /** * Retrieves uniform data. @@ -185,7 +193,7 @@ public: * 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 @@ -193,10 +201,15 @@ public: */ 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 blockSize{}; + std::vector blockSizeAligned{}; }; - /** * Retrieves uniform blocks requirements * @@ -204,7 +217,7 @@ public: */ [[nodiscard]] const UniformBlockMemoryRequirements& GetUniformBlocksMemoryRequirements() const { - return mUniformBlockRequirements; + return mUniformBlockMemoryRequirements; } private: // Data @@ -221,9 +234,10 @@ private: // Data using UniformReflectionContainer = std::vector; - 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 -- 2.7.4