(BlurEffect) Make BlurEffect logic more clean 41/316441/12
authorEunki, Hong <eunkiki.hong@samsung.com>
Wed, 21 Aug 2024 08:15:14 +0000 (17:15 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Fri, 30 Aug 2024 06:04:39 +0000 (15:04 +0900)
- Clamp the downscale factor and blur radius
- Do not re-activate if target size is not changed
- Calculate bell curve width more faster (log n)
- Move CalculateGaussianWeight as inline function, instead of member funtor
- Make the uniform array size at least 2 (if uniform array size is 1, GPU rendering break down.)

Change-Id: Iaf97833ca81ecba9627916b8b70d2c29b4717219
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp
dali-toolkit/internal/controls/render-effects/blur-effect-impl.cpp
dali-toolkit/internal/controls/render-effects/blur-effect-impl.h
dali-toolkit/internal/controls/render-effects/render-effect-impl.cpp
dali-toolkit/internal/graphics/shaders/blur-effect.frag

index d20fd93..8e95732 100644 (file)
@@ -31,7 +31,7 @@ int UtcDaliRenderEffectNewP(void)
   BackgroundBlurEffect blurEffect = BackgroundBlurEffect::New();
   DALI_TEST_CHECK(blurEffect);
 
-  BackgroundBlurEffect blurEffect2 = BackgroundBlurEffect::New(0.5f, 10.0f);
+  BackgroundBlurEffect blurEffect2 = BackgroundBlurEffect::New(0.5f, 10);
   DALI_TEST_CHECK(blurEffect2);
 
   END_TEST;
@@ -42,17 +42,17 @@ int UtcDaliRenderEffectNewN(void)
   ToolkitTestApplication application;
   tet_infoline("UtcDaliRenderEffectNewN");
 
-  try
-  {
-    BackgroundBlurEffect blurEffect  = BackgroundBlurEffect::New(-0.5f, 10.0f);
-    BackgroundBlurEffect blurEffect2 = BackgroundBlurEffect::New(10.0f, 10.0f);
-    DALI_TEST_CHECK(!blurEffect && !blurEffect2);
-  }
-  catch(Dali::DaliException& e)
-  {
-    DALI_TEST_PRINT_ASSERT(e);
-    DALI_TEST_CHECK(true);
-  }
+  tet_printf("Check some invalid parameters clamp internally\n");
+
+  BackgroundBlurEffect blurEffect  = BackgroundBlurEffect::New(-0.5f, 10);
+  BackgroundBlurEffect blurEffect2 = BackgroundBlurEffect::New(10.0f, 10);
+  BackgroundBlurEffect blurEffect3 = BackgroundBlurEffect::New(0.5f, 0);
+  BackgroundBlurEffect blurEffect4 = BackgroundBlurEffect::New(0.5f, 2147483647);
+  DALI_TEST_CHECK(blurEffect);
+  DALI_TEST_CHECK(blurEffect2);
+  DALI_TEST_CHECK(blurEffect3);
+  DALI_TEST_CHECK(blurEffect4);
+
   END_TEST;
 }
 
index 807be26..191c4a3 100644 (file)
@@ -38,6 +38,21 @@ namespace
 static constexpr float    BLUR_EFFECT_DOWNSCALE_FACTOR = 0.4f;
 static constexpr uint32_t BLUR_EFFECT_PIXEL_RADIUS     = 5u;
 static constexpr int32_t  BLUR_EFFECT_ORDER_INDEX      = 101;
+
+static constexpr float MINIMUM_DOWNSCALE_FACTOR = Dali::Math::MACHINE_EPSILON_100;
+static constexpr float MAXIMUM_DOWNSCALE_FACTOR = 1.0f;
+
+static constexpr uint32_t MAXIMUM_BLUR_RADIUS = 1339u; ///< Maximum pixel radius for blur effect. (GL_MAX_FRAGMENT_UNIFORM_COMPONENTS(Usually 1024) - 19 (vertex shader used)) / 3 float = 335
+static_assert(BLUR_EFFECT_PIXEL_RADIUS < ((MAXIMUM_BLUR_RADIUS >> 2) + 1));
+
+/**
+  * @brief Calculates gaussian weight
+  * @param[in] localOffset Input to the function
+  */
+inline float CalculateGaussianWeight(float localOffset, float sigma)
+{
+  return (1.0f / sqrt(2.0f * Dali::Math::PI * sigma)) * exp(-(localOffset / sigma * localOffset / sigma) * 0.5f);
+}
 } // namespace
 
 namespace Dali
