Bug 711043 - Make filters code more consistent in performance. r=roc
authorCameron McCormack <cam@mcc.id.au>
Tue, 20 Mar 2012 22:20:21 +1100
changeset 89814 f56c30e4aa1708975320457d30a75f337ebb7498
parent 89813 60dd3e5fe98950295fa9ef7a4525e1323e0bca67
child 89815 c3704b08e87da996b4bbf732aa1ac24010b4c0c8
push id7330
push usercmccormack@mozilla.com
push dateTue, 20 Mar 2012 11:21:09 +0000
treeherdermozilla-inbound@f56c30e4aa17 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs711043
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 711043 - Make filters code more consistent in performance. r=roc
content/svg/content/src/nsSVGFilters.cpp
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -3795,71 +3795,43 @@ nsSVGFEMorphologyElement::Filter(nsSVGFi
   }
   if (rx == 0 && ry == 0) {
     return NS_OK;
   }
 
   PRUint8* sourceData = aSources[0]->mImage->Data();
   PRUint8* targetData = aTarget->mImage->Data();
   PRUint32 stride = aTarget->mImage->Stride();
-  PRUint32 xExt[4], yExt[4];  // X, Y indices of RGBA extrema
   PRUint8 extrema[4];         // RGBA magnitude of extrema
   PRUint16 op = mEnumAttributes[OPERATOR].GetAnimValue();
 
-  /* Scan the kernel for each pixel to determine max/min RGBA values.  Note that
-   * as we advance in the x direction, each kernel overlaps the previous kernel.
-   * Thus, we can avoid iterating over the entire kernel by comparing the
-   * leading edge of the new kernel against the extrema found in the previous
-   * kernel.   We must still scan the entire kernel if the previous extrema do
-   * not fall within the current kernel or if we are starting a new row.
-   */
+  // Scan the kernel for each pixel to determine max/min RGBA values.
   for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
     PRUint32 startY = NS_MAX(0, y - ry);
     // We need to read pixels not just in 'rect', which is limited to
     // the dirty part of our filter primitive subregion, but all pixels in
     // the given radii from the source surface, so use the surface size here.
     PRUint32 endY = NS_MIN(y + ry, instance->GetSurfaceHeight() - 1);
     for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
       PRUint32 startX = NS_MAX(0, x - rx);
       PRUint32 endX = NS_MIN(x + rx, instance->GetSurfaceWidth() - 1);
       PRUint32 targIndex = y * stride + 4 * x;
 
