{
auto* texture = mTextureMipmapGenerationRequests.front();
- mCurrentContext->BindTexture(texture->GetGlTarget(), texture->GetTextureTypeId(), texture->GetGLTexture());
- mCurrentContext->GenerateMipmap(texture->GetGlTarget());
+ if(mDiscardTextureSet.find(texture) == mDiscardTextureSet.end())
+ {
+ mCurrentContext->BindTexture(texture->GetGlTarget(), texture->GetTextureTypeId(), texture->GetGLTexture());
+ mCurrentContext->GenerateMipmap(texture->GetGlTarget());
- mTextureMipmapGenerationRequests.pop();
+ mTextureMipmapGenerationRequests.pop();
+ }
}
}
// Process main command queue
ProcessCommandQueues();
- // Reset texture cache in the contexts while destroying textures
- ResetTextureCache();
+ if(!mDiscardTextureSet.empty())
+ {
+ // Reset texture cache in the contexts while destroying textures
+ ResetTextureCache();
+ }
- // Reset buffer cache in the contexts while destroying buffers
- ResetBufferCache();
+ if(!mDiscardBufferQueue.empty())
+ {
+ // Reset buffer cache in the contexts while destroying buffers
+ ResetBufferCache();
+ }
// Process discards
// Note : we don't need to be ResourceContext when we destroy resources.
{
if(mContext)
{
- mContext->GetGLStateCache().ResetBufferCache();
+ mContext->ResetBufferCache();
}
for(auto& context : mSurfaceContexts)
{
if(context.second)
{
- context.second->GetGLStateCache().ResetBufferCache();
+ context.second->ResetBufferCache();
}
}
}
Internal::Adaptor::EglSyncImplementation* mEglSyncImplementation{nullptr};
Graphics::GraphicsInterface* mGraphics{nullptr}; // Pointer to owning structure via interface.
- std::queue<GLES::Texture*> mCreateTextureQueue; ///< Create queue for texture resource
- std::unordered_set<GLES::Texture*> mDiscardTextureSet; ///< Discard queue for texture resource
+ std::queue<GLES::Texture*> mCreateTextureQueue; ///< Create queue for texture resource
+ std::unordered_set<const GLES::Texture*> mDiscardTextureSet; ///< Discard queue for texture resource
std::queue<GLES::Buffer*> mCreateBufferQueue; ///< Create queue for buffer resource
std::queue<GLES::Buffer*> mDiscardBufferQueue; ///< Discard queue for buffer resource
#define DALI_GRAPHICS_GLES_CONTEXT_STATE_CACHE_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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/graphics-api/graphics-types.h>
#include <dali/integration-api/gl-abstraction.h>
#include <dali/integration-api/gl-defines.h>
+#include <cstring> ///< for memset
// INTERNAL INCLUDES
#include "gles-framebuffer-state-cache.h"
{
// reset the cached texture id's in case the driver re-uses them
// when creating new textures
- for(unsigned int i = 0; i < MAX_TEXTURE_UNITS; ++i)
- {
- for(unsigned int j = 0; j < MAX_TEXTURE_TARGET; ++j)
- {
- mBoundTextureId[i][j] = 0;
- }
- }
+ memset(&mBoundTextureId, 0, sizeof(mBoundTextureId));
}
/**
namespace
{
DALI_INIT_TIME_CHECKER_FILTER(gTimeCheckerFilter, DALI_EGL_PERFORMANCE_LOG_THRESHOLD_TIME);
-}
+
+/**
+ * Memory compare working on 4-byte types. Since all types used in shaders are
+ * size of 4*N then no need for size and alignment checks.
+ */
+template<class A, class B>
+inline bool memcmp4(A* a, B* b, size_t size)
+{
+ auto* pa = reinterpret_cast<const uint32_t*>(a);
+ auto* pb = reinterpret_cast<const uint32_t*>(b);
+ size >>= 2;
+ while(size-- && *pa++ == *pb++)
+ ;
+ return (-1u == size);
+};
+} // namespace
namespace Dali::Graphics::GLES
{
}
auto* gl = GetGL();
- if(!gl) // early out if no gl
+ if(DALI_UNLIKELY(!gl)) // early out if no gl
{
return;
}
}
}
+ /**
+ * Prepare to buffer range cache for performance.
+ * We could skip various memory reserving when BindBufferRange called.
+ */
+ void PrepareBufferRangeCache(size_t maxBindings)
+ {
+ if(mUniformBufferBindingCache.Count() < maxBindings)
+ {
+ const auto oldCount = mUniformBufferBindingCache.Count();
+ mUniformBufferBindingCache.ResizeUninitialized(maxBindings);
+ for(auto i = oldCount; i < maxBindings; ++i)
+ {
+ mUniformBufferBindingCache[i].buffer = nullptr;
+ }
+ }
+ }
+
+ /**
+ * Binds and cache buffer ranges.
+ * Cache information 'MUST' be cleard when buffer pointer changed, or some programs invalidated.
+ */
+ void BindBufferRange(const UniformBufferBindingDescriptor& binding)
+ {
+ auto* gl = GetGL();
+ if(DALI_UNLIKELY(!gl)) // early out if no gl
+ {
+ return;
+ }
+
+ DALI_ASSERT_DEBUG(mUniformBufferBindingCache.Count() > binding.binding && "PrepareBufferRangeCache not called!");
+
+ auto& cachedBinding = mUniformBufferBindingCache[binding.binding];
+
+ if(!memcmp4(&cachedBinding, &binding, sizeof(UniformBufferBindingDescriptor)))
+ {
+ // Cache not hit. Update cache and call glBindBufferRange
+ memcpy(&cachedBinding, &binding, sizeof(UniformBufferBindingDescriptor));
+ gl->BindBufferRange(GL_UNIFORM_BUFFER, binding.binding, binding.buffer->GetGLBuffer(), GLintptr(binding.offset), GLintptr(binding.dataSize));
+ }
+ }
+
/**
* Get the pointer to the GL implementation
* @return The GL implementation, nullptr if the context has not been created or shutting down
Dali::Vector<UniformBufferBindingDescriptor> mCurrentUBOBindings{};
UniformBufferBindingDescriptor mCurrentStandaloneUBOBinding{};
+ // Keep bind buffer range. Should be cleared if program changed.
+ Dali::Vector<UniformBufferBindingDescriptor> mUniformBufferBindingCache;
+
// Current render pass and render target
const GLES::RenderTarget* mCurrentRenderTarget{nullptr};
const GLES::RenderPass* mCurrentRenderPass{nullptr};
// Each context must have own VAOs as they cannot be shared
std::unordered_map<const GLES::ProgramImpl*, std::map<std::size_t, uint32_t>> mProgramVAOMap; ///< GL program-VAO map
uint32_t mProgramVAOCurrentState{0u}; ///< Currently bound VAO
- GLStateCache mGlStateCache{}; ///< GL status cache
+
+ GLStateCache mGlStateCache{}; ///< GL status cache
std::vector<Dali::GLuint> mDiscardedVAOList{};
void Context::Flush(bool reset, const GLES::DrawCallDescriptor& drawCall, GLES::TextureDependencyChecker& dependencyChecker)
{
auto* gl = mImpl->GetGL();
- if(!gl) // Early out if no gl
+ if(DALI_UNLIKELY(!gl)) // Early out if no gl
{
return;
}
{
mImpl->mNewPipeline->Bind(newProgram->GetImplementation()->GetGlProgram());
programChanged = true;
+
+ ClearUniformBufferCache();
}
// Blend state
}
auto* gl = mImpl->GetGL();
- if(!gl) // Early out if no gl
+ if(DALI_UNLIKELY(!gl)) // Early out if no gl
{
return;
}
}
auto* gl = mImpl->GetGL();
- if(!gl) // Early out if no gl
+ if(DALI_UNLIKELY(!gl)) // Early out if no gl
{
return;
}
void Context::ResolveGpuUniformBuffers()
{
- if(auto* gl = mImpl->GetGL())
+ mImpl->PrepareBufferRangeCache(mImpl->mCurrentUBOBindings.Count());
+ for(const auto& binding : mImpl->mCurrentUBOBindings)
{
- for(const auto& binding : mImpl->mCurrentUBOBindings)
+ if(DALI_LIKELY(binding.buffer && binding.dataSize > 0u))
{
- if(DALI_LIKELY(binding.buffer && binding.dataSize > 0u))
- {
- gl->BindBufferRange(GL_UNIFORM_BUFFER, binding.binding, binding.buffer->GetGLBuffer(), GLintptr(binding.offset), GLintptr(binding.dataSize));
- }
+ mImpl->BindBufferRange(binding);
}
}
}
const auto& targetInfo = renderTarget.GetCreateInfo();
auto* gl = mImpl->GetGL();
- if(!gl) // Early out if no gl
+ if(DALI_UNLIKELY(!gl)) // Early out if no gl
{
return;
}
}
}
+void Context::ClearUniformBufferCache()
+{
+ mImpl->mUniformBufferBindingCache.Clear();
+}
+
void Context::ColorMask(bool enabled)
{
auto* gl = mImpl->GetGL();
}
// Clear cached Vertex buffer.
+ ResetBufferCache();
ClearVertexBufferCache();
- mImpl->mGlStateCache.ResetBufferCache();
-
mImpl->mProgramVAOMap.erase(iter);
}
}
DALI_TIME_CHECKER_END_WITH_MESSAGE(gTimeCheckerFilter, "PrepareForNativeRendering");
}
-void Context::ResetGLESState()
+void Context::ResetBufferCache()
{
mImpl->mGlStateCache.ResetBufferCache();
+ ClearUniformBufferCache();
+}
+
+void Context::ResetGLESState()
+{
mImpl->mGlStateCache.ResetTextureCache();
mImpl->mCurrentPipeline = nullptr;
mImpl->mCurrentIndexBufferBinding = {};
ClearState();
+ ResetBufferCache();
ClearVertexBufferCache();
mImpl->InitializeGlState();
}
void SetDepthTestEnable(bool depthTestEnable);
void SetDepthWriteEnable(bool depthWriteEnable);
+ void ResetBufferCache();
+
void ResetGLESState();
private:
*/
void ClearVertexBufferCache();
+ /**
+ * @brief Clear cached bind buffer state
+ */
+ void ClearUniformBufferCache();
+
private:
struct Impl;
std::unique_ptr<Impl> mImpl;
/*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 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 "gles-graphics-program.h"
// INTERNAL HEADERS
+#include <dali/devel-api/adaptor-framework/file-loader.h>
#include <dali/internal/graphics/common/shader-parser.h>
#include <dali/public-api/dali-adaptor-version.h>
-#include <dali/devel-api/adaptor-framework/file-loader.h>
#include "egl-graphics-controller.h"
#include "gles-graphics-reflection.h"
#include "gles-graphics-shader.h"
// EXTERNAL HEADERS
-#include <iostream>
-#include <filesystem>
#include <unistd.h>
+#include <filesystem>
#include <fstream>
+#include <iostream>
static constexpr const char* FRAGMENT_SHADER_ADVANCED_BLEND_EQUATION_PREFIX =
"#ifdef GL_KHR_blend_equation_advanced\n"
}
// Set up uniform block bindings
- auto binding = 0u;
auto blockCount = reflection->GetUniformBlockCount();
for(uint32_t i = 1; i < blockCount; ++i) // Ignore emulated block at #0
{
// make binding point
auto blockIndex = gl->GetUniformBlockIndex(program, uboInfo.name.c_str());
- gl->UniformBlockBinding(program, blockIndex, binding++);
+ gl->UniformBlockBinding(program, blockIndex, uboInfo.binding);
}
return true;
std::string ProgramImpl::GetProgramBinaryName()
{
// Check shader with dali-version, name and total shader size
- const auto& info = mImpl->createInfo;
- uint32_t totalShaderSize = 0u;
+ const auto& info = mImpl->createInfo;
+ uint32_t totalShaderSize = 0u;
for(const auto& state : *info.shaderState)
{
const auto* shader = static_cast<const GLES::Shader*>(state.shader);
{
auto binaryShaderFilename = GetSystemProgramBinaryPath() + GetProgramBinaryName();
- bool result = false;
+ bool result = false;
Dali::Vector<char> buffer;
result = Dali::FileLoader::ReadFile(binaryShaderFilename, buffer);
void ProgramImpl::SaveProgramBinary()
{
- GLint binaryLength{0u};
- GLint binarySize{0u};
+ GLint binaryLength{0u};
+ GLint binarySize{0u};
GLenum format;
- auto gl = mImpl->controller.GetGL();
+ auto gl = mImpl->controller.GetGL();
if(!gl)
{
DALI_LOG_ERROR("Can't Get GL \n");
std::vector<uint8_t> programBinary(binaryLength);
gl->GetProgramBinary(mImpl->glProgram, binaryLength, &binarySize, &format, programBinary.data());
- if (binarySize != binaryLength)
+ if(binarySize != binaryLength)
{
DALI_LOG_ERROR("Program binary created but size mismatch %d != %d\n", binarySize, binaryLength);
return;
}
- auto programBinaryName = GetSystemProgramBinaryPath() + GetProgramBinaryName();
+ auto programBinaryName = GetSystemProgramBinaryPath() + GetProgramBinaryName();
auto programBinaryNameTemp = programBinaryName + std::to_string(getpid()) + ".tmp";
- bool loaded = SaveFile(programBinaryNameTemp, (unsigned char*)programBinary.data(), binaryLength);
+ bool loaded = SaveFile(programBinaryNameTemp, (unsigned char*)programBinary.data(), binaryLength);
if(!loaded)
{
- DALI_LOG_ERROR("Program binary save failed!! file = %s \n",programBinaryName.c_str());
+ DALI_LOG_ERROR("Program binary save failed!! file = %s \n", programBinaryName.c_str());
return;
}
uint32_t binding;
};
+// Check the size of struct is times of 4, So we could use memcmp4
+static_assert(sizeof(UniformBufferBindingDescriptor) % 4 == 0);
+
/**
* @brief The descriptor of draw call
*/