return NewObject<GLES::Buffer>(bufferCreateInfo, *this, std::move(oldBuffer));
}
-Graphics::UniquePtr<Pipeline> EglGraphicsController::CreatePipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Pipeline>&& oldPipeline)
+Graphics::UniquePtr<Pipeline> EglGraphicsController::CreatePipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
{
- return NewObject<GLES::Pipeline>(pipelineCreateInfo, *this, std::move(oldPipeline));
+ // Create pipeline cache if needed
+ if(!mPipelineCache)
+ {
+ mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
+ }
+
+ return mPipelineCache->GetPipeline(pipelineCreateInfo, std::move(oldPipeline));
}
void EglGraphicsController::AddTexture(GLES::Texture& texture)
#include "gles-context.h"
#include "gles-graphics-buffer.h"
#include "gles-graphics-memory.h"
+#include "gles-graphics-pipeline-cache.h"
#include "gles-graphics-texture.h"
namespace Dali
namespace GLES
{
class CommandBuffer;
-}
+class PipelineCache;
+} // namespace GLES
/**
* EGL Implementation of the graphics controller.
std::queue<TextureUpdateRequest> mTextureUpdateRequests;
std::unique_ptr<GLES::Context> mContext{nullptr}; ///< Context object handling command buffers execution
+
+ std::unique_ptr<GLES::PipelineCache> mPipelineCache{nullptr}; ///< Internal pipeline cache
};
} // namespace Graphics
${adaptor_graphics_dir}/gles-impl/gles-graphics-sampler.cpp
${adaptor_graphics_dir}/gles-impl/gles-graphics-shader.cpp
${adaptor_graphics_dir}/gles-impl/gles-graphics-texture.cpp
+ ${adaptor_graphics_dir}/gles-impl/gles-graphics-pipeline-cache.cpp
${adaptor_graphics_dir}/gles-impl/gles-context.cpp
)
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "gles-graphics-pipeline-cache.h"
+
+// INTERNAL INCLUDES
+#include "gles-graphics-pipeline.h"
+
+namespace Dali::Graphics::GLES
+{
+/**
+ * @brief Implementation of pipeline cache
+ */
+struct PipelineCache::Impl
+{
+ explicit Impl(EglGraphicsController& _controller)
+ : controller(_controller)
+ {
+ }
+
+ ~Impl() = default;
+
+ EglGraphicsController& controller;
+
+ std::vector<UniquePtr<PipelineImpl>> mPipelines;
+};
+
+PipelineCache::PipelineCache(EglGraphicsController& controller)
+{
+ mImpl = std::make_unique<Impl>(controller);
+}
+
+PipelineCache::~PipelineCache() = default;
+
+PipelineImpl* PipelineCache::FindPipelineImpl(const PipelineCreateInfo& info)
+{
+ // hashing by shaders and then states, shaders should use the same pointers
+ for(auto& pipeline : mImpl->mPipelines)
+ {
+ auto& cacheInfo = pipeline->GetCreateInfo();
+
+ if(!info.shaderState)
+ {
+ continue;
+ }
+
+ // TODO: Hash the structures. In GLES the order should not matter
+ // (it matters now to keep it simple)
+ if(info.shaderState->size() == cacheInfo.shaderState->size())
+ {
+ if(memcmp(info.shaderState->data(), cacheInfo.shaderState->data(), sizeof(info.shaderState->size() * sizeof(ShaderState))) != 0)
+ {
+ continue; // early exit
+ }
+
+ // test null states
+ // TODO: create and store such bitmask when pipeline
+ // is being constructed!
+ uint32_t infoBits =
+ (info.inputAssemblyState ? 1 << 0 : 0) |
+ (info.vertexInputState ? 1 << 1 : 0) |
+ (info.viewportState ? 1 << 2 : 0) |
+ (info.depthStencilState ? 1 << 3 : 0) |
+ (info.colorBlendState ? 1 << 4 : 0) |
+ (info.framebufferState ? 1 << 5 : 0) |
+ (info.rasterizationState ? 1 << 6 : 0) |
+ (info.basePipeline ? 1 << 7 : 0);
+
+ uint32_t cacheBits =
+ (cacheInfo.inputAssemblyState ? 1 << 0 : 0) |
+ (cacheInfo.vertexInputState ? 1 << 1 : 0) |
+ (cacheInfo.viewportState ? 1 << 2 : 0) |
+ (cacheInfo.depthStencilState ? 1 << 3 : 0) |
+ (cacheInfo.colorBlendState ? 1 << 4 : 0) |
+ (cacheInfo.framebufferState ? 1 << 5 : 0) |
+ (cacheInfo.rasterizationState ? 1 << 6 : 0) |
+ (cacheInfo.basePipeline ? 1 << 7 : 0);
+
+ if(cacheBits != infoBits)
+ {
+ continue; // early exit
+ }
+
+ // Now compare states
+ // TODO: hash the binary content on pipeline creation
+ // TODO: optimize by adding a lookup table storing size
+ // of each field and generalizing the type (void*)
+
+ auto updateResult = [](const auto& lhs, const auto& rhs) {
+ if(!rhs) return 0;
+ return memcmp(lhs, rhs, sizeof(decltype(*rhs)));
+ };
+
+ auto result = 0u;
+ result |= updateResult(info.vertexInputState, cacheInfo.vertexInputState);
+ result |= updateResult(info.inputAssemblyState, cacheInfo.inputAssemblyState);
+ result |= updateResult(info.rasterizationState, cacheInfo.rasterizationState);
+ result |= updateResult(info.framebufferState, cacheInfo.framebufferState);
+ result |= updateResult(info.colorBlendState, cacheInfo.colorBlendState);
+ result |= updateResult(info.depthStencilState, cacheInfo.depthStencilState);
+ result |= updateResult(info.viewportState, cacheInfo.viewportState);
+
+ // early exit
+ if(!result)
+ {
+ continue;
+ }
+
+ // For now ignoring dynamic state mask and allocator
+
+ // Getting as far as here, we have found our pipeline impl
+ return pipeline.get();
+ }
+ }
+ return nullptr;
+}
+
+Graphics::UniquePtr<Graphics::Pipeline> PipelineCache::GetPipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
+{
+ // create or get from cache
+ auto cachedPipeline = FindPipelineImpl(pipelineCreateInfo);
+
+ if(!cachedPipeline)
+ {
+ // create new pipeline
+ auto pipeline = MakeUnique<GLES::PipelineImpl>(pipelineCreateInfo, mImpl->controller);
+
+ cachedPipeline = pipeline.get();
+
+ // add it to cache
+ mImpl->mPipelines.emplace_back(std::move(pipeline));
+ }
+ else
+ {
+ }
+ // create new pipeline wrapper n
+ auto wrapper = MakeUnique<GLES::Pipeline>(*cachedPipeline);
+ return std::move(wrapper);
+}
+
+} // namespace Dali::Graphics::GLES
\ No newline at end of file
--- /dev/null
+#ifndef DALI_GRAPHICS_GLES_PIPELINE_CACHE_H
+#define DALI_GRAPHICS_GLES_PIPELINE_CACHE_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/graphics-api/graphics-pipeline-create-info.h>
+#include <dali/graphics-api/graphics-pipeline.h>
+
+// INTERNAL INCLUDES
+#include "gles-graphics-resource.h"
+
+namespace Dali::Graphics
+{
+class EglGraphicsController;
+namespace GLES
+{
+class Pipeline;
+class PipelineImpl;
+
+/**
+ * @brief PipelineCache manages pipeline and program
+ * objects so there are no duplicates created.
+ */
+class PipelineCache
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ explicit PipelineCache(EglGraphicsController& controller);
+
+ /**
+ * @brief Destructor
+ */
+ ~PipelineCache();
+
+ /**
+ * @brief Retrieves pipeline matching the spec
+ *
+ * Function returns either existing pipeline if one is found
+ * in the cache or creates new one.
+ *
+ * @param[in] pipelineCreateInfo Valid PipelineCreateInfo structure
+ * @param[in] oldPipeline previous pipeline object
+ * @return Pipeline object
+ */
+ Graphics::UniquePtr<Graphics::Pipeline> GetPipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline);
+
+private:
+ /**
+ * @brief Finds pipeline implementation based on the spec
+ * @param[in] info Valid create info structure
+ * @return Returns pointer to pipeline or nullptr
+ */
+ PipelineImpl* FindPipelineImpl(const PipelineCreateInfo& info);
+
+private:
+ struct Impl;
+ std::unique_ptr<Impl> mImpl;
+};
+} // namespace GLES
+} // namespace Dali::Graphics
+#endif
*
*/
+// CLASS HEADER
#include "gles-graphics-pipeline.h"
+
+// EXTERNAL INCLUDES
#include <dali/integration-api/gl-abstraction.h>
#include <dali/integration-api/gl-defines.h>
#include <memory>
+
+// INTERNAL INCLUDES
#include "egl-graphics-controller.h"
#include "gles-graphics-shader.h"
/**
* Copy of pipeline state, can be also used for internal caching
*/
-struct Pipeline::PipelineState
+struct PipelineImpl::PipelineState
{
- PipelineState() = default;
-
+ PipelineState() = default;
+ ~PipelineState() = default;
ColorBlendState colorBlendState;
DepthStencilState depthStencilState;
std::vector<ShaderState> shaderState;
InputAssemblyState inputAssemblyState;
};
-Pipeline::Pipeline(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
-: PipelineResource(createInfo, controller)
+PipelineImpl::PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
+: mController(controller)
{
// the creation is deferred so it's needed to copy certain parts of the CreateInfo structure
- mPipelineState = std::make_unique<Pipeline::PipelineState>();
+ mPipelineState = std::make_unique<PipelineImpl::PipelineState>();
// Make copies of structured pass by pointers and replace
// stored create info structure fields
CopyStateIfSet(createInfo.viewportState, mPipelineState->viewportState, &mCreateInfo.viewportState);
}
-bool Pipeline::InitializeResource()
+const PipelineCreateInfo& PipelineImpl::GetCreateInfo() const
+{
+ return mCreateInfo;
+}
+
+auto& PipelineImpl::GetController() const
+{
+ return mController;
+}
+
+bool PipelineImpl::InitializeResource()
{
auto& gl = *GetController().GetGL();
mGlProgram = gl.CreateProgram();
return true;
}
-void Pipeline::DestroyResource()
+void PipelineImpl::DestroyResource()
{
if(mGlProgram)
{
}
}
-void Pipeline::DiscardResource()
+void PipelineImpl::DiscardResource()
{
// Pass program to discard queue
}
-uint32_t Pipeline::GetGLProgram() const
+uint32_t PipelineImpl::GetGLProgram() const
{
return mGlProgram;
}
// FIXME: THIS FUNCTION IS NOT IN USE YET, REQUIRES PROPER PIPELINE
-void Pipeline::Bind(GLES::Pipeline* prevPipeline)
+void PipelineImpl::Bind(GLES::PipelineImpl* prevPipeline)
{
// Same pipeline to bind, nothing to do
if(prevPipeline == this)
gl.UseProgram(program);
}
+void PipelineImpl::Retain()
+{
+ ++mRefCount;
+}
+
+void PipelineImpl::Release()
+{
+ --mRefCount;
+}
+
+uint32_t PipelineImpl::GetRefCount() const
+{
+ return mRefCount;
+}
+
+PipelineImpl::~PipelineImpl() = default;
+
+// PIPELINE WRAPPER
+
+const PipelineCreateInfo& Pipeline::GetCreateInfo() const
+{
+ return mPipeline.GetCreateInfo();
+}
+
+EglGraphicsController& Pipeline::GetController() const
+{
+ return mPipeline.GetController();
+}
+
} // namespace Dali::Graphics::GLES
\ No newline at end of file
{
using PipelineResource = Resource<Graphics::Pipeline, Graphics::PipelineCreateInfo>;
-class Pipeline : public PipelineResource
+/**
+ * @brief PipelineWrapper is the object
+ * returned to the client-side
+ */
+class PipelineImpl
{
public:
/**
* @param[in] createInfo valid TextureCreateInfo structure
* @param[in] controller Reference to the Controller
*/
- Pipeline(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+ PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+
+ /**
+ * @brief Destructor
+ */
+ ~PipelineImpl();
/**
* @brief Destroys all the low-level resources used by the class
*/
- void DestroyResource() override;
+ void DestroyResource();
/**
* @brief Initializes low-level resources
*
* @return Tron success
*/
- bool InitializeResource() override;
+ bool InitializeResource();
/**
* @brief Discards object
*/
- void DiscardResource() override;
+ void DiscardResource();
/**
* @brief returns GL program id
*
* @param[in] prevPipeline previous pipeline
*/
- void Bind(GLES::Pipeline* prevPipeline);
+ void Bind(GLES::PipelineImpl* prevPipeline);
/**
* Executes state change function if condition met
}
}
+ void Retain();
+
+ void Release();
+
+ [[nodiscard]] uint32_t GetRefCount() const;
+
+ [[nodiscard]] const PipelineCreateInfo& GetCreateInfo() const;
+
+ [[nodiscard]] auto& GetController() const;
+
private:
/**
* @brief Helper function. Copies state if pointer is set
// Pipeline state is stored as a copy of create info
// data.
struct PipelineState;
- std::unique_ptr<PipelineState> mPipelineState{};
+ std::unique_ptr<PipelineState> mPipelineState;
+
+ EglGraphicsController& mController;
+ PipelineCreateInfo mCreateInfo;
uint32_t mGlProgram{0u};
+
+ uint32_t mRefCount{0u};
+};
+
+/**
+ * @brief Pipeline class wraps a unique pipeline object
+ *
+ */
+class Pipeline : public Graphics::Pipeline
+{
+public:
+ Pipeline() = delete;
+
+ explicit Pipeline(GLES::PipelineImpl& pipeline)
+ : mPipeline(pipeline)
+ {
+ // increase refcount
+ mPipeline.Retain();
+ }
+
+ ~Pipeline() override
+ {
+ // decrease refcount
+ mPipeline.Release();
+ }
+
+ [[nodiscard]] auto& GetPipeline() const
+ {
+ return mPipeline;
+ }
+
+ [[nodiscard]] const PipelineCreateInfo& GetCreateInfo() const;
+
+ [[nodiscard]] EglGraphicsController& GetController() const;
+
+private:
+ GLES::PipelineImpl& mPipeline;
};
} // namespace Dali::Graphics::GLES