Browse Source

Merge branch 'perf' of https://github.com/CendioOssman/tigervnc

tags/v1.4.90
Pierre Ossman 9 years ago
parent
commit
f481203dc0

+ 6
- 2
cmake/StaticBuild.cmake View File

@@ -47,14 +47,18 @@ if(BUILD_STATIC)

set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -Wl,-Bdynamic")

# GnuTLS uses various crypto-api stuff
if (WIN32)
# GnuTLS uses various crypto-api stuff
set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lcrypt32")
# And sockets
set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lws2_32")
endif()

# nanosleep() lives here on Solaris
if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
# nanosleep() lives here on Solaris
set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lrt")
# and socket functions are hidden here
set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lsocket")
endif()

# GnuTLS uses gettext and zlib, so make sure those are always

+ 1
- 0
common/rdr/CMakeLists.txt View File

@@ -4,6 +4,7 @@ add_library(rdr STATIC
Exception.cxx
FdInStream.cxx
FdOutStream.cxx
FileInStream.cxx
HexInStream.cxx
HexOutStream.cxx
InStream.cxx

+ 87
- 0
common/rdr/FileInStream.cxx View File

@@ -0,0 +1,87 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright (C) 2013 D. R. Commander. All Rights Reserved.
* Copyright 2015 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#include <errno.h>

#include <rdr/Exception.h>
#include <rdr/FileInStream.h>

using namespace rdr;

FileInStream::FileInStream(const char *fileName)
{
file = fopen(fileName, "rb");
if (!file)
throw SystemException("fopen", errno);
ptr = end = b;
}

FileInStream::~FileInStream(void) {
if (file) {
fclose(file);
file = NULL;
}
}

void FileInStream::reset(void) {
if (!file)
throw Exception("File is not open");
if (fseek(file, 0, SEEK_SET) != 0)
throw SystemException("fseek", errno);
ptr = end = b;
}

int FileInStream::pos()
{
if (!file)
throw Exception("File is not open");

return ftell(file) + ptr - b;
}

int FileInStream::overrun(int itemSize, int nItems, bool wait)
{
if (itemSize > sizeof(b))
throw Exception("FileInStream overrun: max itemSize exceeded");

if (end - ptr != 0)
memmove(b, ptr, end - ptr);

end -= ptr - b;
ptr = b;


while (end < b + itemSize) {
size_t n = fread((U8 *)end, b + sizeof(b) - end, 1, file);
if (n < 1) {
if (n < 0 || ferror(file))
throw Exception(strerror(errno));
if (feof(file))
throw EndOfStream();
if (n == 0) { return 0; }
}
end += b + sizeof(b) - end;
}

if (itemSize * nItems > end - ptr)
nItems = (end - ptr) / itemSize;

return nItems;
}

+ 50
- 0
common/rdr/FileInStream.h View File

@@ -0,0 +1,50 @@
/* Copyright (C) 2013 D. R. Commander. All Rights Reserved.
* Copyright 2015 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

#ifndef __RDR_FILEINSTREAM_H__
#define __RDR_FILEINSTREAM_H__

#include <stdio.h>

#include <rdr/InStream.h>

namespace rdr {

class FileInStream : public InStream {

public:

FileInStream(const char *fileName);
~FileInStream(void);

void reset(void);

int pos();

protected:
int overrun(int itemSize, int nItems, bool wait = true);

private:
U8 b[131072];
FILE *file;
};

} // end of namespace rdr

#endif

+ 3
- 0
common/rfb/CConnection.h View File

@@ -136,6 +136,9 @@ namespace rfb {
protected:
void setState(stateEnum s) { state_ = s; }

void setReader(CMsgReader *r) { reader_ = r; }
void setWriter(CMsgWriter *w) { writer_ = w; }

private:
// This is a default implementation of fences that automatically
// responds to requests, stating no support for synchronisation.

+ 1
- 1
common/rfb/CMakeLists.txt View File

@@ -67,7 +67,7 @@ if(WIN32)
set(RFB_SOURCES ${RFB_SOURCES} WinPasswdValidator.cxx)
endif(WIN32)

set(RFB_LIBRARIES ${JPEG_LIBRARIES} os rdr)
set(RFB_LIBRARIES ${JPEG_LIBRARIES} os rdr Xregion)

if(HAVE_PAM)
set(RFB_SOURCES ${RFB_SOURCES} UnixPasswordValidator.cxx

+ 8
- 0
common/rfb/CMsgHandler.cxx View File

@@ -74,3 +74,11 @@ void CMsgHandler::endOfContinuousUpdates()
{
cp.supportsContinuousUpdates = true;
}

void CMsgHandler::framebufferUpdateStart()
{
}

void CMsgHandler::framebufferUpdateEnd()
{
}

+ 2
- 2
common/rfb/CMsgHandler.h View File

@@ -57,8 +57,8 @@ namespace rfb {
virtual void endOfContinuousUpdates();
virtual void serverInit() = 0;

virtual void framebufferUpdateStart() = 0;
virtual void framebufferUpdateEnd() = 0;
virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd();
virtual void dataRect(const Rect& r, int encoding) = 0;

virtual void setColourMapEntries(int firstColour, int nColours,

+ 242
- 79
common/rfb/EncodeManager.cxx View File

@@ -23,6 +23,7 @@
#include <rfb/SConnection.h>
#include <rfb/SMsgWriter.h>
#include <rfb/UpdateTracker.h>
#include <rfb/LogWriter.h>

#include <rfb/RawEncoder.h>
#include <rfb/RREEncoder.h>
@@ -33,6 +34,8 @@

using namespace rfb;

static LogWriter vlog("EncodeManager");

// Split each rectangle into smaller ones no larger than this area,
// and no wider than this width.
static const int SubRectMaxArea = 65536;
@@ -73,8 +76,50 @@ struct RectInfo {

};

static const char *encoderClassName(EncoderClass klass)
{
switch (klass) {
case encoderRaw:
return "Raw";
case encoderRRE:
return "RRE";
case encoderHextile:
return "Hextile";
case encoderTight:
return "Tight";
case encoderTightJPEG:
return "Tight (JPEG)";
case encoderZRLE:
return "ZRLE";
}

return "Unknown Encoder Class";
}

static const char *encoderTypeName(EncoderType type)
{
switch (type) {
case encoderSolid:
return "Solid";
case encoderBitmap:
return "Bitmap";
case encoderBitmapRLE:
return "Bitmap RLE";
case encoderIndexed:
return "Indexed";
case encoderIndexedRLE:
return "Indexed RLE";
case encoderFullColour:
return "Full Colour";
}

return "Unknown Encoder Type";
}

EncodeManager::EncodeManager(SConnection* conn_) : conn(conn_)
{
StatsVector::iterator iter;

encoders.resize(encoderClassMax, NULL);
activeEncoders.resize(encoderTypeMax, encoderRaw);

@@ -84,16 +129,78 @@ EncodeManager::EncodeManager(SConnection* conn_) : conn(conn_)
encoders[encoderTight] = new TightEncoder(conn);
encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
encoders[encoderZRLE] = new ZRLEEncoder(conn);

updates = 0;
stats.resize(encoderClassMax);
for (iter = stats.begin();iter != stats.end();++iter) {
StatsVector::value_type::iterator iter2;
iter->resize(encoderTypeMax);
for (iter2 = iter->begin();iter2 != iter->end();++iter2)
memset(&*iter2, 0, sizeof(EncoderStats));
}
}

EncodeManager::~EncodeManager()
{
std::vector<Encoder*>::iterator iter;

logStats();

for (iter = encoders.begin();iter != encoders.end();iter++)
delete *iter;
}

void EncodeManager::logStats()
{
int i, j;

unsigned rects;
unsigned long long pixels, bytes, equivalent;

double ratio;

rects = 0;
pixels = bytes = equivalent = 0;

vlog.info("Framebuffer updates: %u", updates);

for (i = 0;i < stats.size();i++) {
// Did this class do anything at all?
for (j = 0;j < stats[i].size();j++) {
if (stats[i][j].rects != 0)
break;
}
if (j == stats[i].size())
continue;

vlog.info(" %s:", encoderClassName((EncoderClass)i));

for (j = 0;j < stats[i].size();j++) {
if (stats[i][j].rects == 0)
continue;

rects += stats[i][j].rects;
pixels += stats[i][j].pixels;
bytes += stats[i][j].bytes;
equivalent += stats[i][j].equivalent;

ratio = (double)stats[i][j].equivalent / stats[i][j].bytes;

vlog.info(" %s: %u rects, %llu pixels",
encoderTypeName((EncoderType)j),
stats[i][j].rects, stats[i][j].pixels);
vlog.info(" %*s %llu bytes (%g ratio)",
strlen(encoderTypeName((EncoderType)j)), "",
stats[i][j].bytes, ratio);
}
}

ratio = (double)equivalent / bytes;

vlog.info(" Total: %u rects, %llu pixels", rects, pixels);
vlog.info(" %llu bytes (%g ratio)", bytes, ratio);
}

bool EncodeManager::supported(int encoding)
{
switch (encoding) {
@@ -114,6 +221,8 @@ void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb,
int nRects;
Region changed;

updates++;

prepareEncoders();

if (conn->cp.supportsLastRect)
@@ -292,6 +401,40 @@ int EncodeManager::computeNumRects(const Region& changed)
return numRects;
}

Encoder *EncodeManager::startRect(const Rect& rect, int type)
{
Encoder *encoder;
int klass, equiv;

activeType = type;
klass = activeEncoders[activeType];

beforeLength = conn->getOutStream()->length();

stats[klass][activeType].rects++;
stats[klass][activeType].pixels += rect.area();
equiv = 12 + rect.area() * conn->cp.pf().bpp/8;
stats[klass][activeType].equivalent += equiv;

encoder = encoders[klass];
conn->writer()->startRect(rect, encoder->encoding);

return encoder;
}

void EncodeManager::endRect()
{
int klass;
int length;

conn->writer()->endRect();

length = conn->getOutStream()->length() - beforeLength;

klass = activeEncoders[activeType];
stats[klass][activeType].bytes += length;
}

void EncodeManager::writeCopyRects(const UpdateInfo& ui)
{
std::vector<Rect> rects;
@@ -309,84 +452,104 @@ void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb)
std::vector<Rect> rects;
std::vector<Rect>::const_iterator rect;

// FIXME: This gives up after the first rect it finds. A large update
// (like a whole screen refresh) might have lots of large solid
// areas.

changed->get_rects(&rects);
for (rect = rects.begin(); rect != rects.end(); ++rect) {
Rect sr;
int dx, dy, dw, dh;

// We start by finding a solid 16x16 block
for (dy = rect->tl.y; dy < rect->br.y; dy += SolidSearchBlock) {

dh = SolidSearchBlock;
if (dy + dh > rect->br.y)
dh = rect->br.y - dy;

for (dx = rect->tl.x; dx < rect->br.x; dx += SolidSearchBlock) {
// We define it like this to guarantee alignment
rdr::U32 _buffer;
rdr::U8* colourValue = (rdr::U8*)&_buffer;

dw = SolidSearchBlock;
if (dx + dw > rect->br.x)
dw = rect->br.x - dx;

pb->getImage(colourValue, Rect(dx, dy, dx+1, dy+1));

sr.setXYWH(dx, dy, dw, dh);
if (checkSolidTile(sr, colourValue, pb)) {
Rect erb, erp;

Encoder *encoder;

// We then try extending the area by adding more blocks
// in both directions and pick the combination that gives
// the largest area.
sr.setXYWH(dx, dy, rect->br.x - dx, rect->br.y - dy);
extendSolidAreaByBlock(sr, colourValue, pb, &erb);

// Did we end up getting the entire rectangle?
if (erb.equals(*rect))
erp = erb;
else {
// Don't bother with sending tiny rectangles
if (erb.area() < SolidBlockMinArea)
continue;

// Extend the area again, but this time one pixel
// row/column at a time.
extendSolidAreaByPixel(*rect, erb, colourValue, pb, &erp);
}

// Send solid-color rectangle.
encoder = encoders[activeEncoders[encoderSolid]];
conn->writer()->startRect(erp, encoder->encoding);
if (encoder->flags & EncoderUseNativePF) {
encoder->writeSolidRect(erp.width(), erp.height(),
pb->getPF(), colourValue);
} else {
rdr::U32 _buffer2;
rdr::U8* converted = (rdr::U8*)&_buffer2;

conn->cp.pf().bufferFromBuffer(converted, pb->getPF(),
colourValue, 1);

encoder->writeSolidRect(erp.width(), erp.height(),
conn->cp.pf(), converted);
}
conn->writer()->endRect();

changed->assign_subtract(Region(erp));

break;
for (rect = rects.begin(); rect != rects.end(); ++rect)
findSolidRect(*rect, changed, pb);
}

void EncodeManager::findSolidRect(const Rect& rect, Region *changed,
const PixelBuffer* pb)
{
Rect sr;
int dx, dy, dw, dh;

// We start by finding a solid 16x16 block
for (dy = rect.tl.y; dy < rect.br.y; dy += SolidSearchBlock) {

dh = SolidSearchBlock;
if (dy + dh > rect.br.y)
dh = rect.br.y - dy;

for (dx = rect.tl.x; dx < rect.br.x; dx += SolidSearchBlock) {
// We define it like this to guarantee alignment
rdr::U32 _buffer;
rdr::U8* colourValue = (rdr::U8*)&_buffer;

dw = SolidSearchBlock;
if (dx + dw > rect.br.x)
dw = rect.br.x - dx;

pb->getImage(colourValue, Rect(dx, dy, dx+1, dy+1));

sr.setXYWH(dx, dy, dw, dh);
if (checkSolidTile(sr, colourValue, pb)) {
Rect erb, erp;

Encoder *encoder;

// We then try extending the area by adding more blocks
// in both directions and pick the combination that gives
// the largest area.
sr.setXYWH(dx, dy, rect.br.x - dx, rect.br.y - dy);
extendSolidAreaByBlock(sr, colourValue, pb, &erb);

// Did we end up getting the entire rectangle?
if (erb.equals(rect))
erp = erb;
else {
// Don't bother with sending tiny rectangles
if (erb.area() < SolidBlockMinArea)
continue;

// Extend the area again, but this time one pixel
// row/column at a time.
extendSolidAreaByPixel(rect, erb, colourValue, pb, &erp);
}
}

if (dx < rect->br.x)
break;
// Send solid-color rectangle.
encoder = startRect(erp, encoderSolid);
if (encoder->flags & EncoderUseNativePF) {
encoder->writeSolidRect(erp.width(), erp.height(),
pb->getPF(), colourValue);
} else {
rdr::U32 _buffer2;
rdr::U8* converted = (rdr::U8*)&_buffer2;

conn->cp.pf().bufferFromBuffer(converted, pb->getPF(),
colourValue, 1);

encoder->writeSolidRect(erp.width(), erp.height(),
conn->cp.pf(), converted);
}
endRect();

changed->assign_subtract(Region(erp));

// Search remaining areas by recursion
// FIXME: Is this the best way to divide things up?

// Left? (Note that we've already searched a SolidSearchBlock
// pixels high strip here)
if ((erp.tl.x != rect.tl.x) && (erp.height() > SolidSearchBlock)) {
sr.setXYWH(rect.tl.x, erp.tl.y + SolidSearchBlock,
erp.tl.x - rect.tl.x, erp.height() - SolidSearchBlock);
findSolidRect(sr, changed, pb);
}

// Right?
if (erp.br.x != rect.br.x) {
sr.setXYWH(erp.br.x, erp.tl.y, rect.br.x - erp.br.x, erp.height());
findSolidRect(sr, changed, pb);
}

// Below?
if (erp.br.y != rect.br.y) {
sr.setXYWH(rect.tl.x, erp.br.y, rect.width(), rect.br.y - erp.br.y);
findSolidRect(sr, changed, pb);
}

return;
}
}
}
}
@@ -461,7 +624,7 @@ void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb)

// Special exception inherited from the Tight encoder
if (activeEncoders[encoderFullColour] == encoderTightJPEG) {
if (conn->cp.compressLevel < 2)
if ((conn->cp.compressLevel != -1) && (conn->cp.compressLevel < 2))
maxColours = 24;
else
maxColours = 96;
@@ -507,14 +670,14 @@ void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb)
type = encoderIndexed;
}

encoder = encoders[activeEncoders[type]];
encoder = startRect(rect, type);

if (encoder->flags & EncoderUseNativePF)
ppb = preparePixelBuffer(rect, pb, false);

conn->writer()->startRect(rect, encoder->encoding);
encoder->writeRect(ppb, info.palette);
conn->writer()->endRect();

endRect();
}

bool EncodeManager::checkSolidTile(const Rect& r, const rdr::U8* colourValue,

+ 19
- 0
common/rfb/EncodeManager.h View File

@@ -41,6 +41,8 @@ namespace rfb {
EncodeManager(SConnection* conn);
~EncodeManager();

void logStats();

// Hack to let ConnParams calculate the client's preferred encoding
static bool supported(int encoding);

@@ -52,8 +54,12 @@ namespace rfb {

int computeNumRects(const Region& changed);

Encoder *startRect(const Rect& rect, int type);
void endRect();

void writeCopyRects(const UpdateInfo& ui);
void writeSolidRects(Region *changed, const PixelBuffer* pb);
void findSolidRect(const Rect& rect, Region *changed, const PixelBuffer* pb);
void writeRects(const Region& changed, const PixelBuffer* pb);

void writeSubRect(const Rect& rect, const PixelBuffer *pb);
@@ -97,6 +103,19 @@ namespace rfb {
std::vector<Encoder*> encoders;
std::vector<int> activeEncoders;

struct EncoderStats {
unsigned rects;
unsigned long long bytes;
unsigned long long pixels;
unsigned long long equivalent;
};
typedef std::vector< std::vector<struct EncoderStats> > StatsVector;

unsigned updates;
StatsVector stats;
int activeType;
int beforeLength;

class OffsetPixelBuffer : public FullFramePixelBuffer {
public:
OffsetPixelBuffer() {}

+ 26
- 6
common/rfb/PixelBuffer.cxx View File

@@ -99,23 +99,43 @@ ModifiablePixelBuffer::~ModifiablePixelBuffer()
void ModifiablePixelBuffer::fillRect(const Rect& r, Pixel pix)
{
int stride;
U8 *buf, pixbuf[4];
U8 *buf;
int w, h, b;

buf = getBufferRW(r, &stride);
w = r.width();
h = r.height();
b = format.bpp/8;

format.bufferFromPixel(pixbuf, pix);
if (h == 0)
return;

buf = getBufferRW(r, &stride);

while (h--) {
int w_ = w;
while (w_--) {
if (b == 1) {
while (h--) {
memset(buf, pix, w);
buf += stride * b;
}
} else {
U8 pixbuf[4], *start;
int w1;

start = buf;

format.bufferFromPixel(pixbuf, pix);

w1 = w;
while (w1--) {
memcpy(buf, pixbuf, b);
buf += b;
}
buf += (stride - w) * b;
h--;

while (h--) {
memcpy(buf, start, w * b);
buf += stride * b;
}
}

commitBufferRW(r);

+ 2
- 3
common/rfb/SConnection.cxx View File

@@ -48,11 +48,10 @@ const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
const SConnection::AccessRights SConnection::AccessFull = 0xffff;


SConnection::SConnection(bool reverseConnection_)
SConnection::SConnection()
: readyForSetColourMapEntries(false),
is(0), os(0), reader_(0), writer_(0),
security(0), ssecurity(0), state_(RFBSTATE_UNINITIALISED),
reverseConnection(reverseConnection_),
preferredEncoding(encodingRaw)
{
defaultMajorVersion = 3;
@@ -271,7 +270,7 @@ void SConnection::writeConnFailedFromScratch(const char* msg,
os->flush();
}

void SConnection::setEncodings(int nEncodings, rdr::S32* encodings)
void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
{
int i;


+ 7
- 3
common/rfb/SConnection.h View File

@@ -38,7 +38,7 @@ namespace rfb {
class SConnection : public SMsgHandler {
public:

SConnection(bool reverseConnection_);
SConnection();
virtual ~SConnection();

// Methods to initialise the connection
@@ -71,7 +71,7 @@ namespace rfb {

// Overridden from SMsgHandler

virtual void setEncodings(int nEncodings, rdr::S32* encodings);
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);


// Methods to be overridden in a derived class
@@ -183,6 +183,11 @@ namespace rfb {

protected:
void setState(stateEnum s) { state_ = s; }

void setReader(SMsgReader *r) { reader_ = r; }
void setWriter(SMsgWriter *w) { writer_ = w; }

private:
void writeFakeColourMap(void);

bool readyForSetColourMapEntries;
@@ -201,7 +206,6 @@ namespace rfb {
SecurityServer *security;
SSecurity* ssecurity;
stateEnum state_;
bool reverseConnection;
rdr::S32 preferredEncoding;
};
}

+ 1
- 1
common/rfb/SMsgHandler.cxx View File

@@ -39,7 +39,7 @@ void SMsgHandler::setPixelFormat(const PixelFormat& pf)
cp.setPF(pf);
}

void SMsgHandler::setEncodings(int nEncodings, rdr::S32* encodings)
void SMsgHandler::setEncodings(int nEncodings, const rdr::S32* encodings)
{
bool firstFence, firstContinuousUpdates;


+ 1
- 1
common/rfb/SMsgHandler.h View File

@@ -46,7 +46,7 @@ namespace rfb {
virtual void clientInit(bool shared);

virtual void setPixelFormat(const PixelFormat& pf);
virtual void setEncodings(int nEncodings, rdr::S32* encodings);
virtual void setEncodings(int nEncodings, const rdr::S32* encodings);
virtual void framebufferUpdateRequest(const Rect& r, bool incremental) = 0;
virtual void setDesktopSize(int fb_width, int fb_height,
const ScreenSet& layout) = 0;

+ 2
- 28
common/rfb/SMsgWriter.cxx View File

@@ -33,31 +33,15 @@ using namespace rfb;
static LogWriter vlog("SMsgWriter");

SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
: cp(cp_), os(os_), currentEncoding(0),
: cp(cp_), os(os_),
nRectsInUpdate(0), nRectsInHeader(0),
needSetDesktopSize(false), needExtendedDesktopSize(false),
needSetDesktopName(false), needSetCursor(false), needSetXCursor(false),
lenBeforeRect(0), updatesSent(0), rawBytesEquivalent(0)
needSetDesktopName(false), needSetCursor(false), needSetXCursor(false)
{
for (int i = 0; i <= encodingMax; i++) {
bytesSent[i] = 0;
rectsSent[i] = 0;
}
}

SMsgWriter::~SMsgWriter()
{
vlog.info("framebuffer updates %d",updatesSent);
int bytes = 0;
for (int i = 0; i <= encodingMax; i++) {
if (i != encodingCopyRect)
bytes += bytesSent[i];
if (rectsSent[i])
vlog.info(" %s rects %d, bytes %d",
encodingName(i), rectsSent[i], bytesSent[i]);
}
vlog.info(" raw bytes equivalent %llu, compression ratio %f",
rawBytesEquivalent, (double)rawBytesEquivalent / bytes);
}

void SMsgWriter::writeServerInit()
@@ -276,7 +260,6 @@ void SMsgWriter::writeFramebufferUpdateEnd()
os->writeU32(pseudoEncodingLastRect);
}

updatesSent++;
endMsg();
}

@@ -293,11 +276,6 @@ void SMsgWriter::startRect(const Rect& r, int encoding)
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
throw Exception("SMsgWriter::startRect: nRects out of sync");

currentEncoding = encoding;
lenBeforeRect = os->length();
if (encoding != encodingCopyRect)
rawBytesEquivalent += 12 + r.width() * r.height() * (cp->pf().bpp/8);

os->writeS16(r.tl.x);
os->writeS16(r.tl.y);
os->writeU16(r.width());
@@ -307,10 +285,6 @@ void SMsgWriter::startRect(const Rect& r, int encoding)

void SMsgWriter::endRect()
{
if (currentEncoding <= encodingMax) {
bytesSent[currentEncoding] += os->length() - lenBeforeRect;
rectsSent[currentEncoding]++;
}
}

void SMsgWriter::startMsg(int type)

+ 0
- 13
common/rfb/SMsgWriter.h View File

@@ -109,11 +109,6 @@ namespace rfb {
void startRect(const Rect& r, int enc);
void endRect();

int getUpdatesSent() { return updatesSent; }
int getRectsSent(int encoding) { return rectsSent[encoding]; }
int getBytesSent(int encoding) { return bytesSent[encoding]; }
rdr::U64 getRawBytesEquivalent() { return rawBytesEquivalent; }

protected:
void startMsg(int type);
void endMsg();
@@ -137,8 +132,6 @@ namespace rfb {
ConnParams* cp;
rdr::OutStream* os;

int currentEncoding;

int nRectsInUpdate;
int nRectsInHeader;

@@ -149,12 +142,6 @@ namespace rfb {
bool needSetCursor;
bool needSetXCursor;

int lenBeforeRect;
int updatesSent;
int bytesSent[encodingMax+1];
int rectsSent[encodingMax+1];
rdr::U64 rawBytesEquivalent;

typedef struct {
rdr::U16 reason, result;
int fb_width, fb_height;

+ 1
- 1
common/rfb/VNCSConnectionST.cxx View File

@@ -66,7 +66,7 @@ struct RTTInfo {

VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
bool reverse)
: SConnection(reverse), sock(s),
: sock(s), reverseConnection(reverse),
queryConnectTimer(this), inProcessMessages(false),
pendingSyncFence(false), syncFence(false), fenceFlags(0),
fenceDataLen(0), fenceData(NULL),

+ 1
- 0
common/rfb/VNCSConnectionST.h View File

@@ -173,6 +173,7 @@ namespace rfb {

network::Socket* sock;
CharArray peerEndpoint;
bool reverseConnection;

Timer queryConnectTimer;


+ 6
- 0
tests/CMakeLists.txt View File

@@ -7,3 +7,9 @@ target_link_libraries(convperf test_util rfb)

add_executable(conv conv.cxx)
target_link_libraries(conv rfb)

add_executable(decperf decperf.cxx)
target_link_libraries(decperf test_util rfb)

add_executable(encperf encperf.cxx)
target_link_libraries(encperf test_util rfb)

+ 217
- 0
tests/decperf.cxx View File

@@ -0,0 +1,217 @@
/* Copyright 2015 Pierre Ossman <ossman@cendio.se> for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

/*
* This program reads files produced by TightVNC's/TurboVNC's
* compare-encodings. It is basically a dump of the RFB protocol
* from the server side from the ServerInit message and forward.
* It is assumed that the client is using a bgr888 (LE) pixel
* format.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <rdr/Exception.h>
#include <rdr/FileInStream.h>

#include <rfb/CConnection.h>
#include <rfb/CMsgReader.h>
#include <rfb/Decoder.h>
#include <rfb/PixelBuffer.h>
#include <rfb/PixelFormat.h>

#include "util.h"

// FIXME: Files are always in this format
static const rfb::PixelFormat filePF(32, 24, false, true, 255, 255, 255, 0, 8, 16);

class CConn : public rfb::CConnection {
public:
CConn(const char *filename);
~CConn();

virtual void setDesktopSize(int w, int h);
virtual void setPixelFormat(const rfb::PixelFormat& pf);
virtual void setCursor(int, int, const rfb::Point&, void*, void*);
virtual void dataRect(const rfb::Rect&, int);
virtual void setColourMapEntries(int, int, rdr::U16*);
virtual void bell();
virtual void serverCutText(const char*, rdr::U32);

public:
double cpuTime;

protected:
rdr::FileInStream *in;
rfb::Decoder *decoders[rfb::encodingMax+1];
rfb::ManagedPixelBuffer pb;
};

CConn::CConn(const char *filename)
{
int i;

cpuTime = 0.0;

in = new rdr::FileInStream(filename);
setStreams(in, NULL);

memset(decoders, 0, sizeof(decoders));
for (i = 0;i < rfb::encodingMax;i++) {
if (!rfb::Decoder::supported(i))
continue;

decoders[i] = rfb::Decoder::createDecoder(i, this);
}

// Need to skip the initial handshake
setState(RFBSTATE_INITIALISATION);
// That also means that the reader and writer weren't setup
setReader(new rfb::CMsgReader(this, in));
}

CConn::~CConn()
{
int i;

delete in;

for (i = 0;i < rfb::encodingMax;i++)
delete decoders[i];
}

void CConn::setDesktopSize(int w, int h)
{
CConnection::setDesktopSize(w, h);

pb.setSize(cp.width, cp.height);
}

void CConn::setPixelFormat(const rfb::PixelFormat& pf)
{
// Override format
CConnection::setPixelFormat(filePF);

pb.setPF(cp.pf());
}

void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
{
}

void CConn::dataRect(const rfb::Rect &r, int encoding)
{
if (!decoders[encoding])
throw rdr::Exception("Unknown encoding");

startCpuCounter();
decoders[encoding]->readRect(r, &pb);
endCpuCounter();

cpuTime += getCpuCounter();
}

void CConn::setColourMapEntries(int, int, rdr::U16*)
{
}

void CConn::bell()
{
}

void CConn::serverCutText(const char*, rdr::U32)
{
}

static double runTest(const char *fn)
{
CConn *cc;
double time;

cc = new CConn(fn);

try {
while (true)
cc->processMsg();
} catch (rdr::EndOfStream e) {
} catch (rdr::Exception e) {
fprintf(stderr, "Failed to run rfb file: %s\n", e.str());
exit(1);
}

time = cc->cpuTime;

delete cc;

return time;
}

static void sort(double *array, int count)
{
bool sorted;
int i;
do {
sorted = true;
for (i = 1;i < count;i++) {
if (array[i-1] > array[i]) {
double d;
d = array[i];
array[i] = array[i-1];
array[i-1] = d;
sorted = false;
}
}
} while (!sorted);
}

static const int runCount = 9;

int main(int argc, char **argv)
{
int i;
double times[runCount], dev[runCount];
double median, meddev;

if (argc != 2) {
printf("Syntax: %s <rfb file>\n", argv[0]);
return 1;
}

// Warmup
runTest(argv[1]);

// Multiple runs to get a good average
for (i = 0;i < runCount;i++)
times[i] = runTest(argv[1]);

// Calculate median and median deviation
sort(times, runCount);
median = times[runCount/2];

for (i = 0;i < runCount;i++)
dev[i] = fabs((times[i] - median) / median) * 100;

sort(dev, runCount);
meddev = dev[runCount/2];

printf("CPU time: %g s (+/- %g %)\n", median, meddev);

return 0;
}

+ 439
- 0
tests/encperf.cxx View File

@@ -0,0 +1,439 @@
/* Copyright 2015 Pierre Ossman <ossman@cendio.se> for Cendio AB
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/

/*
* This program reads files produced by TightVNC's/TurboVNC's
* fbs-dump, which in turn takes files from rfbproxy. It is
* basically a dump of the RFB protocol from the server side after
* the ServerInit message. Mostly this consists of FramebufferUpdate
* message using the HexTile encoding. Screen size and pixel format
* are not encoded in the file and must be specified by the user.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <rdr/Exception.h>
#include <rdr/OutStream.h>
#include <rdr/FileInStream.h>

#include <rfb/PixelFormat.h>

#include <rfb/CConnection.h>
#include <rfb/CMsgReader.h>
#include <rfb/Decoder.h>
#include <rfb/UpdateTracker.h>

#include <rfb/EncodeManager.h>
#include <rfb/SConnection.h>
#include <rfb/SMsgWriter.h>

#include "util.h"

static rfb::IntParameter width("width", "Frame buffer width", 0);
static rfb::IntParameter height("height", "Frame buffer height", 0);

static rfb::StringParameter format("format", "Pixel format (e.g. bgr888)", "");

// The frame buffer (and output) is always this format
static const rfb::PixelFormat fbPF(32, 24, false, true, 255, 255, 255, 0, 8, 16);

// Encodings to use
static const rdr::S32 encodings[] = {
rfb::encodingTight, rfb::encodingCopyRect, rfb::encodingRRE,
rfb::encodingHextile, rfb::encodingZRLE, rfb::pseudoEncodingLastRect,
rfb::pseudoEncodingQualityLevel0 + 8 };

class DummyOutStream : public rdr::OutStream {
public:
DummyOutStream();

virtual int length();
virtual void flush();

private:
virtual int overrun(int itemSize, int nItems);

int offset;
rdr::U8 buf[131072];
};

class CConn : public rfb::CConnection {
public:
CConn(const char *filename);
~CConn();

double getRatio();

virtual void setDesktopSize(int w, int h);
virtual void setCursor(int, int, const rfb::Point&, void*, void*);
virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd();
virtual void dataRect(const rfb::Rect&, int);
virtual void setColourMapEntries(int, int, rdr::U16*);
virtual void bell();
virtual void serverCutText(const char*, rdr::U32);

public:
double decodeTime;
double encodeTime;

protected:
rdr::FileInStream *in;
rfb::Decoder *decoders[rfb::encodingMax+1];
rfb::ManagedPixelBuffer pb;
rfb::SimpleUpdateTracker updates;
class SConn *sc;
};

class Manager : public rfb::EncodeManager {
public:
Manager(class rfb::SConnection *conn);

double getRatio();
};

class SConn : public rfb::SConnection {
public:
SConn();
~SConn();

void writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb);

double getRatio();

virtual void setAccessRights(AccessRights ar);

virtual void setDesktopSize(int fb_width, int fb_height,
const rfb::ScreenSet& layout);

protected:
DummyOutStream *out;
Manager *manager;
};

DummyOutStream::DummyOutStream()
{
offset = 0;
ptr = buf;
end = buf + sizeof(buf);
}

int DummyOutStream::length()
{
flush();
return offset;
}

void DummyOutStream::flush()
{
offset += ptr - buf;
ptr = buf;
}

int DummyOutStream::overrun(int itemSize, int nItems)
{
flush();
}

CConn::CConn(const char *filename)
{
int i;

decodeTime = 0.0;
encodeTime = 0.0;

in = new rdr::FileInStream(filename);
setStreams(in, NULL);

memset(decoders, 0, sizeof(decoders));
for (i = 0;i < rfb::encodingMax;i++) {
if (!rfb::Decoder::supported(i))
continue;

decoders[i] = rfb::Decoder::createDecoder(i, this);
}

pb.setPF(fbPF);

// Need to skip the initial handshake and ServerInit
setState(RFBSTATE_NORMAL);
// That also means that the reader and writer weren't setup
setReader(new rfb::CMsgReader(this, in));
// Nor the frame buffer size and format
setDesktopSize(width, height);
rfb::PixelFormat pf;
pf.parse(format);
setPixelFormat(pf);

sc = new SConn();
sc->cp.setPF(pb.getPF());
sc->setEncodings(sizeof(encodings)/sizeof(*encodings), encodings);
}

CConn::~CConn()
{
int i;

delete sc;

delete in;

for (i = 0;i < rfb::encodingMax;i++)
delete decoders[i];
}

double CConn::getRatio()
{
return sc->getRatio();
}

void CConn::setDesktopSize(int w, int h)
{
CConnection::setDesktopSize(w, h);

pb.setSize(cp.width, cp.height);
}

void CConn::setCursor(int, int, const rfb::Point&, void*, void*)
{
}

void CConn::framebufferUpdateStart()
{
updates.clear();
}

void CConn::framebufferUpdateEnd()
{
rfb::UpdateInfo ui;
rfb::Region clip(pb.getRect());

updates.getUpdateInfo(&ui, clip);

startCpuCounter();
sc->writeUpdate(ui, &pb);
endCpuCounter();

encodeTime += getCpuCounter();
}

void CConn::dataRect(const rfb::Rect &r, int encoding)
{
if (!decoders[encoding])
throw rdr::Exception("Unknown encoding");

startCpuCounter();
decoders[encoding]->readRect(r, &pb);
endCpuCounter();

decodeTime += getCpuCounter();

if (encoding != rfb::encodingCopyRect) // FIXME
updates.add_changed(rfb::Region(r));
}

void CConn::setColourMapEntries(int, int, rdr::U16*)
{
}

void CConn::bell()
{
}

void CConn::serverCutText(const char*, rdr::U32)
{
}

Manager::Manager(class rfb::SConnection *conn) :
EncodeManager(conn)
{
}

double Manager::getRatio()
{
StatsVector::iterator iter;
unsigned long long bytes, equivalent;

bytes = equivalent = 0;
for (iter = stats.begin();iter != stats.end();++iter) {
StatsVector::value_type::iterator iter2;
for (iter2 = iter->begin();iter2 != iter->end();++iter2) {
bytes += iter2->bytes;
equivalent += iter2->equivalent;
}
}

return (double)equivalent / bytes;
}

SConn::SConn()
{
out = new DummyOutStream;
setStreams(NULL, out);

setWriter(new rfb::SMsgWriter(&cp, out));

manager = new Manager(this);
}

SConn::~SConn()
{
delete manager;
delete out;
}

void SConn::writeUpdate(const rfb::UpdateInfo& ui, const rfb::PixelBuffer* pb)
{
manager->writeUpdate(ui, pb, NULL);
}

double SConn::getRatio()
{
return manager->getRatio();
}

void SConn::setAccessRights(AccessRights ar)
{
}

void SConn::setDesktopSize(int fb_width, int fb_height,
const rfb::ScreenSet& layout)
{
}

static double runTest(const char *fn, double *ratio)
{
CConn *cc;
double time;

cc = new CConn(fn);

try {
while (true)
cc->processMsg();
} catch (rdr::EndOfStream e) {
} catch (rdr::Exception e) {
fprintf(stderr, "Failed to run rfb file: %s\n", e.str());
exit(1);
}

time = cc->encodeTime;
*ratio = cc->getRatio();

delete cc;

return time;
}

static void sort(double *array, int count)
{
bool sorted;
int i;
do {
sorted = true;
for (i = 1;i < count;i++) {
if (array[i-1] > array[i]) {
double d;
d = array[i];
array[i] = array[i-1];
array[i-1] = d;
sorted = false;
}
}
} while (!sorted);
}

static const int runCount = 9;

static void usage(const char *argv0)
{
fprintf(stderr, "Syntax: %s [options] <rfb file>\n", argv0);
fprintf(stderr, "Options:\n");
rfb::Configuration::listParams(79, 14);
exit(1);
}

int main(int argc, char **argv)
{
int i;

const char *fn;

double times[runCount], dev[runCount];
double median, meddev, ratio;

fn = NULL;
for (i = 1; i < argc; i++) {
if (rfb::Configuration::setParam(argv[i]))
continue;

if (argv[i][0] == '-') {
if (i+1 < argc) {
if (rfb::Configuration::setParam(&argv[i][1], argv[i+1])) {
i++;
continue;
}
}
usage(argv[0]);
}

if (fn != NULL)
usage(argv[0]);

fn = argv[i];
}

if (fn == NULL) {
fprintf(stderr, "No file specified!\n\n");
usage(argv[0]);
}

if (!format.hasBeenSet()) {
fprintf(stderr, "Pixel format not specified!\n\n");
usage(argv[0]);
}

if (!width.hasBeenSet() || !height.hasBeenSet()) {
fprintf(stderr, "Frame buffer size not specified!\n\n");
usage(argv[0]);
}

// Warmup
runTest(fn, &ratio);

// Multiple runs to get a good average
for (i = 0;i < runCount;i++)
times[i] = runTest(fn, &ratio);

// Calculate median and median deviation
sort(times, runCount);
median = times[runCount/2];

for (i = 0;i < runCount;i++)
dev[i] = fabs((times[i] - median) / median) * 100;

sort(dev, runCount);
meddev = dev[runCount/2];

printf("CPU time: %g s (+/- %g %)\n", median, meddev);
printf("Ratio: %g\n", ratio);

return 0;
}

+ 61
- 18
tests/util.cxx View File

@@ -17,6 +17,8 @@
*/

#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <windows.h>
@@ -24,52 +26,93 @@
#include <sys/resource.h>
#endif

#include "util.h"

#ifdef WIN32
static FILETIME cpuCounters[2];
typedef FILETIME syscounter_t;
#else
struct rusage cpuCounters[2];
typedef struct rusage syscounter_t;
#endif

static void measureCpu(void *counter)
static syscounter_t _globalCounter[2];
static cpucounter_t globalCounter = _globalCounter;

void startCpuCounter(void)
{
startCpuCounter(globalCounter);
}

void endCpuCounter(void)
{
endCpuCounter(globalCounter);
}

double getCpuCounter(void)
{
return getCpuCounter(globalCounter);
}

cpucounter_t newCpuCounter(void)
{
syscounter_t *c;

c = (syscounter_t*)malloc(sizeof(syscounter_t) * 2);
if (c == NULL)
return NULL;

memset(c, 0, sizeof(syscounter_t) * 2);

return c;
}

void freeCpuCounter(cpucounter_t c)
{
free(c);
}

static void measureCpu(syscounter_t *counter)
{
#ifdef WIN32
FILETIME dummy1, dummy2, dummy3;

GetProcessTimes(GetCurrentProcess(), &dummy1, &dummy2,
&dummy3, (FILETIME*)counter);
&dummy3, counter);
#else
getrusage(RUSAGE_SELF, (struct rusage*)counter);
getrusage(RUSAGE_SELF, counter);
#endif
}

