mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:43:44 +00:00
8f529f64f5
- Bug 932865 - Add ThreadHangStats for collecting background hang telemetry; r=vladan (2f08a076b)
- Bug 932865 - Add way for telemetry to iterate over active threads; r=froydnj (535615d3d)
- Bug 1128768: Part 3 - Update BHR to allow for hang annotations; r=vladan (0b880a667)
- Bug 935092 - Add ThreadStackHelper to get a thread's pesudo-stack; r=BenWa (1422cfe4d)
- Bug 942488 - Don't report pseudo-stacks without SPS profiler. r=nchen (e160a7a08)
- Bug 946817 - Don't assert mPseudoStack on B2G. r=BenWa (9f846df3b)
- Bug 951431 - Don't get stacks during profiler runs on Linux; r=BenWa (15036e907)
- Bug 978262 - Ignore duplicate frames when getting BHR stack. r=froydnj (964721b1b)
- Bug 985155 - Add signal trampoline on ARM Linux to work around kernel bug. r=snorp (cb8a7846c)
- Bug 995730 - Convert xpcom/threads/ to Gecko style. r=froydnj (fe150404e)
- Bug 1013326 - Distinguish chrome and content scripts in pseudostack; r=snorp (81273c977)
- Bug 1023461 - Remove temporary stack buffer in ThreadStackHelper; r=snorp (cf5a717c2)
- Bug 1023461 - Record filename and line number for chrome JS entries; r=snorp (10c89808f)
- Bug 1022456 - Fix modelines in xpcom/{base,glue,io,string,threads}/. (48dbc0416)
- Bug 1016441 - Switch to using real-time signal in ThreadStackHelper; (2c5f818be)
- Bug 1016629 - b. Use RAII class to assign mStackToFill; r=snorp (769eae130)
- Bug 1016629 - c. Add define for ThreadStackHelper pseudostack support; r=snorp (67def0d2f)
- Bug 1016629 - d. Add and implement GetNativeStack method in ThreadStackHelper; r=snorp r=jseward (46c52f2be)
- Bug 1016629 - e. Implement platform-specific code for filling in context; r=snorp r=jseward (e6a66858b)
- Bug 1016629 - g. Avoid ASan flag when copying stack; r=snorp (0159628b5)
- Bug 1045176 - Unbreak build on non-SPS platforms after bug 1016629. (f1d60d838)
- Bug 1047123 - ThreadStackHelper should use UniquePtr<uint8_t[]>, not ScopedDeleteArray. r=jchen (0e4af313c)
- Bug 1049161 - Fix ThreadStackHelper thread handle permissions on Windows; r=snorp (c05172b1c)
- Bug 1050185 - Make ThreadStackHelper::FillThreadContext Valgrind-friendly. r=nchen (368725774)
- Bug 1050440 - Remove repeated js::RunScript frames in ThreadStackHelper (2a79600b3)
- Bug 1046841 - Fix more style violations in previously touched .cpp files in xpcom/. r=froydnj (02afe2493)
- Bug 1069694 - Remove or move around functions in OldDebugAPI. r=shu (177197302)
- Bug 1069694 - Remove OldDebugAPI from the browser. r=shu (b8c917d42)
- Bug 1100911 - For MacOS builds running on Valgrind, make ThreadStackHelper::GetStack be a no-op. r=nchen. (d99c02e16)
- Bug 1091758 - Report full paths for most chrome scripts; r=snorp (2b72e7878)
- Bug 1109291 - Include better paths for hanging chrome scripts in profile extensions directory; r=snorp r=bsmedberg (1997b9532)
- Bug 1113416 - Don't read stack labels inside hang monitor sighandler; r=nfroyd r=snorp (9688f6069)
- bug 1146027 - more final r=froydnj (7b0f295e5)
- Bug 1164090 - Check for Windows path separator in BHR file name; r=snorp (f014b4d78)
- Bug 1169034 - include <cstdlib> in ThreadStackHelper.cpp to declare correct overload for std::abs; r=jseward (874d4447e)
- Bug 1182996 - Fix and add missing namespace comments. rs=ehsan (054fc00b2)
- Bug 932865 - Collect thread hang stats in BackgroundHangMonitor; (ac80c8e9f)
- minor anticipated fixes to get it compiling (2bd701d15)
316 lines
13 KiB
C++
316 lines
13 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "Blur.h"
|
|
|
|
#include "SSEHelpers.h"
|
|
|
|
#include <string.h>
|
|
|
|
namespace mozilla {
|
|
namespace gfx {
|
|
|
|
MOZ_ALWAYS_INLINE
|
|
__m128i Divide(__m128i aValues, __m128i aDivisor)
|
|
{
|
|
const __m128i mask = _mm_setr_epi32(0x0, 0xffffffff, 0x0, 0xffffffff);
|
|
static const union {
|
|
int64_t i64[2];
|
|
__m128i m;
|
|
} roundingAddition = { { int64_t(1) << 31, int64_t(1) << 31 } };
|
|
|
|
__m128i multiplied31 = _mm_mul_epu32(aValues, aDivisor);
|
|
__m128i multiplied42 = _mm_mul_epu32(_mm_srli_epi64(aValues, 32), aDivisor);
|
|
|
|
// Add 1 << 31 before shifting or masking the lower 32 bits away, so that the
|
|
// result is rounded.
|
|
__m128i p_3_1 = _mm_srli_epi64(_mm_add_epi64(multiplied31, roundingAddition.m), 32);
|
|
__m128i p4_2_ = _mm_and_si128(_mm_add_epi64(multiplied42, roundingAddition.m), mask);
|
|
__m128i p4321 = _mm_or_si128(p_3_1, p4_2_);
|
|
return p4321;
|
|
}
|
|
|
|
MOZ_ALWAYS_INLINE
|
|
__m128i BlurFourPixels(const __m128i& aTopLeft, const __m128i& aTopRight,
|
|
const __m128i& aBottomRight, const __m128i& aBottomLeft,
|
|
const __m128i& aDivisor)
|
|
{
|
|
__m128i values = _mm_add_epi32(_mm_sub_epi32(_mm_sub_epi32(aBottomRight, aTopRight), aBottomLeft), aTopLeft);
|
|
return Divide(values, aDivisor);
|
|
}
|
|
|
|
MOZ_ALWAYS_INLINE
|
|
void LoadIntegralRowFromRow(uint32_t *aDest, const uint8_t *aSource,
|
|
int32_t aSourceWidth, int32_t aLeftInflation,
|
|
int32_t aRightInflation)
|
|
{
|
|
int32_t currentRowSum = 0;
|
|
|
|
for (int x = 0; x < aLeftInflation; x++) {
|
|
currentRowSum += aSource[0];
|
|
aDest[x] = currentRowSum;
|
|
}
|
|
for (int x = aLeftInflation; x < (aSourceWidth + aLeftInflation); x++) {
|
|
currentRowSum += aSource[(x - aLeftInflation)];
|
|
aDest[x] = currentRowSum;
|
|
}
|
|
for (int x = (aSourceWidth + aLeftInflation); x < (aSourceWidth + aLeftInflation + aRightInflation); x++) {
|
|
currentRowSum += aSource[aSourceWidth - 1];
|
|
aDest[x] = currentRowSum;
|
|
}
|
|
}
|
|
|
|
// This function calculates an integral of four pixels stored in the 4
|
|
// 32-bit integers on aPixels. i.e. for { 30, 50, 80, 100 } this returns
|
|
// { 30, 80, 160, 260 }. This seems to be the fastest way to do this after
|
|
// much testing.
|
|
MOZ_ALWAYS_INLINE
|
|
__m128i AccumulatePixelSums(__m128i aPixels)
|
|
{
|
|
__m128i sumPixels = aPixels;
|
|
__m128i currentPixels = _mm_slli_si128(aPixels, 4);
|
|
sumPixels = _mm_add_epi32(sumPixels, currentPixels);
|
|
currentPixels = _mm_unpacklo_epi64(_mm_setzero_si128(), sumPixels);
|
|
|
|
return _mm_add_epi32(sumPixels, currentPixels);
|
|
}
|
|
|
|
MOZ_ALWAYS_INLINE void
|
|
GenerateIntegralImage_SSE2(int32_t aLeftInflation, int32_t aRightInflation,
|
|
int32_t aTopInflation, int32_t aBottomInflation,
|
|
uint32_t *aIntegralImage, size_t aIntegralImageStride,
|
|
uint8_t *aSource, int32_t aSourceStride, const IntSize &aSize)
|
|
{
|
|
MOZ_ASSERT(!(aLeftInflation & 3));
|
|
|
|
uint32_t stride32bit = aIntegralImageStride / 4;
|
|
|
|
IntSize integralImageSize(aSize.width + aLeftInflation + aRightInflation,
|
|
aSize.height + aTopInflation + aBottomInflation);
|
|
|
|
LoadIntegralRowFromRow(aIntegralImage, aSource, aSize.width, aLeftInflation, aRightInflation);
|
|
|
|
for (int y = 1; y < aTopInflation + 1; y++) {
|
|
uint32_t *intRow = aIntegralImage + (y * stride32bit);
|
|
uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
|
|
uint32_t *intFirstRow = aIntegralImage;
|
|
|
|
for (int x = 0; x < integralImageSize.width; x += 4) {
|
|
__m128i firstRow = _mm_load_si128((__m128i*)(intFirstRow + x));
|
|
__m128i previousRow = _mm_load_si128((__m128i*)(intPrevRow + x));
|
|
_mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(firstRow, previousRow));
|
|
}
|
|
}
|
|
|
|
for (int y = aTopInflation + 1; y < (aSize.height + aTopInflation); y++) {
|
|
__m128i currentRowSum = _mm_setzero_si128();
|
|
uint32_t *intRow = aIntegralImage + (y * stride32bit);
|
|
uint32_t *intPrevRow = aIntegralImage + (y - 1) * stride32bit;
|
|
uint8_t *sourceRow = aSource + aSourceStride * (y - aTopInflation);
|
|
|
|
uint32_t pixel = sourceRow[0];
|
|
for (int x = 0; x < aLeftInflation; x += 4) {
|
|
__m128i sumPixels = AccumulatePixelSums(_mm_shuffle_epi32(_mm_set1_epi32(pixel), _MM_SHUFFLE(0, 0, 0, 0)));
|
|
|
|
sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
|
|
|
|
currentRowSum = _mm_shuffle_epi32(sumPixels, _MM_SHUFFLE(3, 3, 3, 3));
|
|
|
|
_mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
|
|
}
|
|
for (int x = aLeftInflation; x < (aSize.width + aLeftInflation); x += 4) {
|
|
uint32_t pixels = *(uint32_t*)(sourceRow + (x - aLeftInflation));
|
|
|
|
// It's important to shuffle here. When we exit this loop currentRowSum
|
|
// has to be set to sumPixels, so that the following loop can get the
|
|
// correct pixel for the currentRowSum. The highest order pixel in
|
|
// currentRowSum could've originated from accumulation in the stride.
|
|
currentRowSum = _mm_shuffle_epi32(currentRowSum, _MM_SHUFFLE(3, 3, 3, 3));
|
|
|
|
__m128i sumPixels = AccumulatePixelSums(_mm_unpacklo_epi16(_mm_unpacklo_epi8( _mm_set1_epi32(pixels), _mm_setzero_si128()), _mm_setzero_si128()));
|
|
sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
|
|
|
|
currentRowSum = sumPixels;
|
|
|
|
_mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
|
|
}
|
|
|
|
pixel = sourceRow[aSize.width - 1];
|
|
int x = (aSize.width + aLeftInflation);
|
|
if ((aSize.width & 3)) {
|
|
// Deal with unaligned portion. Get the correct pixel from currentRowSum,
|
|
// see explanation above.
|
|
uint32_t intCurrentRowSum = ((uint32_t*)¤tRowSum)[(aSize.width % 4) - 1];
|
|
for (; x < integralImageSize.width; x++) {
|
|
// We could be unaligned here!
|
|
if (!(x & 3)) {
|
|
// aligned!
|
|
currentRowSum = _mm_set1_epi32(intCurrentRowSum);
|
|
break;
|
|
}
|
|
intCurrentRowSum += pixel;
|
|
intRow[x] = intPrevRow[x] + intCurrentRowSum;
|
|
}
|
|
} else {
|
|
currentRowSum = _mm_shuffle_epi32(currentRowSum, _MM_SHUFFLE(3, 3, 3, 3));
|
|
}
|
|
for (; x < integralImageSize.width; x += 4) {
|
|
__m128i sumPixels = AccumulatePixelSums(_mm_set1_epi32(pixel));
|
|
|
|
sumPixels = _mm_add_epi32(sumPixels, currentRowSum);
|
|
|
|
currentRowSum = _mm_shuffle_epi32(sumPixels, _MM_SHUFFLE(3, 3, 3, 3));
|
|
|
|
_mm_store_si128((__m128i*)(intRow + x), _mm_add_epi32(sumPixels, _mm_load_si128((__m128i*)(intPrevRow + x))));
|
|
}
|
|
}
|
|
|
|
if (aBottomInflation) {
|
|
// Store the last valid row of our source image in the last row of
|
|
// our integral image. This will be overwritten with the correct values
|
|
// in the upcoming loop.
|
|
LoadIntegralRowFromRow(aIntegralImage + (integralImageSize.height - 1) * stride32bit,
|
|
aSource + (aSize.height - 1) * aSourceStride, aSize.width, aLeftInflation, aRightInflation);
|
|
|
|
|
|
for (int y = aSize.height + aTopInflation; y < integralImageSize.height; y++) {
|
|
__m128i *intRow = (__m128i*)(aIntegralImage + (y * stride32bit));
|
|
__m128i *intPrevRow = (__m128i*)(aIntegralImage + (y - 1) * stride32bit);
|
|
__m128i *intLastRow = (__m128i*)(aIntegralImage + (integralImageSize.height - 1) * stride32bit);
|
|
|
|
for (int x = 0; x < integralImageSize.width; x += 4) {
|
|
_mm_store_si128(intRow + (x / 4),
|
|
_mm_add_epi32(_mm_load_si128(intLastRow + (x / 4)),
|
|
_mm_load_si128(intPrevRow + (x / 4))));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempt to do an in-place box blur using an integral image.
|
|
*/
|
|
void
|
|
AlphaBoxBlur::BoxBlur_SSE2(uint8_t* aData,
|
|
int32_t aLeftLobe,
|
|
int32_t aRightLobe,
|
|
int32_t aTopLobe,
|
|
int32_t aBottomLobe,
|
|
uint32_t *aIntegralImage,
|
|
size_t aIntegralImageStride)
|
|
{
|
|
IntSize size = GetSize();
|
|
|
|
MOZ_ASSERT(size.height > 0);
|
|
|
|
// Our 'left' or 'top' lobe will include the current pixel. i.e. when
|
|
// looking at an integral image the value of a pixel at 'x,y' is calculated
|
|
// using the value of the integral image values above/below that.
|
|
aLeftLobe++;
|
|
aTopLobe++;
|
|
int32_t boxSize = (aLeftLobe + aRightLobe) * (aTopLobe + aBottomLobe);
|
|
|
|
MOZ_ASSERT(boxSize > 0);
|
|
|
|
if (boxSize == 1) {
|
|
return;
|
|
}
|
|
|
|
uint32_t reciprocal = uint32_t((uint64_t(1) << 32) / boxSize);
|
|
|
|
uint32_t stride32bit = aIntegralImageStride / 4;
|
|
int32_t leftInflation = RoundUpToMultipleOf4(aLeftLobe).value();
|
|
|
|
GenerateIntegralImage_SSE2(leftInflation, aRightLobe, aTopLobe, aBottomLobe,
|
|
aIntegralImage, aIntegralImageStride, aData,
|
|
mStride, size);
|
|
|
|
__m128i divisor = _mm_set1_epi32(reciprocal);
|
|
|
|
// This points to the start of the rectangle within the IntegralImage that overlaps
|
|
// the surface being blurred.
|
|
uint32_t *innerIntegral = aIntegralImage + (aTopLobe * stride32bit) + leftInflation;
|
|
|
|
IntRect skipRect = mSkipRect;
|
|
int32_t stride = mStride;
|
|
uint8_t *data = aData;
|
|
for (int32_t y = 0; y < size.height; y++) {
|
|
bool inSkipRectY = y > skipRect.y && y < skipRect.YMost();
|
|
|
|
uint32_t *topLeftBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
|
|
uint32_t *topRightBase = innerIntegral + ((y - aTopLobe) * ptrdiff_t(stride32bit) + aRightLobe);
|
|
uint32_t *bottomRightBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) + aRightLobe);
|
|
uint32_t *bottomLeftBase = innerIntegral + ((y + aBottomLobe) * ptrdiff_t(stride32bit) - aLeftLobe);
|
|
|
|
int32_t x = 0;
|
|
// Process 16 pixels at a time for as long as possible.
|
|
for (; x <= size.width - 16; x += 16) {
|
|
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
|
|
x = skipRect.XMost() - 16;
|
|
// Trigger early jump on coming loop iterations, this will be reset
|
|
// next line anyway.
|
|
inSkipRectY = false;
|
|
continue;
|
|
}
|
|
|
|
__m128i topLeft;
|
|
__m128i topRight;
|
|
__m128i bottomRight;
|
|
__m128i bottomLeft;
|
|
|
|
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
|
|
topRight = loadUnaligned128((__m128i*)(topRightBase + x));
|
|
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
|
|
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
|
|
__m128i result1 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
|
|
|
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 4));
|
|
topRight = loadUnaligned128((__m128i*)(topRightBase + x + 4));
|
|
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 4));
|
|
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 4));
|
|
__m128i result2 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
|
|
|
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 8));
|
|
topRight = loadUnaligned128((__m128i*)(topRightBase + x + 8));
|
|
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 8));
|
|
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 8));
|
|
__m128i result3 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
|
|
|
topLeft = loadUnaligned128((__m128i*)(topLeftBase + x + 12));
|
|
topRight = loadUnaligned128((__m128i*)(topRightBase + x + 12));
|
|
bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x + 12));
|
|
bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x + 12));
|
|
__m128i result4 = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
|
|
|
__m128i final = _mm_packus_epi16(_mm_packs_epi32(result1, result2), _mm_packs_epi32(result3, result4));
|
|
|
|
_mm_storeu_si128((__m128i*)(data + stride * y + x), final);
|
|
}
|
|
|
|
// Process the remaining pixels 4 bytes at a time.
|
|
for (; x < size.width; x += 4) {
|
|
if (inSkipRectY && x > skipRect.x && x < skipRect.XMost()) {
|
|
x = skipRect.XMost() - 4;
|
|
// Trigger early jump on coming loop iterations, this will be reset
|
|
// next line anyway.
|
|
inSkipRectY = false;
|
|
continue;
|
|
}
|
|
__m128i topLeft = loadUnaligned128((__m128i*)(topLeftBase + x));
|
|
__m128i topRight = loadUnaligned128((__m128i*)(topRightBase + x));
|
|
__m128i bottomRight = loadUnaligned128((__m128i*)(bottomRightBase + x));
|
|
__m128i bottomLeft = loadUnaligned128((__m128i*)(bottomLeftBase + x));
|
|
|
|
__m128i result = BlurFourPixels(topLeft, topRight, bottomRight, bottomLeft, divisor);
|
|
__m128i final = _mm_packus_epi16(_mm_packs_epi32(result, _mm_setzero_si128()), _mm_setzero_si128());
|
|
|
|
*(uint32_t*)(data + stride * y + x) = _mm_cvtsi128_si32(final);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace gfx
|
|
} // namespace mozilla
|