../dali-adaptor/dali-test-suite-utils/test-graphics-controller.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-texture.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-pipeline.cpp
+ ../dali-adaptor/dali-test-suite-utils/test-graphics-program.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-reflection.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-sampler.cpp
../dali-adaptor/dali-test-suite-utils/test-native-image.cpp
dali-test-suite-utils/test-graphics-command-buffer.cpp
dali-test-suite-utils/test-graphics-controller.cpp
dali-test-suite-utils/test-graphics-pipeline.cpp
+ dali-test-suite-utils/test-graphics-program.cpp
dali-test-suite-utils/test-graphics-reflection.cpp
dali-test-suite-utils/test-graphics-texture.cpp
dali-test-suite-utils/test-graphics-sampler.cpp
#include "test-graphics-buffer.h"
#include "test-graphics-command-buffer.h"
+#include "test-graphics-program.h"
#include "test-graphics-reflection.h"
#include "test-graphics-sampler.h"
#include "test-graphics-texture.h"
return std::make_unique<TestGraphicsPipeline>(mGl, pipelineCreateInfo);
}
+Graphics::UniquePtr<Graphics::Program> TestGraphicsController::CreateProgram(const Graphics::ProgramCreateInfo& programCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram)
+{
+ mCallStack.PushCall("CreateProgram", "");
+ return Graphics::MakeUnique<TestGraphicsProgram>(mGl, programCreateInfo, mVertexFormats);
+}
+
Graphics::UniquePtr<Graphics::Shader> TestGraphicsController::CreateShader(const Graphics::ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Graphics::Shader>&& oldShader)
{
mCallStack.PushCall("Controller::CreateShader", "");
return textureProperties;
}
-const Graphics::Reflection& TestGraphicsController::GetPipelineReflection(const Graphics::Pipeline& pipeline)
+const Graphics::Reflection& TestGraphicsController::GetProgramReflection(const Graphics::Program& program)
{
- static TestGraphicsReflection reflection(mGl);
- mCallStack.PushCall("Controller::GetPipelineReflection", "");
+ mCallStack.PushCall("Controller::GetProgramReflection", "");
- return reflection;
+ return static_cast<const TestGraphicsProgram*>(&program)->GetReflection();
}
bool TestGraphicsController::PipelineEquals(const Graphics::Pipeline& pipeline0, const Graphics::Pipeline& pipeline1) const
return false;
}
+bool TestGraphicsController::GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData)
+{
+ mCallStack.PushCall("Controller::GetProgramParameter", "");
+ return false;
+}
+
} // namespace Dali
Graphics::UniquePtr<Graphics::Pipeline> CreatePipeline(const Graphics::PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline) override;
/**
+ * @brief Creates new Program object
+ *
+ * @param[in] programCreateInfo The valid ProgramCreateInfo structure
+ * @return pointer to the Program object
+ */
+ Graphics::UniquePtr<Graphics::Program> CreateProgram(const Graphics::ProgramCreateInfo& programCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram) override;
+
+ /**
* @brief Creates new Shader object
*
* @param[in] shaderCreateInfo The valid ShaderCreateInfo structure
const Graphics::TextureProperties& GetTextureProperties(const Graphics::Texture& texture) override;
/**
- * @brief Returns the reflection of the given pipeline
+ * @brief Returns the reflection of the given program
*
- * @param[in] pipeline The pipeline
- * @return The reflection of the pipeline
+ * @param[in] program The program
+ * @return The reflection of the program
*/
- const Graphics::Reflection& GetPipelineReflection(const Graphics::Pipeline& pipeline) override;
+ const Graphics::Reflection& GetProgramReflection(const Graphics::Program& program) override;
/**
* @brief Tests whether two Pipelines are the same.
*/
bool PipelineEquals(const Graphics::Pipeline& pipeline0, const Graphics::Pipeline& pipeline1) const override;
+public: // Test Functions
+ void SetVertexFormats(Property::Array& vfs)
+ {
+ mVertexFormats = vfs;
+ }
+
+ void ClearSubmitStack()
+ {
+ mSubmitStack.clear();
+ }
+
+ /**
+ * @brief Retrieves program parameters
+ *
+ * This function can be used to retrieve data from internal implementation
+ *
+ * @param[in] program Valid program object
+ * @param[in] parameterId Integer parameter id
+ * @param[out] outData Pointer to output memory
+ * @return True on success
+ */
+ bool GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData) override;
+
public:
- mutable TraceCallStack mCallStack;
- mutable TraceCallStack mCommandBufferCallStack;
+ mutable TraceCallStack mCallStack;
+ mutable TraceCallStack mCommandBufferCallStack;
+ mutable std::vector<Graphics::SubmitInfo> mSubmitStack;
TestGlAbstraction mGl;
TestGlSyncAbstraction mGlSyncAbstraction;
bool isDiscardQueueEmptyResult{true};
bool isDrawOnResumeRequiredResult{true};
+
+ Property::Array mVertexFormats;
};
} // namespace Dali
if(createInfo.colorBlendState)
colorBlendState = *createInfo.colorBlendState;
- if(createInfo.shaderState)
- shaderState = *createInfo.shaderState;
+ if(createInfo.programState)
+ programState = *createInfo.programState;
if(createInfo.viewportState)
viewportState = *createInfo.viewportState;
TestGlAbstraction& mGl;
Graphics::ColorBlendState colorBlendState;
- std::vector<Graphics::ShaderState> shaderState;
+ Graphics::ProgramState programState;
Graphics::ViewportState viewportState;
Graphics::FramebufferState framebufferState;
Graphics::Pipeline basePipeline;
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test-graphics-program.h"
+
+namespace Dali
+{
+TestGraphicsProgram::TestGraphicsProgram(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats)
+: mGl(gl),
+ mCreateInfo(createInfo),
+ mReflection(gl)
+{
+}
+
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TEST_GRAPHICS_PROGRAM_H
+#define DALI_TEST_GRAPHICS_PROGRAM_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali/graphics-api/graphics-program-create-info.h>
+#include <dali/graphics-api/graphics-program.h>
+#include "test-gl-abstraction.h"
+#include "test-graphics-reflection.h"
+
+namespace Dali
+{
+class TestGraphicsProgram : public Graphics::Program
+{
+public:
+ TestGraphicsProgram(TestGlAbstraction& gl, const Graphics::ProgramCreateInfo& createInfo, Property::Array& vertexFormats);
+
+ // For API
+ const TestGraphicsReflection& GetReflection() const
+ {
+ // Build a reflection
+ return mReflection;
+ }
+
+ // For tests
+ TestGraphicsReflection& GetProgamReflection()
+ {
+ return mReflection;
+ }
+
+public:
+ TestGlAbstraction& mGl;
+ Graphics::ProgramCreateInfo mCreateInfo;
+ TestGraphicsReflection mReflection;
+};
+
+} // namespace Dali
+
+#endif //DALI_TEST_GRAPHICS_PROGRAM_H
../dali-adaptor/dali-test-suite-utils/test-graphics-texture.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-sampler.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-pipeline.cpp
+ ../dali-adaptor/dali-test-suite-utils/test-graphics-program.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-reflection.cpp
../dali-adaptor/dali-test-suite-utils/test-native-image.cpp
../dali-adaptor/dali-test-suite-utils/test-platform-abstraction.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-texture.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-sampler.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-pipeline.cpp
+ ../dali-adaptor/dali-test-suite-utils/test-graphics-program.cpp
../dali-adaptor/dali-test-suite-utils/test-graphics-reflection.cpp
../dali-adaptor/dali-test-suite-utils/test-native-image.cpp
../dali-adaptor/dali-test-suite-utils/test-platform-abstraction.cpp
#include <dali/internal/graphics/gles-impl/gles-graphics-texture.h>
#include <dali/internal/graphics/gles-impl/gles-graphics-types.h>
#include <dali/public-api/common/dali-common.h>
+#include "gles-graphics-program.h"
namespace Dali::Graphics
{
return NewObject<GLES::Buffer>(bufferCreateInfo, *this, std::move(oldBuffer));
}
-Graphics::UniquePtr<Shader> EglGraphicsController::CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Shader>&& oldShader)
-{
- return NewObject<GLES::Shader>(shaderCreateInfo, *this, std::move(oldShader));
-}
-
Graphics::UniquePtr<Pipeline> EglGraphicsController::CreatePipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
{
// Create pipeline cache if needed
return mPipelineCache->GetPipeline(pipelineCreateInfo, std::move(oldPipeline));
}
+Graphics::UniquePtr<Program> EglGraphicsController::CreateProgram(const ProgramCreateInfo& programCreateInfo, UniquePtr<Program>&& oldProgram)
+{
+ // Create program cache if needed
+ if(!mPipelineCache)
+ {
+ mPipelineCache = std::make_unique<GLES::PipelineCache>(*this);
+ }
+
+ return mPipelineCache->GetProgram(programCreateInfo, std::move(oldProgram));
+}
+
+Graphics::UniquePtr<Shader> EglGraphicsController::CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Shader>&& oldShader)
+{
+ return NewObject<GLES::Shader>(shaderCreateInfo, *this, std::move(oldShader));
+}
+
+const Graphics::Reflection& EglGraphicsController::GetProgramReflection(const Graphics::Program& program)
+{
+ return static_cast<const Graphics::GLES::Program*>(&program)->GetReflection();
+}
+
void EglGraphicsController::AddTexture(GLES::Texture& texture)
{
// Assuming we are on the correct context
// Process buffers
ProcessDiscardQueue<GLES::Buffer>(mDiscardBufferQueue);
+
+ // Process pipelines
+ ProcessDiscardQueue<GLES::Pipeline>(mDiscardPipelineQueue);
+
+ // Process programs
+ ProcessDiscardQueue<GLES::Program>(mDiscardProgramQueue);
}
void EglGraphicsController::ProcessCreateQueues()
return Graphics::UniquePtr<Memory>(new GLES::Memory(mapInfo, *this));
}
+bool EglGraphicsController::GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData)
+{
+ return static_cast<GLES::Program*>(&program)->GetImplementation()->GetParameter(parameterId, outData);
+}
+
+GLES::PipelineCache& EglGraphicsController::GetPipelineCache() const
+{
+ return *mPipelineCache;
+}
+
} // namespace Dali::Graphics
#include "gles-context.h"
#include "gles-graphics-buffer.h"
#include "gles-graphics-memory.h"
-#include "gles-graphics-pipeline.h"
#include "gles-graphics-pipeline-cache.h"
+#include "gles-graphics-pipeline.h"
#include "gles-graphics-reflection.h"
#include "gles-graphics-texture.h"
Graphics::UniquePtr<Pipeline> CreatePipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Pipeline>&& oldPipeline) override;
/**
+ * @copydoc Dali::Graphics::CreateProgram()
+ */
+ Graphics::UniquePtr<Program> CreateProgram(const ProgramCreateInfo& programCreateInfo, UniquePtr<Program>&& oldProgram) override;
+
+ /**
* @copydoc Dali::Graphics::CreateShader()
*/
Graphics::UniquePtr<Shader> CreateShader(const ShaderCreateInfo& shaderCreateInfo, Graphics::UniquePtr<Shader>&& oldShader) override;
/**
* @copydoc Dali::Graphics::Controller::GetPipelineReflection()
*/
- const Reflection& GetPipelineReflection(const Pipeline& pipeline) override
- {
- static GLES::Reflection dummy(*this);
- return dummy;
- }
+
+ [[nodiscard]] const Reflection& GetProgramReflection(const Graphics::Program& program) override;
/**
* @copydoc Dali::Graphics::PipelineEquals()
}
/**
+ * @brief Pushes Program to the discard queue
+ *
+ * Function is called from the UniquePtr custom deleter.
+ *
+ * @param[in] program Pointer to the program
+ */
+ void DiscardResource(GLES::Program* program)
+ {
+ mDiscardProgramQueue.push(program);
+ }
+
+ /**
+ * @brief Pushes Pipeline to the discard queue
+ *
+ * Function is called from the UniquePtr custom deleter.
+ *
+ * @param[in] program Pointer to the pipeline
+ */
+ void DiscardResource(GLES::Pipeline* pipeline)
+ {
+ mDiscardPipelineQueue.push(pipeline);
+ }
+
+ /**
* @brief Flushes all pending updates
*
* Function flushes all pending resource constructions,
// Process updates
ProcessTextureUpdateQueue();
+ // Process main command queue
+ ProcessCommandQueues();
+
// Process discards
ProcessDiscardQueues();
- // Process main command queue
- ProcessCommandQueues();
+ // Flush pipeline cache to remove unused pipelines
+ GetPipelineCache().FlushCache();
}
// Test update to tick controller, usually it will run own thread
*/
void ProcessTextureUpdateQueue();
+ /**
+ * @brief Returns program custom parameter
+ *
+ * This function can be used as a backdoor in order to retrieve
+ * certain data out of implementation
+ *
+ * @param[in] program Valid Program object
+ * @param parameterId Integer id of parameter
+ * @param outData Output data
+ * @return True if parameter retrieved
+ */
+ bool GetProgramParameter(Graphics::Program& program, uint32_t parameterId, void* outData) override;
+
+ /**
+ * @brief Returns pipeline cache object
+ *
+ * @return Valid pipeline cache object
+ */
+ [[nodiscard]] GLES::PipelineCache& GetPipelineCache() const;
+
private:
Integration::GlAbstraction* mGlAbstraction{nullptr};
Integration::GlSyncAbstraction* mGlSyncAbstraction{nullptr};
std::queue<GLES::Buffer*> mCreateBufferQueue; ///< Create queue for buffer resource
std::queue<GLES::Buffer*> mDiscardBufferQueue; ///< Discard queue for buffer resource
+ std::queue<GLES::Program*> mDiscardProgramQueue; ///< Discard queue for program resource
+
+ std::queue<GLES::Pipeline*> mDiscardPipelineQueue; ///< Discard queue of pipelines
+
std::queue<GLES::CommandBuffer*> mCommandQueue; ///< we may have more in the future
using TextureUpdateRequest = std::pair<TextureUpdateInfo, TextureUpdateSourceInfo>;
${adaptor_graphics_dir}/gles-impl/gles-graphics-framebuffer.cpp
${adaptor_graphics_dir}/gles-impl/gles-graphics-memory.cpp
${adaptor_graphics_dir}/gles-impl/gles-graphics-pipeline.cpp
+ ${adaptor_graphics_dir}/gles-impl/gles-graphics-program.cpp
${adaptor_graphics_dir}/gles-impl/gles-graphics-reflection.cpp
${adaptor_graphics_dir}/gles-impl/gles-graphics-render-pass.cpp
${adaptor_graphics_dir}/gles-impl/gles-graphics-render-target.cpp
mImpl->mCurrentPipeline = mImpl->mNewPipeline;
mImpl->mNewPipeline = nullptr;
}
+ mImpl->mCurrentPipeline->GetPipeline().Bind(nullptr);
// Blend state
ResolveBlendState();
*
*/
-// CLASS HEADER
#include "gles-graphics-pipeline-cache.h"
-
-// INTERNAL INCLUDES
+#include <algorithm>
#include "gles-graphics-pipeline.h"
+#include "gles-graphics-program.h"
namespace Dali::Graphics::GLES
{
/**
- * @brief Implementation of pipeline cache
+ * @brief custom delete function for cached object
+ */
+template<class T>
+struct CachedObjectDeleter
+{
+ CachedObjectDeleter() = default;
+
+ void operator()(T* object)
+ {
+ // Discard resource (add it to discard queue)
+ object->DiscardResource();
+ }
+};
+
+/**
+ * @brief The order of states being stored in the cache and mask
+ */
+enum class StateLookupIndex : uint32_t
+{
+ COLOR_BLEND_STATE_BIT = 0,
+ VIEWPORT_STATE_BIT = 1,
+ FRAMEBUFFER_STATE_BIT = 2,
+ BASE_PIPELINE_STATE_BIT = 3,
+ DEPTH_STENCIL_STATE_BIT = 4,
+ RASTERIZATION_STATE_BIT = 5,
+ VERTEX_INPUT_STATE_BIT = 6,
+ INPUT_ASSEMBLY_STATE_BIT = 7,
+ MAX_STATE = 8
+};
+
+/**
+ * Helper float compare function
+ */
+static bool cmpf(float A, float B, float epsilon = 0.005f)
+{
+ return (fabs(A - B) < epsilon);
+}
+
+/**
+ * Helper operators
+ */
+static bool operator==(const Graphics::Viewport& lhs, const Graphics::Viewport& rhs)
+{
+ return cmpf(lhs.x, rhs.x) &&
+ cmpf(lhs.y, rhs.y) &&
+ cmpf(lhs.width, rhs.width) &&
+ cmpf(lhs.height, rhs.height) &&
+ cmpf(lhs.minDepth, rhs.minDepth) &&
+ cmpf(lhs.maxDepth, rhs.maxDepth);
+}
+
+static bool operator==(const Graphics::Rect2D& lhs, const Graphics::Rect2D& rhs)
+{
+ return cmpf(lhs.x, rhs.x) &&
+ cmpf(lhs.y, rhs.y) &&
+ cmpf(lhs.width, rhs.width) &&
+ cmpf(lhs.height, rhs.height);
+}
+
+static bool operator==(const Graphics::StencilOpState& lhs, const Graphics::StencilOpState& rhs)
+{
+ return lhs.failOp == rhs.failOp &&
+ lhs.passOp == rhs.passOp &&
+ lhs.depthFailOp == rhs.depthFailOp &&
+ lhs.compareOp == rhs.compareOp &&
+ lhs.compareMask == rhs.compareMask &&
+ lhs.writeMask == rhs.writeMask &&
+ lhs.reference == rhs.reference;
+}
+
+static bool
+operator==(const Dali::Graphics::VertexInputState::Attribute& lhs,
+ const Dali::Graphics::VertexInputState::Attribute& rhs)
+{
+ return lhs.location == rhs.location &&
+ lhs.binding == rhs.binding &&
+ lhs.offset == rhs.offset &&
+ lhs.format == rhs.format;
+}
+
+static bool
+operator==(const Dali::Graphics::VertexInputState::Binding& lhs, const Dali::Graphics::VertexInputState::Binding& rhs)
+{
+ return lhs.stride == rhs.stride &&
+ lhs.inputRate == rhs.inputRate;
+}
+
+using PipelineStateCompateFunctionType = bool(const Graphics::PipelineCreateInfo*,
+ const Graphics::PipelineCreateInfo*);
+
+static std::vector<PipelineStateCompateFunctionType*> STATE_COMPARE_FUNC_TABLE{};
+
+/**
+ * @brief Initialises compare function lookup table
+ */
+void InitialiseStateCompareLookupTable()
+{
+ STATE_COMPARE_FUNC_TABLE = {
+ [](const auto* lhs, const auto* rhs) -> bool // colorBlendState
+ {
+ const auto& lcb = *lhs->colorBlendState;
+ const auto& rcb = *rhs->colorBlendState;
+ return lcb.logicOpEnable == rcb.logicOpEnable &&
+ lcb.logicOp == rcb.logicOp &&
+ cmpf(lcb.blendConstants[0], rcb.blendConstants[0]) &&
+ cmpf(lcb.blendConstants[1], rcb.blendConstants[1]) &&
+ cmpf(lcb.blendConstants[2], rcb.blendConstants[2]) &&
+ cmpf(lcb.blendConstants[3], rcb.blendConstants[3]) &&
+ lcb.blendEnable == rcb.blendEnable &&
+ lcb.srcColorBlendFactor == rcb.srcColorBlendFactor &&
+ lcb.dstColorBlendFactor == rcb.dstColorBlendFactor &&
+ lcb.colorBlendOp == rcb.colorBlendOp &&
+ lcb.srcAlphaBlendFactor == rcb.srcAlphaBlendFactor &&
+ lcb.dstAlphaBlendFactor == rcb.dstAlphaBlendFactor &&
+ lcb.alphaBlendOp == rcb.alphaBlendOp &&
+ lcb.colorComponentWriteBits == rcb.colorComponentWriteBits;
+ },
+ [](const auto* lhs, const auto* rhs) -> bool // viewport state
+ {
+ const auto& lvp = *lhs->viewportState;
+ const auto& rvp = *rhs->viewportState;
+ return lvp.viewport == rvp.viewport &&
+ lvp.scissor == rvp.scissor &&
+ lvp.scissorTestEnable == rvp.scissorTestEnable;
+ },
+ [](const auto* lhs, const auto* rhs) -> bool // framebufferState
+ {
+ const auto& lfb = *lhs->framebufferState;
+ const auto& rfb = *rhs->framebufferState;
+ return lfb.framebuffer == rfb.framebuffer;
+ },
+ [](const auto* lhs, const auto* rhs) -> bool // basePipeline
+ {
+ return lhs->basePipeline == rhs->basePipeline;
+ },
+ [](const auto* lhs, const auto* rhs) -> bool // depthStencilState
+ {
+ const auto& lds = *lhs->depthStencilState;
+ const auto& rds = *rhs->depthStencilState;
+ return lds.depthTestEnable == rds.depthTestEnable &&
+ lds.depthWriteEnable == rds.depthWriteEnable &&
+ lds.depthCompareOp == rds.depthCompareOp &&
+ lds.stencilTestEnable == rds.stencilTestEnable &&
+ lds.front == rds.front &&
+ lds.back == rds.back;
+ },
+ [](const auto* lhs, const auto* rhs) -> bool // rasterizationState
+ {
+ const auto& lrs = *lhs->rasterizationState;
+ const auto& rrs = *rhs->rasterizationState;
+ return lrs.cullMode == rrs.cullMode &&
+ lrs.polygonMode == rrs.polygonMode &&
+ lrs.frontFace == rrs.frontFace;
+ },
+ [](const auto* lhs, const auto* rhs) -> bool // vertexInputState
+ {
+ const auto& lvi = *lhs->vertexInputState;
+ const auto& rvi = *rhs->vertexInputState;
+ return lvi.bufferBindings.size() == rvi.bufferBindings.size() &&
+ lvi.attributes.size() == rvi.attributes.size() &&
+ std::equal(lvi.bufferBindings.begin(), lvi.bufferBindings.end(), rvi.bufferBindings.begin(), [](const auto& lhs, const auto& rhs) {
+ return operator==(lhs, rhs);
+ }) &&
+ std::equal(lvi.attributes.begin(), lvi.attributes.end(), rvi.attributes.begin(), [](const auto& lhs, const auto& rhs) {
+ return operator==(lhs, rhs);
+ });
+ },
+ [](const auto* lhs, const auto* rhs) -> bool // inputAssemblyState
+ {
+ const auto& lia = *lhs->inputAssemblyState;
+ const auto& ria = *rhs->inputAssemblyState;
+ return lia.topology == ria.topology &&
+ lia.primitiveRestartEnable == ria.primitiveRestartEnable;
+ },
+ };
+}
+
+/**
+ * @brief Helper function calculating the bitmask of set states
+ *
+ * @param[in] info Valid PipelineCreateInfo structure
+ * @return bitmask of set states
+ */
+inline uint32_t GetStateBitmask(const PipelineCreateInfo& info)
+{
+ uint32_t mask{0u};
+ mask |= bool(info.colorBlendState) << int(StateLookupIndex::COLOR_BLEND_STATE_BIT);
+ mask |= bool(info.viewportState) << int(StateLookupIndex::VIEWPORT_STATE_BIT);
+ mask |= bool(info.framebufferState) << int(StateLookupIndex::FRAMEBUFFER_STATE_BIT);
+ mask |= bool(info.basePipeline) << int(StateLookupIndex::BASE_PIPELINE_STATE_BIT);
+ mask |= bool(info.depthStencilState) << int(StateLookupIndex::DEPTH_STENCIL_STATE_BIT);
+ mask |= bool(info.rasterizationState) << int(StateLookupIndex::RASTERIZATION_STATE_BIT);
+ mask |= bool(info.vertexInputState) << int(StateLookupIndex::VERTEX_INPUT_STATE_BIT);
+ mask |= bool(info.inputAssemblyState) << int(StateLookupIndex::INPUT_ASSEMBLY_STATE_BIT);
+ return mask;
+}
+
+/**
+ * @brief Implementation of cache
*/
struct PipelineCache::Impl
{
+ /**
+ * @brief Constructor
+ */
explicit Impl(EglGraphicsController& _controller)
: controller(_controller)
{
+ // Initialise lookup table
+ InitialiseStateCompareLookupTable();
}
+ /**
+ * @brief destructor
+ */
~Impl() = default;
- EglGraphicsController& controller;
+ /**
+ * @brief Structure describes a single cache entry
+ */
+ struct CacheEntry
+ {
+ CacheEntry() = default;
+
+ CacheEntry(UniquePtr<PipelineImpl>&& _pipeline, uint32_t _bitmask)
+ : pipeline(std::move(_pipeline)),
+ stateBitmask(_bitmask)
+ {
+ }
+
+ ~CacheEntry() = default;
+
+ CacheEntry(CacheEntry&&) = default;
+ CacheEntry& operator=(CacheEntry&&) = default;
+
+ UniquePtr<PipelineImpl> pipeline{nullptr};
+ uint32_t stateBitmask{0u};
+ };
- std::vector<UniquePtr<PipelineImpl>> mPipelines;
+ EglGraphicsController& controller;
+ std::vector<CacheEntry> entries;
+
+ /**
+ * @brief Sorted array of shaders used to create program
+ */
+ struct ProgramCacheEntry
+ {
+ // sorted array of shaders
+ std::vector<const Shader*> shaders;
+ UniquePtr<ProgramImpl> program{nullptr};
+ };
+
+ std::vector<ProgramCacheEntry> programEntries;
};
PipelineCache::PipelineCache(EglGraphicsController& controller)
PipelineImpl* PipelineCache::FindPipelineImpl(const PipelineCreateInfo& info)
{
- // hashing by shaders and then states, shaders should use the same pointers
- for(auto& pipeline : mImpl->mPipelines)
+ auto bitmask = GetStateBitmask(info);
+
+ for(auto& entry : mImpl->entries)
{
+ auto& pipeline = entry.pipeline;
auto& cacheInfo = pipeline->GetCreateInfo();
-
- if(!info.shaderState)
+ if(!info.programState)
{
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())
+ // Check whether the program is the same
+ if(info.programState->program)
{
- if(memcmp(info.shaderState->data(), cacheInfo.shaderState->data(), sizeof(info.shaderState->size() * sizeof(ShaderState))) != 0)
+ const auto& lhsProgram = *static_cast<const GLES::Program*>(info.programState->program);
+ const auto& rhsProgram = *static_cast<const GLES::Program*>(cacheInfo.programState->program);
+ if(lhsProgram != rhsProgram)
{
- continue; // early exit
+ continue;
}
- // 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)
+ // Test whether set states bitmask matches
+ if(entry.stateBitmask != bitmask)
{
- continue; // early exit
+ continue;
}
- // 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)
+ // Now test only for states that are set
+ auto i = 0;
+ for(i = 0; i < int(StateLookupIndex::MAX_STATE); ++i)
{
- continue;
+ // Test only set states
+ if((entry.stateBitmask & (1 << i)))
+ {
+ if(!STATE_COMPARE_FUNC_TABLE[i](&info, &cacheInfo))
+ {
+ break;
+ }
+ }
}
- // For now ignoring dynamic state mask and allocator
-
+ // TODO: For now ignoring dynamic state mask and allocator
// Getting as far as here, we have found our pipeline impl
- return pipeline.get();
+ if(i == int(StateLookupIndex::MAX_STATE))
+ {
+ return pipeline.get();
+ }
+ }
+ }
+ return nullptr;
+}
+
+ProgramImpl* PipelineCache::FindProgramImpl(const ProgramCreateInfo& info)
+{
+ if(mImpl->programEntries.empty())
+ {
+ return nullptr;
+ }
+
+ // assert if no shaders given
+ std::vector<const Shader*> shaders;
+ shaders.reserve(info.shaderState->size());
+
+ for(auto& state : *info.shaderState)
+ {
+ shaders.push_back(state.shader);
+ }
+
+ // sort
+ std::sort(shaders.begin(), shaders.end());
+
+ for(auto& item : mImpl->programEntries)
+ {
+ if(item.shaders.size() != shaders.size())
+ {
+ continue;
+ }
+
+ int k = shaders.size();
+ while(--k >= 0 && item.shaders[k] == shaders[k])
+ ;
+
+ if(k < 0)
+ {
+ return item.program.get();
}
}
return nullptr;
}
-Graphics::UniquePtr<Graphics::Pipeline> PipelineCache::GetPipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
+Graphics::UniquePtr<Graphics::Pipeline> PipelineCache::GetPipeline(const PipelineCreateInfo& pipelineCreateInfo,
+ Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline)
{
- // create or get from cache
auto cachedPipeline = FindPipelineImpl(pipelineCreateInfo);
+ // Return same pointer if nothing changed
+ if(oldPipeline && *static_cast<GLES::Pipeline*>(oldPipeline.get()) == cachedPipeline)
+ {
+ return std::move(oldPipeline);
+ }
+
if(!cachedPipeline)
{
// create new pipeline
- auto pipeline = MakeUnique<GLES::PipelineImpl>(pipelineCreateInfo, mImpl->controller);
+ auto pipeline = MakeUnique<GLES::PipelineImpl>(pipelineCreateInfo, mImpl->controller, *this);
cachedPipeline = pipeline.get();
// add it to cache
- mImpl->mPipelines.emplace_back(std::move(pipeline));
+ mImpl->entries.emplace_back(std::move(pipeline), GetStateBitmask(pipelineCreateInfo));
}
- else
+
+ auto wrapper = MakeUnique<GLES::Pipeline, CachedObjectDeleter<GLES::Pipeline>>(*cachedPipeline);
+ return std::move(wrapper);
+}
+
+Graphics::UniquePtr<Graphics::Program> PipelineCache::GetProgram(const ProgramCreateInfo& programCreateInfo,
+ Graphics::UniquePtr<Graphics::Program>&& oldProgram)
+{
+ ProgramImpl* cachedProgram = FindProgramImpl(programCreateInfo);
+
+ // Return same pointer if nothing changed
+ if(oldProgram && *static_cast<GLES::Program*>(oldProgram.get()) == cachedProgram)
{
+ return std::move(oldProgram);
}
- // create new pipeline wrapper n
- auto wrapper = MakeUnique<GLES::Pipeline>(*cachedPipeline);
+
+ if(!cachedProgram)
+ {
+ // create new pipeline
+ auto program = MakeUnique<GLES::ProgramImpl>(programCreateInfo, mImpl->controller);
+
+ program->Create();
+
+ cachedProgram = program.get();
+
+ // add it to cache
+ mImpl->programEntries.emplace_back();
+ auto& item = mImpl->programEntries.back();
+ item.program = std::move(program);
+ for(auto& state : *programCreateInfo.shaderState)
+ {
+ item.shaders.push_back(state.shader);
+ }
+
+ std::sort(item.shaders.begin(), item.shaders.end());
+ }
+
+ auto wrapper = MakeUnique<GLES::Program, CachedObjectDeleter<GLES::Program>>(cachedProgram);
return std::move(wrapper);
}
+void PipelineCache::FlushCache()
+{
+ decltype(mImpl->entries) newEntries;
+ newEntries.reserve(mImpl->entries.size());
+
+ for(auto& entry : mImpl->entries)
+ {
+ // Move items which are still in use into the new array
+ if(entry.pipeline->GetRefCount() != 0)
+ {
+ newEntries.emplace_back(std::move(entry));
+ }
+ }
+
+ // Move temporary array in place of stored cache
+ // Unused pipelines will be deleted automatically
+ mImpl->entries = std::move(newEntries);
+
+ // TODO: program cache may require similar action. However,
+ // since there is always one wrapper for Program object
+ // kept in the pipeline, then death of pipeline will result
+ // killing the program (if program isn't in use anymore)
+}
+
} // namespace Dali::Graphics::GLES
\ No newline at end of file
// EXTERNAL INCLUDES
#include <dali/graphics-api/graphics-pipeline-create-info.h>
#include <dali/graphics-api/graphics-pipeline.h>
+#include <dali/graphics-api/graphics-program-create-info.h>
+#include <dali/graphics-api/graphics-program.h>
// INTERNAL INCLUDES
#include "gles-graphics-resource.h"
{
class Pipeline;
class PipelineImpl;
-
+class Program;
+class ProgramImpl;
/**
* @brief PipelineCache manages pipeline and program
* objects so there are no duplicates created.
*/
Graphics::UniquePtr<Graphics::Pipeline> GetPipeline(const PipelineCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Pipeline>&& oldPipeline);
+ /**
+ * @brief Retrieves program matching the spec
+ *
+ * Function returns either existing program if one is found
+ * in the cache or creates new one.
+ *
+ * @param[in] programCreateInfo Valid ProgramCreateInfo structure
+ * @param[in] oldProgram previous program object
+ * @return Program object
+ */
+ Graphics::UniquePtr<Graphics::Program> GetProgram(const ProgramCreateInfo& pipelineCreateInfo, Graphics::UniquePtr<Graphics::Program>&& oldProgram);
+
+ /**
+ * @brief Flushes pipeline and program cache
+ *
+ * Removes cached items when they are no longer needed. This function
+ * should be called at the very end of Controller render loop iteration.
+ */
+ void FlushCache();
+
private:
/**
* @brief Finds pipeline implementation based on the spec
*/
PipelineImpl* FindPipelineImpl(const PipelineCreateInfo& info);
+ /**
+ * @brief Finds program implementation based on the spec
+ * @param[in] info Valid create info structure
+ * @return Returns pointer to program or nullptr
+ */
+ ProgramImpl* FindProgramImpl(const ProgramCreateInfo& info);
+
private:
struct Impl;
std::unique_ptr<Impl> mImpl;
#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"
-
-namespace
-{
-struct LegacyProgram : Dali::Graphics::ExtensionCreateInfo
-{
- uint32_t programId;
-};
-} // namespace
+#include "gles-graphics-pipeline-cache.h"
+#include "gles-graphics-program.h"
namespace Dali::Graphics::GLES
{
{
PipelineState() = default;
~PipelineState() = default;
- ColorBlendState colorBlendState;
- DepthStencilState depthStencilState;
- std::vector<ShaderState> shaderState;
- ViewportState viewportState;
- FramebufferState framebufferState;
- RasterizationState rasterizationState;
- VertexInputState vertexInputState;
- InputAssemblyState inputAssemblyState;
+
+ // for maintaining correct lifecycle, the owned program
+ // wrapper must be created
+ UniquePtr<Program> program;
+
+ ColorBlendState colorBlendState;
+ DepthStencilState depthStencilState;
+ ProgramState programState;
+ ViewportState viewportState;
+ FramebufferState framebufferState;
+ RasterizationState rasterizationState;
+ VertexInputState vertexInputState;
+ InputAssemblyState inputAssemblyState;
+
+ PipelineCache* pipelineCache{};
};
-PipelineImpl::PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
-: mController(controller),
- mReflection(controller)
+PipelineImpl::PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller, PipelineCache& pipelineCache)
+: mController(controller)
{
// the creation is deferred so it's needed to copy certain parts of the CreateInfo structure
mPipelineState = std::make_unique<PipelineImpl::PipelineState>();
CopyStateIfSet(createInfo.inputAssemblyState, mPipelineState->inputAssemblyState, &mCreateInfo.inputAssemblyState);
CopyStateIfSet(createInfo.vertexInputState, mPipelineState->vertexInputState, &mCreateInfo.vertexInputState);
CopyStateIfSet(createInfo.rasterizationState, mPipelineState->rasterizationState, &mCreateInfo.rasterizationState);
+ CopyStateIfSet(createInfo.programState, mPipelineState->programState, &mCreateInfo.programState);
CopyStateIfSet(createInfo.framebufferState, mPipelineState->framebufferState, &mCreateInfo.framebufferState);
CopyStateIfSet(createInfo.colorBlendState, mPipelineState->colorBlendState, &mCreateInfo.colorBlendState);
CopyStateIfSet(createInfo.depthStencilState, mPipelineState->depthStencilState, &mCreateInfo.depthStencilState);
- CopyStateIfSet(createInfo.shaderState, mPipelineState->shaderState, &mCreateInfo.shaderState);
+ CopyStateIfSet(createInfo.programState, mPipelineState->programState, &mCreateInfo.programState);
CopyStateIfSet(createInfo.viewportState, mPipelineState->viewportState, &mCreateInfo.viewportState);
- if(createInfo.nextExtension)
- {
- LegacyProgram* legacyProgram = static_cast<LegacyProgram*>(createInfo.nextExtension);
- mGlProgram = legacyProgram->programId;
- mReflection.SetGlProgram(mGlProgram);
- printf("GLES::Pipeline: mProgramId: %u\n", mGlProgram);
- }
+ // This program doesn't need custom deleter
+ auto programImpl = static_cast<const GLES::Program*>(createInfo.programState->program)->GetImplementation();
+ mPipelineState->program = MakeUnique<GLES::Program>(programImpl);
- const std::vector<Graphics::ShaderState>* shaderStates = mCreateInfo.shaderState;
- if(shaderStates)
- {
- printf("GLES::Pipeline: shaderStates %p, shaderStates size: %lu\n", shaderStates, shaderStates->size());
+ // To make sure the program is alive as long as the pipeline is!
+ mCreateInfo.programState->program = mPipelineState->program.get();
- std::for_each(shaderStates->begin(), shaderStates->end(), [](Graphics::ShaderState shaderState) {
- const Graphics::Shader* shader = shaderState.shader;
- Graphics::PipelineStage pipelineStage = shaderState.pipelineStage;
- printf("GLES::Pipeline: shader: %p, pipelineStage: %d\n", shader, static_cast<int>(pipelineStage));
- });
- }
+ // Set pipeline cache
+ mPipelineState->pipelineCache = &pipelineCache;
}
const PipelineCreateInfo& PipelineImpl::GetCreateInfo() const
return mController;
}
-bool PipelineImpl::InitializeResource()
-{
- auto& gl = *GetController().GetGL();
- // mGlProgram = gl.CreateProgram();
-
- for(auto& shader : mPipelineState->shaderState)
- {
- // TODO: Compile shader (shouldn't compile if already did so)
- const auto glesShader = static_cast<const GLES::Shader*>(shader.shader);
- if(glesShader->Compile())
- {
- // Attach shader
- gl.AttachShader(mGlProgram, glesShader->GetGLShader());
- }
- else
- {
- // failure
- gl.DeleteProgram(mGlProgram);
- return false;
- }
- }
-
- // Link program
- gl.LinkProgram(mGlProgram);
-
- // Check for errors
- // TODO:
-
- return true;
-}
-
-void PipelineImpl::DestroyResource()
-{
- if(mGlProgram)
- {
- auto& gl = *GetController().GetGL();
- gl.DeleteProgram(mGlProgram);
- }
-}
-
-void PipelineImpl::DiscardResource()
-{
- // Pass program to discard queue
-}
-
-uint32_t PipelineImpl::GetGLProgram() const
-{
- return mGlProgram;
-}
-
-// FIXME: THIS FUNCTION IS NOT IN USE YET, REQUIRES PROPER PIPELINE
void PipelineImpl::Bind(GLES::PipelineImpl* prevPipeline)
{
- // Same pipeline to bind, nothing to do
- if(prevPipeline == this)
- {
- return;
- }
-
- auto& gl = *GetController().GetGL();
-
- // ------------------ VIEWPORT
-
- const auto& viewportState = mPipelineState->viewportState;
- auto setViewportState = [this, &gl, viewportState]() {
- if(viewportState.scissorTestEnable)
- {
- gl.Enable(GL_SCISSOR_TEST);
- const auto& scissor = mPipelineState->viewportState.scissor;
- gl.Scissor(scissor.x, scissor.y, scissor.width, scissor.height);
- }
- else
- {
- gl.Disable(GL_SCISSOR_TEST);
- const auto& scissor = mPipelineState->viewportState.scissor;
- gl.Scissor(scissor.x, scissor.y, scissor.width, scissor.height);
- }
- };
-
- // Resolve viewport/scissor state change
- ExecuteStateChange(setViewportState, prevPipeline->GetCreateInfo().viewportState, &mPipelineState->viewportState);
-
- // ---------------------- PROGRAM
- auto program = mGlProgram;
-
- gl.UseProgram(program);
+ auto& gl = *GetController().GetGL();
+ auto glProgram = static_cast<const GLES::Program*>(GetCreateInfo().programState->program)->GetImplementation()->GetGlProgram();
+ gl.UseProgram(glProgram);
}
void PipelineImpl::Retain()
return mPipeline.GetController();
}
+Pipeline::~Pipeline()
+{
+ // decrease refcount
+ if(mPipeline.GetRefCount())
+ {
+ mPipeline.Release();
+ }
+}
+
+void Pipeline::DiscardResource()
+{
+ // Send pipeline to discard queue if refcount is 0
+ GetController().DiscardResource(this);
+}
+
} // namespace Dali::Graphics::GLES
namespace Dali::Graphics::GLES
{
-using PipelineResource = Resource<Graphics::Pipeline, Graphics::PipelineCreateInfo>;
+class PipelineCache;
/**
- * @brief PipelineWrapper is the object
- * returned to the client-side
+ * @brief PipelineImpl is the implementation of Pipeline
+ *
+ * PipelineImpl is owned by the pipeline cache. The client-side
+ * will receive Graphics::Pipeline objects which are only
+ * wrappers for this implementation. The lifecycle of
+ * PipelineImpl is managed by the PipelineCache.
*/
class PipelineImpl
{
* @brief Constructor
* @param[in] createInfo valid TextureCreateInfo structure
* @param[in] controller Reference to the Controller
+ * @param[in] pipelineCache Reference to valid pipeline cache
*/
- PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+ PipelineImpl(const Graphics::PipelineCreateInfo& createInfo, Graphics::EglGraphicsController& controller, PipelineCache& pipelineCache);
/**
* @brief Destructor
~PipelineImpl();
/**
- * @brief Destroys all the low-level resources used by the class
- */
- void DestroyResource();
-
- /**
- * @brief Initializes low-level resources
- *
- * @return Tron success
- */
- bool InitializeResource();
-
- /**
- * @brief Discards object
- */
- void DiscardResource();
-
- /**
- * @brief returns GL program id
- * @return GL program id
- */
- [[nodiscard]] uint32_t GetGLProgram() const;
-
- /**
* @brief Binds pipeline
*
* Binds Pipeline by binding GL program and flushing state.
void Bind(GLES::PipelineImpl* prevPipeline);
/**
- * Executes state change function if condition met
+ * @brief Increases ref count
*/
- template<typename FUNC, typename STATE>
- void ExecuteStateChange(FUNC& func, const STATE* prevPipelineState, const STATE* thisPipelineState)
- {
- if(!prevPipelineState)
- {
- func();
- }
- else
- {
- // binary test and execute when different
- if(memcmp(prevPipelineState, thisPipelineState, sizeof(STATE)) != 0)
- {
- func();
- }
- }
- }
-
void Retain();
+ /**
+ * @brief Decreases ref count
+ */
void Release();
+ /**
+ * @brief Retrieves ref count
+ * @return Refcount value
+ */
[[nodiscard]] uint32_t GetRefCount() const;
+ /**
+ * @brief Returns PipelineCreateInfo structure
+ *
+ * @return PipelineCreateInfo structure
+ */
[[nodiscard]] const PipelineCreateInfo& GetCreateInfo() const;
+ /**
+ * @brief Returns controller
+ *
+ * @return Reference to the Controller
+ */
[[nodiscard]] auto& GetController() const;
- Graphics::GLES::Reflection& GetReflection();
-
private:
/**
* @brief Helper function. Copies state if pointer is set
}
}
- // Pipeline state is stored as a copy of create info
- // data.
+ // Pipeline state is store locally so any assigned pointers on a
+ // client-side may go safely out of scope.
struct PipelineState;
std::unique_ptr<PipelineState> mPipelineState;
EglGraphicsController& mController;
PipelineCreateInfo mCreateInfo;
- Graphics::GLES::Reflection mReflection;
-
- uint32_t mGlProgram{0u};
-
uint32_t mRefCount{0u};
};
/**
- * @brief Pipeline class wraps a unique pipeline object
- *
+ * @brief Pipeline class wraps the PipelineImpl
*/
class Pipeline : public Graphics::Pipeline
{
public:
Pipeline() = delete;
+ /**
+ * @brief Constructor
+ * @param pipeline Pipeline implementation
+ */
explicit Pipeline(GLES::PipelineImpl& pipeline)
: mPipeline(pipeline)
{
mPipeline.Retain();
}
- ~Pipeline() override
- {
- // decrease refcount
- mPipeline.Release();
- }
+ /**
+ * @brief Destructor
+ */
+ ~Pipeline() override;
+ /**
+ * @brief Returns pipeline implementation
+ *
+ * @return Valid pipeline implementation
+ */
[[nodiscard]] auto& GetPipeline() const
{
return mPipeline;
}
+ /**
+ * @brief Returns create info structure
+ *
+ * @return Valid create info structure
+ */
[[nodiscard]] const PipelineCreateInfo& GetCreateInfo() const;
+ /**
+ * @brief Returns controller
+ *
+ * @return reference to Controller
+ */
[[nodiscard]] EglGraphicsController& GetController() const;
+ bool operator==(const PipelineImpl* impl) const
+ {
+ return &mPipeline == impl;
+ }
+
+ /**
+ * @brief Run by UniquePtr to discard resource
+ */
+ void DiscardResource();
+
+ /**
+ * @brief Destroy resource
+ *
+ * Despite this class doesn't inherit Resource it must provide
+ * (so it won't duplicate same data) same set of functions
+ * so it can work with resource management functions of Controller.
+ */
+ void DestroyResource()
+ {
+ // Nothing to do here
+ }
+
private:
GLES::PipelineImpl& mPipeline;
};
--- /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-program.h"
+
+// INTERNAL HEADERS
+#include "egl-graphics-controller.h"
+#include "gles-graphics-reflection.h"
+#include "gles-graphics-shader.h"
+
+// EXTERNAL HEADERS
+#include <dali/integration-api/gl-abstraction.h>
+#include <dali/integration-api/gl-defines.h>
+
+namespace Dali::Graphics::GLES
+{
+struct ProgramImpl::Impl
+{
+ explicit Impl(EglGraphicsController& _controller, const ProgramCreateInfo& info)
+ : controller(_controller)
+ {
+ createInfo = info;
+ if(info.shaderState)
+ {
+ createInfo.shaderState = new std::vector<ShaderState>(*info.shaderState);
+ }
+ }
+
+ ~Impl()
+ {
+ delete createInfo.shaderState;
+ }
+
+ EglGraphicsController& controller;
+ ProgramCreateInfo createInfo;
+ uint32_t glProgram{};
+ uint32_t refCount{0u};
+
+ std::unique_ptr<GLES::Reflection> reflection{nullptr};
+};
+
+ProgramImpl::ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
+{
+ // Create implementation
+ mImpl = std::make_unique<Impl>(controller, createInfo);
+
+ // Build reflection
+ mImpl->reflection = std::make_unique<GLES::Reflection>(*this, controller);
+}
+
+ProgramImpl::~ProgramImpl() = default;
+
+bool ProgramImpl::Destroy()
+{
+ if(mImpl->glProgram)
+ {
+ auto& gl = *mImpl->controller.GetGL();
+ gl.DeleteProgram(mImpl->glProgram);
+ return true;
+ }
+ return false;
+}
+
+bool ProgramImpl::Create()
+{
+ // Create and link new program
+ auto& gl = *mImpl->controller.GetGL();
+ auto program = gl.CreateProgram();
+
+ const auto& info = mImpl->createInfo;
+ for(const auto& state : *info.shaderState)
+ {
+ const auto* shader = static_cast<const GLES::Shader*>(state.shader);
+
+ // Compile shader first (ignored when compiled)
+ shader->Compile();
+
+ gl.AttachShader(program, shader->GetGLShader());
+ }
+ gl.LinkProgram(program);
+
+ GLint status{0};
+ gl.GetProgramiv(program, GL_LINK_STATUS, &status);
+ if(status != GL_TRUE)
+ {
+ char output[4096];
+ GLsizei size{0u};
+ gl.GetProgramInfoLog(program, 4096, &size, output);
+
+ // log on error
+ // TODO: un-printf-it
+ printf("Log: %s\n", output);
+ gl.DeleteProgram(program);
+ return false;
+ }
+
+ mImpl->glProgram = program;
+
+ // Initialize reflection
+ mImpl->reflection->BuildUniformReflection();
+ mImpl->reflection->BuildVertexAttributeReflection();
+ mImpl->reflection->BuildUniformBlockReflection();
+
+ return true;
+}
+
+uint32_t ProgramImpl::GetGlProgram() const
+{
+ return mImpl->glProgram;
+}
+
+uint32_t ProgramImpl::Retain()
+{
+ return ++mImpl->refCount;
+}
+
+uint32_t ProgramImpl::Release()
+{
+ return --mImpl->refCount;
+}
+
+const Graphics::Reflection& ProgramImpl::GetReflection() const
+{
+ return *mImpl->reflection;
+}
+
+bool ProgramImpl::GetParameter(uint32_t parameterId, void* out)
+{
+ if(parameterId == 1) // a magic number to access program id
+ {
+ *reinterpret_cast<decltype(&mImpl->glProgram)>(out) = mImpl->glProgram;
+ return true;
+ }
+ return false;
+}
+
+EglGraphicsController& ProgramImpl::GetController() const
+{
+ return mImpl->controller;
+}
+
+const ProgramCreateInfo& ProgramImpl::GetCreateInfo() const
+{
+ return mImpl->createInfo;
+}
+
+Program::~Program()
+{
+ // Destroy GL resources of implementation. This should happen
+ // only if there's no more pipelines using this program so
+ // it is safe to do it in the destructor
+ if(!mProgram->Release())
+ {
+ mProgram->Destroy();
+ }
+}
+
+const Graphics::Reflection& Program::GetReflection() const
+{
+ return mProgram->GetReflection();
+}
+
+EglGraphicsController& Program::GetController() const
+{
+ return GetImplementation()->GetController();
+}
+
+const ProgramCreateInfo& Program::GetCreateInfo() const
+{
+ return GetImplementation()->GetCreateInfo();
+}
+
+void Program::DiscardResource()
+{
+ GetController().DiscardResource(this);
+}
+
+}; // namespace Dali::Graphics::GLES
--- /dev/null
+#ifndef DALI_GRAPHICS_GLES_PROGRAM_H
+#define DALI_GRAPHICS_GLES_PROGRAM_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-program-create-info.h>
+#include <dali/graphics-api/graphics-program.h>
+
+// INTERNAL INCLUDES
+#include "gles-graphics-resource.h"
+
+namespace Dali::Graphics
+{
+class Reflection;
+namespace GLES
+{
+/**
+ * @brief Program implementation
+ *
+ * Program implementation is owned only by the PipelineCache
+ *
+ * Like pipeline, it's created and managed by the PipelineCache
+ */
+class ProgramImpl
+{
+public:
+ /**
+ * @brief Constructor
+ *
+ * @param[in] createInfo Valid create info structure
+ * @param[in] controller Valid reference to the controller object
+ */
+ ProgramImpl(const Graphics::ProgramCreateInfo& createInfo, Graphics::EglGraphicsController& controller);
+
+ /**
+ * @brief Destructor
+ */
+ ~ProgramImpl();
+
+ /**
+ * @brief Destroys GL resources associated with the Program
+ *
+ * @return True on success
+ */
+ bool Destroy();
+
+ /**
+ * @brief Creates GL resource for this Program
+ *
+ * @return True on success
+ */
+ bool Create();
+
+ /**
+ * @brief Returns GL program id
+ *
+ * @return GL program id
+ */
+ [[nodiscard]] uint32_t GetGlProgram() const;
+
+ /**
+ * @brief Increases ref count
+ *
+ * @return new refcount
+ */
+ uint32_t Retain();
+
+ /**
+ * @brief Decreases ref count
+ *
+ * @return New refcount
+ */
+ uint32_t Release();
+
+ /**
+ * @brief Returns reflection
+ *
+ * @return Valid reflection associated with the Program
+ */
+ [[nodiscard]] const Graphics::Reflection& GetReflection() const;
+
+ /**
+ * @brief Returns controller
+ *
+ * @return Valid Controller object
+ */
+ [[nodiscard]] EglGraphicsController& GetController() const;
+
+ /**
+ * @brief Returns create info structure
+ *
+ * @return Reference to valid create info structure
+ */
+ [[nodiscard]] const ProgramCreateInfo& GetCreateInfo() const;
+
+ /**
+ * @brief Returns parameter value specified by parameterId
+ *
+ * This function can be used as a backdoor into the implementation
+ * used to retrieve internal data.
+ *
+ * @param[in] parameterId Integer parameter id
+ * @param[out] out Pointer to write to
+ *
+ * @return True on success
+ */
+ bool GetParameter(uint32_t parameterId, void* out);
+
+private:
+ friend class Program;
+
+ struct Impl;
+ std::unique_ptr<Impl> mImpl;
+};
+
+///////////////////////////////////////////////////////////////
+
+/**
+ * @brief Wrapper for the pipeline implementation
+ *
+ * This object is returned back to the client-side
+ */
+class Program : public Graphics::Program
+{
+public:
+ /**
+ * @brief Constructor
+ *
+ * @param[in] impl Pointer to valid implementation
+ */
+ explicit Program(ProgramImpl* impl)
+ : mProgram(impl)
+ {
+ mProgram->Retain();
+ }
+
+ /**
+ * @brief Destructor
+ */
+ ~Program() override;
+
+ /**
+ * @brief Returns reference to the Reflection object
+
+ * @return Reflection
+ */
+ [[nodiscard]] const Graphics::Reflection& GetReflection() const;
+
+ /**
+ * @brief Retrieves internal program implementation
+ *
+ * @return Valid pointer to the ProgramImpl object
+ */
+ [[nodiscard]] ProgramImpl* GetImplementation() const
+ {
+ return mProgram;
+ }
+
+ /**
+ * @brief Returns controller
+ *
+ * @return controller
+ */
+ [[nodiscard]] EglGraphicsController& GetController() const;
+
+ /**
+ * @brief Returns create info structure
+ *
+ * @return create info structure
+ */
+ [[nodiscard]] const ProgramCreateInfo& GetCreateInfo() const;
+
+ bool operator==(const GLES::Program& program) const
+ {
+ return (program.mProgram == mProgram);
+ }
+
+ bool operator==(const GLES::ProgramImpl* programImpl) const
+ {
+ return (programImpl == mProgram);
+ }
+
+ bool operator!=(const GLES::Program& program) const
+ {
+ return (program.mProgram != mProgram);
+ }
+
+ /**
+ * @brief Run by UniquePtr to discard resource
+ */
+ void DiscardResource();
+
+ /**
+ * @brief Destroying GL resources
+ *
+ * This function is kept for compatibility with Resource<> class
+ * so can the object can be use with templated functions.
+ */
+ void DestroyResource()
+ {
+ // nothing to do here
+ }
+
+private:
+ ProgramImpl* mProgram{nullptr};
+};
+} // namespace GLES
+} // namespace Dali::Graphics
+
+#endif //DALI_GRAPHICS_PROGRAM_H
#include <GLES3/gl3.h>
#include <GLES3/gl31.h>
+#include "gles-graphics-program.h"
+
#include <iostream>
namespace
}
}
-int GetGLDataTypeSize(GLenum type)
+uint32_t GetGLDataTypeSize(GLenum type)
{
// There are many more types than what are covered here, but
// they are not supported in dali.
} // namespace
-namespace Dali
-{
-namespace Graphics
-{
-namespace GLES
+namespace Dali::Graphics::GLES
{
-Reflection::Reflection(Graphics::EglGraphicsController& controller)
+Reflection::Reflection(GLES::ProgramImpl& program, Graphics::EglGraphicsController& controller)
: Graphics::Reflection(),
mController(controller),
- mGlProgram(0u)
+ mProgram(program)
{
}
-Reflection::~Reflection()
-{
-}
+Reflection::~Reflection() = default;
void Reflection::BuildVertexAttributeReflection()
{
+ auto glProgram = mProgram.GetGlProgram();
+
int written, size, location, maxLength, nAttribs;
GLenum type;
char* name;
auto gl = mController.GetGL();
- gl->GetProgramiv(mGlProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength);
- gl->GetProgramiv(mGlProgram, GL_ACTIVE_ATTRIBUTES, &nAttribs);
+ gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &maxLength);
+ gl->GetProgramiv(glProgram, GL_ACTIVE_ATTRIBUTES, &nAttribs);
mVertexInputAttributes.clear();
mVertexInputAttributes.resize(nAttribs);
name = new GLchar[maxLength];
for(int i = 0; i < nAttribs; i++)
{
- gl->GetActiveAttrib(mGlProgram, i, maxLength, &written, &size, &type, name);
- location = gl->GetAttribLocation(mGlProgram, name);
+ gl->GetActiveAttrib(glProgram, i, maxLength, &written, &size, &type, name);
+ location = gl->GetAttribLocation(glProgram, name);
AttributeInfo attributeInfo;
attributeInfo.location = location;
void Reflection::BuildUniformReflection()
{
+ auto glProgram = mProgram.GetGlProgram();
+
int maxLen;
char* name;
auto gl = mController.GetGL();
- gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen);
- gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORMS, &numUniforms);
+ gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLen);
+ gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORMS, &numUniforms);
mUniformBlocks.clear();
mDefaultUniformBlock.members.clear();
name = new char[maxLen];
- int maxUniformLocations;
- gl->GetProgramiv(mGlProgram, GL_MAX_UNIFORM_LOCATIONS, &maxUniformLocations);
+ using UniformLocationSizePair = std::pair<uint32_t, uint32_t>;
+ std::vector<UniformLocationSizePair> uniformSizes;
- std::vector<int> uniformSize;
- uniformSize.reserve(maxUniformLocations);
+ uniformSizes.reserve(numUniforms);
for(int i = 0; i < numUniforms; ++i)
{
int size;
GLenum type;
int written;
- gl->GetActiveUniform(mGlProgram, i, maxLen, &written, &size, &type, name);
- int location = gl->GetUniformLocation(mGlProgram, name);
- uniformSize[location] = GetGLDataTypeSize(type);
+ gl->GetActiveUniform(glProgram, i, maxLen, &written, &size, &type, name);
+ int location = gl->GetUniformLocation(glProgram, name);
+ uniformSizes.push_back(std::make_pair(location, GetGLDataTypeSize(type)));
Dali::Graphics::UniformInfo uniformInfo;
uniformInfo.name = name;
// Calculate the uniform offset
for(unsigned int i = 0; i < mDefaultUniformBlock.members.size(); ++i)
{
- mDefaultUniformBlock.members[i].offset = i == 0 ? 0 : mDefaultUniformBlock.members[i - 1].offset + uniformSize[mDefaultUniformBlock.members[i - 1].location];
+ if(i == 0)
+ {
+ mDefaultUniformBlock.members[i].offset = 0;
+ }
+ else
+ {
+ uint32_t previousUniformLocation = mDefaultUniformBlock.members[i - 1].location;
+ auto previousUniform = std::find_if(uniformSizes.begin(), uniformSizes.end(), [&previousUniformLocation](const UniformLocationSizePair& iter) { return iter.first == previousUniformLocation; });
+ mDefaultUniformBlock.members[i].offset = mDefaultUniformBlock.members[i - 1].offset + previousUniform->second;
+ }
}
- mDefaultUniformBlock.size = mDefaultUniformBlock.members.back().offset + uniformSize[mDefaultUniformBlock.members.back().location];
+ uint32_t lastUniformLocation = mDefaultUniformBlock.members.back().location;
+ auto lastUniform = std::find_if(uniformSizes.begin(), uniformSizes.end(), [&lastUniformLocation](const UniformLocationSizePair& iter) { return iter.first == lastUniformLocation; });
+ mDefaultUniformBlock.size = mDefaultUniformBlock.members.back().offset + lastUniform->second;
mUniformBlocks.push_back(mDefaultUniformBlock);
// TODO: Maybe this is not needed if uniform block is not support by dali shaders?
void Reflection::BuildUniformBlockReflection()
{
- auto gl = mController.GetGL();
-
- int numUniformBlocks = 0;
- gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
+ auto gl = mController.GetGL();
+ auto glProgram = mProgram.GetGlProgram();
+ int numUniformBlocks = 0;
+ gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCKS, &numUniformBlocks);
mUniformBlocks.clear();
mUniformBlocks.resize(numUniformBlocks);
int uniformBlockMaxLength = 0;
- gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &uniformBlockMaxLength);
+ gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &uniformBlockMaxLength);
char* uniformBlockName = new char[uniformBlockMaxLength];
for(int i = 0; i < numUniformBlocks; i++)
int length;
int blockBinding;
int blockDataSize;
- gl->GetActiveUniformBlockName(mGlProgram, i, uniformBlockMaxLength, &length, uniformBlockName);
- gl->GetActiveUniformBlockiv(mGlProgram, i, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
- gl->GetActiveUniformBlockiv(mGlProgram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &blockDataSize);
+ gl->GetActiveUniformBlockName(glProgram, i, uniformBlockMaxLength, &length, uniformBlockName);
+ gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
+ gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_DATA_SIZE, &blockDataSize);
Dali::Graphics::UniformBlockInfo uniformBlockInfo;
uniformBlockInfo.name = uniformBlockName;
uniformBlockInfo.binding = blockBinding;
int nUnis;
- gl->GetActiveUniformBlockiv(mGlProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &nUnis);
+ gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &nUnis);
int* unifIndexes = new GLint[nUnis];
- gl->GetActiveUniformBlockiv(mGlProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, unifIndexes);
+ gl->GetActiveUniformBlockiv(glProgram, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, unifIndexes);
char* uniformName{};
int maxUniLen;
- gl->GetProgramiv(mGlProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniLen);
+ gl->GetProgramiv(glProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniLen);
for(int unif = 0; unif < nUnis; ++unif)
{
int size;
GLenum type;
- gl->GetActiveUniform(mGlProgram, uniIndex, maxUniLen, &length, &size, &type, uniformName);
- int location = gl->GetUniformLocation(mGlProgram, uniformName);
+ gl->GetActiveUniform(glProgram, uniIndex, maxUniLen, &length, &size, &type, uniformName);
+ int location = gl->GetUniformLocation(glProgram, uniformName);
Dali::Graphics::UniformInfo uniform;
uniform.name = uniformName;
return Graphics::ShaderLanguage::GLSL_3_2;
}
-void Reflection::SetGlProgram(uint32_t glProgram)
-{
- mGlProgram = glProgram;
-
- BuildVertexAttributeReflection();
- BuildUniformReflection();
-}
-
-} // namespace GLES
-} // namespace Graphics
-} // namespace Dali
+} // namespace Dali::Graphics::GLES
#include <dali/graphics-api/graphics-types.h>
#include <dali/integration-api/gl-abstraction.h>
-namespace Dali
-{
-namespace Graphics
+namespace Dali::Graphics
{
class EglGraphicsController;
namespace GLES
{
+class ProgramImpl;
constexpr uint32_t ERROR_ATTRIBUTE_NOT_FOUND(-1u);
/**
class Reflection : public Dali::Graphics::Reflection
{
public:
- Reflection(Graphics::EglGraphicsController& controller);
- virtual ~Reflection();
+ explicit Reflection(GLES::ProgramImpl& program, Graphics::EglGraphicsController& controller);
+
+ ~Reflection() override;
// not copyable
Reflection(const Reflection&) = delete;
* @param [in] name The name of vertex attribute
* @return The index of the vertex attribute in the shader
*/
- uint32_t GetVertexAttributeLocation(const std::string& name) const;
+ [[nodiscard]] uint32_t GetVertexAttributeLocation(const std::string& name) const override;
/**
* @brief Gets the format of a vertex attribute.
* @param [in] location The location of vertex attribute
* @return The format of a vertex attribute
*/
- Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeFormat(uint32_t location) const;
+ [[nodiscard]] Dali::Graphics::VertexInputAttributeFormat GetVertexAttributeFormat(uint32_t location) const override;
/**
* @brief Gets the name of a vertex attribute.
* @param [in] location The location of vertex attribute
* @return The name of the vertex attribute
*/
- std::string GetVertexAttributeName(uint32_t location) const;
+ [[nodiscard]] std::string GetVertexAttributeName(uint32_t location) const override;
/**
* @brief Gets the locations of all the vertex attribute in the shader.
*
* @return A vector of the locations of all the vertex attributes in the shader
*/
- std::vector<uint32_t> GetVertexAttributeLocations() const;
+ [[nodiscard]] std::vector<uint32_t> GetVertexAttributeLocations() const override;
// Uniform blocks
*
* @return The number of uniform blocks
*/
- uint32_t GetUniformBlockCount() const;
+ [[nodiscard]] uint32_t GetUniformBlockCount() const override;
/**
* @brief Gets the binding point to which the uniform block with the given index is binded.
* @param [in] index The index of the uniform block
* @return The binding point
*/
- uint32_t GetUniformBlockBinding(uint32_t index) const;
+ [[nodiscard]] uint32_t GetUniformBlockBinding(uint32_t index) const override;
/**
* @brief Gets the size of the uniform block with the given index.
* @param [in] index The index of the uniform block
* @return The size of the uniform block
*/
- uint32_t GetUniformBlockSize(uint32_t index) const;
+ [[nodiscard]] uint32_t GetUniformBlockSize(uint32_t index) const override;
/**
* @brief Retrieves the information of the uniform block with the given index.
* @param [out] out A structure that contains the information of the uniform block
* @return Whether the uniform block exists or not
*/
- bool GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const;
+ bool GetUniformBlock(uint32_t index, Dali::Graphics::UniformBlockInfo& out) const override;
/**
* @brief Gets the binding points of all the uniform blocks in the shader.
*
* @return A vector of binding points
*/
- std::vector<uint32_t> GetUniformBlockLocations() const;
+ [[nodiscard]] std::vector<uint32_t> GetUniformBlockLocations() const override;
/**
* @brief Gets the name of uniform block with the given index.
* @param [in] blockIndex The index of the uniform block
* @return The name of the uniform block
*/
- std::string GetUniformBlockName(uint32_t blockIndex) const;
+ [[nodiscard]] std::string GetUniformBlockName(uint32_t blockIndex) const override;
/**
* @brief Gets the number of uniforms in the uniform block with the given index.
* @param [in] blockIndex The index of the uniform block
* @return The number of uniforms in the uniform block
*/
- uint32_t GetUniformBlockMemberCount(uint32_t blockIndex) const;
+ [[nodiscard]] uint32_t GetUniformBlockMemberCount(uint32_t blockIndex) const override;
/**
* @brief Gets the name of the uniform in the given location within the uniform block.
* @param [in] memberLocation The location of the uniform within the uniform block
* @return The name of the uniform
*/
- std::string GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const;
+ [[nodiscard]] std::string GetUniformBlockMemberName(uint32_t blockIndex, uint32_t memberLocation) const override;
/**
* @brief Gets the byte offset of the uniform in the given location within the uniform block.
* @param [in] memberLocation The location of the uniform within the uniform block
* @return The byte offset of the uniform
*/
- uint32_t GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const;
+ [[nodiscard]] uint32_t GetUniformBlockMemberOffset(uint32_t blockIndex, uint32_t memberLocation) const override;
// Named uniforms
* @param [out] out The information of the uniform
* @return Whether the uniform exists or not
*/
- bool GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const;
+ [[nodiscard]] bool GetNamedUniform(const std::string& name, Dali::Graphics::UniformInfo& out) const override;
// Sampler
*
* @return A vector of the sampler uniforms
*/
- std::vector<Dali::Graphics::UniformInfo> GetSamplers() const;
+ [[nodiscard]] std::vector<Dali::Graphics::UniformInfo> GetSamplers() const override;
// Language
*
* @return The language of the shader
*/
- Graphics::ShaderLanguage GetLanguage() const;
-
- // Other
+ [[nodiscard]] Graphics::ShaderLanguage GetLanguage() const override;
- /**
- * @brief Set the program object whose reflection to be built
- *
- * @param [in] glProgram The program object
- */
- void SetGlProgram(uint32_t glProgram);
-
-private:
+public:
/**
* @brief Build the reflection of vertex attributes
*/
private:
Graphics::EglGraphicsController& mController; ///< The Graphics controller
- uint32_t mGlProgram; ///< The GL program object
+ GLES::ProgramImpl& mProgram; ///< The Program object
struct AttributeInfo
{
};
} // namespace GLES
-} // namespace Graphics
-} // namespace Dali
+} // namespace Dali::Graphics
#endif // DALI_GRAPHICS_GLES_REFLECTION_H
*
*/
+// CLASS HEADER
#include "gles-graphics-shader.h"
-#include <dali/integration-api/gl-abstraction.h>
-#include <vector>
-#include "egl-graphics-controller.h"
-#include <GLES3/gl3.h>
+// INTERNAL INCLUDES
+#include "egl-graphics-controller.h"
-namespace Dali
+namespace Dali::Graphics::GLES
{
-namespace Graphics
-{
-namespace GLES
+struct Shader::Impl
{
+ Impl() = default;
+ ~Impl() = default;
+
+ std::vector<char> source{};
+ uint32_t glShader{};
+};
+
Shader::Shader(const Graphics::ShaderCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
: ShaderResource(createInfo, controller)
{
- if(mCreateInfo.sourceData && mCreateInfo.sourceSize)
+ // push shader to the create queue
+ mImpl = std::make_unique<Impl>();
+
+ // Make a copy of source code
+ mImpl->source.resize(createInfo.sourceSize);
+ std::copy(reinterpret_cast<const char*>(mCreateInfo.sourceData),
+ reinterpret_cast<const char*>(mCreateInfo.sourceData) + mCreateInfo.sourceSize,
+ mImpl->source.begin());
+
+ // Substitute pointer
+ mCreateInfo.sourceData = mImpl->source.data();
+}
+
+bool Shader::Compile() const
+{
+ auto& gl = *GetController().GetGL();
+ if(!mImpl->glShader)
{
- printf("GLES::Shader: stage: %d, sourceMode: %d, size: %u, source:\n\n%s\n", (int)mCreateInfo.pipelineStage, (int)mCreateInfo.sourceMode, mCreateInfo.sourceSize, static_cast<const char*>(mCreateInfo.sourceData));
+ GLenum pipelineStage{0u};
+ switch(GetCreateInfo().pipelineStage)
+ {
+ case Graphics::PipelineStage::TOP_OF_PIPELINE:
+ {
+ break;
+ }
+ case Graphics::PipelineStage::VERTEX_SHADER:
+ {
+ pipelineStage = GL_VERTEX_SHADER;
+ break;
+ }
+ case Graphics::PipelineStage::GEOMETRY_SHADER:
+ {
+ break;
+ }
+ case Graphics::PipelineStage::FRAGMENT_SHADER:
+ {
+ pipelineStage = GL_FRAGMENT_SHADER;
+ break;
+ }
+ case Graphics::PipelineStage::COMPUTE_SHADER:
+ {
+ break;
+ }
+ case Graphics::PipelineStage::TESSELATION_CONTROL:
+ {
+ break;
+ }
+ case Graphics::PipelineStage::TESSELATION_EVALUATION:
+ {
+ break;
+ }
+ case Graphics::PipelineStage::BOTTOM_OF_PIPELINE:
+ {
+ break;
+ }
+ }
+
+ if(pipelineStage)
+ {
+ auto shader = gl.CreateShader(pipelineStage);
+ const auto src = reinterpret_cast<const char*>(GetCreateInfo().sourceData);
+ GLint size = GetCreateInfo().sourceSize;
+ gl.ShaderSource(shader, 1, const_cast<const char**>(&src), &size);
+ gl.CompileShader(shader);
+
+ GLint status{0};
+ gl.GetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if(status != GL_TRUE)
+ {
+ char output[4096];
+ GLsizei size{0u};
+ gl.GetShaderInfoLog(shader, 4096, &size, output);
+ printf("Code: %s\n", reinterpret_cast<const char*>(GetCreateInfo().sourceData));
+ printf("Log: %s\n", output);
+ gl.DeleteShader(shader);
+ return false;
+ }
+
+ // TODO: check error
+ mImpl->glShader = shader;
+ }
+ return true;
}
+ return true;
+}
+
+uint32_t Shader::GetGLShader() const
+{
+ return mImpl->glShader;
}
-} // namespace GLES
-} // namespace Graphics
-} // namespace Dali
+} // namespace Dali::Graphics::GLES
\ No newline at end of file
* @brief Compiles shader
* @return
*/
- bool Compile() const
- {
- mGlShader = 0;
- return true;
- }
+ bool Compile() const;
/**
* @brief Called when UniquePtr<> on client-side dies
// TODO: Implement moving to the discard queue
}
- uint32_t GetGLShader() const
- {
- return mGlShader;
- }
+ uint32_t GetGLShader() const;
private:
- mutable uint32_t mGlShader;
+ struct Impl;
+ std::unique_ptr<Impl> mImpl;
};
} // namespace Dali::Graphics::GLES