-      // We need to scan the entire kernel
-      if (x == rect.x || xExt[0]  <= startX || xExt[1] <= startX ||
-          xExt[2] <= startX || xExt[3] <= startX) {
-        PRUint32 i;
-        for (i = 0; i < 4; i++) {
-          extrema[i] = sourceData[targIndex + i];
-        }
-        for (PRUint32 y1 = startY; y1 <= endY; y1++) {
-          for (PRUint32 x1 = startX; x1 <= endX; x1++) {
-            for (i = 0; i < 4; i++) {
-              PRUint8 pixel = sourceData[y1 * stride + 4 * x1 + i];
-              if ((extrema[i] >= pixel &&
-                   op == nsSVGFEMorphologyElement::SVG_OPERATOR_ERODE) ||
-                  (extrema[i] <= pixel &&
-                   op == nsSVGFEMorphologyElement::SVG_OPERATOR_DILATE)) {
-                extrema[i] = pixel;
-                xExt[i] = x1;
-                yExt[i] = y1;
-              }
-            }
-          }
-        }
-      } else { // We only need to look at the newest column
-        for (PRUint32 y1 = startY; y1 <= endY; y1++) {
+      for (PRUint32 i = 0; i < 4; i++) {
+        extrema[i] = sourceData[targIndex + i];
+      }
+      for (PRUint32 y1 = startY; y1 <= endY; y1++) {
+        for (PRUint32 x1 = startX; x1 <= endX; x1++) {
           for (PRUint32 i = 0; i < 4; i++) {
-            PRUint8 pixel = sourceData[y1 * stride + 4 * endX + i];
-            if ((extrema[i] >= pixel &&
+            PRUint8 pixel = sourceData[y1 * stride + 4 * x1 + i];
+            if ((extrema[i] > pixel &&
                  op == nsSVGFEMorphologyElement::SVG_OPERATOR_ERODE) ||
-                (extrema[i] <= pixel &&
+                (extrema[i] < pixel &&
                  op == nsSVGFEMorphologyElement::SVG_OPERATOR_DILATE)) {
-                extrema[i] = pixel;
-                xExt[i] = endX;
-                yExt[i] = y1;
+              extrema[i] = pixel;
             }
           }
         }
       }
       targetData[targIndex  ] = extrema[0];
       targetData[targIndex+1] = extrema[1];
       targetData[targIndex+2] = extrema[2];
       targetData[targIndex+3] = extrema[3];
@@ -5114,24 +5086,21 @@ nsSVGFELightingElement::Filter(nsSVGFilt
 
       if (spotLight) {
         float S[3];
         S[0] = pointsAt[0] - lightPos[0];
         S[1] = pointsAt[1] - lightPos[1];
         S[2] = pointsAt[2] - lightPos[2];
         NORMALIZE(S);
         float dot = -DOT(L, S);
-        if (dot < cosConeAngle) {
-          color = NS_RGB(0, 0, 0);
-        } else {
-          float tmp = pow(dot, specularExponent);
-          color = NS_RGB(PRUint8(NS_GET_R(lightColor) * tmp),
-                         PRUint8(NS_GET_G(lightColor) * tmp),
-                         PRUint8(NS_GET_B(lightColor) * tmp));
-        }
+        if (dot < cosConeAngle) dot = 0;
+        float tmp = pow(dot, specularExponent);
+        color = NS_RGB(PRUint8(NS_GET_R(lightColor) * tmp),
+                       PRUint8(NS_GET_G(lightColor) * tmp),
+                       PRUint8(NS_GET_B(lightColor) * tmp));
       } else {
         color = lightColor;
       }
 
       LightPixel(N, L, color, targetData + index);
     }
   }
 
@@ -5288,29 +5257,24 @@ nsSVGFEDiffuseLightingElement::Attribute
 
 void
 nsSVGFEDiffuseLightingElement::LightPixel(const float *N, const float *L,
                                           nscolor color, PRUint8 *targetData)
 {
   float diffuseNL =
     mNumberAttributes[DIFFUSE_CONSTANT].GetAnimValue() * DOT(N, L);
 
-  if (diffuseNL > 0) {
-    targetData[GFX_ARGB32_OFFSET_B] =
-      NS_MIN(PRUint32(diffuseNL * NS_GET_B(color)), 255U);
-    targetData[GFX_ARGB32_OFFSET_G] =
-      NS_MIN(PRUint32(diffuseNL * NS_GET_G(color)), 255U);
-    targetData[GFX_ARGB32_OFFSET_R] =
-      NS_MIN(PRUint32(diffuseNL * NS_GET_R(color)), 255U);
-  } else {
-    targetData[GFX_ARGB32_OFFSET_B] = 0;
-    targetData[GFX_ARGB32_OFFSET_G] = 0;
-    targetData[GFX_ARGB32_OFFSET_R] = 0;
-  }
-
+  if (diffuseNL < 0) diffuseNL = 0;
+
+  targetData[GFX_ARGB32_OFFSET_B] =
+    NS_MIN(PRUint32(diffuseNL * NS_GET_B(color)), 255U);
+  targetData[GFX_ARGB32_OFFSET_G] =
+    NS_MIN(PRUint32(diffuseNL * NS_GET_G(color)), 255U);
+  targetData[GFX_ARGB32_OFFSET_R] =
+    NS_MIN(PRUint32(diffuseNL * NS_GET_R(color)), 255U);
   targetData[GFX_ARGB32_OFFSET_A] = 255;
 }
 
 //---------------------SpecularLighting------------------------
 
 typedef nsSVGFELightingElement nsSVGFESpecularLightingElementBase;
 
 class nsSVGFESpecularLightingElement : public nsSVGFESpecularLightingElementBase,
@@ -5455,37 +5419,34 @@ nsSVGFESpecularLightingElement::LightPix
   H[0] = L[0];
   H[1] = L[1];
   H[2] = L[2] + 1;
   NORMALIZE(H);
 
   float kS = mNumberAttributes[SPECULAR_CONSTANT].GetAnimValue();
   float dotNH = DOT(N, H);
 
-  if (dotNH > 0 && kS > 0) {
-    float specularNH =
-      kS * pow(dotNH, mNumberAttributes[SPECULAR_EXPONENT].GetAnimValue());
-
-    targetData[GFX_ARGB32_OFFSET_B] =
-      NS_MIN(PRUint32(specularNH * NS_GET_B(color)), 255U);
-    targetData[GFX_ARGB32_OFFSET_G] =
-      NS_MIN(PRUint32(specularNH * NS_GET_G(color)), 255U);
-    targetData[GFX_ARGB32_OFFSET_R] =
-      NS_MIN(PRUint32(specularNH * NS_GET_R(color)), 255U);
-
-    targetData[GFX_ARGB32_OFFSET_A] =
-      NS_MAX(targetData[GFX_ARGB32_OFFSET_B],
-             NS_MAX(targetData[GFX_ARGB32_OFFSET_G],
-                    targetData[GFX_ARGB32_OFFSET_R]));
-  } else {
-    targetData[GFX_ARGB32_OFFSET_B] = 0;
-    targetData[GFX_ARGB32_OFFSET_G] = 0;
-    targetData[GFX_ARGB32_OFFSET_R] = 0;
-    targetData[GFX_ARGB32_OFFSET_A] = 255;
-  }
+  bool invalid = dotNH <= 0 || kS <= 0;
+  kS *= invalid ? 0 : 1;
+  PRUint8 minAlpha = invalid ? 255 : 0;
+
+  float specularNH =
+    kS * pow(dotNH, mNumberAttributes[SPECULAR_EXPONENT].GetAnimValue());
+
+  targetData[GFX_ARGB32_OFFSET_B] =
+    NS_MIN(PRUint32(specularNH * NS_GET_B(color)), 255U);
+  targetData[GFX_ARGB32_OFFSET_G] =
+    NS_MIN(PRUint32(specularNH * NS_GET_G(color)), 255U);
+  targetData[GFX_ARGB32_OFFSET_R] =
+    NS_MIN(PRUint32(specularNH * NS_GET_R(color)), 255U);
+
+  targetData[GFX_ARGB32_OFFSET_A] =
+    NS_MAX(minAlpha, NS_MAX(targetData[GFX_ARGB32_OFFSET_B],
+                            NS_MAX(targetData[GFX_ARGB32_OFFSET_G],
+                                   targetData[GFX_ARGB32_OFFSET_R])));
 }
 
 //---------------------Image------------------------
 
 nsSVGElement::StringInfo nsSVGFEImageElement::sStringInfo[2] =
 {
   { &nsGkAtoms::result, kNameSpaceID_None, true },
   { &nsGkAtoms::href, kNameSpaceID_XLink, true }
@@ -5984,16 +5945,18 @@ nsSVGFEDisplacementMapElement::Filter(ns
   PRInt32 width = instance->GetSurfaceWidth();
   PRInt32 height = instance->GetSurfaceHeight();
 
   PRUint8* sourceData = aSources[0]->mImage->Data();
   PRUint8* displacementData = aSources[1]->mImage->Data();
   PRUint8* targetData = aTarget->mImage->Data();
   PRUint32 stride = aTarget->mImage->Stride();
 
+  static PRUint8 dummyData[4] = { 0, 0, 0, 0 };
+
   static const PRUint16 channelMap[5] = {
                              0,
                              GFX_ARGB32_OFFSET_R,
                              GFX_ARGB32_OFFSET_G,
                              GFX_ARGB32_OFFSET_B,
                              GFX_ARGB32_OFFSET_A };
   PRUint16 xChannel = channelMap[mEnumAttributes[CHANNEL_X].GetAnimValue()];
   PRUint16 yChannel = channelMap[mEnumAttributes[CHANNEL_Y].GetAnimValue()];
@@ -6006,23 +5969,30 @@ nsSVGFEDisplacementMapElement::Filter(ns
       PRUint32 targIndex = y * stride + 4 * x;
       // At some point we might want to replace this with a bilinear sample.
       PRInt32 sourceX = x +
         NSToIntFloor(scaleOver255 * displacementData[targIndex + xChannel] +
                 scaleAdjustment);
       PRInt32 sourceY = y +
         NSToIntFloor(scaleOver255 * displacementData[targIndex + yChannel] +
                 scaleAdjustment);
-      if (sourceX < 0 || sourceX >= width ||
-          sourceY < 0 || sourceY >= height) {
-        *(PRUint32*)(targetData + targIndex) = 0;
+
+      bool outOfBounds = sourceX < 0 || sourceX >= width ||
+                         sourceY < 0 || sourceY >= height;
+      PRUint8* data;
+      PRInt32 multiplier;
+      if (outOfBounds) {
+        data = dummyData;
+        multiplier = 0;
       } else {
-        *(PRUint32*)(targetData + targIndex) =
-          *(PRUint32*)(sourceData + sourceY * stride + 4 * sourceX);
+        data = sourceData;
+        multiplier = 1;
       }
+      *(PRUint32*)(targetData + targIndex) =
+        *(PRUint32*)(data + multiplier * (sourceY * stride + 4 * sourceX));
     }
   }
   return NS_OK;
 }
 
 bool
 nsSVGFEDisplacementMapElement::AttributeAffectsRendering(PRInt32 aNameSpaceID,
                                                          nsIAtom* aAttribute) const