diff -r 000000000000 -r bde4ae8d615e os/graphics/windowing/windowserver/nonnga/SERVER/ScreenRedraw.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/graphics/windowing/windowserver/nonnga/SERVER/ScreenRedraw.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,733 @@ +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// SCREEN_REDRAW.CPP +// +// + +#include "ScreenRedraw.h" + +#include + +#include "debugbar.h" +#include "screen.h" +#include "inifile.h" +#include "offscreenbitmap.h" +#include "wspluginmanager.h" +#include "pointer.h" +#include "rootwin.h" +#include "walkwindowtree.h" +#include "wstop.h" +#include "WsMemMgr.h" +#include "Graphics/WsRenderStageFactory.h" +#include "Graphics/WsRenderStage.h" +#include "EVENT.H" + +GLREF_D CDebugLogBase *wsDebugLog; + +#ifdef USE_DEBUG_REGIONS +# define DEBUG_REGION(col,fill,reg) DebugRegion(col,fill,reg) +# define DEBUG_RECT(col,fill,rect) DebugRect(col,fill,rect) +#else +# define DEBUG_REGION(col,fill,reg) +# define DEBUG_RECT(col,fill,rect) +#endif + +#if defined(__WINS__) && defined(_DEBUG) +# define DEBUGOSB { CWsOffScreenBitmap * ofb = iScreen.OffScreenBitmap(); if (ofb) ofb->Update(); } +#else +# define DEBUGOSB +#endif + +#ifdef _DEBUG +# define LOG_SCREEN_REDRAW_START {if (wsDebugLog) {_LIT(KLogScreenRedrawStart, ">> CScreenRedraw::OnAnimation()"); wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, KLogScreenRedrawStart);}} +# define LOG_SCREEN_REDRAW_END {if (wsDebugLog) {_LIT(KLogScreenRedrawEnd, "<< CScreenRedraw::OnAnimation()"); wsDebugLog->MiscMessage(CDebugLogBase::ELogEverything, KLogScreenRedrawEnd);}} +#else +# define LOG_SCREEN_REDRAW_START +# define LOG_SCREEN_REDRAW_END +#endif + +CScreenRedraw::TTimedRect::TTimedRect(const TRect& aRect, const TTime& aTime): + iRect(aRect), iTime(aTime) + { + } + +TInt CScreenRedraw::TTimedRect::Compare(const TTimedRect& aOne,const TTimedRect& aOther) + { + if(aOne.iTime < aOther.iTime) + return -1; + else if(aOne.iTime > aOther.iTime) + return 1; + else + return 0; + } + +CScreenRedraw * CScreenRedraw::NewL(CScreen& aScreen) + { + CScreenRedraw * self = new (ELeave) CScreenRedraw(aScreen); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +CScreenRedraw::CScreenRedraw(CScreen& aScreen): iScreen(aScreen) + { + } + +CScreenRedraw::~CScreenRedraw() + { + CWsRenderStage * stage = iRenderStages; + while (stage!=NULL) + { + CWsRenderStage *next=stage->Next(); + delete stage; + stage=next; + } + iTimedDrawRect.Close(); + iInvalid.Close(); + iTopLayer.Close(); + iBannedRegion.Close(); + } + +void CScreenRedraw::ConstructL() + { + CWsPluginManager * pluginMgr = CWsTop::WindowServer()->PluginManager(); + + // Setup the render stages for this screen: + _LIT(KDefaultRenderStages, "std"); + _LIT(KDefaultFlickerFreeRenderStages, "flickerbuffer std"); + _LIT(KRenderStages,"RENDERSTAGES"); + TPtrC stagesString; + const TBool customStages = WsIniFile->FindVar(iScreen.ScreenNumber(),KRenderStages,stagesString); + + // If noone specifies stages for this screen, assume the standard implementation: + const TDesC * stages; + if (customStages) + stages = &stagesString; + else if (iScreen.OffScreenBitmap()) + stages = &KDefaultFlickerFreeRenderStages(); + else + stages = &KDefaultRenderStages(); + + CWsRenderStage * lastStage = 0; + + // Parse the string for implementation IDs: + TLex lex(*stages); + while(true) + { + TPtrC ptr = lex.NextToken(); + if (ptr.Length() > 0) + { + TInt err = KErrNone; + MWsRenderStageFactory * factory = pluginMgr->FindNamedImplementation(ptr); + if (factory) + { + CWsRenderStage * stage = 0; + TRAP(err, stage = factory->CreateStageL(static_cast(&iScreen), this)); + if (err == KErrNone) + { + if (!stage) + { + err = KErrNotFound; + } + else + { + if (lastStage) + lastStage->SetNext(stage); + else + iRenderStages = stage; + lastStage = stage; + } + } + } + else + { + err = KErrNotFound; + } + + if (wsDebugLog) + { + TBuf<64> buf; + if (err == KErrNone) + { + _LIT(KAddedRenderStage,"Added render stage: "); + buf.Append(KAddedRenderStage); + buf.Append(ptr); + wsDebugLog->MiscMessage(CDebugLogBase::ELogImportant,buf); + } + else + { + _LIT(KMissingRenderStage,"Failed to add render stage (%d): "); + buf.Append(KMissingRenderStage); + buf.Append(ptr); + wsDebugLog->MiscMessage(CDebugLogBase::ELogImportant,buf,err); + } + } + } + else + { + break; + } + } + } + +const TTime& CScreenRedraw::Now() const + { + if(!iAnimating) + { + iNow.UniversalTime(); + } + return iNow; + } + +void CScreenRedraw::ScheduleRender(const TTimeIntervalMicroSeconds& aFromNow) + { + iRenderScheduled = ETrue; + TTime then(Now() + aFromNow); + if ((!iScheduled) || then < iNext) + iNext = then; + iScheduled = ETrue; + CWsTop::WindowServer()->AnimationScheduler()->ScheduleAnimation(iScreen,iNext); + } + +void CScreenRedraw::ScheduleRedraw() + { + iNext = Now(); + iScheduled = ETrue; + + // The other scheduler also removes future animations which this one encompasses. + // We choose not to do the region calculations needed to achieve that here. + MWsAnimationScheduler* animSched=CWsTop::WindowServer()->AnimationScheduler(); + if (animSched) + { + animSched->ScheduleRedraw(iScreen,iNext); + } + } + +void CScreenRedraw::ScheduleAnimation(const TRect& aRect,const TTimeIntervalMicroSeconds& aFromNow,const TTimeIntervalMicroSeconds& /*aFreq*/,const TTimeIntervalMicroSeconds& /*aStop*/) + { + TRect test(aRect); + test.Intersection(iScreen.DrawableArea()); + if(!test.IsEmpty()) + { + const TTime then(Now() + aFromNow); + TTimedRect tRect(aRect, then); + + const TInt error = iTimedDrawRect.InsertInOrderAllowRepeats(tRect,TTimedRect::Compare); + if (KErrNone == error) + { + if (iScheduled) + { + if (then < iNext) + { + iNext = then; + } + } + else + { + iNext = then; + iScheduled = ETrue; + } + // remove further futures that are completely contained + TInt count = iTimedDrawRect.Count(); + for(TInt i=0; i then.Int64()) + { + TRect rect(aRect); + rect.BoundingRect(future.iRect); + if(rect == aRect) // future is completely contained within aRect + { + iTimedDrawRect.Remove(i); + count--; + i--; + } + } + } + CWsTop::WindowServer()->AnimationScheduler()->ScheduleAnimation(iScreen,iNext); + + // Blue rectangles for scheduled animations + DEBUG_RECT(TRgb(0x00, 0x00, 0xFF),TRgb(0x00, 0x00, 0xFF, 0x20),&aRect); + } + } + } + +// This adds a region to the stored invalid region. +// The invalid region is the area of the screen that needs to be redrawn in addition to any animations. +// The draw region is the area of the screen on which only the top window needs to be redrawn. +// If the top window has transparency, this can only be true when it has been made newly visible. +// The value of aSchedule could be determined automatically from iAnimating, but passing it this way +// allows us to have the assert, which is a very valuable assert. +void CScreenRedraw::AddRedrawRegion(const TRegion& aRegion, TBool aSchedule, TRedrawDepth aDepth) + { + WS_ASSERT_DEBUG(!iAnimating || !aSchedule, EWsPanicScheduledRedraw); + + if(aRegion.CheckError()) + { + iInvalid.ForceError(); + + if (aSchedule) + ScheduleRedraw(); + } + else if(aRegion.Count()) // often called despite window not being visible + { + if (aDepth == ERedrawAll) + { + // red lines for an invalid region which is ready to be drawn + DEBUG_REGION(TRgb(0xFF, 0x00, 0x00),TRgb(0xFF, 0x00, 0x00, 0x20),&aRegion); + + iInvalid.Union(aRegion); + + if (aSchedule) + ScheduleRedraw(); + } + else + { + // yellow lines for a valid region which we will draw on top of + DEBUG_REGION(TRgb(0xFF, 0xFF, 0x00),TRgb(0xFF, 0xFF, 0x00, 0x20),&aRegion); + + iTopLayer.Union(aRegion); + + if (aSchedule) + ScheduleRedraw(); + } + } + } + +// This causes any asynchronously scheduled redraw to happen immediately +// It should be avoided where possible for performance reasons, but is +// needed whenever the redraw store is discarded for a window which still +// has a redraw region pending. +void CScreenRedraw::DoRedrawNow() + { + if(!iAnimating) + CWsTop::WindowServer()->AnimationScheduler()->DoRedrawNow(iScreen); + } + +#ifdef USE_DEBUG_REGIONS +void CScreenRedraw::DebugRect(TRgb aColor, TRgb aFill, const TRect* aRect) + { + if (aRect) + { + CFbsBitGc * gc = iScreen.GetBitGc(); + gc->SetPenColor(aColor); + gc->SetPenStyle(CGraphicsContext::ESolidPen); + gc->SetPenSize(TSize(2,2)); + gc->SetBrushColor(aFill); + gc->SetBrushStyle(CGraphicsContext::ESolidBrush); + TRect smaller = *aRect; + smaller.iBr.iX -= 1; + smaller.iBr.iY -= 1; + gc->DrawRect(smaller); + iScreen.Update(); + } + } + +void CScreenRedraw::DebugRegion(TRgb aColor, TRgb aFill, const TRegion * aRegion) + { + if (aRegion) + { + CFbsBitGc * gc = iScreen.GetBitGc(); + gc->SetPenColor(aColor); + gc->SetPenStyle(CGraphicsContext::ESolidPen); + gc->SetPenSize(TSize(2,2)); + gc->SetBrushColor(aFill); + gc->SetBrushStyle(CGraphicsContext::ESolidBrush); + for (const TRect *rect = aRegion->RectangleList(); rect - aRegion->RectangleList() < aRegion->Count(); ++rect) + { + TRect smaller = *rect; + smaller.iBr.iX -= 1; + smaller.iBr.iY -= 1; + gc->DrawRect(smaller); + } + iScreen.Update(); + } + } +#endif + +void CScreenRedraw::OnAnimation() + { + LOG_SCREEN_REDRAW_START + ASSERT(!iAnimating); + ASSERT(iScheduled); + iAnimating = ETrue; + iScheduled = EFalse; + TBool futureAnimationRequired = EFalse; + + CWsActiveScheduler::Static()->PrepareDraw(); + + // Calculate any updates required by region changes: + RegionUpdate(); + + // Add the timed rectangles to the invalid region: + iNow.UniversalTime(); + TInt count(iTimedDrawRect.Count()); + while (0 < count) + { + if(iTimedDrawRect[0].iTime.Int64() <= iNow.Int64()) + { + iInvalid.AddRect(iTimedDrawRect[0].iRect); + iTimedDrawRect.Remove(0); + count--; + } + else + { + futureAnimationRequired = ETrue; + break; + } + } + + // Animating rectangles could cause iInvalid to overlap iTopLayer, + // in which case iTopLayer won't work. + iTopLayer.SubRegion(iInvalid); + iTopLayer.Intersect(iScreen.RootWindow()->WindowArea()); + iTopLayer.Tidy(); + // Anything in the top layer is implcitly invalid + iInvalid.Union(iTopLayer); + iInvalid.Intersect(iScreen.RootWindow()->WindowArea()); + +/* + if (const CDebugBar* dbg = iScreen.DebugBar()) + { + iTopLayer.SubRect(dbg->Rect()); + iInvalid.SubRect(dbg->Rect()); + } +*/ + + iInvalid.Tidy(); + + if(iInvalid.CheckError()) //Will: agree with Andy, want bounding rects instead! + { + iTopLayer.Clear(); + iInvalid.Clear(); + iInvalid.Copy(iScreen.RootWindow()->WindowArea()); // assumed cannot fail, all regions can contain at least 1 rect.. + } + + iInvalid.SubRegion( iBannedRegion ); + iInvalid.Tidy(); + iTopLayer.SubRegion( iBannedRegion ); + iTopLayer.Tidy(); + + STACK_REGION invalidCopy; + invalidCopy.Copy(iInvalid); + TWalkWindowTreeScheduleRegions regionScheduler(&invalidCopy, iTopLayer); + TWalkWindowTreeScheduleFallback fallbackScheduler(iScreen.FallbackMap()); + TWalkWindowTreeSchedule * scheduler = ®ionScheduler; + + // At this point, if the DEBUG_REGION is being used: + // Red represents invalid regions that need to be redrawn completely. + // Yellow represents regions that only need the top window to be drawn. + // Blue represents regions which are being animated server side. + if (iRenderScheduled || !iInvalid.IsEmpty()) + { + iRenderScheduled = EFalse; + // invalidCopy.ForceError(); //### DEBUG + + iScreen.RootWindow()->WalkWindowTree(regionScheduler,EWalkChildren); + if (!regionScheduler.ScheduledRegionsOk()) + { + // our region calculations for what to draw failed at some point. + // From this point on we MUST NOT rely on allocating memory + // Andy - localRedrawRegion allocates + // Andy - setPenSize allocates (even if you don't call it) + // Andy - all draw commands add to a gdi dirty region (which allocates) + // Andy - combining client clipping regions with window clipping regions allocates + scheduler = &fallbackScheduler; + iScreen.FallbackMap()->Prepare(); + iScreen.RootWindow()->WalkWindowTree(fallbackScheduler,EWalkChildren); + } + + CWsActiveScheduler::Static()->StartDraw(); + CWsMemoryManager::Static()->EnableReserve(); + + if (&fallbackScheduler == scheduler) + iAnimationRegion = iScreen.FallbackMap()->Region(); + else + iAnimationRegion = &iInvalid; + + // Redraw debug regions more brightly than before: + DEBUG_REGION(TRgb(0xFF, 0x00, 0x00),TRgb(0xFF, 0x00, 0x00, 0x80),&iInvalid); + DEBUG_REGION(TRgb(0xFF, 0xFF, 0x00),TRgb(0xFF, 0xFF, 0x00, 0x80),&iTopLayer); + + RWsRegion accumulatedDrawing; + + // Pipe the drawing into the first render stage: + CFbsBitGc * stageGc = iRenderStages->Begin(); + + for (CWsWindow * win = scheduler->HeadWindow(); win; win = win->NextScheduled()) + { + const TRegion * targetRegion = scheduler->Region(win); + const TRect * screenRect = 0; + if ((&fallbackScheduler == scheduler) && !targetRegion->IsContainedBy(iScreen.RootWindow()->Abs())) + { + screenRect = &iScreen.RootWindow()->Abs(); + } + if (!screenRect) + { + // Purple regions are about to be drawn + DEBUG_REGION(TRgb(0x80, 0x00, 0x80),TRgb(0x80, 0x00, 0x80, 0x80),targetRegion); + // Do the drawing + stageGc->Reset(); + win->Render(stageGc, *targetRegion); + accumulatedDrawing.Union(*targetRegion); + // Green regions have been drawn + DEBUG_REGION(TRgb(0x00, 0xFF, 0x00),TRgb(0x00, 0xFF, 0x00, 0x80),targetRegion); + } + else + { + // Our region overlaps the edges of the screen, and we have no memory + // to create a clipped one, so we will use a single-rect region for each rect + // and call Draw multiple times. + TRegionFix<1> rectRegion; + for (const TRect * rect = targetRegion->RectangleList() + targetRegion->Count() - 1; rect >= targetRegion->RectangleList(); --rect) + { + rectRegion.Clear(); + TRect combined(*screenRect); + combined.Intersection(*rect); + rectRegion.AddRect(combined); + // Purple regions are about to be drawn + DEBUG_REGION(TRgb(0x80, 0x00, 0x80),TRgb(0x80, 0x00, 0x80, 0x80),&rectRegion); + // Do the drawing + stageGc->Reset(); + win->Render(stageGc, rectRegion); + accumulatedDrawing.AddRect(combined); + // Green regions have been drawn + DEBUG_REGION(TRgb(0x00, 0xFF, 0x00),TRgb(0x00, 0xFF, 0x00, 0x80),&rectRegion); + } + } + DEBUGOSB + } + + DEBUGOSB + iScreen.SpriteManager()->DrawFloatingSprites(stageGc,accumulatedDrawing); // we limit sprite drawing over only actually affected ares but necessary to all "planned" for redraw + if (!accumulatedDrawing.CheckError() && iScreen.SpriteManager()->SpriteCount() == 0) + { + iAnimationRegion = &accumulatedDrawing; + } + + // Tell the render stage we've finished: + iRenderStages->End(); + + // We nolonger need the regions + for (CWsWindow * win = scheduler->HeadWindow(); win; win = win->NextScheduled()) + { + win->ClearScheduledRegion(); + } + + CWsMemoryManager::Static()->DisableReserve(); + + if (const CDebugBar* dbg = iScreen.DebugBar()) + { + dbg->RedrawDebugBar(); + } + + // At this point, if the DEBUG_REGION is being used, there should usually be only green regions + // displayed. If we see red or yellow, then something didn't get redrawn that should have been. + // If we see purple then a window has disobeyed the const setting on the region to draw. + // Red or yellow is valid - a window can decide it isn't ready to draw yet - purple is very bad. + iScreen.Update(); + // At this point, if the DEBUG_REGION is being used, there should be no regions visible + // of any colour. If we see green, then it means an area of the screen was drawn to which + // wasn't invalid, or the screen update call failed. The former is more likely. + // If we still see red or yellow it is a region that is not yet ready to draw. + + const TRect* rect = iAnimationRegion->RectangleList(); + TInt pixels = 0; + for(TInt r = iAnimationRegion->Count(); r>0; r--) + { + pixels += (rect->Width()*rect->Height()); + rect++; + } + CWsActiveScheduler::Static()->StopDraw(pixels); + + TWindowServerEvent::NotifyScreenDrawingEvent(iAnimationRegion); + + iAnimationRegion = 0; + accumulatedDrawing.Close(); + iInvalid.Clear(); + } + else + { + CWsActiveScheduler::Static()->CancelPrepare(); + } + + iInvalid.Clear(); + iTopLayer.Clear(); + invalidCopy.Close(); + iAnimating = EFalse; + + if (futureAnimationRequired && iTimedDrawRect.Count() > 0 && !iScheduled) + { + // If this flag is set then it means there were already some animations scheduled when we ran, + // but they themselves didn't run. We need to make sure we have _something_ scheduled. + CWsTop::WindowServer()->AnimationScheduler()->ScheduleAnimation(iScreen, iTimedDrawRect[0].iTime); + iScheduled = ETrue; + } + + if(iObserver) + { + iObserver->ScreenUpdated(iScreen.ScreenNumber()); + iObserver=NULL; //once signalled we are never going to call it again + } + LOG_SCREEN_REDRAW_END + } + +// +void CScreenRedraw::DiscardAllSchedules() + { + ASSERT(!iAnimating); + + iTimedDrawRect.Reset(); + iInvalid.Clear(); + iInvalid.Tidy(); + } + +/** +Indicates that a window has moved or changed ordinal position so that the visible regions +of all windows needs to be recalculated +*/ +void CScreenRedraw::ScheduleRegionUpdate(const TRegion* aDefinitelyDirty) + { + iRegionUpdateScheduled = ETrue; + ScheduleRedraw(); + if(aDefinitelyDirty) + { + iInvalid.Union(*aDefinitelyDirty); + // Cyan regions for invalidations caused by this: + DEBUG_REGION(TRgb(0x00, 0xFF, 0xFF),TRgb(0x00, 0xFF, 0xFF, 0x20),aDefinitelyDirty); + } + } + +/** +Recalculates visible regions and schedules redraws or queues redraw events in response to +any changes +*/ +void CScreenRedraw::RegionUpdate() + { + if (iRegionUpdateScheduled) + { + iRegionUpdateScheduled = EFalse; + + TWalkWindowTreeUpdateRegions updater(iScreen); + updater.Walk(); + + WsPointer::ReLogCurrentWindow(); + CWsTop::TriggerRedraws(iScreen.RootWindow()); + } + } + +void CScreenRedraw::SetObserver(MWsScreenRedrawObserver* aObserver) + { + iObserver = aObserver; + } + +TBool CScreenRedraw::IsUpdatePending() + { + if(iScheduled || iAnimating) + return ETrue; + else + return EFalse; + } + +/** + Overidding MWsObjectProvider +*/ +TAny* CScreenRedraw::ResolveObjectInterface(TUint aTypeId) + { + TAny* interface = NULL; + + switch (aTypeId) + { + case KWsScreenRedraw: + interface = static_cast(this); + break; + } + + if (!interface) + { + interface = iRenderStages->ResolveObjectInterface(aTypeId); + } + + return interface; + } + +const TRegion * CScreenRedraw::AnimationRegion() const + { + if (iAnimating) + return iAnimationRegion; + else + return 0; + } + +void CScreenRedraw::UpdateDevice() + { + iScreen.Update(); + } + +void CScreenRedraw::BanThisRegionUpdate( TRegion& aForbiddenRegion ) + { + iBannedRegion.Union( aForbiddenRegion ); + iBannedRegion.Tidy(); + } + +void CScreenRedraw::LiftRegionUpdateBan( TRegion& aFormerForbiddenRegion ) + { + iBannedRegion.SubRegion( aFormerForbiddenRegion ); + iBannedRegion.Tidy(); + } + +void CScreenRedraw::AcceptFadeRequest( CWsWindow* aWin, TBool aFadeOn, TBool aFadeBehind, TBool aIncludeChildren ) + { + if ( aFadeOn ) + { + TWalkWindowTreeScheduleFadeNoRedraw walkerFadeNoRedraw; + if ( aFadeBehind ) + { + aWin->WalkWindowTree( walkerFadeNoRedraw, EWalkBehind ); + } + else + { + if(aWin->WinType() != EWinTypeGroup) + { + ScheduleRegionUpdate( aWin->VisibleRegionIfValid() ); + } + + if ( aIncludeChildren ) + { + aWin->WalkWindowTree( walkerFadeNoRedraw, EWalkChildren ); + } + } + } + else + { // fade off, just initiate redraw + TWalkWindowTreeScheduleRedraws walkerRedraw( TWalkWindowTreeScheduleRedraws::ERedrawFilterOmitDSA ) ; + if ( aFadeBehind ) + { + aWin->WalkWindowTree( walkerRedraw, EWalkBehind ); + } + else + { // fade this win not behind + if ( !aWin->IsDSAHost() ) + { + AddRedrawRegion( aWin->VisibleRegion() ); + } + if ( aIncludeChildren ) + { + aWin->WalkWindowTree( walkerRedraw, EWalkChildren ); + } + } + } + ScheduleRegionUpdate(NULL); + }