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]>
* Axel Dörfler, [email protected]
* Stephan Aßmus <[email protected]>
* Christian Packmann
*/
@@ -35,16 +36,77 @@
port_id gAppServerPort;
static AppServer *sAppServer;
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
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.
*/
AppServer::AppServer()
: MessageLooper("app_server"),
:
MessageLooper("app_server"),
fMessagePort(-1),
fDesktops(),
fDesktopLock("AppServerDesktopLock")
@@ -70,10 +132,12 @@ AppServer::AppServer()
gScreenManager = new ScreenManager();
gScreenManager->Run();
// Create the bitmap allocator. Object declared in BitmapManager.cpp
gBitmapManager = new BitmapManager();
// Initialize SIMD flags
detect_simd();
#if 0
_LaunchCursorThread();
#endif
@@ -148,7 +212,7 @@ Desktop *
AppServer::_FindDesktop(uid_t userID)
{
BAutolock locker(fDesktopLock);
for (int32 i = 0; i < fDesktops.CountItems(); 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
\param code ID of the message sent
\param buffer Attachment buffer for the message.
*/
void
AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg)
+5
View File
@@ -56,5 +56,10 @@ class AppServer : public MessageLooper {
extern BitmapManager *gBitmapManager;
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 */
+2
View File
@@ -21,4 +21,6 @@ StaticLibrary libpainter.a :
PixelFormat.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 2005-2009, Stephan Aßmus <[email protected]>.
* 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 <stdio.h>
#include <string.h>
@@ -54,6 +55,8 @@
#include "Painter.h"
#include "AppServer.h"
using std::nothrow;
#undef TRACE
@@ -78,41 +81,43 @@ using std::nothrow;
// constructor
Painter::Painter()
: fBuffer(),
fPixelFormat(fBuffer, &fPatternHandler),
fBaseRenderer(fPixelFormat),
fUnpackedScanline(),
fPackedScanline(),
fSubpixPackedScanline(),
fSubpixUnpackedScanline(),
fSubpixRasterizer(),
fRasterizer(),
fSubpixRenderer(fBaseRenderer),
fRenderer(fBaseRenderer),
fRendererBin(fBaseRenderer),
:
fBuffer(),
fPixelFormat(fBuffer, &fPatternHandler),
fBaseRenderer(fPixelFormat),
fUnpackedScanline(),
fPackedScanline(),
fSubpixPackedScanline(),
fSubpixUnpackedScanline(),
fSubpixRasterizer(),
fRasterizer(),
fSubpixRenderer(fBaseRenderer),
fRenderer(fBaseRenderer),
fRendererBin(fBaseRenderer),
fPath(),
fCurve(fPath),
fPath(),
fCurve(fPath),
fSubpixelPrecise(false),
fValidClipping(false),
fDrawingText(false),
fAttached(false),
fSubpixelPrecise(false),
fValidClipping(false),
fDrawingText(false),
fAttached(false),
fPenSize(1.0),
fClippingRegion(NULL),
fDrawingMode(B_OP_COPY),
fAlphaSrcMode(B_PIXEL_ALPHA),
fAlphaFncMode(B_ALPHA_OVERLAY),
fLineCapMode(B_BUTT_CAP),
fLineJoinMode(B_MITER_JOIN),
fMiterLimit(B_DEFAULT_MITER_LIMIT),
fPenSize(1.0),
fClippingRegion(NULL),
fDrawingMode(B_OP_COPY),
fAlphaSrcMode(B_PIXEL_ALPHA),
fAlphaFncMode(B_ALPHA_OVERLAY),
fLineCapMode(B_BUTT_CAP),
fLineJoinMode(B_MITER_JOIN),
fMiterLimit(B_DEFAULT_MITER_LIMIT),
fPatternHandler(),
fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline,
fPatternHandler(),
fTextRenderer(fSubpixRenderer, fRenderer, fRendererBin, fUnpackedScanline,
fSubpixUnpackedScanline, fSubpixRasterizer)
{
fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode, false);
fPixelFormat.SetDrawingMode(fDrawingMode, fAlphaSrcMode, fAlphaFncMode,
false);
#if ALIASED_DRAWING
fRasterizer.gamma(agg::gamma_threshold(0.5));
@@ -131,8 +136,9 @@ Painter::~Painter()
void
Painter::AttachToBuffer(RenderingBuffer* buffer)
{
if (buffer && buffer->InitCheck() >= B_OK &&
(buffer->ColorSpace() == B_RGBA32 || buffer->ColorSpace() == B_RGB32)) {
if (buffer && buffer->InitCheck() >= B_OK
&& (buffer->ColorSpace() == B_RGBA32
|| buffer->ColorSpace() == B_RGB32)) {
// TODO: implement drawing on B_RGB24, B_RGB15, B_RGB16,
// B_CMAP8 and B_GRAY8 :-[
// (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 srcBPR = srcBuffer.stride();
bool optimizeForLowFilterRatio = xScale == yScale
&& (xScale == 1.5 || xScale == 2.0 || xScale == 2.5 || xScale == 3.0);
// Figure out which version of the code we want to use...
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
fBaseRenderer.first_clip_box();
@@ -2236,161 +2258,248 @@ Painter::_DrawBitmapBilinearCopy32(agg::rendering_buffer& srcBuffer,
//printf("x: %ld - %ld\n", xIndexL, xIndexR);
//printf("y: %ld - %ld\n", y1, y2);
if (optimizeForLowFilterRatio) {
// In this mode, we anticipate to hit many destination pixels that
// map directly to a source pixel, we have more branches in the
// inner loop but save time because of the special cases. If there
// are too few direct hit pixels, the branches only waste time.
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;
switch (codeSelect) {
case kOptimizeForLowFilterRatio:
{
// In this mode, we anticipate to hit many destination pixels
// that map directly to a source pixel, we have more branches
// in the inner loop but save time because of the special
// cases. If there are too few direct hit pixels, the branches
// only waste time.
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)
register const uint8* src
= srcBuffer.row_ptr(yWeights[y1].index);
// buffer handle for destination to be incremented per pixel
register uint8* d = dst;
// 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;
if (wTop == 255) {
for (int32 x = xIndexL; x <= xIndexR; x++) {
const uint8* s = src + xWeights[x].index;
// This case is important to prevent out
// of bounds access at bottom edge of the source
// bitmap. If the scale is low and integer, it will
// also help the speed.
if (xWeights[x].weight == 255) {
// As above, but to prevent out of bounds
// on the right edge.
*(uint32*)d = *(uint32*)s;
} else {
// Only the left and right pixels are interpolated,
// since the top row has 100% weight.
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;
if (wTop == 255) {
for (int32 x = xIndexL; x <= xIndexR; x++) {
const uint8* s = src + xWeights[x].index;
// This case is important to prevent out
// of bounds access at bottom edge of the source
// bitmap. If the scale is low and integer, it will
// also help the speed.
if (xWeights[x].weight == 255) {
// As above, but to prevent out of bounds
// on the right edge.
*(uint32*)d = *(uint32*)s;
} else {
// Only the left and right pixels are
// interpolated, since the top row has 100%
// weight.
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;
}
d += 4;
}
} else {
for (int32 x = xIndexL; x <= xIndexR; x++) {
const uint8* s = src + xWeights[x].index;
if (xWeights[x].weight == 255) {
// Prevent out of bounds access on the right edge
// or simply speed up.
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;
} else {
// 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;
} else {
for (int32 x = xIndexL; x <= xIndexR; x++) {
const uint8* s = src + xWeights[x].index;
if (xWeights[x].weight == 255) {
// Prevent out of bounds access on the right
// edge or simply speed up.
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;
} else {
// 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;
// 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[0] = t0 >> 16;
d[1] = t1 >> 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
// for all clipping rects!
int32 yMax = y2;
if (yWeights[yMax].weight == 255)
yMax--;
int32 xIndexMax = xIndexR;
if (xWeights[xIndexMax].weight == 255)
xIndexMax--;
case kUsePlainCVersion:
{
// 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.
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;
// 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--;
// 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
= srcBuffer.row_ptr(yWeights[y1].index);
= srcBuffer.row_ptr(yWeights[y2].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;
if (yMax < y2) {
for (int32 x = xIndexL; x <= xIndexMax; x++) {
const uint8* s = src + xWeights[x].index;
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;
}
}
// 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* 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;
*(uint32*)d = *(uint32*)s;
}
break;
}
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
// 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;
if (yMax < y2) {
for (int32 x = xIndexL; x <= xIndexMax; x++) {
const uint8* s = src + xWeights[x].index;
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;
if (yMax < y2) {
for (int32 x = xIndexL; x <= xIndexMax; x++) {
const uint8* s = src + xWeights[x].index;
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
if (yMax < y2 && xIndexMax < xIndexR) {
const uint8* s = src + xWeights[xIndexR].index;
*(uint32*)d = *(uint32*)s;
// pixel in bottom right corner if necessary
if (yMax < y2 && xIndexMax < xIndexR) {
const uint8* s = src + xWeights[xIndexR].index;
*(uint32*)d = *(uint32*)s;
}
break;
}
}
} // switch(codeselect)
} while (fBaseRenderer.next_clip_box());
#ifdef FILTER_INFOS_ON_HEAP
@@ -24,6 +24,15 @@
#include <Font.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 BRegion;
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