Patch by Christian Packmann:

* Implemented a CPU feature detection function in AppServer.cpp.
  The results are put into the global variable gAppServerSIMDFlags.
* Implemented an SIMD accelerated version of the bilinear bitmap
  scaling code that is the backend of BView::DrawBitmap(...,
  uint32 options) used by the MediaPlayer to smoothly upscale
  movies when no video overlay is available. The speed up is very
  noticable and a Core 2 Duo @ 1.8 GHz can play at 1920x1200 now
  without breaking a sweat. There is currently one SIMD version
  implemented which uses MMX and plain SSE.

Very cool! Thanks a lot!


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31165 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2009-06-21 22:07:54 +00:00
parent 926b6fdb08
commit 92772bbddd
6 changed files with 579 additions and 173 deletions
+69 -5
View File
@@ -6,6 +6,7 @@
* DarkWyrm <[email protected]> * DarkWyrm <[email protected]>
* Axel Dörfler, [email protected] * Axel Dörfler, [email protected]
* Stephan Aßmus <[email protected]> * Stephan Aßmus <[email protected]>
* Christian Packmann
*/ */
@@ -35,16 +36,77 @@
port_id gAppServerPort; port_id gAppServerPort;
static AppServer *sAppServer; static AppServer *sAppServer;
BTokenSpace gTokenSpace; BTokenSpace gTokenSpace;
uint32 gAppServerSIMDFlags = 0;
/*! Detect SIMD flags for use in AppServer. Checks all CPUs in the system
and chooses the minimum supported set of instructions. */
static void
detect_simd()
{
// Only scan CPUs for which we are certain the SIMD flags are properly
// defined.
char* vendorNames[] = {
"GenuineIntel",
"AuthenticAMD",
"CentaurHauls", // Via CPUs, MMX and SSE support
"RiseRiseRise", // should be MMX-only
"CyrixInstead", // MMX-only, but custom MMX extensions
"GenuineTMx86", // MMX and SSE
0
};
system_info sysInfo;
if (get_system_info(&sysInfo) != B_OK || sysInfo.cpu_count < 1)
return;
// We start out with all flags set and end up with only those flags
// supported across all CPUs found.
uint32 appServerSIMD = 0xffffffff;
for (int32 cpu = 0; cpu < sysInfo.cpu_count; cpu++) {
cpuid_info cpuInfo;
get_cpuid(&cpuInfo, 0, cpu);
// Get the vendor string and terminate it manually
char vendor[13];
memcpy(vendor, cpuInfo.eax_0.vendor_id, 12);
vendor[12] = 0;
bool vendorFound = false;
for (uint32 i = 0; vendorNames[i] != 0; i++) {
if (strcmp(vendor, vendorNames[i]) == 0)
vendorFound = true;
}
uint32 cpuSIMD = 0;
uint32 maxStdFunc = cpuInfo.regs.eax;
if (vendorFound && maxStdFunc >= 1) {
get_cpuid(&cpuInfo, 1, 0);
uint32 edx = cpuInfo.regs.edx;
if (edx & (1 << 23))
cpuSIMD |= APPSERVER_SIMD_MMX;
if (edx & (1 << 25))
cpuSIMD |= APPSERVER_SIMD_SSE;
} else {
// no flags can be identified
cpuSIMD = 0;
}
appServerSIMD &= cpuSIMD;
}
gAppServerSIMDFlags = appServerSIMD;
}
/*! /*!
\brief Constructor \brief Constructor
This loads the default fonts, allocates all the major global variables, spawns the main housekeeping This loads the default fonts, allocates all the major global variables, spawns the main housekeeping
threads, loads user preferences for the UI and decorator, and allocates various locks. threads, loads user preferences for the UI and decorator, and allocates various locks.
*/ */
AppServer::AppServer() AppServer::AppServer()
: MessageLooper("app_server"), :
MessageLooper("app_server"),
fMessagePort(-1), fMessagePort(-1),
fDesktops(), fDesktops(),
fDesktopLock("AppServerDesktopLock") fDesktopLock("AppServerDesktopLock")
@@ -70,10 +132,12 @@ AppServer::AppServer()
gScreenManager = new ScreenManager(); gScreenManager = new ScreenManager();
gScreenManager->Run(); gScreenManager->Run();
// Create the bitmap allocator. Object declared in BitmapManager.cpp // Create the bitmap allocator. Object declared in BitmapManager.cpp
gBitmapManager = new BitmapManager(); gBitmapManager = new BitmapManager();
// Initialize SIMD flags
detect_simd();
#if 0 #if 0
_LaunchCursorThread(); _LaunchCursorThread();
#endif #endif
@@ -148,7 +212,7 @@ Desktop *
AppServer::_FindDesktop(uid_t userID) AppServer::_FindDesktop(uid_t userID)
{ {
BAutolock locker(fDesktopLock); BAutolock locker(fDesktopLock);
for (int32 i = 0; i < fDesktops.CountItems(); i++) { for (int32 i = 0; i < fDesktops.CountItems(); i++) {
Desktop* desktop = fDesktops.ItemAt(i); Desktop* desktop = fDesktops.ItemAt(i);
@@ -164,7 +228,7 @@ AppServer::_FindDesktop(uid_t userID)
\brief Message handling function for all messages sent to the app_server \brief Message handling function for all messages sent to the app_server
\param code ID of the message sent \param code ID of the message sent
\param buffer Attachment buffer for the message. \param buffer Attachment buffer for the message.
*/ */
void void
AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg) AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg)
+5
View File
@@ -56,5 +56,10 @@ class AppServer : public MessageLooper {
extern BitmapManager *gBitmapManager; extern BitmapManager *gBitmapManager;
extern port_id gAppServerPort; extern port_id gAppServerPort;
extern uint32 gAppServerSIMDFlags;
// Defines for SIMD support. Early implementation, subject to change
#define APPSERVER_SIMD_MMX (1 << 0)
#define APPSERVER_SIMD_SSE (1 << 1)
#endif /* APP_SERVER_H */ #endif /* APP_SERVER_H */
+2
View File
@@ -21,4 +21,6 @@ StaticLibrary libpainter.a :
PixelFormat.cpp PixelFormat.cpp
AGGTextRenderer.cpp AGGTextRenderer.cpp
painter_bilinear_scale.nasm
; ;
+277 -168
View File
@@ -1,12 +1,13 @@
/* /*
* Copyright 2005-2007, Stephan Aßmus <[email protected]>. * Copyright 2009, Christian Packmann.
* Copyright 2008, Andrej Spielmann <[email protected]>. * Copyright 2008, Andrej Spielmann <[email protected]>.
* Copyright 2005-2009, Stephan Aßmus <[email protected]>.
* All rights reserved. Distributed under the terms of the MIT License. * All rights reserved. Distributed under the terms of the MIT License.
*
* API to the Anti-Grain Geometry based "Painter" drawing backend. Manages
* rendering pipe-lines for stroke, fills, bitmap and text rendering.
*/ */
/*! API to the Anti-Grain Geometry based "Painter" drawing backend. Manages
rendering pipe-lines for stroke, fills, bitmap and text rendering.*/
#include <new> #include <new>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -54,6 +55,8 @@
#include "Painter.h" #include "Painter.h"
#include "AppServer.h"
using std::nothrow; using std::nothrow;
#undef TRACE #undef TRACE
@@ -78,41 +81,43 @@ using std::nothrow;
// constructor // constructor
Painter::Painter() Painter::Painter()
: fBuffer(), :
fPixelFormat(fBuffer, &fPatternHandler), fBuffer(),
fBaseRenderer(fPixelFormat), fPixelFormat(fBuffer, &fPatternHandler),
fUnpackedScanline(), fBaseRenderer(fPixelFormat),
fPackedScanline(), fUnpackedScanline(),
fSubpixPackedScanline(), fPackedScanline(),
fSubpixUnpackedScanline(), fSubpixPackedScanline(),
fSubpixRasterizer(), fSubpixUnpackedScanline(),
fRasterizer(), fSubpixRasterizer(),
fSubpixRenderer(fBaseRenderer), fRasterizer(),
fRenderer(fBaseRenderer), fSubpixRenderer(fBaseRenderer),
fRendererBin(fBaseRenderer), fRenderer(fBaseRenderer),
fRendererBin(fBaseRenderer),
fPath(), fPath(),
fCurve(fPath), fCurve(fPath),
fSubpixelPrecise(false), fSubpixelPrecise(false),
fValidClipping(false), fValidClipping(false),
fDrawingText(false), fDrawingText(false),
fAttached(false), fAttached(false),
fPenSize(1.0), fPenSize(1.0),
fClippingRegion(NULL), fClippingRegion(NULL),
fDrawingMode(B_OP_COPY), fDrawingMode(B_OP_COPY),
fAlphaSrcMode(B_PIXEL_ALPHA), fAlphaSrcMode(B_PIXEL_ALPHA),
fAlphaFncMode(B_ALPHA_OVERLAY), fAlphaFncMode(B_ALPHA_OVERLAY),
fLineCapMode(B_BUTT_CAP), fLineCapMode(B_BUTT_CAP),
fLineJoinMode(B_MITER_JOIN), fLineJoinMode(B_MITER_JOIN),
fMiterLimit(B_DEFAULT_MITER_LIMIT), fMiterLimit(B_DEFAULT_MITER_LIMIT),
fPatternHandler(), fPatternHandler(),
fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline, fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline,
fSubpixUnpackedScanline, fSubpixRasterizer) fSubpixUnpackedScanline, fSubpixRasterizer)
{ {
fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, false); fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode,
false);
#if ALIASED_DRAWING #if ALIASED_DRAWING
fRasterizer.gamma(agg::gamma_threshold(0.5)); fRasterizer.gamma(agg::gamma_threshold(0.5));
@@ -131,8 +136,9 @@ Painter::~Painter()
void void
Painter::AttachToBuffer(RenderingBuffer* buffer) Painter::AttachToBuffer(RenderingBuffer* buffer)
{ {
if (buffer && buffer->InitCheck() >= B_OK && if (buffer && buffer->InitCheck() >= B_OK
(buffer->ColorSpace() == B_RGBA32 || buffer->ColorSpace() == B_RGB32)) { && (buffer->ColorSpace() == B_RGBA32
|| buffer->ColorSpace() == B_RGB32)) {
// TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16, // TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16,
// B_CMAP8 and B_GRAY8 :-[ // B_CMAP8 and B_GRAY8 :-[
// (if ever we want to support some devices where this gives // (if ever we want to support some devices where this gives
@@ -2207,8 +2213,24 @@ Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer,
const uint32 dstBPR = fBuffer.stride(); const uint32 dstBPR = fBuffer.stride();
const uint32 srcBPR = srcBuffer.stride(); const uint32 srcBPR = srcBuffer.stride();
bool optimizeForLowFilterRatio = xScale == yScale // Figure out which version of the code we want to use...
&& (xScale == 1.5 || xScale == 2.0 || xScale == 2.5 || xScale == 3.0); enum {
kOptimizeForLowFilterRatio = 0,
kUsePlainCVersion,
kUseSIMDVersion
};
int codeSelect = kUsePlainCVersion;
uint32 neededSIMDFlags = (APPSERVER_SIMD_MMX | APPSERVER_SIMD_SSE);
if ((gAppServerSIMDFlags & neededSIMDFlags) == neededSIMDFlags)
codeSelect = kUseSIMDVersion;
else {
if (xScale == yScale && (xScale == 1.5 || xScale == 2.0
|| xScale == 2.5 || xScale == 3.0)) {
codeSelect = kOptimizeForLowFilterRatio;
}
}
// iterate over clipping boxes // iterate over clipping boxes
fBaseRenderer.first_clip_box(); fBaseRenderer.first_clip_box();
@@ -2236,161 +2258,248 @@ Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer,
//printf("x: %ld - %ld\n", xIndexL, xIndexR); //printf("x: %ld - %ld\n", xIndexL, xIndexR);
//printf("y: %ld - %ld\n", y1, y2); //printf("y: %ld - %ld\n", y1, y2);
if (optimizeForLowFilterRatio) { switch (codeSelect) {
// In this mode, we anticipate to hit many destination pixels that case kOptimizeForLowFilterRatio:
// map directly to a source pixel, we have more branches in the {
// inner loop but save time because of the special cases. If there // In this mode, we anticipate to hit many destination pixels
// are too few direct hit pixels, the branches only waste time. // that map directly to a source pixel, we have more branches
for (; y1 <= y2; y1++) { // in the inner loop but save time because of the special
// cache the weight of the top and bottom row // cases. If there are too few direct hit pixels, the branches
const uint16 wTop = yWeights[y1].weight; // only waste time.
const uint16 wBottom = 255 - yWeights[y1].weight; for (; y1 <= y2; y1++) {
// cache the weight of the top and bottom row
const uint16 wTop = yWeights[y1].weight;
const uint16 wBottom = 255 - yWeights[y1].weight;
// buffer offset into source (top row) // buffer offset into source (top row)
register const uint8* src register const uint8* src
= srcBuffer.row_ptr(yWeights[y1].index); = srcBuffer.row_ptr(yWeights[y1].index);
// buffer handle for destination to be incremented per pixel // buffer handle for destination to be incremented per
register uint8* d = dst; // pixel
register uint8* d = dst;
if (wTop == 255) { if (wTop == 255) {
for (int32 x = xIndexL; x <= xIndexR; x++) { for (int32 x = xIndexL; x <= xIndexR; x++) {
const uint8* s = src + xWeights[x].index; const uint8* s = src + xWeights[x].index;
// This case is important to prevent out // This case is important to prevent out
// of bounds access at bottom edge of the source // of bounds access at bottom edge of the source
// bitmap. If the scale is low and integer, it will // bitmap. If the scale is low and integer, it will
// also help the speed. // also help the speed.
if (xWeights[x].weight == 255) { if (xWeights[x].weight == 255) {
// As above, but to prevent out of bounds // As above, but to prevent out of bounds
// on the right edge. // on the right edge.
*(uint32*)d = *(uint32*)s; *(uint32*)d = *(uint32*)s;
} else { } else {
// Only the left and right pixels are interpolated, // Only the left and right pixels are
// since the top row has 100% weight. // interpolated, since the top row has 100%
const uint16 wLeft = xWeights[x].weight; // weight.
const uint16 wRight = 255 - wLeft; const uint16 wLeft = xWeights[x].weight;
d[0] = (s[0] * wLeft + s[4] * wRight) >> 8; const uint16 wRight = 255 - wLeft;
d[1] = (s[1] * wLeft + s[5] * wRight) >> 8; d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
d[2] = (s[2] * wLeft + s[6] * wRight) >> 8; d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
}
d += 4;
} }
d += 4; } else {
} for (int32 x = xIndexL; x <= xIndexR; x++) {
} else { const uint8* s = src + xWeights[x].index;
for (int32 x = xIndexL; x <= xIndexR; x++) { if (xWeights[x].weight == 255) {
const uint8* s = src + xWeights[x].index; // Prevent out of bounds access on the right
if (xWeights[x].weight == 255) { // edge or simply speed up.
// Prevent out of bounds access on the right edge const uint8* sBottom = s + srcBPR;
// or simply speed up. d[0] = (s[0] * wTop + sBottom[0] * wBottom)
const uint8* sBottom = s + srcBPR; >> 8;
d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; d[1] = (s[1] * wTop + sBottom[1] * wBottom)
d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; >> 8;
d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; d[2] = (s[2] * wTop + sBottom[2] * wBottom)
} else { >> 8;
// calculate the weighted sum of all four } else {
// interpolated pixels // calculate the weighted sum of all four
const uint16 wLeft = xWeights[x].weight; // interpolated pixels
const uint16 wRight = 255 - wLeft; const uint16 wLeft = xWeights[x].weight;
// left and right of top row const uint16 wRight = 255 - wLeft;
uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; // left and right of top row
uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; uint32 t0 = (s[0] * wLeft + s[4] * wRight)
uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; * wTop;
uint32 t1 = (s[1] * wLeft + s[5] * wRight)
* wTop;
uint32 t2 = (s[2] * wLeft + s[6] * wRight)
* wTop;
// left and right of bottom row // left and right of bottom row
s += srcBPR; s += srcBPR;
t0 += (s[0] * wLeft + s[4] * wRight) * wBottom; t0 += (s[0] * wLeft + s[4] * wRight) * wBottom;
t1 += (s[1] * wLeft + s[5] * wRight) * wBottom; t1 += (s[1] * wLeft + s[5] * wRight) * wBottom;
t2 += (s[2] * wLeft + s[6] * wRight) * wBottom; t2 += (s[2] * wLeft + s[6] * wRight) * wBottom;
d[0] = t0 >> 16; d[0] = t0 >> 16;
d[1] = t1 >> 16; d[1] = t1 >> 16;
d[2] = t2 >> 16; d[2] = t2 >> 16;
}
d += 4;
} }
d += 4;
} }
dst += dstBPR;
} }
dst += dstBPR; break;
} }
} else {
// In this mode we anticipate many pixels wich need filtering,
// there are no special cases for direct hit pixels except for the
// last column/row and the right/bottom corner pixel.
// The last column/row handling does not need to be performed case kUsePlainCVersion:
// for all clipping rects! {
int32 yMax = y2; // In this mode we anticipate many pixels wich need filtering,
if (yWeights[yMax].weight == 255) // there are no special cases for direct hit pixels except for
yMax--; // the last column/row and the right/bottom corner pixel.
int32 xIndexMax = xIndexR;
if (xWeights[xIndexMax].weight == 255)
xIndexMax--;
for (; y1 <= yMax; y1++) { // The last column/row handling does not need to be performed
// cache the weight of the top and bottom row // for all clipping rects!
const uint16 wTop = yWeights[y1].weight; int32 yMax = y2;
const uint16 wBottom = 255 - yWeights[y1].weight; if (yWeights[yMax].weight == 255)
yMax--;
int32 xIndexMax = xIndexR;
if (xWeights[xIndexMax].weight == 255)
xIndexMax--;
// buffer offset into source (top row) for (; y1 <= yMax; y1++) {
// cache the weight of the top and bottom row
const uint16 wTop = yWeights[y1].weight;
const uint16 wBottom = 255 - yWeights[y1].weight;
// buffer offset into source (top row)
register const uint8* src
= srcBuffer.row_ptr(yWeights[y1].index);
// buffer handle for destination to be incremented per
// pixel
register uint8* d = dst;
for (int32 x = xIndexL; x <= xIndexMax; x++) {
const uint8* s = src + xWeights[x].index;
// calculate the weighted sum of all four
// interpolated pixels
const uint16 wLeft = xWeights[x].weight;
const uint16 wRight = 255 - wLeft;
// left and right of top row
uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop;
uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop;
uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop;
// left and right of bottom row
s += srcBPR;
t0 += (s[0] * wLeft + s[4] * wRight) * wBottom;
t1 += (s[1] * wLeft + s[5] * wRight) * wBottom;
t2 += (s[2] * wLeft + s[6] * wRight) * wBottom;
d[0] = t0 >> 16;
d[1] = t1 >> 16;
d[2] = t2 >> 16;
d += 4;
}
// last column of pixels if necessary
if (xIndexMax < xIndexR) {
const uint8* s = src + xWeights[xIndexR].index;
const uint8* sBottom = s + srcBPR;
d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
}
dst += dstBPR;
}
// last row of pixels if necessary
// buffer offset into source (bottom row)
register const uint8* src register const uint8* src
= srcBuffer.row_ptr(yWeights[y1].index); = srcBuffer.row_ptr(yWeights[y2].index);
// buffer handle for destination to be incremented per pixel // buffer handle for destination to be incremented per pixel
register uint8* d = dst; register uint8* d = dst;
for (int32 x = xIndexL; x <= xIndexMax; x++) { if (yMax < y2) {
const uint8* s = src + xWeights[x].index; for (int32 x = xIndexL; x <= xIndexMax; x++) {
// calculate the weighted sum of all four const uint8* s = src + xWeights[x].index;
// interpolated pixels const uint16 wLeft = xWeights[x].weight;
const uint16 wLeft = xWeights[x].weight; const uint16 wRight = 255 - wLeft;
const uint16 wRight = 255 - wLeft; d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
// left and right of top row d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
uint32 t0 = (s[0] * wLeft + s[4] * wRight) * wTop; d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
uint32 t1 = (s[1] * wLeft + s[5] * wRight) * wTop; d += 4;
uint32 t2 = (s[2] * wLeft + s[6] * wRight) * wTop; }
// left and right of bottom row
s += srcBPR;
t0 += (s[0] * wLeft + s[4] * wRight) * wBottom;
t1 += (s[1] * wLeft + s[5] * wRight) * wBottom;
t2 += (s[2] * wLeft + s[6] * wRight) * wBottom;
d[0] = t0 >> 16;
d[1] = t1 >> 16;
d[2] = t2 >> 16;
d += 4;
} }
// last column of pixels if necessary
if (xIndexMax < xIndexR) { // pixel in bottom right corner if necessary
if (yMax < y2 && xIndexMax < xIndexR) {
const uint8* s = src + xWeights[xIndexR].index; const uint8* s = src + xWeights[xIndexR].index;
const uint8* sBottom = s + srcBPR; *(uint32*)d = *(uint32*)s;
d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8; }
d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8; break;
d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8; }
case kUseSIMDVersion:
{
// Basically the same as the "standard" mode, but we use SIMD
// routines for the processing of the single display lines.
// The last column/row handling does not need to be performed
// for all clipping rects!
int32 yMax = y2;
if (yWeights[yMax].weight == 255)
yMax--;
int32 xIndexMax = xIndexR;
if (xWeights[xIndexMax].weight == 255)
xIndexMax--;
for (; y1 <= yMax; y1++) {
// cache the weight of the top and bottom row
const uint16 wTop = yWeights[y1].weight;
const uint16 wBottom = 255 - yWeights[y1].weight;
// buffer offset into source (top row)
const uint8* src = srcBuffer.row_ptr(yWeights[y1].index);
// buffer handle for destination to be incremented per
// pixel
uint8* d = dst;
bilinear_scale_xloop_mmxsse(src, dst, xWeights, xIndexL,
xIndexMax, wTop, srcBPR);
// increase pointer by processed pixels
d += (xIndexMax - xIndexL + 1) * 4;
// last column of pixels if necessary
if (xIndexMax < xIndexR) {
const uint8* s = src + xWeights[xIndexR].index;
const uint8* sBottom = s + srcBPR;
d[0] = (s[0] * wTop + sBottom[0] * wBottom) >> 8;
d[1] = (s[1] * wTop + sBottom[1] * wBottom) >> 8;
d[2] = (s[2] * wTop + sBottom[2] * wBottom) >> 8;
}
dst += dstBPR;
} }
dst += dstBPR; // last row of pixels if necessary
} // buffer offset into source (bottom row)
register const uint8* src
= srcBuffer.row_ptr(yWeights[y2].index);
// buffer handle for destination to be incremented per pixel
register uint8* d = dst;
// last row of pixels if necessary if (yMax < y2) {
// buffer offset into source (bottom row) for (int32 x = xIndexL; x <= xIndexMax; x++) {
register const uint8* src = srcBuffer.row_ptr(yWeights[y2].index); const uint8* s = src + xWeights[x].index;
// buffer handle for destination to be incremented per pixel const uint16 wLeft = xWeights[x].weight;
register uint8* d = dst; const uint16 wRight = 255 - wLeft;
d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
if (yMax < y2) { d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
for (int32 x = xIndexL; x <= xIndexMax; x++) { d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
const uint8* s = src + xWeights[x].index; d += 4;
const uint16 wLeft = xWeights[x].weight; }
const uint16 wRight = 255 - wLeft;
d[0] = (s[0] * wLeft + s[4] * wRight) >> 8;
d[1] = (s[1] * wLeft + s[5] * wRight) >> 8;
d[2] = (s[2] * wLeft + s[6] * wRight) >> 8;
d += 4;
} }
}
// pixel in bottom right corner if necessary // pixel in bottom right corner if necessary
if (yMax < y2 && xIndexMax < xIndexR) { if (yMax < y2 && xIndexMax < xIndexR) {
const uint8* s = src + xWeights[xIndexR].index; const uint8* s = src + xWeights[xIndexR].index;
*(uint32*)d = *(uint32*)s; *(uint32*)d = *(uint32*)s;
}
break;
} }
} } // switch(codeselect)
} while (fBaseRenderer.next_clip_box()); } while (fBaseRenderer.next_clip_box());
#ifdef FILTER_INFOS_ON_HEAP #ifdef FILTER_INFOS_ON_HEAP
@@ -24,6 +24,15 @@
#include <Font.h> #include <Font.h>
#include <Rect.h> #include <Rect.h>
// Prototypes for assembler routines
extern "C" {
void bilinear_scale_xloop_mmxsse(const uint8* src, void* dst, void* xWeights,
uint32 xmin, uint32 xmax, uint32 wTop, uint32 srcBPR );
}
extern uint32 gAppServerSIMDFlags;
class BBitmap; class BBitmap;
class BRegion; class BRegion;
class BGradient; class BGradient;
@@ -0,0 +1,217 @@
;
; Copyright 2009, Christian Packmann.
; All rights reserved.
; Distributed under the terms of the MIT License, see
; http://www.opensource.org/licenses/mit-license.php
; Assembly code for Painter::_DrawBitmapBilinearCopy32() in Painter.cpp
; This code implements only the inner x-loop, all other processing
; is done in the C code.
; ****** GENERAL NOTES *****
; The implemented algorithm looks like this:
; (pixLT * leftWeight + pixRT * rightWeight) * topWeight
; +
; (pixLB * leftWeight + pixRB * rightWeight) * bottomWeight
;
; with LT = LeftTop, RT = RightTop, LB = LeftBottom, RB = RightBottom
;
; For more detailed information, see the C implementation in
; Painter.cpp
;
; Implementation notes:
; The calculations are performed with 16-bit arithmetic. All values
; are held in vars/registers as 8-bit values high-shifted by 8 bits;
; i.e. 255<<8. This works because PMULHUW is used for MULs, and this
; algorithm limits the variable values appropriately during all steps.
; This will not work for all algorithms, so take note of that if you
; want to recycle some of the code.
; Notes on the code itself:
; I've tried to keep the code small. That's why I'm using memory accesses
; via index registers as much as possible. This costs execution time due
; to the generated µops, but should minimize decode bandwidth pressure
; due to the many MMX instructions.
; Temporary variables are always stored to the stack instead of global
; data space for this reason. So far I haven't exceeded 8-byte offsets,
; so the instructions only need to encode a BYTE-offset instead of a DWORD.
; Notes on code formatting/comments:
; - integer and vector instructions are indented differently. I find this
; helpful when parsing code, especially when I haven't looked at it for a
; longer time.
; - I've tried to comment the code so that it will be understandable and
; maintainable in the future, and also by other persons than myself.
; The current comments aren't yet fully standardized, I'm still working
; on a coherent system for indicating the variables held within a register
; which will help in understanding the data flow. Any suggestions
; regarding this are welcome.
; - Abbreviations for datatypes:
; B = Byte 8 bit
; W = Word 16 bit
; DW = Doubleword 32 bit
; QW = Quadword 64 bit
; DQ = Doublequad 128 bit
; A "p" in front of one of the datatypes signifies that the
; variable/register is encoded in packed form; i.e. pW means
; "packed Words"; four Words for a MMX register, 8 for a SSE register.
; This should help in understanding the logical meaning of the data
; transformations.
; For better readability, the datatype indicator for a register is
; breacketed with '#', a MMX register with 2 uint32 of value 255 would be
; #pD# 255 255
; ****** Global exports *****
; Do NOT use '_' in front of your defines, this is done
; with YASMs --prefix option at assembly time.
GLOBAL bilinear_scale_xloop_mmxsse
; ********************
; ****** DATA ******
; ********************
SECTION .data
DATA_SECTION:
ALIGN 16
DATA_SSSE3:
; data which is identical for MMX and SSE code is shared by declaring
; it as DQ but providing two labels. MMX code just accesses the
; first half.
c4x16UW_129_LShift8: TIMES 4 dw 129<<8
c4x16UW_255_LShift8: TIMES 4 dw 255<<8
c2x32UD_ff000000: TIMES 4 DD 0xff000000
; Argument definitions
; Parameter offsets assume "push ebp"
PAR_srcPtr EQU 8
PAR_dstPtr EQU 12
PAR_xWeightPtr EQU 16
PAR_xmin EQU 20
PAR_xmax EQU 24
PAR_wTop EQU 28
PAR_srcBPR EQU 32
; Stack storage definitions
ST_Q_wTop EQU 0
ST_Q_wBottom EQU 8
ST_Q_c4x16UW_129_LShift8 EQU 16
ST_Q_c4x16UW_255_LShift8 EQU 24
ST_Q_lftWeight_A EQU 32
ST_Q_rgtWeight_A EQU 40
ST_Q_lftWeight_B EQU 48
ST_Q_rgtWeight_B EQU 56
; ********************
; ****** CODE ******
; ********************
SECTION .code
; void bilinear_scale_xloop_mmxsse(void* src, void* dst, void* xWeights,
; uint32 xmin, uint32 xmax, uint16 wTop, uint32 srcBPR )
; Loop stats:
; 34 instructions (6 moves, 5 integer, 23 vector)
; 12 memory accesses
ALIGN 16
bilinear_scale_xloop_mmxsse:
push ebp
mov ebp, esp
and esp, 0xfffffff8 ; align stack to 8-byte boundary
push ebx
push edi
push esi
sub esp, 4 + 32 ; +4 aligns to 8-byte boundary again; add 4 x QW
; xmin > xmax?
mov eax, [ebp + PAR_xmin]
cmp eax, [ebp + PAR_xmax]
ja .exit
; preparations
; prepare wTop
mov eax, [ebp + PAR_wTop] ; #pB#: 0 0 0 top
shl eax, 8 ; #pB#: 0 0 top 0
movd mm0, eax ; #pW# 0 0 0 top
pshufw mm0, mm0, 00000000b ; #pW# top top top top
movq [esp + ST_Q_wTop], mm0
; move constants
movq mm5, [c4x16UW_255_LShift8]
movq [esp + ST_Q_c4x16UW_255_LShift8], mm5
; prepare wBottom
movq mm1, mm5 ; #pW# 255 255 255 255
psubw mm1, mm0 ; 255 - wTop = wBottom
movq [esp + ST_Q_wBottom], mm1
; load params; leave ebx, ecx as scratch
mov eax, [ebp + PAR_xmin] ; loop counter
mov edx, [ebp + PAR_xWeightPtr] ; xWeights array
mov esi, [ebp + PAR_srcPtr] ; source bitmap
mov edi, [ebp + PAR_dstPtr] ; desination bitmap
movq mm6, [c4x16UW_129_LShift8]
movq mm7, [c2x32UD_ff000000]
; main loop
ALIGN 16
.loop:
; load Left/Right weights into mm0/mm1
movzx ebx, WORD [edx + eax*4 + 2] ; xWeights + x*4 + 2-> FilterInfo[x].weight
shl ebx, 8 ; #pB# 0 0 leftW 0
pxor mm2, mm2 ; clear before use
movd mm0, ebx ; #pW# 0 0 0 leftW
movq mm1, [esp + ST_Q_c4x16UW_255_LShift8]
pshufw mm0, mm0, 00000000b ; #pW# lW lW lW lW
psubw mm1, mm0 ; #pW# rW rW rW rW
movzx ecx, WORD [edx + eax*4] ; xWeights + x*4 -> FilterInfo[x].index
pxor mm3, mm3 ; clear before use
mov ebx, ecx
; process top and bottom pixels, interleave instructions to avoid latencies
pxor mm4, mm4 ; clear before use
; unpack pixel to high byte
punpcklbw mm2, [esi + ecx] ; pixLeftTop
; unpack pixel to high byte
punpcklbw mm3, [esi + ecx + 4] ; pixRightTop
add ebx, [ebp + PAR_srcBPR] ; address:bottom pixels
pmulhuw mm2, mm0 ; pixLT * leftWeight
pmulhuw mm3, mm1 ; pixRT * rightWeight
; calc address for bottom pix
pxor mm5, mm5 ; clear before use
punpcklbw mm4, [esi + ebx] ; pixLeftBottom
punpcklbw mm5, [esi + ebx + 4] ; pixRightBottom
pmulhuw mm4, mm0 ; pixLB * leftWeight
pmulhuw mm5, mm1 ; pixRB * rightWeight
paddw mm2, mm3 ; pixLT + pixRT
paddw mm4, mm5 ; pixLB + pixRB
pmulhuw mm2, [esp + ST_Q_wTop] ; * weightTop
pmulhuw mm4, [esp + ST_Q_wBottom] ; * weightBottom
; add both temp results
paddw mm2, mm4
; divide by 65025 using integer reciprocal: (*129 >> 7)
pmulhuw mm2, mm6
psrlw mm2, 7
; pack & store
packuswb mm2, mm2
por mm2, mm7 ; | 0xff000000
movd [edi], mm2 ; store pixel as DWord
add edi, 4
; loopctr <= xmax?
inc eax
cmp eax, [ebp + PAR_xmax]
jle .loop
.exit:
emms ; Don't EVER forget to call EMMS!
add esp, 4 + 32 ; restore stack pointer
pop esi
pop edi
pop ebx
mov esp, ebp
pop ebp
ret