added 1 changeset to branch 'refs/remotes/looncraz-github/ScreenStream' old head: e2bacf5298027add4b696482de30ff3947964040 new head: 62c2759aca0918326563bd8132afd8d531b896a5 overview: https://github.com/looncraz/haiku/compare/e2bacf529802...62c2759aca09 ---------------------------------------------------------------------------- 62c2759aca09: Refactoring Complete Rendering now occurs on the server side, and memory usage is reduced by half thanks to now longer using a backing buffer. Using a special inclusive raw copy in ServerBitmap for performance as well as including the full area (akin to BeOS) on region updates. Found that ServerApp was not settings it fClientToken. Performance is improved, memory performance is cut in half, and there are no more missing dirty regions on the screen (as far sc I can see). [ looncraz <looncraz@xxxxxxxxxxxx> ] ---------------------------------------------------------------------------- Commit: 62c2759aca0918326563bd8132afd8d531b896a5 Author: looncraz <looncraz@xxxxxxxxxxxx> Date: Mon Mar 30 01:01:04 2015 UTC ---------------------------------------------------------------------------- 15 files changed, 536 insertions(+), 169 deletions(-) headers/os/interface/ScreenStream.h | 13 +- src/kits/interface/ScreenStream.cpp | 140 ++++++++++++-------- src/servers/app/Desktop.cpp | 54 +++++++- src/servers/app/IntRect.h | 31 +++-- src/servers/app/ServerApp.cpp | 10 ++ src/servers/app/ServerApp.h | 2 + src/servers/app/ServerBitmap.cpp | 75 +++++++++++ src/servers/app/ServerBitmap.h | 12 +- src/servers/app/ServerWindow.cpp | 2 +- src/servers/app/drawing/DrawingEngine.cpp | 6 +- src/servers/app/drawing/DrawingEngine.h | 2 +- src/servers/app/drawing/HWInterface.cpp | 177 +++++++++++++++++++++++--- src/servers/app/drawing/HWInterface.h | 13 +- src/servers/app/drawing/UpdateStream.cpp | 131 +++++++++++-------- src/servers/app/drawing/UpdateStream.h | 37 ++++-- ---------------------------------------------------------------------------- diff --git a/headers/os/interface/ScreenStream.h b/headers/os/interface/ScreenStream.h index 5adb6a2..135250f 100644 --- a/headers/os/interface/ScreenStream.h +++ b/headers/os/interface/ScreenStream.h @@ -17,7 +17,8 @@ namespace BPrivate { class BScreenStream { public: - BScreenStream(screen_id id = B_MAIN_SCREEN_ID); + BScreenStream(screen_id id = B_MAIN_SCREEN_ID, + bool enableBitmap = true); virtual ~BScreenStream(); status_t InitCheck() const; @@ -41,8 +42,8 @@ public: void ShowCursor(); bigtime_t LastUpdate() const; - bigtime_t MaxUpdateInterval() const; - void SetMaxUpdateInterval(bigtime_t interval); + uint32 GetMaxFrameRate() const; + void SetMaxFrameRate(uint32 rate); private: status_t _UpdateBitmap(); @@ -51,13 +52,15 @@ private: mutable BLocker fLock; BScreen fScreen; BBitmap* fBitmap; - BBitmap* fBackMap; BRegion fRegion; BRegion fRegionMaskUpdate; BRegion fRegionMask; + bigtime_t fLastUpdate; bigtime_t fMaxUpdateInterval; - bool fShowCursor; + uint32 fMaxFrameRate; + + bool fHideCursor; bool fEnableBitmap; status_t fInitStatus; diff --git a/src/kits/interface/ScreenStream.cpp b/src/kits/interface/ScreenStream.cpp index 7a7dc5e..dd1cffd 100644 --- a/src/kits/interface/ScreenStream.cpp +++ b/src/kits/interface/ScreenStream.cpp @@ -1,15 +1,23 @@ #include <stdio.h> +#include <Application.h> #include <Autolock.h> -#include <Bitmap.h> +// Is there a way to get rid of this? +# undef private +# define private public +# include <Bitmap.h> +# undef private +# define private private #include <GraphicsDefs.h> #include <Region.h> #include <Screen.h> #include "ScreenStream.h" #include <OS.h> -#include "private/app/ServerProtocol.h" +#include "private/app/AppMisc.h" #include "private/app/DesktopLink.h" +#include "private/app/ServerProtocol.h" + #define DEBUGSS @@ -28,32 +36,57 @@ doesn't take different screens into account at all when reading the screen bitmap, so it would be a moot point until the protocol includes the screen ID. + + Our protocol always sends the screen ID, but does nothing with it, + for now. */ -BScreenStream::BScreenStream(screen_id id) +BScreenStream::BScreenStream(screen_id id, bool enableBitmap) : fScreen(id), fBitmap(NULL), fLastUpdate(0), fMaxUpdateInterval(16666), // 60hz, 16.67ms - fShowCursor(true), - fEnableBitmap(true), + fHideCursor(false), + fEnableBitmap(enableBitmap), fInitStatus(B_NO_INIT), fPortLink(new BPrivate::DesktopLink()), fStreamID(0) { + if (be_app == NULL) + debugger("BApplication object required!"); + + int32 appClientToken = _get_object_token_(be_app); + int32 bitmapToken = -1; + + if (enableBitmap) { + fInitStatus = _UpdateBitmap(); + if (fInitStatus != B_OK) + return; + + bitmapToken = fBitmap->_ServerToken(); + } + int32 reply = B_NO_MEMORY; if (fPortLink != NULL && fPortLink->InitCheck() == B_OK) { + fPortLink->StartMessage(AS_CREATE_SCREEN_STREAM); + if (fPortLink->Attach(fPortLink->ReceiverPort()) == B_OK && fPortLink->Attach<screen_id>(id) == B_OK + && fPortLink->Attach<int32>(appClientToken) == B_OK + && fPortLink->Attach<int32>(bitmapToken) == B_OK && fPortLink->FlushWithReply(reply) == B_OK) { - if (fPortLink->Read(&fStreamID) != B_OK) - fInitStatus = B_BAD_VALUE; - } else + if (reply == B_OK) { + if (fPortLink->Read(&fStreamID) != B_OK) + fInitStatus = B_BAD_VALUE; + } + } else { fPortLink->CancelMessage(); + STRACE(("Message send failed!\n")); + } } fInitStatus = reply; @@ -93,12 +126,16 @@ BScreenStream::Update() if (InitCheck() != B_OK) return InitCheck(); + if (fBitmap == NULL + && fEnableBitmap) + return _UpdateBitmap(); + // Limit to a maximum interval while (system_time() - fLastUpdate < fMaxUpdateInterval) { if (fScreen.WaitForRetrace() != B_OK) snooze(fMaxUpdateInterval/10); } - + BAutolock _(fLock); fLastUpdate = system_time(); bool success = false; @@ -108,6 +145,7 @@ BScreenStream::Update() if (fPortLink->Attach(fPortLink->ReceiverPort()) == B_OK && fPortLink->Attach(fScreen.ID()) == B_OK && fPortLink->Attach(fStreamID) == B_OK + && fPortLink->Attach(fHideCursor) == B_OK && fPortLink->FlushWithReply(reply) == B_OK) { if (reply == B_OK) { success = true; @@ -117,7 +155,8 @@ BScreenStream::Update() fRegionMaskUpdate = fRegion; fRegionMaskUpdate.IntersectWith(&fRegionMask); fRegion.Exclude(&fRegionMask); - error = _UpdateBitmap(); + if (fEnableBitmap) + error = _UpdateBitmap(); } } else { @@ -208,7 +247,7 @@ bool BScreenStream::IsCursorHidden() const { BAutolock _(fLock); - return fShowCursor; + return fHideCursor; } @@ -216,7 +255,7 @@ void BScreenStream::HideCursor() { BAutolock _(fLock); - fShowCursor = false; + fHideCursor = true; } @@ -224,7 +263,7 @@ void BScreenStream::ShowCursor() { BAutolock _(fLock); - fShowCursor = true; + fHideCursor = false; } @@ -236,19 +275,26 @@ BScreenStream::LastUpdate() const } -bigtime_t -BScreenStream::MaxUpdateInterval() const +uint32 +BScreenStream::GetMaxFrameRate() const { BAutolock _(fLock); - return fMaxUpdateInterval; + return fMaxFrameRate; } void -BScreenStream::SetMaxUpdateInterval(bigtime_t interval) +BScreenStream::SetMaxFrameRate(uint32 rate) { BAutolock _(fLock); - fMaxUpdateInterval = interval; + + if (rate < 1) + rate = 1; + if (rate > 60) + rate = 60; + + fMaxFrameRate = rate; + fMaxUpdateInterval = 1000000 / rate; } @@ -263,44 +309,19 @@ BScreenStream::_UpdateBitmap() // First update is always a full-update if (fBitmap == NULL) { - error = fScreen.GetBitmap(&fBitmap, fShowCursor); - if (error == B_OK) - error = fScreen.GetBitmap(&fBackMap, fShowCursor); - return error; - } - - - - BRect screenFrame = fBitmap->Bounds(); - - for (int i = 0; i < fRegion.CountRects(); ++i) { - BRect frame = fRegion.RectAt(i); - - if (frame.IsValid()) { - - // Include a slightly larger region: - frame.InsetBy(-5, -5); - _Constrain(frame, screenFrame); - - error = fScreen.ReadBitmap(fBackMap, fShowCursor, &frame); - if (error != B_OK) { - STRACE(("Error reading bitmap from screen!\n")); - } - - fBitmap->ImportBits(fBackMap, BPoint(0, 0), - frame.LeftTop(), frame.Width(), frame.Height()); - - } else { - STRACE(("Invalid rect in region!\n\t")); - #ifdef DEBUGSS - frame.PrintToStream(); - #endif + error = fScreen.GetBitmap(&fBitmap, !fHideCursor); + if (error != B_OK) { + STRACE(("ERROR on GetBitmap(): %li\n", error)); + return error; } + + fRegionMaskUpdate = fRegionMask; } + BRect screenFrame = fBitmap->Bounds(); BRegion forbidden = fRegionMaskUpdate; - int32 ppr = fBitmap->BytesPerRow()/4; + int32 pixelsPerRow = fBitmap->BytesPerRow()/4; uint32 color = 0xffaa0000ul; uint32* bits = (uint32*)fBitmap->Bits(); @@ -308,14 +329,21 @@ BScreenStream::_UpdateBitmap() for (int i = 0; i < forbidden.CountRects(); ++i) { BRect rect = forbidden.RectAt(i); _Constrain(rect, screenFrame); - int yStart = rect.top; - int xStart = rect.left; - int yEnd = yStart + rect.Height(); - int xEnd = xStart + rect.Width(); + // TODO: + // why in the heck is this STILL required?!? + // The server cleans it up, the draws should never + // attempt to be out of bounds... + // Obviously it is an off-by-one error somewhere + // (ran into this before in the same area, seems to be + // a bug with clipping, but all the code looked good). + int yStart = (int)rect.top; + int xStart = (int)rect.left; + int yEnd = yStart + (int)rect.Height(); + int xEnd = xStart + (int)rect.Width(); for (int y = yStart; y < yEnd; ++y) for (int x = xStart; x < xEnd; ++x) - bits[(y*ppr) + x] = color; + bits[(y*pixelsPerRow) + x] = color; } fRegionMaskUpdate.MakeEmpty(); diff --git a/src/servers/app/Desktop.cpp b/src/servers/app/Desktop.cpp index 224973e..a4a36ce 100644 --- a/src/servers/app/Desktop.cpp +++ b/src/servers/app/Desktop.cpp @@ -1428,6 +1428,7 @@ Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace) newDirtyRegion.Exclude(©Region); MarkDirty(newDirtyRegion); + _SetBackground(background); _WindowChanged(window); @@ -1486,7 +1487,7 @@ Desktop::ResizeWindowBy(Window* window, float x, float y) // make sure the window cannot mark stuff dirty outside // its visible region... newDirtyRegion.IntersectWith(&window->VisibleRegion()); - // ...because we do this outself + // ...because we do this ourself newDirtyRegion.Include(&previouslyOccupiedRegion); MarkDirty(newDirtyRegion); @@ -2533,8 +2534,10 @@ Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) fApplicationsLock.Unlock(); - if (removeApp != NULL) + if (removeApp != NULL) { + //HWInterface()->DeleteUpdateStreams(removeApp->ClientToken()); removeApp->Quit(fShutdownSemaphore); + } if (fQuitting && count <= 1) { // wait for the last app to die @@ -2654,15 +2657,37 @@ Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) // Create an UpdateStream and attach it to the our HWInterface screen_id screenID = B_MAIN_SCREEN_ID; port_id replyPort = -1; + int32 clientToken = -1; + int32 bitmapToken = -1; if (link.Read<port_id>(&replyPort) != B_OK - || link.Read<screen_id>(&screenID) != B_OK) + || link.Read<screen_id>(&screenID) != B_OK + || link.Read<int32>(&clientToken) != B_OK + || link.Read<int32>(&bitmapToken) != B_OK) break; - UpdateStream* stream = HWInterface()->CreateUpdateStream(); + ServerBitmap* bitmap = NULL; + + // find bitmap for bitmapToken + if (bitmapToken >= 0) { + BAutolock appsLock(fApplicationsLock); + ServerApp* app = NULL; + for (int32 i = fApplications.CountItems(); i-- > 0;) { + app = fApplications.ItemAt(i); + if (app->ClientToken() == clientToken) { + bitmap = app->GetBitmap(bitmapToken); + break; + } + } + } + + status_t error = B_OK; + UpdateStream* stream = HWInterface() + ->CreateUpdateStream(clientToken, bitmap, &error); + BPrivate::LinkSender replyLink(replyPort); if (stream == NULL) { - replyLink.StartMessage(B_PERMISSION_DENIED); + replyLink.StartMessage(error); replyLink.Flush(); break; } @@ -2678,10 +2703,12 @@ Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) screen_id screenID = B_MAIN_SCREEN_ID; uint32 streamID = 0; port_id replyPort = -1; + bool hideCursor = false; if (link.Read<port_id>(&replyPort) != B_OK - || link.Read<screen_id>(&screenID) - || link.Read<uint32>(&streamID)) + || link.Read<screen_id>(&screenID) != B_OK + || link.Read<uint32>(&streamID) != B_OK + || link.Read<bool>(&hideCursor) != B_OK) break; UpdateStream* stream = HWInterface()->FindUpdateStream(streamID); @@ -2693,10 +2720,22 @@ Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) break; } + stream->Lock(); + stream->SetCursorHidden(hideCursor); + status_t error = HWInterface()->RefreshUpdateStream(stream); + + if (error != B_OK) { + replyLink.StartMessage(error); + replyLink.Flush(); + stream->Unlock(); + break; + } + replyLink.StartMessage(B_OK); replyLink.AttachRegion(stream->GetRegion()); replyLink.Flush(); stream->MakeEmpty(); + stream->Unlock(); break; } @@ -3298,6 +3337,7 @@ Desktop::_SetBackground(BRegion& background) GetDrawingEngine()->UnlockParallelAccess(); } } + } diff --git a/src/servers/app/IntRect.h b/src/servers/app/IntRect.h index f273bb8..5afbaf3 100644 --- a/src/servers/app/IntRect.h +++ b/src/servers/app/IntRect.h @@ -21,29 +21,30 @@ class IntRect { int32 top; int32 right; int32 bottom; - + IntRect(); + IntRect(clipping_rect r); IntRect(const IntRect& r); IntRect(const BRect& r); IntRect(int32 l, int32 t, int32 r, int32 b); IntRect(const IntPoint& lt, const IntPoint& rb); - + IntRect& operator=(const IntRect &r); void Set(int32 l, int32 t, int32 r, int32 b); - + void PrintToStream() const; - + IntPoint LeftTop() const; IntPoint RightBottom() const; IntPoint LeftBottom() const; IntPoint RightTop() const; - + void SetLeftTop(const IntPoint& p); void SetRightBottom(const IntPoint& p); void SetLeftBottom(const IntPoint& p); void SetRightTop(const IntPoint& p); - + // transformation void InsetBy(const IntPoint& p); void InsetBy(int32 dx, int32 dy); @@ -51,7 +52,7 @@ class IntRect { void OffsetBy(int32 dx, int32 dy); void OffsetTo(const IntPoint& p); void OffsetTo(int32 x, int32 y); - + // expression transformations IntRect& InsetBySelf(const IntPoint& p); IntRect& InsetBySelf(int32 dx, int32 dy); @@ -65,11 +66,11 @@ class IntRect { IntRect& OffsetToSelf(int32 dx, int32 dy); IntRect OffsetToCopy(const IntPoint& p); IntRect OffsetToCopy(int32 dx, int32 dy); - + // comparison bool operator==(const IntRect& r) const; bool operator!=(const IntRect& r) const; - + // intersection and union IntRect operator&(const IntRect& r) const; IntRect operator|(const IntRect& r) const; @@ -79,7 +80,7 @@ class IntRect { operator BRect() const { return BRect(left, top, right, bottom); } - + bool Intersects(const IntRect& r) const; bool IsValid() const; int32 Width() const; @@ -130,6 +131,16 @@ IntRect::IntRect() inline +IntRect::IntRect(clipping_rect r) +{ + left = r.left; + top = r.top; + right = r.right; + bottom = r.bottom; +} + + +inline IntRect::IntRect(int32 l, int32 t, int32 r, int32 b) { left = l; diff --git a/src/servers/app/ServerApp.cpp b/src/servers/app/ServerApp.cpp index 678ab3e..b9e89b8 100644 --- a/src/servers/app/ServerApp.cpp +++ b/src/servers/app/ServerApp.cpp @@ -94,6 +94,8 @@ ServerApp::ServerApp(Desktop* desktop, port_id clientReplyPort, fMessagePort(-1), fClientReplyPort(clientReplyPort), + fClientLooperPort(clientLooperPort), + fClientToken(clientToken), fDesktop(desktop), fSignature(signature), fClientTeam(clientTeam), @@ -472,6 +474,14 @@ ServerApp::RemovePicture(ServerPicture* picture) } +/*! BApplication's BHandler token */ +int32 +ServerApp::ClientToken() const +{ + return fClientToken; +} + + /*! Called from the ClientMemoryAllocator whenever a server area could be deleted. A message is then sent to the client telling it that it can delete its diff --git a/src/servers/app/ServerApp.h b/src/servers/app/ServerApp.h index 84c88af..dec56fa 100644 --- a/src/servers/app/ServerApp.h +++ b/src/servers/app/ServerApp.h @@ -96,6 +96,8 @@ public: void NotifyDeleteClientArea(area_id serverArea); + int32 ClientToken() const; + private: virtual void _GetLooperName(char* name, size_t size); virtual void _DispatchMessage(int32 code, diff --git a/src/servers/app/ServerBitmap.cpp b/src/servers/app/ServerBitmap.cpp index 0123bbf..c9e5334 100644 --- a/src/servers/app/ServerBitmap.cpp +++ b/src/servers/app/ServerBitmap.cpp @@ -21,6 +21,7 @@ #include "HWInterface.h" #include "InterfacePrivate.h" #include "Overlay.h" +#include "RenderingBuffer.h" #include "ServerApp.h" @@ -158,6 +159,80 @@ ServerBitmap::ImportBits(const void *bits, int32 bitsLength, int32 bytesPerRow, } +status_t +ServerBitmap::CopyBits(const RenderingBuffer* buffer, + const IntRect& copyFrame) +{ + if (buffer == NULL) + return B_BAD_VALUE; + + int8 bytesPerPixel = BytesPerPixel(buffer->ColorSpace()); + + if (BytesPerPixel(ColorSpace()) != bytesPerPixel) + return B_MISMATCHED_VALUES; + + if (bytesPerPixel != 4) + return B_NOT_ALLOWED; + + // inlined- copybits 32... + // the app_server only works in 32-bit anyway - as it is + + IntRect frame = copyFrame; + frame.InsetBy(-1, -1); // become "inclusive" + frame = frame & Bounds(); + if (buffer->Bounds() != Bounds() + || !frame.IsValid()) + return B_ERROR; + + uint32* destBits = (uint32*)Bits(); + uint32* srcBits = (uint32*)buffer->Bits(); + + uint32 pixPerRowDest = BytesPerRow()/bytesPerPixel; + uint32 pixPerRowSrc = buffer->BytesPerRow()/bytesPerPixel; + + for (int32 y = frame.top; y < frame.bottom; ++y) + for (int32 x = frame.left; x < frame.right; ++x) + destBits[(y * pixPerRowDest) + x] + = srcBits[(y * pixPerRowSrc)+x]; + + return B_OK; +} + + +int8 +ServerBitmap::BytesPerPixel(color_space space) +{ + /* + Only recognizes RGB format, for now... + and only those with whole-bytes + TODO: move this somewhere better + */ + switch (space) { + case B_RGB32: + case B_RGBA32: + case B_RGB32_BIG: + case B_RGBA32_BIG: + return 4; + case B_RGB24: + case B_RGB24_BIG: + return 3; + case B_RGB16: + case B_RGB16_BIG: + case B_RGB15: + case B_RGB15_BIG: + return 2; + case B_GRAY8: + case B_CMAP8: + return 1; + default: + break; + } + + return -1; +} + + + area_id ServerBitmap::Area() const { diff --git a/src/servers/app/ServerBitmap.h b/src/servers/app/ServerBitmap.h index e3b4b36..cd0212b 100644 --- a/src/servers/app/ServerBitmap.h +++ b/src/servers/app/ServerBitmap.h @@ -17,11 +17,12 @@ #include <Referenceable.h> #include "ClientMemoryAllocator.h" - +#include "IntPoint.h" class BitmapManager; class HWInterface; class Overlay; +class RenderingBuffer; class ServerApp; @@ -80,8 +81,17 @@ public: BPoint from, BPoint to, int32 width, int32 height); + // Copy bits raw from the rendering buffer into ourselves + // The colorspaces must be the same size per pixel, we do + // not worry about conversion. + // Further, the buffer must have our same Bounds() + status_t CopyBits(const RenderingBuffer* buffer, + const IntRect& copyFrame); + void PrintToStream(); + int8 BytesPerPixel(color_space space); + protected: friend class BitmapManager; diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index 88d1c02..61b52a8c 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -351,7 +351,7 @@ ServerWindow::_Show() fDesktop->ShowWindow(fWindow); if (fDirectWindowInfo && fDirectWindowInfo->IsFullScreen()) _ResizeToFullScreen(); - + fDesktop->LockSingleWindow(); } diff --git a/src/servers/app/drawing/DrawingEngine.cpp b/src/servers/app/drawing/DrawingEngine.cpp index b5c425a..4a6b47b 100644 --- a/src/servers/app/drawing/DrawingEngine.cpp +++ b/src/servers/app/drawing/DrawingEngine.cpp @@ -1070,7 +1070,7 @@ DrawingEngine::FillRegion(BRegion& r) doInSoftware = false; } } - + if (doInSoftware && (fAvailableHWAccleration & HW_ACC_INVERT_REGION) != 0 && fPainter->Pattern() == B_SOLID_HIGH @@ -1551,6 +1551,8 @@ DrawingEngine::CopyRect(BRect src, int32 xOffset, int32 yOffset) const dst.OffsetBy(xOffset, yOffset); } } + + return dst; } @@ -1619,7 +1621,7 @@ DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height, inline void -DrawingEngine::_CopyToFront(const BRect& frame) +DrawingEngine::_CopyToFront(const IntRect& frame) { if (fCopyToFront) fGraphicsCard->Invalidate(frame); diff --git a/src/servers/app/drawing/DrawingEngine.h b/src/servers/app/drawing/DrawingEngine.h index dd15db2..26dc55d 100644 --- a/src/servers/app/drawing/DrawingEngine.h +++ b/src/servers/app/drawing/DrawingEngine.h @@ -192,7 +192,7 @@ private: uint32 height, uint32 bytesPerRow, int32 xOffset, int32 yOffset) const; - inline void _CopyToFront(const BRect& frame); + inline void _CopyToFront(const IntRect& frame); Painter* fPainter; HWInterface* fGraphicsCard; diff --git a/src/servers/app/drawing/HWInterface.cpp b/src/servers/app/drawing/HWInterface.cpp index a0a86fb..8b975e5 100644 --- a/src/servers/app/drawing/HWInterface.cpp +++ b/src/servers/app/drawing/HWInterface.cpp @@ -346,20 +346,34 @@ HWInterface::IsDoubleBuffered() const } +//#pragma mark - + + UpdateStream* -HWInterface::CreateUpdateStream() +HWInterface::CreateUpdateStream(int32 clientToken, ServerBitmap* bitmap, + status_t* errorOut) { LockExclusiveAccess(); UpdateStream* stream = NULL; - if (DrawingBuffer() != NULL) + status_t error = B_OK; + + if (DrawingBuffer() != NULL) { stream = new(std::nothrow) UpdateStream(fNextUpdateStreamID++, - DrawingBuffer()->Bounds(), &fRegionPool); + &fRegionPool, clientToken, bitmap); - if (stream != NULL) - fUpdateStreams.AddItem(stream); + if (stream != NULL) + fUpdateStreams.AddItem(stream); + else + error = B_NO_MEMORY; + } else + error = B_NO_INIT; UnlockExclusiveAccess(); + + if (errorOut != NULL) + *errorOut = error; + return stream; } @@ -383,6 +397,116 @@ HWInterface::DeleteUpdateStream(UpdateStream* stream) } +/*! Delete any and all UpdateStreams belonging to an application */ +status_t +HWInterface::DeleteUpdateStreams(int32 clientToken) +{ + // Called when a client (BApplication) is destroyed + // either cleanly, or by crashing. + if (clientToken < 0) + return B_BAD_INDEX; + + LockExclusiveAccess(); + BList theDead; + status_t error = B_BAD_VALUE; + UpdateStream* stream = NULL; + for (int index = 0; index < fUpdateStreams.CountItems(); ++index) { + stream = (UpdateStream*)fUpdateStreams.ItemAt(index); + if (stream->ClientToken() == clientToken) { + theDead.AddItem(stream); + delete stream; + error = B_OK; + } + } + + for (int index = 0; index < theDead.CountItems(); ++index) + fUpdateStreams.RemoveItem(theDead.ItemAt(index)); + + UnlockExclusiveAccess(); + return error; +} + + +status_t +HWInterface::RefreshUpdateStream(UpdateStream* stream) +{ + if (stream == NULL) + return B_BAD_VALUE; + + if (!stream->Lock()) + return B_PERMISSION_DENIED; + + ServerBitmap* bitmap = stream->GetBitmap(); + + // If the stream does not have a bitmap, that is just fine! + if (bitmap == NULL) { + stream->Unlock(); + return B_OK; + } + + BRegion update = stream->GetRegion(); + bool hideCursor = stream->IsCursorHidden(); + RenderingBuffer* buffer = NULL; + + // The back buffer should not contain a copy of the cursor, + // it is just used to buffer the draws to the front buffer, + // so if it contains a drawing of the cursor, that is just + // wasteful as the buffers are never swapped. + if (hideCursor) { + if (BackBuffer() != NULL) { + buffer = BackBuffer(); + hideCursor = false; + } else { + // TODO: + // verify update region include cursor frame? + // (almost always does, though, due to batching) +// LockExclusiveAccess(); + hideCursor = IsCursorVisible(); + if (hideCursor) + HideFloatingOverlays(); + // SetCursorVisible(false); +// UnlockExclusiveAccess(); + } + } + + LockParallelAccess(); + + if (buffer == NULL) + buffer = FrontBuffer(); + + status_t error = B_NO_INIT; + + IntRect rect; + + if (buffer != NULL) { + error = B_OK; + BPoint bitmapOffset; + BPoint bufferOffset; + + for (int index = 0; index < update.CountRects(); ++index) { + rect = (IntRect)update.RectAtInt(index); + + error = bitmap->CopyBits(buffer, rect); + + if (error != B_OK) + break; + } + } + + UnlockParallelAccess(); + + if (hideCursor) { +// LockExclusiveAccess(); + ShowFloatingOverlays(); + // SetCursorVisible(true); +// UnlockExclusiveAccess(); + } + + stream->Unlock(); + return error; +} + + UpdateStream* HWInterface::FindUpdateStream(uint32 clientID) { @@ -400,6 +524,9 @@ HWInterface::FindUpdateStream(uint32 clientID) } +//#pragma mark - + + /*! The object needs to be already locked! */ status_t @@ -438,6 +565,9 @@ HWInterface::Invalidate(const BRect& frame) #endif return CopyBackToFront(frame); } + + _NotifyUpdateStreams(frame); + return B_OK; } @@ -452,12 +582,8 @@ HWInterface::CopyBackToFront(const BRect& frame) IntRect area(frame); - if (!backBuffer || !frontBuffer) { - if (area.IsValid()) - _NotifyUpdateStreams(area); - + if (!backBuffer || !frontBuffer) return B_NO_INIT; - } // we need to mess with the area, but it is const IntRect bufferClip(backBuffer->Bounds()); @@ -506,15 +632,28 @@ HWInterface::_CopyBackToFront(/*const*/ BRegion& region) void -HWInterface::_NotifyUpdateStreams(const IntRect& frame) -{ // only called while already locked, but we need to - // serialize accesses to the region. +HWInterface::_NotifyUpdateStreams(const IntRect& _frame) +{ // only called while already locked, but we need to serialize accesses to + // the update batch region. BAutolock _(fUpdateBatchRegionLock); - if (system_time() - fLastUpdateBatch < 8888) { + // Only update at clients at ~60hz + if (system_time() - fLastUpdateBatch < 16000) { + // including a larger area to catch a long-lived off-by-one error + // somewhere in the clipping code + IntRect frame(_frame); + frame.InsetBy(-2, -2); fUpdateBatchRegion.Include((clipping_rect)frame); return; } + RenderingBuffer* buffer = DrawingBuffer(); + // validity already checked + + // Guarantee the updated region fits without our buffer bounds. + BRegion bufferRegion; + bufferRegion.Set((clipping_rect)buffer->Bounds()); + fUpdateBatchRegion.IntersectWith(&bufferRegion); + fLastUpdateBatch = system_time(); UpdateStream* stream = NULL; for (int index = 0; index < fUpdateStreams.CountItems(); ++index) { @@ -1180,12 +1319,14 @@ HWInterface::_AdoptDragBitmap(const ServerBitmap* bitmap, const BPoint& offset) void HWInterface::_NotifyFrameBufferChanged() { - if (DrawingBuffer() != NULL) { + RenderingBuffer* buffer = DrawingBuffer(); + if (buffer != NULL) { + BRegion region; + region.Set((clipping_rect)buffer->Bounds()); UpdateStream* stream = NULL; - IntRect frame = DrawingBuffer()->Bounds(); - for (int index = 0; index < fUpdateStreams.CountItems(); ++index) { + for (int32 index = 0; index < fUpdateStreams.CountItems(); ++index) { stream = (UpdateStream*)fUpdateStreams.ItemAt(index); - stream->SetFrame(frame); + stream->IntersectWith(region); } } diff --git a/src/servers/app/drawing/HWInterface.h b/src/servers/app/drawing/HWInterface.h index 9144fba..638e6af 100644 --- a/src/servers/app/drawing/HWInterface.h +++ b/src/servers/app/drawing/HWInterface.h @@ -160,9 +160,16 @@ public: virtual bool IsDoubleBuffered() const; // Per-client updated regions - UpdateStream* CreateUpdateStream(); + // Do not call while holding a parallel lock! + virtual UpdateStream* CreateUpdateStream(int32 clientToken = -1, + ServerBitmap* bitmap = NULL, + status_t* error = NULL); virtual status_t DeleteUpdateStream(UpdateStream* stream); - UpdateStream* FindUpdateStream(uint32 clientID); + virtual status_t DeleteUpdateStreams(int32 clientToken); + virtual status_t RefreshUpdateStream(UpdateStream* stream); + virtual UpdateStream* FindUpdateStream(uint32 clientID); + // FindUpdateStream() can be called while holding a parallel lock + // or without a lock at all (self-locking) // Invalidate is used for scheduling an area for updating virtual status_t InvalidateRegion(BRegion& region); @@ -269,6 +276,8 @@ private: UpdateQueue* fUpdateExecutor; BList fListeners; + +protected: BList fUpdateStreams; BRegion fUpdateBatchRegion; BLocker fUpdateBatchRegionLock; diff --git a/src/servers/app/drawing/UpdateStream.cpp b/src/servers/app/drawing/UpdateStream.cpp index 08e8956..dca346e 100644 --- a/src/servers/app/drawing/UpdateStream.cpp +++ b/src/servers/app/drawing/UpdateStream.cpp @@ -2,47 +2,55 @@ #include <Region.h> #include <OS.h> - +#include "ServerBitmap.h" #include "UpdateStream.h" -/* - Represents either a server-side or client-side party which needs to be - aware of changes made to the screen since it was last updated. +/*! Represents either a server-side or client-side party which needs to be + aware of changes made to the HWInterface buffer since it was last updated. - We are owned by HWInterface and the fewest possible number of us - should be created since we are updated on each and every updated area - on the screen. + Owned by HWInterface and derived objects. Does not assume the HWInterface + buffer implies the screen's frame buffer, since this will not be the case + with, for example, compositing. */ -UpdateStream::UpdateStream(uint32 id, const IntRect& frame, - RegionPool* regionPool) +UpdateStream::UpdateStream(uint32 id, RegionPool* regionPool, + int32 clientToken, ServerBitmap* bitmap) : + fLock("UpdateStream"), fID(id), - fRegion(regionPool->GetRegion()), - fMaskRegion(NULL), - fFrame(regionPool->GetRegion()), + fRegion(NULL), + fLastRegion(NULL), + fBitmap(bitmap), + fClientToken(clientToken), + fHideCursor(false), fRegionPool(regionPool) { - fFrame->Set((clipping_rect&)frame); + if (fRegionPool != NULL) { + fRegion = fRegionPool->GetRegion(); + fLastRegion = fRegionPool->GetRegion(); + } else { + fRegion = new(std::nothrow) BRegion(); + fLastRegion = new(std::nothrow) BRegion(); + + } } UpdateStream::~UpdateStream() { - fRegionPool->Recycle(fRegion); - fRegionPool->Recycle(fFrame); - - if (fMaskRegion != NULL) - fRegionPool->Recycle(fMaskRegion); + if (fRegionPool != NULL) + fRegionPool->Recycle(fRegion); + else + delete fRegion; } void UpdateStream::Include(const clipping_rect& frame) { - BAutolock _(fLock); + AutoWriteLocker _(&fLock); fRegion->Include(frame); } @@ -50,15 +58,28 @@ UpdateStream::Include(const clipping_rect& frame) void UpdateStream::Include(const BRegion& region) { - BAutolock _(fLock); + AutoWriteLocker _(&fLock); fRegion->Include(®ion); } void +UpdateStream::IntersectWith(const BRegion& region) +{ + // ensure our region is not out of frame + AutoWriteLocker _(&fLock); + fRegion->IntersectWith(®ion); +} + + +void UpdateStream::MakeEmpty() { - BAutolock _(fLock); + AutoWriteLocker _(&fLock); + + BRegion* tempRegionPointer = fRegion; + fRegion = fLastRegion; + fLastRegion = tempRegionPointer; fRegion->MakeEmpty(); } @@ -66,68 +87,68 @@ UpdateStream::MakeEmpty() const BRegion& UpdateStream::GetRegion() const { - BAutolock _(fLock); - if (fMaskRegion != NULL) - fRegion->Exclude(fMaskRegion); - - fRegion->IntersectWith(fFrame); + AutoReadLocker _(&fLock); return *fRegion; } +const BRegion& +UpdateStream::GetLastRegion() const +{ + AutoReadLocker _(&fLock); + return *fLastRegion; +} + + uint32 UpdateStream::ID() const -{ +{ // doesn't change, no need to lock return fID; } -/*! Ignore updates in an area of the screen. -*/ - void -UpdateStream::SetMaskRegion(const BRegion& mask) +UpdateStream::SetCursorHidden(bool hidden) { - BAutolock _(fLock); - if (fMaskRegion == NULL) - fMaskRegion = fRegionPool->GetRegion(); - - *fMaskRegion = mask; + AutoWriteLocker _(&fLock); + fHideCursor = hidden; } -void -UpdateStream::UnsetMaskRegion() +bool +UpdateStream::IsCursorHidden() const { - BAutolock _(fLock); - if (fMaskRegion) { - fMaskRegion->MakeEmpty(); - fRegionPool->Recycle(fMaskRegion); - fMaskRegion = NULL; - } + AutoReadLocker _(&fLock); + return fHideCursor; +} + + +int32 +UpdateStream::ClientToken() const +{ // doesn't change, no need to lock + return fClientToken; } -BRegion* -UpdateStream::GetMaskRegion() +ServerBitmap* +UpdateStream::GetBitmap() { - BAutolock _(fLock); - return fMaskRegion; + return fBitmap; } -const BRegion* -UpdateStream::GetMaskRegion() const +bool +UpdateStream::Lock() { - BAutolock _(fLock); - return fMaskRegion; + // Writer threads can nest and also gain read locks + // others must wait. + return fLock.WriteLock(); } void -UpdateStream::SetFrame(const IntRect& frame) +UpdateStream::Unlock() { - BAutolock _(fLock); - fFrame->Set((clipping_rect&)frame); + fLock.WriteUnlock(); } diff --git a/src/servers/app/drawing/UpdateStream.h b/src/servers/app/drawing/UpdateStream.h index ae6122e..73f3ba8 100644 --- a/src/servers/app/drawing/UpdateStream.h +++ b/src/servers/app/drawing/UpdateStream.h @@ -8,38 +8,53 @@ #include "IntRect.h" +#include "MultiLocker.h" #include "RegionPool.h" +class ServerBitmap; + + class UpdateStream { public: - UpdateStream(uint32 id, const IntRect& frame, - RegionPool* regionPool); + UpdateStream(uint32 id, RegionPool* pool, + int32 clientToken = -1, + ServerBitmap* bitmap = NULL); ~UpdateStream(); void Include(const clipping_rect& frame); void Include(const BRegion& region); + void IntersectWith(const BRegion& region); + void MakeEmpty(); const BRegion& GetRegion() const; + const BRegion& GetLastRegion() const; uint32 ID() const; - void SetMaskRegion(const BRegion& mask); - void UnsetMaskRegion(); - BRegion* GetMaskRegion(); - const BRegion* GetMaskRegion() const; + void SetCursorHidden(bool hidden); + bool IsCursorHidden() const; - void SetFrame(const IntRect& frame); + int32 ClientToken() const; + ServerBitmap* GetBitmap(); + + bool Lock(); + void Unlock(); private: - mutable BLocker fLock; + mutable MultiLocker fLock; uint32 fID; - mutable BRegion* fRegion; - BRegion* fMaskRegion; - BRegion* fFrame; + + BRegion* fRegion; + BRegion* fLastRegion; + + ServerBitmap* fBitmap; + int32 fClientToken; + bool fHideCursor; RegionPool* fRegionPool; + // owned by HWInterface };