return true;
}
+bool TestGlAbstraction::IsMultisampledRenderToTextureSupported()
+{
+ return true;
+}
+
bool TestGlAbstraction::IsBlendEquationSupported(DevelBlendEquation::Type blendEquation)
{
return true;
bool IsAdvancedBlendEquationSupported() override;
+ bool IsMultisampledRenderToTextureSupported() override;
+
bool IsBlendEquationSupported(DevelBlendEquation::Type blendEquation) override;
std::string GetShaderVersionPrefix();
{
}
+ inline void FramebufferTexture2DMultisample(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) override
+ {
+ // TODO : Check it if need
+ FramebufferTexture2D(target, attachment, textarget, texture, level);
+ }
+
inline void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) override
{
}
#define DALI_TEST_GRAPHICS_APPLICATION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
}
/**
+ * @return true if multisampled render to texture is supported
+ */
+ bool IsMultisampledRenderToTextureSupported() override
+ {
+ return true;
+ }
+
+ /**
* @return true if graphics subsystem is initialized
*/
bool IsInitialized() override
}
/**
+ * @return the maximum texture samples when we use multisampled texture
+ */
+ uint8_t GetMaxTextureSamples() override
+ {
+ return 8u;
+ }
+
+ /**
* @return the version number of the shader language
*/
uint32_t GetShaderLanguageVersion() override
virtual void EmitSocketAvailable(Accessible* obj) = 0;
/**
+ * @brief Emits ScrollStarted event on at-spi bus.
+ *
+ * @param obj Accessible Object
+ */
+ virtual void EmitScrollStarted(Accessible *obj) = 0;
+
+ /**
+ * @brief Emits ScrollFinished event on at-spi bus.
+ *
+ * @param obj Accessible Object
+ */
+ virtual void EmitScrollFinished(Accessible *obj) = 0;
+
+ /**
* @brief Emits state-changed event on at-spi bus.
*
* @param[in] obj The accessible object
ATTRIBUTES_CHANGED,\r
MOVED_OUT,\r
WINDOW_CHANGED,\r
+ SCROLL_STARTED,\r
+ SCROLL_FINISHED,\r
MAX_COUNT\r
};\r
\r
void EmitSocketAvailable();
/**
+ * @brief Emits "ScrollStarted" event.
+ *
+ */
+ void EmitScrollStarted();
+
+ /**
+ * @brief Emits "ScrollFinished" event.
+ *
+ */
+ void EmitScrollFinished();
+
+ /**
* @brief Emits "highlighted" event.
*
* @param[in] event The enumerated window event
}
}
+void Accessible::EmitScrollStarted()
+{
+ if(auto bridgeData = GetBridgeData())
+ {
+ bridgeData->mBridge->EmitScrollStarted(this);
+ }
+}
+
+void Accessible::EmitScrollFinished()
+{
+ if(auto bridgeData = GetBridgeData())
+ {
+ bridgeData->mBridge->EmitScrollFinished(this);
+ }
+}
+
void Accessible::Emit(WindowEvent event, unsigned int detail)
{
if(auto bridgeData = GetBridgeData())
obj->GetAddress(),
{"", "root"});
}
+
+void BridgeObject::EmitScrollStarted(Accessible* obj)
+{
+ if(!IsUp() || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::SCROLL_STARTED])
+ {
+ return;
+ }
+
+ mDbusServer.emit2<Address>(
+ GetAccessiblePath(obj),
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT),
+ "ScrollStarted",
+ {"", "root"});
+}
+
+void BridgeObject::EmitScrollFinished(Accessible* obj)
+{
+ if(!IsUp() || obj->IsHidden() || obj->GetSuppressedEvents()[AtspiEvent::SCROLL_FINISHED])
+ {
+ return;
+ }
+
+ mDbusServer.emit2<Address>(
+ GetAccessiblePath(obj),
+ Accessible::GetInterfaceName(AtspiInterface::EVENT_OBJECT),
+ "ScrollFinished",
+ {"", "root"});
+}
\ No newline at end of file
*/
void EmitSocketAvailable(Dali::Accessibility::Accessible* obj) override;
+ /**
+ * @copydoc Dali::Accessibility::Bridge::EmitScrollStarted()
+ */
+ void EmitScrollStarted(Dali::Accessibility::Accessible* obj) override;
+
+ /**
+ * @copydoc Dali::Accessibility::Bridge::EmitScrollFinished()
+ */
+ void EmitScrollFinished(Dali::Accessibility::Accessible* obj) override;
+
protected:
DBus::DBusInterfaceDescription::SignalId mStateChanged;
};
{
}
+void Accessibility::Accessible::EmitScrollStarted()
+{
+}
+
+void Accessibility::Accessible::EmitScrollFinished()
+{
+}
+
void Accessibility::Accessible::NotifyAccessibilityStateChange(Accessibility::States states, bool isRecursive)
{
}
{
}
+ void EmitScrollStarted(Accessibility::Accessible* obj) override
+ {
+ }
+
+ void EmitScrollFinished(Accessibility::Accessible* obj) override
+ {
+ }
+
void EmitStateChanged(Accessibility::Accessible* obj, Accessibility::State state, int newValue, int reserved) override
{
}
#define DALI_INTERNAL_BASE_GRAPHICS_INTERFACE_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
virtual bool IsAdvancedBlendEquationSupported() = 0;
/**
+ * @return true if multisampled render to texture is supported
+ */
+ virtual bool IsMultisampledRenderToTextureSupported() = 0;
+
+ /**
* @return true if graphics subsystem is initialized
*/
virtual bool IsInitialized() = 0;
virtual uint32_t GetMaxTextureSize() = 0;
/**
+ * @return the maximum texture samples when we use multisampled texture
+ */
+ virtual uint8_t GetMaxTextureSamples() = 0;
+
+ /**
* @return the version number of the shader language
*/
virtual uint32_t GetShaderLanguageVersion() = 0;
${adaptor_graphics_dir}/gles/egl-sync-implementation.cpp
${adaptor_graphics_dir}/gles/egl-context-helper-implementation.cpp
${adaptor_graphics_dir}/gles/gl-extensions.cpp
+ ${adaptor_graphics_dir}/gles/gl-extensions-support.cpp
${adaptor_graphics_dir}/gles/gl-proxy-implementation.cpp
${adaptor_graphics_dir}/gles/egl-graphics-factory.cpp
${adaptor_graphics_dir}/gles/egl-graphics.cpp
Framebuffer::Framebuffer(const Graphics::FramebufferCreateInfo& createInfo, Graphics::EglGraphicsController& controller)
: FramebufferResource(createInfo, controller)
{
+ // Check whether we need to consider multisampling
+ if(createInfo.multiSamplingLevel > 1u && controller.GetGraphicsInterface()->IsMultisampledRenderToTextureSupported())
+ {
+ mMultisamples = std::min(createInfo.multiSamplingLevel, controller.GetGraphicsInterface()->GetMaxTextureSamples());
+ }
+
// Add framebuffer to the Resource queue
mController.AddFramebuffer(*this);
}
AttachTexture(depthTexture, attachmentId, 0, mCreateInfo.depthStencilAttachment.depthLevel);
}
- else if(mCreateInfo.depthStencilAttachment.depthUsage == Graphics::DepthStencilAttachment::Usage::WRITE &&
- mCreateInfo.depthStencilAttachment.stencilUsage == Graphics::DepthStencilAttachment::Usage::WRITE)
- {
- // Create depth+stencil renderbuffer
- gl->GenRenderbuffers(1, &mStencilBufferId);
- gl->BindRenderbuffer(GL_RENDERBUFFER, mStencilBufferId);
- gl->RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mCreateInfo.size.width, mCreateInfo.size.height);
- gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mStencilBufferId);
- }
- else if(mCreateInfo.depthStencilAttachment.depthUsage == Graphics::DepthStencilAttachment::Usage::WRITE)
- {
- // Create depth renderbuffer
- gl->GenRenderbuffers(1, &mDepthBufferId);
- gl->BindRenderbuffer(GL_RENDERBUFFER, mDepthBufferId);
- gl->RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, mCreateInfo.size.width, mCreateInfo.size.height);
- gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepthBufferId);
- }
- else if(mCreateInfo.depthStencilAttachment.stencilUsage == Graphics::DepthStencilAttachment::Usage::WRITE)
+ else
{
- // Create stencil renderbuffer
- gl->GenRenderbuffers(1, &mStencilBufferId);
- gl->BindRenderbuffer(GL_RENDERBUFFER, mStencilBufferId);
- gl->RenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, mCreateInfo.size.width, mCreateInfo.size.height);
- gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mStencilBufferId);
+ const bool depthWrite = mCreateInfo.depthStencilAttachment.depthUsage == Graphics::DepthStencilAttachment::Usage::WRITE;
+ const bool stencilWrite = mCreateInfo.depthStencilAttachment.stencilUsage == Graphics::DepthStencilAttachment::Usage::WRITE;
+
+ // Check whether we need to use RenderBuffer
+ if(depthWrite || stencilWrite)
+ {
+ // if stencil is write, use renderbuffer as mStencilBufferId.
+ uint32_t& bufferId = stencilWrite ? mStencilBufferId : mDepthBufferId;
+ const auto internalFormat = depthWrite ? (stencilWrite ? GL_DEPTH24_STENCIL8 : GL_DEPTH_COMPONENT16) : GL_STENCIL_INDEX8;
+ const auto attachment = depthWrite ? (stencilWrite ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT) : GL_STENCIL_ATTACHMENT;
+
+ gl->GenRenderbuffers(1, &bufferId);
+ gl->BindRenderbuffer(GL_RENDERBUFFER, bufferId);
+
+ if(mMultisamples <= 1u)
+ {
+ gl->RenderbufferStorage(GL_RENDERBUFFER, internalFormat, mCreateInfo.size.width, mCreateInfo.size.height);
+ }
+ else
+ {
+ gl->RenderbufferStorageMultisample(GL_RENDERBUFFER, mMultisamples, internalFormat, mCreateInfo.size.width, mCreateInfo.size.height);
+ }
+ gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, bufferId);
+ }
}
context->BindFrameBuffer(GL_FRAMEBUFFER, 0);
if(gl)
{
auto graphicsTexture = static_cast<const GLES::Texture*>(texture);
- if(graphicsTexture->GetCreateInfo().textureType == Graphics::TextureType::TEXTURE_2D)
+ auto textarget = (graphicsTexture->GetCreateInfo().textureType == Graphics::TextureType::TEXTURE_2D) ? graphicsTexture->GetGlTarget() : GL_TEXTURE_CUBE_MAP_POSITIVE_X + layerId;
+ if(mMultisamples <= 1u)
{
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, attachmentId, graphicsTexture->GetGlTarget(), graphicsTexture->GetGLTexture(), levelId);
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, attachmentId, textarget, graphicsTexture->GetGLTexture(), levelId);
}
else
{
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, attachmentId, GL_TEXTURE_CUBE_MAP_POSITIVE_X + layerId, graphicsTexture->GetGLTexture(), levelId);
+ gl->FramebufferTexture2DMultisample(GL_FRAMEBUFFER, attachmentId, textarget, graphicsTexture->GetGLTexture(), levelId, mMultisamples);
}
}
}
#define DALI_GRAPHICS_GLES_FRAMEBUFFER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
uint32_t mFramebufferId{0u};
uint32_t mDepthBufferId{0u};
uint32_t mStencilBufferId{0u};
+ uint32_t mMultisamples{1u};
bool mInitialized{false};
};
void EglGraphics::CacheConfigurations(ConfigurationManager& configurationManager)
{
mGLES->SetIsAdvancedBlendEquationSupported(configurationManager.IsAdvancedBlendEquationSupported());
+ mGLES->SetIsMultisampledRenderToTextureSupported(configurationManager.IsMultisampledRenderToTextureSupported());
mGLES->SetShadingLanguageVersion(configurationManager.GetShadingLanguageVersion());
}
#define DALI_INTERNAL_BASE_GRAPHICS_IMPLEMENTATION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
return mGLES->IsAdvancedBlendEquationSupported();
}
+ bool IsMultisampledRenderToTextureSupported() override
+ {
+ return mGLES->IsMultisampledRenderToTextureSupported();
+ }
+
/**
* @return true if graphics subsystem is initialized
*/
return mGLES->GetMaxTextureSize();
}
+ uint8_t GetMaxTextureSamples() override
+ {
+ return mGLES->GetMaxTextureSamples();
+ }
+
uint32_t GetShaderLanguageVersion() override
{
return mGLES->GetShadingLanguageVersion();
--- /dev/null
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/graphics/gles/gl-extensions-support.h>
+
+// EXTERNAL HEADER
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include <algorithm> // for std::find_if
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <utility> // for std::pair
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+namespace
+{
+static constexpr const char* KHR_BLEND_EQUATION_ADVANCED = "GL_KHR_blend_equation_advanced";
+static constexpr const char* EXT_MULTISAMPLED_RENDER_TO_TEXTURE = "GL_EXT_multisampled_render_to_texture";
+
+} // namespace
+
+namespace GlExtensionCache
+{
+void GlExtensionSupportedCacheList::EnsureGlExtensionSupportedCheck()
+{
+ // Note that this function calls at most one time.
+ // But the number of GL_EXTENSIONS itmes are so vairous.
+ // We need to reduce extension checkup
+
+ const char* const extensionStr = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
+ std::istringstream stream(extensionStr);
+ std::string currentExtension;
+
+ // Create expected string-to-checkertype convertor.
+ using StringTypePair = std::pair<std::string_view, GlExtensionCheckerType>;
+ // clang-format off
+ std::vector<StringTypePair> extensionStringToTypeList
+ {
+ {KHR_BLEND_EQUATION_ADVANCED, GlExtensionCheckerType::BLEND_EQUATION_ADVANCED },
+
+#ifndef DALI_PROFILE_UBUNTU
+ // @todo : Current ubuntu profile's multisamples FBO make crash when eglDestroyContext.
+ // Invalidate multisampled render to texture feature hardly.
+ {EXT_MULTISAMPLED_RENDER_TO_TEXTURE, GlExtensionCheckerType::MULTISAMPLED_RENDER_TO_TEXTURE},
+#endif //DALI_PROFILE_UBUNTU
+
+ ///< Append additional extension checker type here.
+ };
+ // clang-format on
+
+ while(!extensionStringToTypeList.empty() && std::getline(stream, currentExtension, ' '))
+ {
+ auto findResult = std::find_if(extensionStringToTypeList.begin(),
+ extensionStringToTypeList.end(),
+ [¤tExtension](const StringTypePair& value) {
+ return value.first == currentExtension;
+ });
+ if(findResult != extensionStringToTypeList.end())
+ {
+ auto type = findResult->second;
+
+ // Mark as True.
+ MarkSupported(type, true);
+
+ // Remove from list. we don't need to check this extension.
+ extensionStringToTypeList.erase(findResult);
+ }
+ }
+
+ // Set supported as false if extension keyword not exist.
+ SetAllUncachedAsNotSupported();
+}
+} // namespace GlExtensionCache
+} // namespace Adaptor
+} // namespace Internal
+} // namespace Dali
--- /dev/null
+#ifndef DALI_INTERNAL_GL_EXTENSION_SUPPORT_H
+#define DALI_INTERNAL_GL_EXTENSION_SUPPORT_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.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/common/vector-wrapper.h>
+#include <cstdint>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+/**
+ * Common extensions support checker type.
+ * Type value should be start with 0, and increase continuous.
+ */
+using ExtensionCheckerType = uint32_t;
+
+/**
+ * @brief Store the supported information cached or not.
+ */
+struct ExtensionSupportedCache
+{
+ ExtensionSupportedCache()
+ : isSupported{false},
+ cached{false}
+ {
+ }
+ bool isSupported : 1;
+ bool cached : 1;
+};
+
+/**
+ * @brief Extension Supported caching system interface.
+ *
+ * Create ExtensionSupportedCache list and Set/Get supported value.
+ * Use ExtensionCheckerType as index of container.
+ *
+ * It will help to check gl extension & egl extention support.
+ */
+struct ExtensionSupportedCacheListInterface
+{
+ ExtensionSupportedCacheListInterface(const uint32_t maxCount)
+ : mCachedItemCount(0u),
+ mMaxCount(maxCount),
+ mData{maxCount}
+ {
+ }
+
+ /**
+ * @brief Check whether we need to check some more extension types or not.
+ * @return True if some extension type to check remained. False if we checked all extension types.
+ */
+ inline bool NeedFullCheck() const
+ {
+ return mCachedItemCount < mMaxCount;
+ }
+
+ /**
+ * @brief Set extension is supported or not
+ * If we already cached the result before, just ignored.
+ *
+ * @param[in] type The index of extension type.
+ * @param[in] isSupported Whether this extension supported or not. Default as true.
+ */
+ inline void MarkSupported(ExtensionCheckerType type, bool isSupported = true)
+ {
+ auto& cache = mData[static_cast<uint32_t>(type)];
+ if(!cache.cached)
+ {
+ cache.cached = true;
+ cache.isSupported = isSupported;
+ ++mCachedItemCount;
+ }
+ }
+
+ /**
+ * @brief Get extension is supported or not.
+ *
+ * @param[in] type The index of extension type.
+ * @return True if we cached extension as supported. False otherwise.
+ */
+ inline bool IsSupported(ExtensionCheckerType type) const
+ {
+ return mData[static_cast<uint32_t>(type)].isSupported;
+ }
+
+ /**
+ * @brief Get extension is cached or not.
+ *
+ * @param[in] type The index of extension type.
+ * @return True if we cached extension. False otherwise.
+ */
+ inline bool IsCached(ExtensionCheckerType type) const
+ {
+ return mData[static_cast<uint32_t>(type)].cached;
+ }
+
+ /**
+ * @brief Mark all uncached extension type as not supported.
+ * After this API called, we can assume that every extensions are cached.
+ */
+ void SetAllUncachedAsNotSupported()
+ {
+ if(NeedFullCheck())
+ {
+ for(auto&& iter : mData)
+ {
+ if(!iter.cached)
+ {
+ iter.isSupported = false;
+ iter.cached = true;
+ }
+ }
+
+ // Mark all cached.
+ mCachedItemCount = mMaxCount;
+ }
+ }
+
+ uint32_t mCachedItemCount;
+ const uint32_t mMaxCount;
+
+ std::vector<ExtensionSupportedCache> mData;
+};
+
+/**
+ * Gl extensions support checker system.
+ */
+namespace GlExtensionCache
+{
+enum GlExtensionCheckerType
+{
+ BLEND_EQUATION_ADVANCED = 0,
+ MULTISAMPLED_RENDER_TO_TEXTURE,
+ ///< Append additional extension checker type here.
+ EXTENSION_CHECKER_TYPE_MAX,
+};
+
+/**
+ * @brief Extension Supported caching system for gl.
+ */
+struct GlExtensionSupportedCacheList : public ExtensionSupportedCacheListInterface
+{
+ GlExtensionSupportedCacheList()
+ : ExtensionSupportedCacheListInterface(static_cast<uint32_t>(GlExtensionCheckerType::EXTENSION_CHECKER_TYPE_MAX))
+ {
+ }
+
+ /**
+ * Ensure that we check all gl extension features for this system.
+ */
+ void EnsureGlExtensionSupportedCheck();
+};
+} // namespace GlExtensionCache
+
+} // namespace Adaptor
+
+} // namespace Internal
+
+} // namespace Dali
+
+#endif /* DALI_INTERNAL_GL_EXTENSION_SUPPORT_H */
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
#ifdef GL_KHR_blend_equation_advanced
mBlendBarrierKHR(nullptr),
#endif
+#ifdef GL_EXT_multisampled_render_to_texture
+ mGlRenderbufferStorageMultisampleEXT(nullptr),
+ mGlFramebufferTexture2DMultisampleEXT(nullptr),
+#endif
mInitialized(false)
{
}
{
// initialize extension on first use as on some hw platforms a context
// has to be bound for the extensions to return correct pointer
- if(!mInitialized)
+ if(DALI_UNLIKELY(!mInitialized))
{
Initialize();
}
{
// initialize extension on first use as on some hw platforms a context
// has to be bound for the extensions to return correct pointer
- if(!mInitialized)
+ if(DALI_UNLIKELY(!mInitialized))
{
Initialize();
}
{
// initialize extension on first use as on some hw platforms a context
// has to be bound for the extensions to return correct pointer
- if(!mInitialized)
+ if(DALI_UNLIKELY(!mInitialized))
{
Initialize();
}
{
// initialize extension on first use as on some hw platforms a context
// has to be bound for the extensions to return correct pointer
- if(!mInitialized)
+ if(DALI_UNLIKELY(!mInitialized))
{
Initialize();
}
return false;
}
+void GlExtensions::RenderbufferStorageMultisampleEXT(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
+{
+ // initialize extension on first use as on some hw platforms a context
+ // has to be bound for the extensions to return correct pointer
+ if(DALI_UNLIKELY(!mInitialized))
+ {
+ Initialize();
+ }
+
+#ifdef GL_EXT_multisampled_render_to_texture
+ if(mGlRenderbufferStorageMultisampleEXT)
+ {
+ mGlRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
+ }
+ else
+ {
+ DALI_LOG_ERROR("Error: glRenderbufferStorageMultisampleEXT extension is not available\n");
+ DALI_ASSERT_DEBUG(0);
+ }
+#endif
+}
+
+void GlExtensions::FramebufferTexture2DMultisampleEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples)
+{
+ // initialize extension on first use as on some hw platforms a context
+ // has to be bound for the extensions to return correct pointer
+ if(DALI_UNLIKELY(!mInitialized))
+ {
+ Initialize();
+ }
+
+#ifdef GL_EXT_multisampled_render_to_texture
+ if(mGlFramebufferTexture2DMultisampleEXT)
+ {
+ mGlFramebufferTexture2DMultisampleEXT(target, attachment, textarget, texture, level, samples);
+ }
+ else
+ {
+ DALI_LOG_ERROR("Error: glFramebufferTexture2DMultisampleEXT extension is not available\n");
+ DALI_ASSERT_DEBUG(0);
+ }
+#endif
+}
+
void GlExtensions::Initialize()
{
mInitialized = true;
#ifdef GL_KHR_blend_equation_advanced
mBlendBarrierKHR = reinterpret_cast<PFNGLBLENDBARRIERKHRPROC>(eglGetProcAddress("glBlendBarrierKHR"));
#endif
+
+#ifdef GL_EXT_multisampled_render_to_texture
+ mGlRenderbufferStorageMultisampleEXT = reinterpret_cast<PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC>(eglGetProcAddress("glRenderbufferStorageMultisampleEXT"));
+ mGlFramebufferTexture2DMultisampleEXT = reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC>(eglGetProcAddress("glFramebufferTexture2DMultisampleEXT"));
+#endif
}
} // namespace Adaptor
#define DALI_INTERNAL_GL_EXTENSION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
*/
bool BlendBarrierKHR();
+ /**
+ * GLES extension
+ * Establish data storage, format, dimensions and sample count of a renderbuffer object's image
+ *
+ * @param[in] target Specifies a binding to which the target of the allocation and must be GL_RENDERBUFFER.
+ * @param[in] samples Specifies the number of samples to be used for the renderbuffer object's storage. Must not bigger than MAX_SAMPLES_EXT.
+ * @param[in] internalformat Specifies the internal format to use for the renderbuffer object's image.
+ * @param[in] width Specifies the width of the renderbuffer, in pixels.
+ * @param[in] height Specifies the height of the renderbuffer, in pixels.
+ */
+ void RenderbufferStorageMultisampleEXT(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
+
+ /**
+ * GLES extension
+ * Enables multisampled rendering into the images of a texture object.
+ * If samples as 0, same as FramebufferTexture2D
+ *
+ * @param[in] target Specifies the framebuffer target. The symbolic constant must be GL_FRAMEBUFFER.
+ * @param[in] attachment Specifies the attachment point to which an image from texture should be attached.
+ * @param[in] textarget Specifies the texture target.
+ * @param[in] texture Specifies the texture object whose image is to be attached.
+ * @param[in] level Specifies the mipmap level of the texture image to be attached, which must be 0.
+ * @param[in] samples The number of samples to the texture. Must not bigger than MAX_SAMPLES_EXT.
+ */
+ void FramebufferTexture2DMultisampleEXT(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples);
+
private:
/**
* Lazy Initialize extensions on first use
PFNGLBLENDBARRIERKHRPROC mBlendBarrierKHR;
#endif
+#ifdef GL_EXT_multisampled_render_to_texture
+ PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC mGlRenderbufferStorageMultisampleEXT;
+ PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC mGlFramebufferTexture2DMultisampleEXT;
+#endif
+
bool mInitialized;
};
#define DALI_INTERNAL_GL_IMPLEMENTATION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
#include <dali/devel-api/threading/conditional-wait.h>
#include <dali/integration-api/gl-abstraction.h>
#include <dali/internal/graphics/common/egl-include.h>
+#include <dali/public-api/common/vector-wrapper.h>
#include <cstdlib>
#include <cstring>
#include <memory>
// INTERNAL INCLUDES
+#include <dali/internal/graphics/gles/gl-extensions-support.h>
#include <dali/internal/graphics/gles/gles-abstraction.h>
#include <dali/internal/graphics/gles/gles2-implementation.h>
#include <dali/internal/graphics/gles/gles3-implementation.h>
static constexpr int32_t INITIAL_GLES_VERSION = 30;
static constexpr int32_t GLES_VERSION_SUPPORT_BLEND_EQUATION_ADVANCED = 32;
static constexpr const char* LEGACY_SHADING_LANGUAGE_VERSION = "100";
-static constexpr const char* KHR_BLEND_EQUATION_ADVANCED = "GL_KHR_blend_equation_advanced";
static constexpr const char* DEFAULT_SAMPLER_TYPE = "sampler2D";
static constexpr const char* OES_EGL_IMAGE_EXTERNAL_STRING = "#extension GL_OES_EGL_image_external:require\n";
static constexpr const char* OES_EGL_IMAGE_EXTERNAL_STRING_ESSL3 = "#extension GL_OES_EGL_image_external_essl3:require\n";
+
} // namespace
/**
{
public:
GlImplementation()
- : mContextCreatedWaitCondition(),
+ : mGlExtensionSupportedCacheList(),
+ mContextCreatedWaitCondition(),
mMaxTextureSize(0),
+ mMaxTextureSamples(0),
mVertexShaderPrefix(""),
mGlesVersion(INITIAL_GLES_VERSION),
mShadingLanguageVersion(100),
mShadingLanguageVersionCached(false),
mIsSurfacelessContextSupported(false),
- mIsAdvancedBlendEquationSupportedCached(false),
- mIsAdvancedBlendEquationSupported(false),
mIsContextCreated(false)
{
mImpl.reset(new Gles3Implementation());
if(mGlesVersion >= GLES_VERSION_SUPPORT_BLEND_EQUATION_ADVANCED)
{
- mIsAdvancedBlendEquationSupported = true;
+ SetIsAdvancedBlendEquationSupported(true);
}
- else
+
+ if(mGlExtensionSupportedCacheList.NeedFullCheck())
{
- // when mIsAdvancedBlendEquationSupported is cached, we don't need to check all the extensions.
- if(!mIsAdvancedBlendEquationSupportedCached)
- {
- const char* const extensionStr = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
- std::istringstream stream(extensionStr);
- std::string currentExtension;
- while(std::getline(stream, currentExtension, ' '))
- {
- if(currentExtension == KHR_BLEND_EQUATION_ADVANCED)
- {
- mIsAdvancedBlendEquationSupported = true;
- break;
- }
- }
- }
+ // fully check gl extensions if we miss some extension supported
+ mGlExtensionSupportedCacheList.EnsureGlExtensionSupportedCheck();
+ }
+
+ if(IsMultisampledRenderToTextureSupported())
+ {
+ glGetIntegerv(GL_MAX_SAMPLES_EXT, &mMaxTextureSamples);
}
if(!mShadingLanguageVersionCached)
void SetIsAdvancedBlendEquationSupported(const bool isSupported)
{
- mIsAdvancedBlendEquationSupported = isSupported;
- mIsAdvancedBlendEquationSupportedCached = true;
+ mGlExtensionSupportedCacheList.MarkSupported(GlExtensionCache::GlExtensionCheckerType::BLEND_EQUATION_ADVANCED, isSupported);
}
bool IsAdvancedBlendEquationSupported()
{
ConditionalWait::ScopedLock lock(mContextCreatedWaitCondition);
- if(!mIsContextCreated && !mIsAdvancedBlendEquationSupportedCached)
+
+ const auto type = GlExtensionCache::GlExtensionCheckerType::BLEND_EQUATION_ADVANCED;
+ if(!mIsContextCreated && !mGlExtensionSupportedCacheList.IsCached(type))
+ {
+ mContextCreatedWaitCondition.Wait(lock);
+ }
+ return mGlExtensionSupportedCacheList.IsSupported(type);
+ }
+
+ void SetIsMultisampledRenderToTextureSupported(const bool isSupported)
+ {
+ mGlExtensionSupportedCacheList.MarkSupported(GlExtensionCache::GlExtensionCheckerType::MULTISAMPLED_RENDER_TO_TEXTURE, isSupported);
+ }
+
+ bool IsMultisampledRenderToTextureSupported()
+ {
+ ConditionalWait::ScopedLock lock(mContextCreatedWaitCondition);
+
+ const auto type = GlExtensionCache::GlExtensionCheckerType::MULTISAMPLED_RENDER_TO_TEXTURE;
+ if(!mIsContextCreated && !mGlExtensionSupportedCacheList.IsCached(type))
{
mContextCreatedWaitCondition.Wait(lock);
}
- return mIsAdvancedBlendEquationSupported;
+ return mGlExtensionSupportedCacheList.IsSupported(type);
}
bool IsBlendEquationSupported(DevelBlendEquation::Type blendEquation)
return mMaxTextureSize;
}
+ int GetMaxTextureSamples()
+ {
+ ConditionalWait::ScopedLock lock(mContextCreatedWaitCondition);
+ if(!mIsContextCreated)
+ {
+ mContextCreatedWaitCondition.Wait(lock);
+ }
+ return mMaxTextureSamples;
+ }
+
int GetGlesVersion()
{
ConditionalWait::ScopedLock lock(mContextCreatedWaitCondition);
if(versionPosition != std::string::npos)
{
std::string extensionString;
- size_t shadingLanguageVersionPosition = shader.find_first_not_of(" \t", versionPosition + versionString.length());
+ size_t shadingLanguageVersionPosition = shader.find_first_not_of(" \t", versionPosition + versionString.length());
if(shadingLanguageVersionPosition != std::string::npos &&
shader.substr(shadingLanguageVersionPosition, 3) == LEGACY_SHADING_LANGUAGE_VERSION)
{
mImpl->RenderbufferStorageMultisample(target, samples, internalformat, width, height);
}
+ void FramebufferTexture2DMultisample(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) override
+ {
+ mImpl->FramebufferTexture2DMultisample(target, attachment, textarget, texture, level, samples);
+ }
+
void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) override
{
mImpl->FramebufferTextureLayer(target, attachment, texture, level, layer);
void BlendBarrier(void)
{
- if(mIsAdvancedBlendEquationSupported)
+ if(mGlExtensionSupportedCacheList.IsSupported(GlExtensionCache::GlExtensionCheckerType::BLEND_EQUATION_ADVANCED))
{
mImpl->BlendBarrier();
}
private:
std::unique_ptr<GlesAbstraction> mImpl;
+ GlExtensionCache::GlExtensionSupportedCacheList mGlExtensionSupportedCacheList;
+
ConditionalWait mContextCreatedWaitCondition;
GLint mMaxTextureSize;
+ GLint mMaxTextureSamples;
std::string mShaderVersionPrefix;
std::string mVertexShaderPrefix;
std::string mFragmentShaderPrefix;
int32_t mShadingLanguageVersion;
bool mShadingLanguageVersionCached;
bool mIsSurfacelessContextSupported;
- bool mIsAdvancedBlendEquationSupportedCached;
- bool mIsAdvancedBlendEquationSupported;
bool mIsContextCreated;
};
#define DALI_INTERNAL_GLES_ABSTRACTION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
virtual void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) = 0;
+ virtual void FramebufferTexture2DMultisample(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) = 0;
+
virtual void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) = 0;
virtual GLvoid* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) = 0;
#define DALI_INTERNAL_GLES2_IMPLEMENTATION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) override
{
- DALI_LOG_ERROR("glRenderbufferStorageMultisample is not supported in OpenGL es 2.0\n");
+ mGlExtensions.RenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
+ }
+
+ void FramebufferTexture2DMultisample(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) override
+ {
+ mGlExtensions.FramebufferTexture2DMultisampleEXT(target, attachment, textarget, texture, level, samples);
}
void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) override
#define DALI_INTERNAL_GLES3_IMPLEMENTATION_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
void RenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) override
{
- glRenderbufferStorageMultisample(target, samples, internalformat, width, height);
+ // @note even gles 3.0 support glRenderbufferStorageMultisample, We cannot use that API with FramebufferTexture2DMultisampleEXT cause vendor issue.
+ // Since dali only use RenderbufferStorageMultisample API for FBO MSAA, just keep to use extension API.
+ mGlExtensions.RenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height);
+ }
+
+ void FramebufferTexture2DMultisample(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) override
+ {
+ mGlExtensions.FramebufferTexture2DMultisampleEXT(target, attachment, textarget, texture, level, samples);
}
void FramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) override
{
namespace
{
-const std::string SYSTEM_CACHE_FILE = "gpu-environment.conf";
-const std::string DALI_ENV_MULTIPLE_WINDOW_SUPPORT = "DALI_ENV_MULTIPLE_WINDOW_SUPPORT";
-const std::string DALI_BLEND_EQUATION_ADVANCED_SUPPORT = "DALI_BLEND_EQUATION_ADVANCED_SUPPORT";
-const std::string DALI_GLSL_VERSION = "DALI_GLSL_VERSION";
+const std::string SYSTEM_CACHE_FILE = "gpu-environment.conf";
+const std::string DALI_ENV_MULTIPLE_WINDOW_SUPPORT = "DALI_ENV_MULTIPLE_WINDOW_SUPPORT";
+const std::string DALI_BLEND_EQUATION_ADVANCED_SUPPORT = "DALI_BLEND_EQUATION_ADVANCED_SUPPORT";
+const std::string DALI_MULTISAMPLED_RENDER_TO_TEXTURE_SUPPORT = "DALI_MULTISAMPLED_RENDER_TO_TEXTURE_SUPPORT";
+const std::string DALI_GLSL_VERSION = "DALI_GLSL_VERSION";
} // unnamed namespace
mShaderLanguageVersion(0u),
mIsMultipleWindowSupported(true),
mIsAdvancedBlendEquationSupported(true),
+ mIsMultisampledRenderToTextureSupported(true),
mMaxTextureSizeCached(false),
mIsMultipleWindowSupportedCached(false),
mIsAdvancedBlendEquationSupportedCached(false),
+ mIsMultisampledRenderToTextureSupportedCached(false),
mShaderLanguageVersionCached(false)
{
}
mIsAdvancedBlendEquationSupported = std::atoi(value.c_str());
mIsAdvancedBlendEquationSupportedCached = true;
}
+ else if(!mIsMultisampledRenderToTextureSupportedCached && name == DALI_MULTISAMPLED_RENDER_TO_TEXTURE_SUPPORT)
+ {
+ std::getline(subStream, value);
+ mIsMultisampledRenderToTextureSupported = std::atoi(value.c_str());
+ mIsMultisampledRenderToTextureSupportedCached = true;
+ }
else if(!mShaderLanguageVersionCached && name == DALI_GLSL_VERSION)
{
std::getline(subStream, value);
return mIsAdvancedBlendEquationSupported;
}
+bool ConfigurationManager::IsMultisampledRenderToTextureSupported()
+{
+ if(!mIsMultisampledRenderToTextureSupportedCached)
+ {
+ RetrieveKeysFromConfigFile(mSystemCacheFilePath);
+
+ if(!mIsMultisampledRenderToTextureSupportedCached)
+ {
+ if(!mGraphics->IsInitialized())
+ {
+ // Wait until graphics subsystem is initialised, but this will happen once per factory reset.
+ // This method blocks until the render thread has initialised the graphics.
+ mThreadController->WaitForGraphicsInitialization();
+ }
+
+ // Query from Graphics Subsystem and save the cache
+ mIsMultisampledRenderToTextureSupported = mGraphics->IsMultisampledRenderToTextureSupported();
+ mIsMultisampledRenderToTextureSupportedCached = true;
+
+ Dali::FileStream configFile(mSystemCacheFilePath, Dali::FileStream::READ | Dali::FileStream::APPEND | Dali::FileStream::TEXT);
+ std::fstream& stream = dynamic_cast<std::fstream&>(configFile.GetStream());
+ if(stream.is_open())
+ {
+ stream << DALI_MULTISAMPLED_RENDER_TO_TEXTURE_SUPPORT << " " << mIsMultisampledRenderToTextureSupported << std::endl;
+ }
+ else
+ {
+ DALI_LOG_ERROR("Fail to open file : %s\n", mSystemCacheFilePath.c_str());
+ }
+ }
+ }
+
+ return mIsMultisampledRenderToTextureSupported;
+}
+
} // namespace Adaptor
} // namespace Internal
#define DALI_INTERNAL_ENVIRONMENT_CONFIGURATION_MANAGER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * 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.
/**
* @brief Check whether blend equation advanced (extension) is supported
- * @return Whether blend equation advanced (extension is supported
+ * @return Whether blend equation advanced (extension) is supported
*/
bool IsAdvancedBlendEquationSupported();
+ /**
+ * @brief Check whether multisampled render to texture (extension) is supported
+ * @return Whether multisampled render to texture (extension) is supported
+ */
+ bool IsMultisampledRenderToTextureSupported();
// Deleted copy constructor.
ConfigurationManager(const ConfigurationManager&) = delete;
// Deleted move assignment operator.
ConfigurationManager& operator=(const ConfigurationManager&&) = delete;
-private: // Data
- std::string mSystemCacheFilePath; ///< The path of system cache file
- GraphicsInterface* mGraphics; ///< Graphics interface
- ThreadController* mThreadController; ///< The thread controller
- unsigned int mMaxTextureSize; ///< The largest texture that the GL can handle
- unsigned int mShaderLanguageVersion; ///< The shader language version that the system supports.
- bool mIsMultipleWindowSupported : 1; ///< Whether multiple window is supported by the GLES
- bool mIsAdvancedBlendEquationSupported : 1; ///< Whether blend equation advanced (extension) is supported by the GLES
- bool mMaxTextureSizeCached : 1; ///< Whether we have checked the maximum texture size
- bool mIsMultipleWindowSupportedCached : 1; ///< Whether we have checked the support of multiple window
- bool mIsAdvancedBlendEquationSupportedCached : 1; ///< Whether we have checked the support of blend equation advanced (extension)
- bool mShaderLanguageVersionCached : 1; ///< Whether we have checked the shader language version
+private: // Data
+ std::string mSystemCacheFilePath; ///< The path of system cache file
+ GraphicsInterface* mGraphics; ///< Graphics interface
+ ThreadController* mThreadController; ///< The thread controller
+ unsigned int mMaxTextureSize; ///< The largest texture that the GL can handle
+ unsigned int mShaderLanguageVersion; ///< The shader language version that the system supports.
+ bool mIsMultipleWindowSupported : 1; ///< Whether multiple window is supported by the GLES
+ bool mIsAdvancedBlendEquationSupported : 1; ///< Whether blend equation advanced (extension) is supported by the GLES
+ bool mIsMultisampledRenderToTextureSupported : 1; ///< Whether multisampled render to texture (extension) is supported by the GLES
+ bool mMaxTextureSizeCached : 1; ///< Whether we have checked the maximum texture size
+ bool mIsMultipleWindowSupportedCached : 1; ///< Whether we have checked the support of multiple window
+ bool mIsAdvancedBlendEquationSupportedCached : 1; ///< Whether we have checked the support of blend equation advanced (extension)
+ bool mIsMultisampledRenderToTextureSupportedCached : 1; ///< Whether we have checked the support of multisampled render to texture (extension)
+ bool mShaderLanguageVersionCached : 1; ///< Whether we have checked the shader language version
};
} // namespace Adaptor
auto rootLayer = mScene.GetRootLayer();
auto accessible = Accessibility::Accessible::Get(rootLayer);
bridge->AddTopLevelWindow(accessible);
+
+ // Emit Window create event
+ // Create and Destory signal only emit in multi-window environment, so it does not emit on default layer.
+ bridge->Emit(accessible, Accessibility::WindowEvent::CREATE);
}
bridge->EnabledSignal().Connect(this, &Window::OnAccessibilityEnabled);
{
const unsigned int ADAPTOR_MAJOR_VERSION = 2;
const unsigned int ADAPTOR_MINOR_VERSION = 2;
-const unsigned int ADAPTOR_MICRO_VERSION = 3;
+const unsigned int ADAPTOR_MICRO_VERSION = 4;
const char* const ADAPTOR_BUILD_DATE = __DATE__ " " __TIME__;
#ifdef DEBUG_ENABLED
Name: dali2-adaptor
Summary: The DALi Tizen Adaptor
-Version: 2.2.3
+Version: 2.2.4
Release: 1
Group: System/Libraries
License: Apache-2.0 and BSD-3-Clause and MIT