author | Gareth Stockwell <gareth.stockwell@accenture.com> |
Fri, 22 Oct 2010 11:38:29 +0100 | |
branch | bug235_bringup_0 |
changeset 206 | c170e304623f |
parent 0 | 5d03bc08d59c |
permissions | -rw-r--r-- |
// 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 <hal.h> #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<MWsRenderStageFactory>(ptr); if (factory) { CWsRenderStage * stage = 0; TRAP(err, stage = factory->CreateStageL(static_cast<MWsScreen*>(&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<count; i++) { const TTimedRect& future = iTimedDrawRect[i]; if(future.iTime.Int64() > 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<MWsScreenRedraw*>(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); }