@@ -68,7 +83,22 @@ BlurEffectImpl::BlurEffectImpl(float downscaleFactor, uint32_t blurRadius, bool
   mBellCurveWidth(0.001f),
   mIsBackground(isBackground)
 {
-  DALI_ASSERT_ALWAYS(downscaleFactor <= 1.0 && 0.0 < downscaleFactor);
+  if(DALI_UNLIKELY(mDownscaleFactor < MINIMUM_DOWNSCALE_FACTOR || mDownscaleFactor > MAXIMUM_DOWNSCALE_FACTOR))
+  {
+    const float fixedDownScaleFactor = Dali::Clamp(mDownscaleFactor, MINIMUM_DOWNSCALE_FACTOR, MAXIMUM_DOWNSCALE_FACTOR);
+    DALI_LOG_ERROR("Downscale factor is out of bound: %f. Use %f instead.\n", mDownscaleFactor, fixedDownScaleFactor);
+    mDownscaleFactor = fixedDownScaleFactor;
+  }
+  if(DALI_UNLIKELY(blurRadius > MAXIMUM_BLUR_RADIUS))
+  {
+    const uint32_t fixedBlurRadius      = MAXIMUM_BLUR_RADIUS;
+    const float    fixedDownScaleFactor = Dali::Clamp(mDownscaleFactor * static_cast<float>(fixedBlurRadius) / static_cast<float>(blurRadius), MINIMUM_DOWNSCALE_FACTOR, MAXIMUM_DOWNSCALE_FACTOR);
+    DALI_LOG_ERROR("Blur radius is out of bound: %u. Use %u and make downscale factor %f to %f.\n", blurRadius, fixedBlurRadius, mDownscaleFactor, fixedDownScaleFactor);
+    mDownscaleFactor = fixedDownScaleFactor;
+    mPixelRadius     = (fixedBlurRadius >> 2) + 1;
+  }
+
+  DALI_ASSERT_DEBUG((mPixelRadius != 0u) && "mPixelRadius chould not be zero! their was some logical error occured.");
 }
 
 BlurEffectImpl::~BlurEffectImpl()
@@ -111,19 +141,35 @@ void BlurEffectImpl::OnInitialize()
   // Create shaders
 
   std::ostringstream fragmentStringStream;
-  fragmentStringStream << "#define NUM_SAMPLES " << mPixelRadius << "\n";
+  fragmentStringStream << "#define NUM_SAMPLES " << (DALI_LIKELY(mPixelRadius > 1u) ? mPixelRadius : 2u) << "\n";
   fragmentStringStream << SHADER_BLUR_EFFECT_FRAG;
   std::string fragmentSource(fragmentStringStream.str());
 
-  float sigma = 0.5f;
+  // Calculate bell curve width
   {
-    float epsilon = 1e-2f / (mPixelRadius * 2);
-    while((CalculateGaussianWeight((mPixelRadius * 2) - 1, sigma) < epsilon) && (sigma < 50.0f))
+    const float epsilon     = 1e-2f / (mPixelRadius * 2);
+    const float localOffset = (mPixelRadius * 2) - 1;
+
+    float lowerBoundSigma = 0.001f;
+    float upperBoundSigma = 171.352f; ///< bell curve width for MAXIMUM_BLUR_RADIUS case
+
+    int       trialCount        = 0;
+    const int maximumTrialCount = 50;
+    while(trialCount++ < maximumTrialCount && upperBoundSigma - lowerBoundSigma > Math::MACHINE_EPSILON_10000)
     {
-      sigma += 1.0f;
+      const float sigma = (lowerBoundSigma + upperBoundSigma) * 0.5f;
+      if(CalculateGaussianWeight(localOffset, sigma) < epsilon)
+      {
+        lowerBoundSigma = sigma;
+      }
+      else
+      {
+        upperBoundSigma = sigma;
+      }
     }
+
+    mBellCurveWidth = (lowerBoundSigma + upperBoundSigma) * 0.5f;
   }
-  mBellCurveWidth = sigma;
 
   DALI_LOG_INFO(gRenderEffectLogFilter, Debug::Verbose, "[BlurEffect:%p] mBellCurveWidth calculated! [radius:%u][sigma:%f]\n", this, mPixelRadius, mBellCurveWidth);
 
@@ -184,17 +230,17 @@ void BlurEffectImpl::OnActivate()
 
   // Prepare resource
   // original texture output
-  mInputBackgroundFrameBuffer    = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::NONE);
+  mInputBackgroundFrameBuffer    = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::DEPTH_STENCIL);
   Texture inputBackgroundTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, downsampledWidth, downsampledHeight);
   mInputBackgroundFrameBuffer.AttachColorTexture(inputBackgroundTexture);
 
   // half-blurred output
