* implemented a BRegion pool per WindowLayer which is supposed

to cut down on BRegion related allocations, cannot really tell
  if it speeds things up
* used the new BRegion pool in WindowLayer and ViewLayer whereever
  a BRegion was used on the stack
* fixed the debugging stuff in MultiLocker - it will get you into
  the debugger if you
    - try to nest read locks
    - try to write lock when your are a reader already
    - don't match up nested locks when your a writer
    -> but only if you #define DEBUG 1 in the .cpp, is off by default now
* went over WindowLayer, ServerWindow, Desktop and a few other places
  and fixed the locking for use with the MultiLocker, the "a reader can
  not become a writer" is especially tricky, feel free to review the
  changes
* activated the MultiLocker, I tested this quite a bit, if there are
  problems simply turn on DEBUG and you should drop into the debugger
  right where the problem is... hope all is good


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17046 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2006-04-07 19:14:25 +00:00
parent 791b9c2141
commit 39c9925fcf
15 changed files with 471 additions and 259 deletions
-2
View File
@@ -199,8 +199,6 @@ enum {
AS_SCREENMODE_CHANGED,
// Graphics calls
AS_BEGIN_TRANSACTION,
AS_END_TRANSACTION,
AS_SET_HIGH_COLOR,
AS_SET_LOW_COLOR,
AS_SET_VIEW_COLOR,
+7 -8
View File
@@ -50,6 +50,9 @@
# define STRACE(a) ;
#endif
#if !USE_MULTI_LOCKER
# define AutoWriteLocker BAutolock
#endif
class KeyboardFilter : public EventFilter {
public:
@@ -1850,18 +1853,14 @@ Desktop::ViewUnderMouse(const WindowLayer* window)
WindowLayer *
Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID)
{
LockSingleWindow();
for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (window->ServerWindow()->ClientToken() == token
&& window->ServerWindow()->ClientTeam() == teamID) {
UnlockSingleWindow();
return window;
}
}
UnlockSingleWindow();
return NULL;
}
@@ -1869,7 +1868,7 @@ Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID)
void
Desktop::MinimizeApplication(team_id team)
{
BAutolock locker(fWindowLock);
AutoWriteLocker locker(fWindowLock);
// Just minimize all windows of that application
@@ -1886,7 +1885,7 @@ Desktop::MinimizeApplication(team_id team)
void
Desktop::BringApplicationToFront(team_id team)
{
BAutolock locker(fWindowLock);
AutoWriteLocker locker(fWindowLock);
// TODO: for now, just maximize all windows of that application
@@ -1929,7 +1928,7 @@ Desktop::WindowAction(int32 windowToken, int32 action)
void
Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
{
BAutolock locker(fWindowLock);
AutoWriteLocker locker(fWindowLock);
// compute the number of windows
@@ -1961,7 +1960,7 @@ Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
void
Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
{
BAutolock locker(fWindowLock);
AutoWriteLocker locker(fWindowLock);
BAutolock tokenLocker(BPrivate::gDefaultTokens);
::ServerWindow* window;
+1 -1
View File
@@ -31,7 +31,7 @@
#include <Region.h>
#include <Window.h>
#define USE_MULTI_LOCKER 0
#define USE_MULTI_LOCKER 1
#if USE_MULTI_LOCKER
# include "MultiLocker.h"
+1
View File
@@ -38,6 +38,7 @@ Server app_server :
PNGDump.cpp
RAMLinkMsgReader.cpp
RGBColor.cpp
RegionPool.cpp
ScreenManager.cpp
ServerApp.cpp
ServerBitmap.cpp
+36 -5
View File
@@ -11,7 +11,7 @@
#include <OS.h>
//#define TIMING 1
#define DEBUG 1
//#define DEBUG 1
MultiLocker::MultiLocker(const char* semaphoreBaseName)
@@ -132,6 +132,11 @@ MultiLocker::ReadLock()
if (fInit == B_OK) {
if (IsWriteLocked()) {
//the writer simply increments the nesting
#if DEBUG
if (fWriterNest < 0)
debugger("ReadLock() - negative writer nest count\n");
#endif
fWriterNest++;
locked = true;
} else {
@@ -174,10 +179,22 @@ MultiLocker::WriteLock()
if (IsWriteLocked(&stack_base, &thread)) {
//already the writer - increment the nesting count
#if DEBUG
if (fWriterNest < 0)
debugger("WriteLock() - negative nest count\n");
#endif
fWriterNest++;
locked = true;
} else {
//new writer acquiring the lock
#if DEBUG
// NOTE: IsReadLocked() tells you
// if this thread really holds the
// "read lock" only in DEBUG mode!
if (IsReadLocked())
debugger("Reader wants to become writer!");
#endif
if (atomic_add(&fLockCount, 1) >= 1) {
//another writer in the lock - acquire the semaphore
locked = (acquire_sem_etc(fWriterLock, 1, B_DO_NOT_RESCHEDULE,
@@ -227,6 +244,12 @@ MultiLocker::ReadUnlock()
if (IsWriteLocked()) {
//writers simply decrement the nesting count
fWriterNest--;
#if DEBUG
if (fWriterNest < 0)
debugger("ReadUnlock() - negative writer nest count\n");
#endif
unlocked = true;
} else {
//decrement and retrieve the read counter
@@ -237,7 +260,7 @@ MultiLocker::ReadUnlock()
B_DO_NOT_RESCHEDULE) == B_OK);
} else unlocked = true;
#ifdef DEBUG
#if DEBUG
//unregister if we released the lock
if (unlocked) unregister_thread();
#endif
@@ -265,6 +288,12 @@ MultiLocker::WriteUnlock()
//if this is a nested lock simply decrement the nest count
if (fWriterNest > 0) {
fWriterNest--;
#if DEBUG
if (fWriterNest < 0)
debugger("WriteUnlock(): nest count now negative\n");
#endif
unlocked = true;
} else {
//writer finally unlocking
@@ -416,14 +445,16 @@ MultiLocker::IsReadLocked()
void
MultiLocker::register_thread()
{
#ifdef DEBUG
#if DEBUG
#if TIMING
bigtime_t start = system_time();
#endif
thread_id thread = find_thread(NULL);
ASSERT_WITH_MESSAGE(fDebugArray[thread%fMaxThreads] == 0,"Nested ReadLock!\n");
if (fDebugArray[thread%fMaxThreads] != 0)
debugger("Nested ReadLock!\n");
fDebugArray[thread%fMaxThreads]++;
#if TIMING
@@ -439,7 +470,7 @@ MultiLocker::register_thread()
void
MultiLocker::unregister_thread()
{
#ifdef DEBUG
#if DEBUG
#if TIMING
bigtime_t start = system_time();
#endif
+29 -8
View File
@@ -1,10 +1,20 @@
/* MultiLocker.h */
/*
Copyright 2005-2006, Haiku.
Distributed under the terms of the MIT license.
Copyright 1999, Be Incorporated. All Rights Reserved.
This file may be used under the terms of the Be Sample Code License.
*/
/* multiple-reader single-writer locking class */
/** multiple-reader single-writer locking class */
// IMPORTANT:
// * nested read locks are not supported
// * a reader becomming the write is not supported
// * nested write locks are supported
// * a writer can do read locks, even nested ones
// * in case of problems, #define DEBUG 1 in the .cpp
#ifndef MULTI_LOCKER_H
#define MULTI_LOCKER_H
@@ -31,7 +41,8 @@ class MultiLocker {
bool WriteUnlock();
//does the current thread hold a write lock ?
bool IsWriteLocked(uint32 *stack_base = NULL, thread_id *thread = NULL);
bool IsWriteLocked(uint32 *stack_base = NULL,
thread_id *thread = NULL);
//in DEBUG mode returns whether the lock is held
//in non-debug mode returns true
bool IsReadLocked();
@@ -82,31 +93,41 @@ class MultiLocker {
class AutoWriteLocker {
public:
AutoWriteLocker(MultiLocker* lock)
: fLock(*lock)
{
fLock.WriteLock();
}
AutoWriteLocker(MultiLocker& lock)
: fLock(lock)
{
fLock->WriteLock();
fLock.WriteLock();
}
~AutoWriteLocker()
{
fLock->WriteUnlock();
fLock.WriteUnlock();
}
private:
MultiLocker* fLock;
MultiLocker& fLock;
};
class AutoReadLocker {
public:
AutoReadLocker(MultiLocker* lock)
: fLock(*lock)
{
fLock.ReadLock();
}
AutoReadLocker(MultiLocker& lock)
: fLock(lock)
{
fLock->ReadLock();
fLock.ReadLock();
}
~AutoReadLocker()
{
fLock->ReadUnlock();
fLock.ReadUnlock();
}
private:
MultiLocker* fLock;
MultiLocker& fLock;
};
+103
View File
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2006, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "RegionPool.h"
#include <new>
#include <stdio.h>
#if DEBUG_LEAK
#include <debugger.h>
#endif
#include <Region.h>
using std::nothrow;
RegionPool::RegionPool()
: fAvailable(4)
#if DEBUG_LEAK
,fUsed(4)
#endif
{
}
RegionPool::~RegionPool()
{
#if DEBUG_LEAK
if (fUsed.CountItems() > 0)
debugger("RegionPool::~RegionPool() - some regions still in use!");
#endif
int32 count = fAvailable.CountItems();
for (int32 i = 0; i < count; i++)
delete (BRegion*)fAvailable.ItemAtFast(i);
}
BRegion*
RegionPool::GetRegion()
{
BRegion* region = (BRegion*)fAvailable.RemoveItem(
fAvailable.CountItems() - 1);
if (!region) {
region = new (nothrow) BRegion();
if (!region) {
// whoa
fprintf(stderr, "RegionPool::GetRegion() - "
"no memory!\n");
}
}
#if DEBUG_LEAK
fUsed.AddItem(region);
#endif
return region;
}
BRegion*
RegionPool::GetRegion(const BRegion& other)
{
BRegion* region;
int32 count = fAvailable.CountItems();
if (count > 0) {
region = (BRegion*)fAvailable.RemoveItem(count - 1);
*region = other;
} else {
region = new (nothrow) BRegion(other);
if (!region) {
// whoa
fprintf(stderr, "RegionPool::GetRegion() - "
"no memory!\n");
}
}
#if DEBUG_LEAK
fUsed.AddItem(region);
#endif
return region;
}
void
RegionPool::Recycle(BRegion* region)
{
if (!fAvailable.AddItem(region)) {
// at least don't leak the region...
fprintf(stderr, "RegionPool::Recycle() - "
"no memory!\n");
delete region;
} else {
// prepare for next usage
region->MakeEmpty();
}
#if DEBUG_LEAK
fUsed.RemoveItem(region);
#endif
}
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2006, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef REGION_POOL_H
#define REGION_POOL_H
#include <List.h>
class BRegion;
#define DEBUG_LEAK 0
class RegionPool {
public:
RegionPool();
virtual ~RegionPool();
BRegion* GetRegion();
BRegion* GetRegion(const BRegion& other);
void Recycle(BRegion* region);
private:
BList fAvailable;
#if DEBUG_LEAK
BList fUsed;
#endif
};
#endif // REGION_POOL_H
+1
View File
@@ -194,6 +194,7 @@ ServerBitmap::_HandleSpace(color_space space, int32 bytesPerRow)
case B_YCbCr422:
case B_YUV422:
minBPR = (fWidth + 3) / 4 * 8;
// TODO: huh? why not simply fWidth * 2 ?!?
fBitsPerPixel = 16;
break;
+30 -34
View File
@@ -51,7 +51,6 @@
#include "ServerPicture.h"
#include "ServerProtocol.h"
#include "WindowLayer.h"
#include "Workspace.h"
#include "WorkspacesLayer.h"
#include "ServerWindow.h"
@@ -386,8 +385,11 @@ ServerWindow::SetTitle(const char* newTitle)
rename_thread(Thread(), name);
}
if (fWindowLayer != NULL)
if (fWindowLayer != NULL) {
fDesktop->UnlockSingleWindow();
fDesktop->SetWindowTitle(fWindowLayer, newTitle);
fDesktop->LockSingleWindow();
}
}
@@ -584,10 +586,12 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
link.Read<bool>(&activate);
fDesktop->UnlockSingleWindow();
if (activate)
fDesktop->ActivateWindow(fWindowLayer);
else
fDesktop->SendWindowBehind(fWindowLayer, NULL);
fDesktop->LockSingleWindow();
break;
}
case AS_SEND_BEHIND:
@@ -602,7 +606,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
WindowLayer *behindOf;
if ((behindOf = fDesktop->FindWindowLayerByClientToken(token, teamID)) != NULL) {
fDesktop->UnlockSingleWindow();
fDesktop->SendWindowBehind(fWindowLayer, behindOf);
fDesktop->LockSingleWindow();
status = B_OK;
} else
status = B_NAME_NOT_FOUND;
@@ -617,23 +623,6 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
NotifyQuitRequested();
break;
case AS_BEGIN_TRANSACTION:
{
STRACE(("ServerWindow %s: Message AS_BEGIN_TRANSACTION unimplemented\n",
Title()));
// TODO: we could probably do a bit more here...
// TODO: AS_BEGIN_TRANSACTION
//fWindowLayer->DisableUpdateRequests();
break;
}
case AS_END_TRANSACTION:
{
STRACE(("ServerWindow %s: Message AS_END_TRANSACTION unimplemented\n",
Title()));
// TODO: AS_END_TRANSACTION
//fWindowLayer->EnableUpdateRequests();
break;
}
case AS_ENABLE_UPDATES:
{
STRACE(("ServerWindow %s: Message AS_ENABLE_UPDATES unimplemented\n",
@@ -683,8 +672,10 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
|| windowLayer->Feel() != B_NORMAL_WINDOW_FEEL) {
status = B_BAD_VALUE;
} else {
fDesktop->UnlockSingleWindow();
status = fDesktop->AddWindowToSubset(fWindowLayer, windowLayer)
? B_OK : B_NO_MEMORY;
fDesktop->LockSingleWindow();
}
}
@@ -702,7 +693,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
WindowLayer* windowLayer = fDesktop->FindWindowLayerByClientToken(
token, App()->ClientTeam());
if (windowLayer != NULL) {
fDesktop->UnlockSingleWindow();
fDesktop->RemoveWindowFromSubset(fWindowLayer, windowLayer);
fDesktop->LockSingleWindow();
status = B_OK;
} else
status = B_BAD_VALUE;
@@ -725,8 +718,11 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
? B_OK : B_BAD_VALUE;
}
if (status == B_OK && !fWindowLayer->IsOffscreenWindow())
if (status == B_OK && !fWindowLayer->IsOffscreenWindow()) {
fDesktop->UnlockSingleWindow();
fDesktop->SetWindowLook(fWindowLayer, (window_look)look);
fDesktop->LockSingleWindow();
}
fLink.StartMessage(status);
fLink.Flush();
@@ -744,8 +740,11 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
? B_OK : B_BAD_VALUE;
}
if (status == B_OK && !fWindowLayer->IsOffscreenWindow())
if (status == B_OK && !fWindowLayer->IsOffscreenWindow()) {
fDesktop->UnlockSingleWindow();
fDesktop->SetWindowFeel(fWindowLayer, (window_feel)feel);
fDesktop->LockSingleWindow();
}
fLink.StartMessage(status);
fLink.Flush();
@@ -763,8 +762,11 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
? B_OK : B_BAD_VALUE;
}
if (status == B_OK && !fWindowLayer->IsOffscreenWindow())
if (status == B_OK && !fWindowLayer->IsOffscreenWindow()) {
fDesktop->UnlockSingleWindow();
fDesktop->SetWindowFlags(fWindowLayer, flags);
fDesktop->LockSingleWindow();
}
fLink.StartMessage(status);
fLink.Flush();
@@ -801,7 +803,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
STRACE(("ServerWindow %s: Message AS_SET_WORKSPACES %lx\n",
Title(), newWorkspaces));
fDesktop->UnlockSingleWindow();
fDesktop->SetWindowWorkspaces(fWindowLayer, newWorkspaces);
fDesktop->LockSingleWindow();
break;
}
case AS_WINDOW_RESIZE:
@@ -820,7 +824,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
// pragmatically set window bounds
fLink.StartMessage(B_BUSY);
} else {
fDesktop->UnlockSingleWindow();
fDesktop->ResizeWindowBy(fWindowLayer, xResizeBy, yResizeBy);
fDesktop->LockSingleWindow();
fLink.StartMessage(B_OK);
}
fLink.Flush();
@@ -842,7 +848,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
// pragmatically set window positions
fLink.StartMessage(B_BUSY);
} else {
fDesktop->UnlockSingleWindow();
fDesktop->MoveWindowBy(fWindowLayer, xMoveBy, yMoveBy);
fDesktop->LockSingleWindow();
fLink.StartMessage(B_OK);
}
fLink.Flush();
@@ -901,22 +909,12 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link)
case AS_BEGIN_UPDATE:
DTRACE(("ServerWindowo %s: AS_BEGIN_UPDATE\n", Title()));
// NOTE: when line below is turned on, the behaviour starts to
// be very much like on R5, but if the client crashes in
// one of it's BView's Draw() functions, AS_END_UPDATE is
// never received and the app_server is toast! Maybe R5
// does something like this, but if the write lock cannot
// be optained within a certain amount of time, it crashes
// whoever holds the read lock on purpose.
//fDesktop->LockSingleWindow();
fWindowLayer->BeginUpdate(fLink);
break;
case AS_END_UPDATE:
DTRACE(("ServerWindowo %s: AS_END_UPDATE\n", Title()));
fWindowLayer->EndUpdate();
//fDesktop->UnlockSingleWindow();
break;
case AS_GET_MOUSE:
@@ -2697,7 +2695,6 @@ ServerWindow::_SetCurrentLayer(ViewLayer* layer)
fCurrentDrawingRegionValid = false;
#if 0
#if DELAYED_BACKGROUND_CLEARING
fWindowLayer->ReadLockWindows();
if (fCurrentLayer && fCurrentLayer->IsBackgroundDirty() && fWindowLayer->InUpdate()) {
DrawingEngine* drawingEngine = fWindowLayer->GetDrawingEngine();
if (drawingEngine->Lock()) {
@@ -2714,7 +2711,6 @@ fWindowLayer->ReadLockWindows();
drawingEngine->Unlock();
}
}
fWindowLayer->ReadUnlockWindows();
#endif
#endif // 0
}
+62 -27
View File
@@ -198,8 +198,12 @@ ViewLayer::AddChild(ViewLayer* layer)
// trigger redraw
BRect clippedFrame = layer->Frame();
ConvertToVisibleInTopView(&clippedFrame);
BRegion dirty(clippedFrame);
fWindow->MarkContentDirty(dirty);
BRegion* dirty = fWindow->GetRegion();
if (dirty) {
dirty->Set(clippedFrame);
fWindow->MarkContentDirty(*dirty);
fWindow->RecycleRegion(dirty);
}
}
}
}
@@ -244,8 +248,12 @@ ViewLayer::RemoveChild(ViewLayer* layer)
// trigger redraw
BRect clippedFrame = layer->Frame();
ConvertToVisibleInTopView(&clippedFrame);
BRegion dirty(clippedFrame);
fWindow->MarkContentDirty(dirty);
BRegion* dirty = fWindow->GetRegion();
if (dirty) {
dirty->Set(clippedFrame);
fWindow->MarkContentDirty(*dirty);
fWindow->RecycleRegion(dirty);
}
}
}
@@ -642,13 +650,19 @@ ViewLayer::MoveBy(int32 x, int32 y, BRegion* dirtyRegion)
// clipping oldVisibleBounds to newVisibleBounds
// makes sure we don't copy parts hidden under
// parent views
BRegion copyRegion(oldVisibleBounds & newVisibleBounds);
fWindow->CopyContents(&copyRegion, x, y);
BRegion* region = fWindow->GetRegion();
if (region) {
region->Set(oldVisibleBounds & newVisibleBounds);
fWindow->CopyContents(region, x, y);
region->Set(oldVisibleBounds);
newVisibleBounds.OffsetBy(x, y);
region->Exclude(newVisibleBounds);
dirtyRegion->Include(dirty);
fWindow->RecycleRegion(region);
}
BRegion dirty(oldVisibleBounds);
newVisibleBounds.OffsetBy(x, y);
dirty.Exclude(newVisibleBounds);
dirtyRegion->Include(&dirty);
#endif
}
@@ -681,32 +695,37 @@ ViewLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
oldBounds.right -= x;
oldBounds.bottom -= y;
BRegion dirty(Bounds());
dirty.Include(oldBounds);
BRegion* dirty = fWindow->GetRegion();
if (!dirty)
return;
dirty->Set(Bounds());
dirty->Include(oldBounds);
if (!(fFlags & B_FULL_UPDATE_ON_RESIZE)) {
// the dirty region is just the difference of
// old and new bounds
dirty.Exclude(oldBounds & Bounds());
dirty->Exclude(oldBounds & Bounds());
}
InvalidateScreenClipping(true);
if (dirty.CountRects() > 0) {
if (dirty->CountRects() > 0) {
// exclude children, they are expected to
// include their own dirty regions in ParentResized()
for (ViewLayer* child = FirstChild(); child; child = child->NextSibling()) {
if (child->IsVisible()) {
BRect previousChildVisible(child->Frame() & oldBounds & Bounds());
if (dirty.Frame().Intersects(previousChildVisible)) {
dirty.Exclude(previousChildVisible);
if (dirty->Frame().Intersects(previousChildVisible)) {
dirty->Exclude(previousChildVisible);
}
}
}
ConvertToScreen(&dirty);
dirtyRegion->Include(&dirty);
ConvertToScreen(dirty);
dirtyRegion->Include(dirty);
}
fWindow->RecycleRegion(dirty);
}
// layout the children
@@ -795,10 +814,16 @@ ViewLayer::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion)
// find the dirty region as far as we are
// concerned
BRegion dirty(oldBounds);
BRegion* dirty = fWindow->GetRegion();
if (!dirty)
return;
dirty->Set(oldBounds);
stillVisibleBounds.OffsetBy(-x, -y);
dirty.Exclude(stillVisibleBounds);
dirtyRegion->Include(&dirty);
dirty->Exclude(stillVisibleBounds);
dirtyRegion->Include(dirty);
fWindow->RecycleRegion(dirty);
// the screen clipping of this view and it's
// childs is no longer valid
@@ -843,12 +868,18 @@ ViewLayer::CopyBits(BRect src, BRect dst, BRegion& windowContentClipping)
BRect dirtyDst(dst);
ConvertToVisibleInTopView(&dirtyDst);
BRegion dirty(dirtyDst);
BRegion* dirty = fWindow->GetRegion();
if (!dirty)
return;
dirty->Set(dirtyDst);
// exclude the part that we could copy
visibleSrc.OffsetBy(xOffset, yOffset);
dirty.Exclude(visibleSrc);
dirty.IntersectWith(&ScreenClipping(&windowContentClipping));
fWindow->MarkContentDirty(dirty);
dirty->Exclude(visibleSrc);
dirty->IntersectWith(&ScreenClipping(&windowContentClipping));
fWindow->MarkContentDirty(*dirty);
fWindow->RecycleRegion(dirty);
}
@@ -1024,8 +1055,12 @@ ViewLayer::SetHidden(bool hidden)
// trigger a redraw
BRect clippedBounds = Bounds();
ConvertToVisibleInTopView(&clippedBounds);
BRegion dirty(clippedBounds);
fWindow->MarkContentDirty(dirty);
BRegion* dirty = fWindow->GetRegion();
if (!dirty)
return;
dirty->Set(clippedBounds);
fWindow->MarkContentDirty(*dirty);
fWindow->RecycleRegion(dirty);
}
}
} else {
+145 -164
View File
@@ -88,6 +88,8 @@ WindowLayer::WindowLayer(const BRect& frame, const char *name,
fEffectiveDrawingRegion(),
fEffectiveDrawingRegionValid(false),
fRegionPool(),
fIsClosing(false),
fIsMinimizing(false),
fIsZooming(false),
@@ -122,9 +124,7 @@ WindowLayer::WindowLayer(const BRect& frame, const char *name,
fMinWidth(1),
fMaxWidth(32768),
fMinHeight(1),
fMaxHeight(32768),
fReadLocked(false)
fMaxHeight(32768)
{
// make sure our arguments are valid
if (!IsValidLook(fLook))
@@ -175,29 +175,6 @@ WindowLayer::~WindowLayer()
}
bool
WindowLayer::ReadLockWindows()
{
if (fDesktop)
fReadLocked = fDesktop->LockSingleWindow();
else
fReadLocked = true;
return fReadLocked;
}
void
WindowLayer::ReadUnlockWindows()
{
if (fReadLocked) {
if (fDesktop)
fDesktop->UnlockSingleWindow();
fReadLocked = false;
}
}
void
WindowLayer::SetClipping(BRegion* stillAvailableOnScreen)
{
@@ -413,29 +390,27 @@ WindowLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
void
WindowLayer::ScrollViewBy(ViewLayer* view, int32 dx, int32 dy)
{
// this can be executed from any thread, but if the
// desktop thread is executing this, it should have
// the write lock, otherwise it is not prevented
// from executing this at the same time as the window
// is doing something else here!
// this is executed in ServerWindow with the Readlock
// held
if (!view || view == fTopLayer || (dx == 0 && dy == 0))
return;
if (fDesktop && fDesktop->LockSingleWindow()) {
BRegion dirty;
view->ScrollBy(dx, dy, &dirty);
BRegion* dirty = fRegionPool.GetRegion();
if (!dirty)
return;
view->ScrollBy(dx, dy, dirty);
//fDrawingEngine->FillRegion(dirty, RGBColor(255, 0, 255, 255));
//snooze(2000);
if (IsVisible() && view->IsVisible()) {
dirty.IntersectWith(&VisibleContentRegion());
_TriggerContentRedraw(dirty);
}
fDesktop->UnlockSingleWindow();
if (IsVisible() && view->IsVisible()) {
dirty->IntersectWith(&VisibleContentRegion());
_TriggerContentRedraw(*dirty);
}
fRegionPool.Recycle(dirty);
}
@@ -443,64 +418,67 @@ WindowLayer::ScrollViewBy(ViewLayer* view, int32 dx, int32 dy)
void
WindowLayer::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
{
if (IsVisible() && fDesktop && fDesktop->LockSingleWindow()) {
BRegion newDirty(*region);
// executed in ServerWindow thread with the read lock held
if (!IsVisible())
return;
// clip the region to the visible contents at the
// source and destination location (note that VisibleContentRegion()
// is used once to make sure it is valid, then fVisibleContentRegion
// is used directly)
region->IntersectWith(&VisibleContentRegion());
BRegion* newDirty = fRegionPool.GetRegion(*region);
// clip the region to the visible contents at the
// source and destination location (note that VisibleContentRegion()
// is used once to make sure it is valid, then fVisibleContentRegion
// is used directly)
region->IntersectWith(&VisibleContentRegion());
if (region->CountRects() > 0) {
region->OffsetBy(xOffset, yOffset);
region->IntersectWith(&fVisibleContentRegion);
if (region->CountRects() > 0) {
region->OffsetBy(xOffset, yOffset);
region->IntersectWith(&fVisibleContentRegion);
if (region->CountRects() > 0) {
// if the region still contains any rects
// offset to source location again
region->OffsetBy(-xOffset, -yOffset);
// the part which we can copy is not dirty
newDirty.Exclude(region);
fDrawingEngine->CopyRegion(region, xOffset, yOffset);
// if the region still contains any rects
// offset to source location again
region->OffsetBy(-xOffset, -yOffset);
// the part which we can copy is not dirty
newDirty->Exclude(region);
fDrawingEngine->CopyRegion(region, xOffset, yOffset);
// move along the already dirty regions that are common
// with the region that we could copy
_ShiftPartOfRegion(&fDirtyRegion, region, xOffset, yOffset);
if (fPendingUpdateSession.IsUsed())
_ShiftPartOfRegion(&fPendingUpdateSession.DirtyRegion(), region, xOffset, yOffset);
// move along the already dirty regions that are common
// with the region that we could copy
_ShiftPartOfRegion(&fDirtyRegion, region, xOffset, yOffset);
if (fPendingUpdateSession.IsUsed())
_ShiftPartOfRegion(&fPendingUpdateSession.DirtyRegion(), region, xOffset, yOffset);
if (fCurrentUpdateSession.IsUsed()) {
// if there are parts in the current update session
// that intersect with the copied region, we cannot
// simply shift them as with the other dirty regions
// - we cannot change the update rect already told to the
// client, that's why we transfer those parts to the
// new dirty region instead
BRegion common(*region);
// see if there is a common part at all
common.IntersectWith(&fCurrentUpdateSession.DirtyRegion());
if (common.CountRects() > 0) {
// cut the common part from the region
fCurrentUpdateSession.DirtyRegion().Exclude(&common);
newDirty.Include(&common);
}
if (fCurrentUpdateSession.IsUsed()) {
// if there are parts in the current update session
// that intersect with the copied region, we cannot
// simply shift them as with the other dirty regions
// - we cannot change the update rect already told to the
// client, that's why we transfer those parts to the
// new dirty region instead
BRegion* common = fRegionPool.GetRegion(*region);
// see if there is a common part at all
common->IntersectWith(&fCurrentUpdateSession.DirtyRegion());
if (common->CountRects() > 0) {
// cut the common part from the region
fCurrentUpdateSession.DirtyRegion().Exclude(common);
newDirty->Include(common);
}
fRegionPool.Recycle(common);
}
}
// what is left visible from the original region
// at the destination after the region which could be
// copied has been excluded, is considered dirty
// NOTE: it may look like dirty regions are not moved
// if no region could be copied, but that's alright,
// since these parts will now be in newDirty anyways
// (with the right offset)
newDirty.OffsetBy(xOffset, yOffset);
newDirty.IntersectWith(&fVisibleContentRegion);
if (newDirty.CountRects() > 0)
ProcessDirtyRegion(newDirty);
fDesktop->UnlockSingleWindow();
}
// what is left visible from the original region
// at the destination after the region which could be
// copied has been excluded, is considered dirty
// NOTE: it may look like dirty regions are not moved
// if no region could be copied, but that's alright,
// since these parts will now be in newDirty anyways
// (with the right offset)
newDirty->OffsetBy(xOffset, yOffset);
newDirty->IntersectWith(&fVisibleContentRegion);
if (newDirty->CountRects() > 0)
ProcessDirtyRegion(*newDirty);
fRegionPool.Recycle(newDirty);
}
@@ -537,14 +515,11 @@ WindowLayer::ViewAt(const BPoint& where)
{
ViewLayer* view = NULL;
if (ReadLockWindows()) {
if (!fContentRegionValid)
_UpdateContentRegion();
if (!fContentRegionValid)
_UpdateContentRegion();
view = fTopLayer->ViewAt(where, &fContentRegion);
view = fTopLayer->ViewAt(where, &fContentRegion);
ReadUnlockWindows();
}
return view;
}
@@ -641,16 +616,18 @@ WindowLayer::ProcessDirtyRegion(BRegion& region)
void
WindowLayer::RedrawDirtyRegion()
{
if (!fDesktop || !fDesktop->LockSingleWindow())
return;
// executed from ServerWindow with the read lock held
if (IsVisible()) {
_DrawBorder();
BRegion dirtyContentRegion(VisibleContentRegion());
dirtyContentRegion.IntersectWith(&fDirtyRegion);
BRegion* dirtyContentRegion =
fRegionPool.GetRegion(VisibleContentRegion());
dirtyContentRegion->IntersectWith(&fDirtyRegion);
_TriggerContentRedraw(dirtyContentRegion);
_TriggerContentRedraw(*dirtyContentRegion);
fRegionPool.Recycle(dirtyContentRegion);
}
// reset the dirty region, since
@@ -658,11 +635,9 @@ WindowLayer::RedrawDirtyRegion()
// thread wanted to mark something
// dirty in the mean time, it was
// blocking on the global region lock to
// get write access, since we held the
// read lock for the whole time.
// get write access, since we're holding
// the read lock for the whole time.
fDirtyRegion.MakeEmpty();
fDesktop->UnlockSingleWindow();
}
@@ -685,23 +660,19 @@ WindowLayer::MarkContentDirty(BRegion& regionOnScreen)
// since this won't affect other windows, read locking
// is sufficient. If there was no dirty region before,
// an update message is triggered
if (!fHidden && fDesktop && fDesktop->LockSingleWindow()) {
regionOnScreen.IntersectWith(&VisibleContentRegion());
_TriggerContentRedraw(regionOnScreen);
if (fHidden)
return;
fDesktop->UnlockSingleWindow();
}
regionOnScreen.IntersectWith(&VisibleContentRegion());
_TriggerContentRedraw(regionOnScreen);
}
void
WindowLayer::InvalidateView(ViewLayer* layer, BRegion& layerRegion)
{
if (layer && IsVisible() && fDesktop && fDesktop->LockSingleWindow()) {
if (!layer->IsVisible()) {
fDesktop->UnlockSingleWindow();
return;
}
if (layer && IsVisible() && layer->IsVisible()) {
if (!fContentRegionValid)
_UpdateContentRegion();
@@ -715,8 +686,6 @@ WindowLayer::InvalidateView(ViewLayer* layer, BRegion& layerRegion)
_TriggerContentRedraw(layerRegion);
}
fDesktop->UnlockSingleWindow();
}
}
@@ -796,12 +765,12 @@ WindowLayer::MouseDown(BMessage* message, BPoint where, int32* _viewToken)
}
// redraw decorator
BRegion visibleBorder;
GetBorderRegion(&visibleBorder);
visibleBorder.IntersectWith(&VisibleRegion());
BRegion* visibleBorder = fRegionPool.GetRegion();
GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&VisibleRegion());
fDrawingEngine->Lock();
fDrawingEngine->ConstrainClippingRegion(&visibleBorder);
fDrawingEngine->ConstrainClippingRegion(visibleBorder);
if (fIsZooming) {
fDecorator->SetZoom(true);
@@ -813,6 +782,8 @@ WindowLayer::MouseDown(BMessage* message, BPoint where, int32* _viewToken)
fDrawingEngine->Unlock();
fRegionPool.Recycle(visibleBorder);
// based on what the Decorator returned, properly place this window.
if (action == DEC_MOVETOBACK) {
fDesktop->SendWindowBehind(this);
@@ -853,12 +824,12 @@ WindowLayer::MouseUp(BMessage* message, BPoint where, int32* _viewToken)
click_type action = _ActionFor(message);
// redraw decorator
BRegion visibleBorder;
GetBorderRegion(&visibleBorder);
visibleBorder.IntersectWith(&VisibleRegion());
BRegion* visibleBorder = fRegionPool.GetRegion();
GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&VisibleRegion());
fDrawingEngine->Lock();
fDrawingEngine->ConstrainClippingRegion(&visibleBorder);
fDrawingEngine->ConstrainClippingRegion(visibleBorder);
if (fIsZooming) {
fIsZooming = false;
@@ -886,6 +857,8 @@ WindowLayer::MouseUp(BMessage* message, BPoint where, int32* _viewToken)
}
fDrawingEngine->Unlock();
fRegionPool.Recycle(visibleBorder);
}
fIsDragging = false;
fIsResizing = false;
@@ -923,12 +896,12 @@ WindowLayer::MouseMoved(BMessage *message, BPoint where, int32* _viewToken,
}
if (fDecorator) {
BRegion visibleBorder;
GetBorderRegion(&visibleBorder);
visibleBorder.IntersectWith(&VisibleRegion());
BRegion* visibleBorder = fRegionPool.GetRegion();
GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&VisibleRegion());
fDrawingEngine->Lock();
fDrawingEngine->ConstrainClippingRegion(&visibleBorder);
fDrawingEngine->ConstrainClippingRegion(visibleBorder);
if (fIsZooming) {
fDecorator->SetZoom(_ActionFor(message) == DEC_ZOOM);
@@ -939,6 +912,7 @@ WindowLayer::MouseMoved(BMessage *message, BPoint where, int32* _viewToken,
}
fDrawingEngine->Unlock();
fRegionPool.Recycle(visibleBorder);
}
BPoint delta = where - fLastMousePosition;
@@ -1071,9 +1045,12 @@ WindowLayer::SetFocus(bool focus)
// so the window thread cannot be
// accessing fIsFocus
BRegion dirty(fBorderRegion);
dirty.IntersectWith(&fVisibleRegion);
fDesktop->MarkDirty(dirty);
BRegion* dirty = fRegionPool.GetRegion(fBorderRegion);
if (dirty) {
dirty->IntersectWith(&fVisibleRegion);
fDesktop->MarkDirty(*dirty);
fRegionPool.Recycle(dirty);
}
fIsFocus = focus;
if (fDecorator)
@@ -1602,16 +1579,19 @@ void
WindowLayer::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
int32 xOffset, int32 yOffset)
{
BRegion common(*regionToShift);
BRegion* common = fRegionPool.GetRegion(*regionToShift);
if (!common)
return;
// see if there is a common part at all
common.IntersectWith(region);
if (common.CountRects() > 0) {
common->IntersectWith(region);
if (common->CountRects() > 0) {
// cut the common part from the region,
// offset that to destination and include again
region->Exclude(&common);
common.OffsetBy(xOffset, yOffset);
region->Include(&common);
region->Exclude(common);
common->OffsetBy(xOffset, yOffset);
region->Include(common);
}
fRegionPool.Recycle(common);
}
@@ -1655,19 +1635,22 @@ WindowLayer::_DrawBorder()
return;
// construct the region of the border that needs redrawing
BRegion dirtyBorderRegion;
GetBorderRegion(&dirtyBorderRegion);
BRegion* dirtyBorderRegion = fRegionPool.GetRegion();
if (!dirtyBorderRegion)
return;
GetBorderRegion(dirtyBorderRegion);
// intersect with our visible region
dirtyBorderRegion.IntersectWith(&fVisibleRegion);
dirtyBorderRegion->IntersectWith(&fVisibleRegion);
// intersect with the dirty region
dirtyBorderRegion.IntersectWith(&fDirtyRegion);
dirtyBorderRegion->IntersectWith(&fDirtyRegion);
if (dirtyBorderRegion.CountRects() > 0 && fDrawingEngine->Lock()) {
fDrawingEngine->ConstrainClippingRegion(&dirtyBorderRegion);
fDecorator->Draw(dirtyBorderRegion.Frame());
if (dirtyBorderRegion->CountRects() > 0 && fDrawingEngine->Lock()) {
fDrawingEngine->ConstrainClippingRegion(dirtyBorderRegion);
fDecorator->Draw(dirtyBorderRegion->Frame());
fDrawingEngine->Unlock();
}
fRegionPool.Recycle(dirtyBorderRegion);
}
@@ -1730,15 +1713,10 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link)
// NOTE: since we might "shift" parts of the
// internal dirty regions from the desktop thread
// in response to WindowLayer::ResizeBy(), which
// might move arround views, this function needs to block
// on the global clipping lock so that the internal
// might move arround views, the user of this function
// needs to hold the global clipping lock so that the internal
// dirty regions are not messed with from the Desktop thread
// and ServerWindow thread at the same time.
if (!fDesktop->LockSingleWindow()) {
link.StartMessage(B_ERROR);
link.Flush();
return;
}
if (fUpdateRequested) {
// make the pending update session the current update session
@@ -1760,8 +1738,15 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link)
if (!fContentRegionValid)
_UpdateContentRegion();
BRegion dirty(fCurrentUpdateSession.DirtyRegion());
dirty.IntersectWith(&VisibleContentRegion());
BRegion* dirty = fRegionPool.GetRegion(
fCurrentUpdateSession.DirtyRegion());
if (!dirty) {
link.StartMessage(B_ERROR);
link.Flush();
return;
}
dirty->IntersectWith(&VisibleContentRegion());
//fDrawingEngine->FillRegion(dirty, RGBColor(255, 0, 0, 255));
@@ -1772,10 +1757,10 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link)
link.Attach<float>(fFrame.Width());
link.Attach<float>(fFrame.Height());
// append he update rect in screen coords
link.Attach<BRect>(dirty.Frame());
link.Attach<BRect>(dirty->Frame());
// find and attach all views that intersect with
// the dirty region
fTopLayer->AddTokensForLayersInRegion(link, dirty, &fContentRegion);
fTopLayer->AddTokensForLayersInRegion(link, *dirty, &fContentRegion);
// mark the end of the token "list"
link.Attach<int32>(B_NULL_TOKEN);
link.Flush();
@@ -1783,17 +1768,17 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link)
#if DELAYED_BACKGROUND_CLEARING
// NOTE: turning off DELAYED_BACKGROUND_CLEARING will
// need investigation if it even still works...
fTopLayer->Draw(fDrawingEngine, &dirty,
fTopLayer->Draw(fDrawingEngine, dirty,
&fContentRegion, true);
#endif
fRegionPool.Recycle(dirty);
#endif
} else {
printf("BeginUpdate() but no update requested!!\n");
link.StartMessage(B_ERROR);
link.Flush();
fprintf(stderr, "WindowLayer::BeginUpdate() - no update requested!\n");
}
fDesktop->UnlockSingleWindow();
}
@@ -1801,8 +1786,6 @@ void
WindowLayer::EndUpdate()
{
// NOTE: see comment in _BeginUpdate()
if (!fDesktop->LockSingleWindow())
return;
if (fInUpdate) {
fCurrentUpdateSession.SetUsed(false);
@@ -1816,8 +1799,6 @@ WindowLayer::EndUpdate()
} else {
fUpdateRequested = false;
}
fDesktop->UnlockSingleWindow();
}
+13 -6
View File
@@ -13,6 +13,7 @@
#include "Decorator.h"
#include "ViewLayer.h"
#include "RegionPool.h"
#include "ServerWindow.h"
#include "WindowList.h"
@@ -56,10 +57,6 @@ class WindowLayer {
::ServerWindow* ServerWindow() const { return fWindow; }
::EventTarget& EventTarget() const { return fWindow->EventTarget(); }
// for convenience, handles window not attached to Desktop
bool ReadLockWindows();
void ReadUnlockWindows();
// setting and getting the "hard" clipping, you need to have
// WriteLock()ed the clipping!
void SetClipping(BRegion* stillAvailableOnScreen);
@@ -116,6 +113,16 @@ class WindowLayer {
DrawingEngine* GetDrawingEngine() const
{ return fDrawingEngine; }
// managing a region pool
::RegionPool* RegionPool()
{ return &fRegionPool; }
inline BRegion* GetRegion()
{ return fRegionPool.GetRegion(); }
inline BRegion* GetRegion(const BRegion& copy)
{ return fRegionPool.GetRegion(copy); }
inline void RecycleRegion(BRegion* region)
{ fRegionPool.Recycle(region); }
void CopyContents(BRegion* region,
int32 xOffset, int32 yOffset);
@@ -248,6 +255,8 @@ class WindowLayer {
BRegion fEffectiveDrawingRegion;
bool fEffectiveDrawingRegionValid;
::RegionPool fRegionPool;
BObjectList<WindowLayer> fSubsets;
// TODO: remove those some day (let the decorator handle that stuff)
@@ -322,8 +331,6 @@ class WindowLayer {
int32 fMaxWidth;
int32 fMinHeight;
int32 fMaxHeight;
mutable bool fReadLocked;
};
#endif // WINDOW_LAYER_H
+6 -2
View File
@@ -77,14 +77,18 @@ Workspace::Workspace(Desktop& desktop, int32 index)
fDesktop(desktop),
fCurrentWorkspace(index == desktop.CurrentWorkspace())
{
fDesktop.LockSingleWindow();
// fDesktop.LockSingleWindow();
// TODO: in which threads is this being used?
// from my investigations, it is used in the
// WorkspacesLayer::Draw(), which would have
// to hold the read lock already
RewindWindows();
}
Workspace::~Workspace()
{
fDesktop.UnlockSingleWindow();
// fDesktop.UnlockSingleWindow();
}
+3 -2
View File
@@ -20,8 +20,8 @@ UseFreeTypeHeaders ;
local defines = [ FDefines TEST_MODE=1 ] ;
# USE_DIRECT_WINDOW_TEST_MODE=1
SubDirCcFlags $(defines) -fcheck-memory-usage -D_NO_INLINE_ASM ;
SubDirC++Flags $(defines) -fcheck-memory-usage -D_NO_INLINE_ASM ;
SubDirCcFlags $(defines) ; #-fcheck-memory-usage -D_NO_INLINE_ASM ;
SubDirC++Flags $(defines) ; #-fcheck-memory-usage -D_NO_INLINE_ASM ;
SEARCH_SOURCE += $(appServerDir) [ FDirName $(appServerDir) drawing ] ;
@@ -112,6 +112,7 @@ Server haiku_app_server :
DefaultDecorator.cpp
OffscreenServerWindow.cpp
OffscreenWindowLayer.cpp
RegionPool.cpp
ServerPicture.cpp
ServerScreen.cpp
ViewLayer.cpp