When any Controller::Create...(...) function is being called, the last arguments takes a pointer to the old object. Originally, the implementation was supposed to use that object in order to recycle it (when possible). That code has never been implemented. This patch adds TryRecycle() method to the Graphics::Resource<> class and calls TryRecycle() if a valid pointer is being passed to the NewObject(). If TryRecycle() returns false (object couldn't be recycled) then original path is taken and new object is created (old is discarded). Otherwise, the object implementation should handle recycling.
The GLES graphics buffer is recycled when:
- The new spec (create info) matches old spec
- There is valid GPU/CPU storage already allocated
For a GPU-allocated buffer, the orphaning takes place. For CPU-allocated buffer, nothing changes.
Change-Id: I5969161c1004b2f0728b5dc9323e6caa88fab84b
SET(TC_SOURCES
utc-Dali-GlImplementation.cpp
utc-Dali-GlesImplementation.cpp
+ utc-Dali-GraphicsBuffer.cpp
utc-Dali-GraphicsFramebuffer.cpp
utc-Dali-GraphicsGeometry.cpp
utc-Dali-GraphicsNativeImage.cpp
--- /dev/null
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali-test-suite-utils.h>
+#include <dali/dali.h>
+
+#include <dali/internal/graphics/gles-impl/egl-graphics-controller.h>
+#include <test-actor-utils.h>
+#include <test-graphics-application.h>
+#include <test-graphics-framebuffer.h>
+
+using namespace Dali;
+
+namespace
+{
+} // namespace
+
+int UtcDaliBufferReuseTest(void)
+{
+ TestGraphicsApplication app;
+ tet_infoline("UtcDaliBufferReuseTest: Tests whether GLES buffer can be reused (orphaning content)");
+
+ auto& controller = app.GetGraphicsController();
+
+ Graphics::BufferCreateInfo info;
+ info.size = 1024;
+ info.usage = 0u | Graphics::BufferUsage::VERTEX_BUFFER;
+ info.propertiesFlags = 0u;
+
+ auto buffer = controller.CreateBuffer(info, nullptr);
+ controller.WaitIdle();
+
+ DALI_TEST_NOT_EQUALS((void*)buffer.get(), (void*)nullptr, 0, TEST_LOCATION);
+
+ // New buffer with different spec, should create new object
+ Graphics::BufferCreateInfo info2;
+ info.size = 2024;
+ info.usage = 0u | Graphics::BufferUsage::VERTEX_BUFFER;
+ info.propertiesFlags = 0u;
+
+ auto buffer2 = controller.CreateBuffer(info2, std::move(buffer));
+ controller.WaitIdle();
+
+ DALI_TEST_NOT_EQUALS(buffer.get(), buffer2.get(), 0, TEST_LOCATION);
+
+ auto ptr = buffer2.get(); // store pointer for testing, the uptr will be emptied
+ // Create new buffer using the same spec
+ auto buffer3 = controller.CreateBuffer(info2, std::move(buffer2));
+ controller.WaitIdle();
+
+ DALI_TEST_EQUALS(ptr, buffer3.get(), TEST_LOCATION);
+
+ END_TEST;
+}
}
else // Use standard allocator
{
+ // We are given all object for recycling
+ if(oldObject)
+ {
+ auto reusedObject = oldObject.release();
+ // If succeeded, attach the object to the unique_ptr and return it back
+ if(static_cast<GLESType*>(reusedObject)->TryRecycle(info, controller))
+ {
+ return UPtr(reusedObject, GLESDeleter<GLESType>());
+ }
+ else
+ {
+ // can't reuse so kill object by giving it back to original
+ // unique pointer.
+ oldObject.reset(reusedObject);
+ }
+ }
+
+ // Create brand new object
return UPtr(new GLESType(info, controller), GLESDeleter<GLESType>());
}
}
}
}
+void EglGraphicsController::WaitIdle()
+{
+ Flush();
+}
+
void EglGraphicsController::PresentRenderTarget(RenderTarget* renderTarget)
{
GLES::CommandBuffer* presentCommandBuffer{nullptr};
#define DALI_EGL_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.
/**
* @copydoc Dali::Graphics::WaitIdle()
*/
- void WaitIdle() override
- {
- }
+ void WaitIdle() override;
/**
* @copydoc Dali::Graphics::Pause()
/*
- * 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.
controller.AddBuffer(*this);
}
+bool Buffer::TryRecycle(const Graphics::BufferCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
+{
+ // Compare whether specs are same and the buffer is allocated
+ mSetForGLRecycling = false;
+
+ // if different buffer spec, we need new buffer
+ if(!(createInfo.size == mCreateInfo.size
+ && createInfo.allocationCallbacks == mCreateInfo.allocationCallbacks
+ && createInfo.propertiesFlags == mCreateInfo.propertiesFlags
+ && createInfo.usage == mCreateInfo.usage
+ && createInfo.nextExtension == mCreateInfo.nextExtension ))
+ {
+ return false;
+ }
+
+ // GL resource hasn't been allocated yet, we need new buffer
+ if(mBufferId == 0)
+ {
+ return false;
+ }
+
+ // Make sure the buffer will be reinitialized
+ controller.AddBuffer(*this);
+
+ mSetForGLRecycling = true;
+
+ return true;
+}
+
bool Buffer::InitializeResource()
{
// CPU allocated uniform buffer is a special "compatibility" mode
InitializeGPUBuffer();
}
+ // make sure recycling mode is disabled after (re)initializing resource
+ mSetForGLRecycling = false;
return true;
}
// Just allocate memory
// @TODO put better CPU memory management in place
const auto allocators = GetCreateInfo().allocationCallbacks;
+
+ // Early out if we recycle the buffer
+ if(mBufferPtr && mSetForGLRecycling)
+ {
+ return;
+ }
+
if(allocators)
{
mBufferPtr = allocators->allocCallback(mCreateInfo.size, 0, allocators->userData);
return;
}
- gl->GenBuffers(1, &mBufferId);
+ // If mBufferId is already set and we recycling the buffer (orphaning)
+ if(!mSetForGLRecycling && !mBufferId)
+ {
+ gl->GenBuffers(1, &mBufferId);
+ }
context->BindBuffer(GL_ARRAY_BUFFER, mBufferId);
gl->BufferData(GL_ARRAY_BUFFER, GLsizeiptr(mCreateInfo.size), nullptr, GL_STATIC_DRAW);
}
#define DALI_GRAPHICS_GLES_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.
void Bind(Graphics::BufferUsage bindingTarget) const;
+ bool TryRecycle(const Graphics::BufferCreateInfo& createInfo, Graphics::EglGraphicsController& controller) override;
+
[[nodiscard]] uint32_t GetGLBuffer() const
{
return mBufferId;
void* mBufferPtr{nullptr}; // CPU allocated memory
bool mCpuAllocated{false};
bool mTransient{false};
+
+ bool mSetForGLRecycling{false}; ///< If flag set true the buffer will recycle
};
} // namespace GLES
} // namespace Dali::Graphics
#define DALI_GRAPHICS_GLES_RESOURCE_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.
}
/**
+ * @brief Tries to recycle Graphics resource
+ *
+ * If False returned, the object must be initialized with use of constructor
+ *
+ * By default, all graphics resources are non-recyclable
+ *
+ * @param[in] createInfo CreateInfo structure of new object
+ * @param[in] controller Reference to the controller
+ * @return True on success, False otherwise
+ */
+ virtual bool TryRecycle(const CreateInfo& createInfo, Graphics::EglGraphicsController& controller)
+ {
+ return false;
+ }
+
+ /**
* @brief Destructor
*/
~Resource() override = default;