Anti-tearing with CDirectScreenBitmap
This article discusses how CDirectScreenBitmap (also known as Anti-Tearing API) can be used to synchronize drawing to the LCD refresh rate and thereby avoid "tearing".
Article Metadata
Introduction
When developing games with fast-paced graphics it is desirable to avoid using Windows server when drawing to screen. There are several options to access screen directly, see here.
This article focuses on using CDirectScreenBitmap class, which is also know as the Anti-Tearing API (a brief description of CDirectScreenBitmap can be found in TSS000307 - Direct screen access with CDirectScreenBitmap). With this API it is possible to synchronize drawing to the LCD refresh rate (which is typically between 60 and 75Hz). More details about the phenomenon "tearing" can be read here.
Sample code
This sample code contains a class (partially) that implements anti-tearing rendering.
// -------------------------------------------
// header file
// -------------------------------------------
...
#include <cdsb.h> //CDirectScreenBitmap link to scdv.lib
#define TMpPixel TUint32
class CRendering : public CActive, public MDirectScreenAccess
{
// Construction, etc
...
public:
void ProcessFrame();
void BeginDraw();
void EndDraw();
public: // from MDirectScreenAccess
virtual void Restart( RDirectScreenAccess::TTerminationReasons aReason );
virtual void AbortNow( RDirectScreenAccess::TTerminationReasons aReason );
protected: // from CActive
void DoCancel();
void RunL();
private:
CRendering();
void ConstructL( RWindow& aWindow );
private:
CDirectScreenAccess* iDrawer;
CDirectScreenBitmap* iDSBitmap;
TMpPixel* iScreenAddress;
};
...
// -------------------------------------------
// cpp file
// -------------------------------------------
...
CRendering::~CRendering()
{
// "Cancel" is a meaningless call, since the service
// (video driver's screen update process) is not cancellable.
// When destroying this active object, you must make sure,
// that the last update request (CDirectScreenBitmap::EndUpdate()) is completed.
// Assuming that LCD refresh rate is not less than 60 Hz,
// the wait time should be more than 1/60 secs.
// (Otherwise a stay signal comes.)
Cancel();
delete iDrawer;
delete iDSBitmap;
}
CRendering::CRendering():
CActive( CActive::EPriorityStandard )
{
}
void CRendering::ConstructL( RWindow& aWindow )
{
CActiveScheduler::Add( this );
// Setting up direct screen access
iDSBitmap = CDirectScreenBitmap::NewL();
iDrawer = CDirectScreenAccess::NewL(
CEikonEnv::Static()->WsSession(),
*CEikonEnv::Static()->ScreenDevice(),
aWindow,
*this);
CEikonEnv::Static()->WsSession().Flush();
iDrawer->StartL();
CFbsBitGc* gc = iDrawer->Gc();
RRegion* region = iDrawer->DrawingRegion();
gc->SetClippingRegion(region);
// It may happen that a device does not support double buffering.
User::LeaveIfError(
iDSBitmap->Create(
TRect(0, 0, KMpScreenWidth, KMpScreenHeight), CDirectScreenBitmap::EDoubleBuffer));
}
void CRendering::Restart( RDirectScreenAccess::TTerminationReasons /*aReason*/ )
{
TRAPD( err, iDrawer->StartL() ); // You may panic here, if you want
CFbsBitGc* gc = iDrawer->Gc();
RRegion* region = iDrawer->DrawingRegion();
gc->SetClippingRegion(region);
iDSBitmap->Create(
TRect(0, 0, KMpScreenWidth, KMpScreenHeight), CDirectScreenBitmap::EDoubleBuffer);
// Put some code here to continue game engine
}
void CRendering::AbortNow( RDirectScreenAccess::TTerminationReasons /*aReason*/ )
{
// Put some code here to suspend game engine
iDSBitmap->Close();
}
void CRendering::RunL()
{
// Video driver finished to draw the last frame on the screen
// You may initiate rendering the next frame from here,
// but it would be slow, since there is a delay between CDirectScreenBitmap::EndUpdate()
// and the completition of screen refresh by the video driver
}
void CRendering::DoCancel()
{
// Cancel not implemented in service provider, so we can't do anything here
}
void CRendering::BeginDraw()
{
// Obtain the screen address every time before drawing the frame,
// since the address always changes
TAcceleratedBitmapInfo bitmapInfo;
iDSBitmap->BeginUpdate(bitmapInfo);
iScreenAddress = (TMpPixel*)bitmapInfo.iAddress;
}
void CRendering::EndDraw()
{
if (IsActive())
{
Cancel();
}
iDSBitmap->EndUpdate(iStatus);
SetActive();
// We don't need to wait to complete the request
// We can start making the next frame instead.
}
void CRendering::ProcessFrame()
{
// This method is responsible to render and draw a frame to the screen
BeginDraw();
// render the frame using iScreenAddress
EndDraw();
}


18 Sep
2009
Direct screen access is a way of drawing to the screen without using the window server. As this avoids client-server communication, it is much faster, and may be useful for games and video. This article provides code snippet demonstrating the usage of CDirectScreenBitmap.
A good reference for beginners to start with.
Langfinger - Very good but can crash the phone if not handled with care
If you really need a high framerate on the 5th edition devices there is no way around this API if you also want make use of the OS graphics calls. Otherwise you could just use OpenGL to draw directly to the frame buffer, should be around the same efficiency maybe slower(OpenGL that is).
Either way, if you use it be prepared to spend a lot of time on preventing phone crashes, this especially happens if the Application or exist button is pushed since the OS seems not to give you enough time sometimes to free up the screen which results in a blue screen and the restarting of the phone.Langfinger 02:34, 8 December 2011 (EET)