void startCpuCounter(void)
void startCpuCounter(cpucounter_t c)
{
measureCpu(&cpuCounters[0]);
syscounter_t *s = (syscounter_t*)c;
measureCpu(&s[0]);
}

void endCpuCounter(void)
void endCpuCounter(cpucounter_t c)
{
measureCpu(&cpuCounters[1]);
syscounter_t *s = (syscounter_t*)c;
measureCpu(&s[1]);
}

double getCpuCounter(void)
double getCpuCounter(cpucounter_t c)
{
syscounter_t *s = (syscounter_t*)c;
double seconds;

#ifdef WIN32
uint64_t counters[2];

counters[0] = (uint64_t)cpuCounters[0].dwHighDateTime << 32 |
cpuCounters[0].dwLowDateTime;
counters[1] = (uint64_t)cpuCounters[1].dwHighDateTime << 32 |
cpuCounters[1].dwLowDateTime;
counters[0] = (uint64_t)s[0].dwHighDateTime << 32 |
s[0].dwLowDateTime;
counters[1] = (uint64_t)s[1].dwHighDateTime << 32 |
s[1].dwLowDateTime;

seconds = (double)(counters[1] - counters[2]) / 10000000.0;
#else
seconds = (double)(cpuCounters[1].ru_utime.tv_sec -
cpuCounters[0].ru_utime.tv_sec);
seconds += (double)(cpuCounters[1].ru_utime.tv_usec -
cpuCounters[0].ru_utime.tv_usec) / 1000000.0;
seconds = (double)(s[1].ru_utime.tv_sec -
s[0].ru_utime.tv_sec);
seconds += (double)(s[1].ru_utime.tv_usec -
s[0].ru_utime.tv_usec) / 1000000.0;
#endif

return seconds;

+ 10
- 0
tests/util.h View File

@@ -19,9 +19,19 @@
#ifndef __TESTS_UTIL_H__
#define __TESTS_UTIL_H__

typedef void* cpucounter_t;

void startCpuCounter(void);
void endCpuCounter(void);

double getCpuCounter(void);

cpucounter_t newCpuCounter(void);
void freeCpuCounter(cpucounter_t c);

void startCpuCounter(cpucounter_t c);
void endCpuCounter(cpucounter_t c);

double getCpuCounter(cpucounter_t c);

#endif

Loading…
Cancel
Save