-  mTemporaryFrameBuffer    = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::NONE);
+  mTemporaryFrameBuffer    = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::DEPTH_STENCIL);
   Texture temporaryTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, downsampledWidth, downsampledHeight);
   mTemporaryFrameBuffer.AttachColorTexture(temporaryTexture);
 
   // blurred output
-  mSourceFrameBuffer    = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::NONE);
+  mSourceFrameBuffer    = FrameBuffer::New(downsampledWidth, downsampledHeight, FrameBuffer::Attachment::DEPTH_STENCIL);
   Texture sourceTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, downsampledWidth, downsampledHeight);
   mSourceFrameBuffer.AttachColorTexture(sourceTexture);
 
@@ -333,11 +379,13 @@ Vector2 BlurEffectImpl::GetTargetSizeForValidTexture() const
 
 void BlurEffectImpl::SetShaderConstants(float downsampledWidth, float downsampledHeight)
 {
-  std::vector<float> uvOffsets(mPixelRadius);
-  std::vector<float> weights(mPixelRadius);
+  const uint32_t numSamples = DALI_LIKELY(mPixelRadius > 1u) ? mPixelRadius : 2u;
+
+  std::vector<float> uvOffsets(numSamples);
+  std::vector<float> weights(numSamples);
 
   // generate bell curve kernel
-  unsigned int       halfSize = mPixelRadius * 2;
+  unsigned int       halfSize = numSamples * 2;
   std::vector<float> halfSideKernel(halfSize);
 
   halfSideKernel[0]  = CalculateGaussianWeight(0.0f, mBellCurveWidth);
@@ -355,14 +403,14 @@ void BlurEffectImpl::SetShaderConstants(float downsampledWidth, float downsample
   halfSideKernel[0] *= 0.5f;
 
   // compress kernel
-  for(unsigned int i = 0; i < mPixelRadius; i++)
+  for(unsigned int i = 0; i < numSamples; i++)
   {
     weights[i]   = halfSideKernel[2 * i] + halfSideKernel[2 * i + 1];
     uvOffsets[i] = 2.0f * i + halfSideKernel[2 * i + 1] / weights[i];
   }
 
   // set shader constants
-  for(unsigned int i = 0; i < mPixelRadius; ++i)
+  for(unsigned int i = 0; i < numSamples; ++i)
   {
     mHorizontalBlurActor.RegisterProperty(GetSampleOffsetsPropertyName(i), Vector2(uvOffsets[i] / downsampledWidth, 0.0f));
     mHorizontalBlurActor.RegisterProperty(GetSampleWeightsPropertyName(i), weights[i]);
index d9db051..c32eccc 100644 (file)
@@ -114,15 +114,6 @@ private:
   Vector2 GetTargetSizeForValidTexture() const;
 
   /**
-   * @brief Calculates gaussian weight
-   * @param[in] localOffset Input to the function
-   */
-  inline float CalculateGaussianWeight(float localOffset, float sigma) const
-  {
-    return (1.0f / sqrt(2.0f * Math::PI * sigma)) * exp(-(localOffset * localOffset) * (1.0f / (2.0f * sigma * sigma)));
-  }
-
-  /**
    * @brief Sets shader constants, gaussian kernel weights and pixel offsets.
    * @param[in] downsampledWidth Downsized width of input texture.
    * @param[in] downsampledHeight Downsized height of input texture.
index 6c58cdd..9bf8272 100644 (file)
@@ -208,9 +208,10 @@ void RenderEffectImpl::OnSizeSet(PropertyNotification& source)
   Dali::Toolkit::Control ownerControl = mOwnerControl.GetHandle();
   if(ownerControl)
   {
-    mTargetSize = ownerControl.GetCurrentProperty<Vector2>(Actor::Property::SIZE);
-    if(IsActivated())
+    const auto targetSize = ownerControl.GetCurrentProperty<Vector2>(Actor::Property::SIZE);
+    if(mTargetSize != targetSize && IsActivated())
     {
+      mTargetSize = targetSize;
       Deactivate();
       Activate();
     }
index 626d27b..0f0ca14 100644 (file)
@@ -1,6 +1,5 @@
 varying highp vec2 vTexCoord;
 uniform sampler2D sTexture;
-uniform lowp vec4 uColor;
 uniform highp vec2 uSampleOffsets[NUM_SAMPLES];
 uniform highp float uSampleWeights[NUM_SAMPLES];