DSA(直接写屏)和双缓冲

2024-02-07 05:38
文章标签 直接 缓冲 dsa 写屏

本文主要是介绍DSA(直接写屏)和双缓冲,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

DSA(直接写屏)和双缓冲

 

http://blog.chinaunix.net/u3/103999/showart_2063360.html

 

1.DSA(直接写屏)
1.1介绍
    使用GDI在屏幕上描画需要一个上下文转换,这会减慢描画速度。为了绕过繁琐的上下文转换,可以直接访问屏幕。这被称作直接屏幕访问。

    直接写屏就是得到屏幕的显存地址,从而直接对显存进行操作,使用直接写屏是为了加快显示速度。

    直接写屏一般会在游戏和视频中被用到。
 

1.2实现方式
    在Symbian OS中有三种方法来直接在屏幕上描画。
   
    1> CFbsScreenDevice
    CFbsScreenDevice是一个可以被发送到屏幕驱动程序SCDV.DLL的图形设备。在创建一个CFbsBitGc图形上下文之后,它能像任何其他的图形设备一样使用。然而,可以直接在屏幕上描画,而不需要使用窗口服务器。

    以下是class CFbsScreenDevice的描述:
    A graphics device interface that provides direct access to the screen, without the mediation of the window server.
    The interface adds sprite support to the CFbsDevice base class.

    示例代码:

#include <FBS.H> // fbscli.lib
#include <BITSTD.H>
// bitgdi.lib

// User::LeaveIfError(RFbsSession::Connect());

CWsScreenDevice* wsScreenDev = CEikonEnv::Static()->ScreenDevice()
;

CFbsScreenDevice* screenDev = CFbsScreenDevice::NewL(KNullDesC, 
        wsScreenDev->DisplayMode())
;

CFbsBitGc * gc = NULL;
User::LeaveIfError(screenDev->CreateContext( gc ))
;

wsScreenDev->SetAutoUpdate(ETrue);
// Sets or unsets auto-update for the screen.

gc->DrawRect(TRect(TPoint (10, 10), TSize (60, 60)) ); // draw a rect by DSA
/* if not call SetAutoUpdate(ETrue) before, it must call Update() manually to update screen. */
// screenDev->Update();


delete gc; gc = NULL;
delete screenDev; screenDev = NULL
;

// RFbsSession::Disconnect();


   
    2> 直接操作屏幕内存地址
    直接在屏幕上描画的另一种方法是从系统中查询屏幕内存地址,这可以使用 UserSrv::ScreenInfo 方法来实现。
    屏幕内存有一个32字节的头。
    即使在屏幕内存内写数据比CFbsScreenDevice稍微快一点,但是功能可能根据硬件和屏幕的设备驱动程序的不同而有差异。在一些基于Symbian OS的终端中,屏幕在内存变化的时候自动从屏幕内存中更新,而在其他的终端中描画需要明确的激活。屏幕内存地址只对目标硬件有效,因此描画代码需要分为硬件和模拟器两部分。在模拟器环境中,可以描画到一个屏外位图中,而不是屏幕内存中,然后使用正常的窗口服务器描画方法位块传送到屏幕上。环境可以通过使用__WINS__条件宏定义来检测出来。

#ifdef __WINS__ // Emulator environment
// Draw to an off-screen bitmap
#else
// Hardware environment
// Draw directly to the screen memory
#endif

   
    示例代码:

#include <e32event.h>
#include <e32svr.h>

TUint16 * screenDataAddr = NULL;

#if defined(__WINS__)
CWsBitmap * screenBufBmp = new (ELeave) CWsBitmap(
        CCoeEnv::Static()->WsSession() );
CleanupStack::PushL(screenBufBmp);

TSize size = CCoeEnv::Static()->ScreenDevice()->SizeInPixels();
User::LeaveIfError(screenBufBmp->Create(size,
        CCoeEnv::Static()->ScreenDevice()->DisplayMode() ));

screenBufBmp->LockHeap();
screenDataAddr = (TUint16*)screenBufBmp->DataAddress();

#
else
// device
TPckgBuf<TScreenInfoV01> screenInfoPckg;
UserSvr::ScreenInfo(screenInfoPckg);

TScreenInfoV01& screenInfo = screenInfoPckg();
if(screenInfo.iScreenAddressValid)
    {
    screenDataAddr = (TUint16*)screenInfo.iScreenAddress;
    
    
/* skip the palette data in the beginning of frame buffer.
     * series 60 devices have 32 Bytes palette at the beginning.
     * But be careful: UIQ devices haven't.
     */

    screenDataAddr += 16;
    }
#endif
// __WINS__

/* DSA.
 * when you copy bitmap data directly to video memory, it is essential to ensure
 * your screen mode is same as bitmap mode!
 */
if(screenDataAddr)

    {
    Mem::Copy((void *)screenDataAddr, (void *)iOffScreenBmp->DataAddress(),
        bmpLenInBytes);

    }

#if defined(__WINS__)
screenBufBmp->UnlockHeap();

.
.. // bitblt the screenBufBmp to Screen

CleanupStack::PopAndDestroy(screenBufBmp);

#
else // device
/**Once when the drawing is completed, the screen should be updated such that
 * the changes made to the screen memory will be reflected on the screen.
 * The general way of doing this is by adding a redraw event(TRawEvent::ERedraw)
 * to the system queue using UserSvr::AddEvent() API, which updates the screen
 * immediately.
 *
 * But the TRawEvent::ERedraw is a event generated by the host OS ( typically by
 * WM_PAINT ) which is meant for emulator environment( as per the S60 SDK help
 * document ). However on S60 2nd edition devices, the screen gets updated
 * properly by adding this event to the system queue. But S60 3rd Edition
 * devices does not have any immediate effect after the Redraw event is added to
 * the system queue. The screen is updated only after notifying the screen
 * device about the out of date region(s), which is a known issue.
 *
 * Also accessing the display memory with UserSvr::ScreenInfo() is deprecated
 * from S60 3rd Edition onwards.
 */

TRawEvent redraw;
redraw.Set(TRawEvent::ERedraw);
UserSvr::AddEvent(redraw)
;

/**As a workaround solution, it is still possible to use the old drawing method
 * and force the screen to update itself by specifying the out of date region
 * using CFbsScreenDevice's Update() API. The code snippet for doing the same is
 * as follows:
 */

CFbsScreenDevice* iMyScreenDev = CFbsScreenDevice::NewL(0 ,displayMode);
// the screennumber will be 0 if phone supports single screen where as the
// displaymode can be as per your choice

RRegion iMyregion;
iMyregion.AddRect(TRect(0,0,240,320));
// the out of date rect region.
iMyScreenDev->Update(iMyregion);
iMyregion.Close();

#endif



    注意:以上两种直接描画方法的一个共同的问题是窗口服务器不了解描画,因此它不能通知应用程序是否出现另一个窗口或者窗口组。 即使当应用程序得到一个事件而失去焦点的时候,它们也不能停止直接描画,因为直接描画实在太快了,并且屏幕内容有可能被弄乱。这可能发生在当在玩游戏的时候,突然有电话打进来的情况下。
   
    3> CDirectScreenAccess
    新近的GT 6.1版本提供了一个应用编程接口用于直接描画,将能解决前面提到的问题。 
    这个应用编程接口由两个类组成:一个MDirectScreenAccess类,提供用于应用程序的回调方法,还有一个CDirectScreenAccess类处理与窗口服务器的通讯。
    CDirectScreenAccess的NewL方法获得一个窗口服务器会话、CONE的图形设备、应用程序窗口和一个到MDirectedScreenAccess导出类的指针作为参数。 在CDirectScreenAccess::StartL被调用来激活直接描画支持之前,客户端窗口服务器缓冲应该溢出。为了能自动更新屏幕,屏幕设备的SetAutoUpdate方法需要使用ETrue参数(CFbsScreenDevice::SetAutoUpdate(ETrue) ),否则gc的draw指令不会在模拟器中立即显示,而是在下一次冲刷(Flush)视窗服务器时才显示。当直接描画支持激活的时候,CDirectScreenAccess产生一个CFbsBitGc图形上下文,可以被应用程序用来在屏幕上绘画。
    当另一个窗口出现在应用程序窗口上时,CDirectScreenAccess从窗口服务器取得一个事件来中断描画。 CDirectScreenAccess然后调用MDirectScreenAccess派生类的AbortNow方法,这个方法必须被应用程序重载以便中断描画。为了防止屏幕被弄乱,窗口服务器直到中断描画事件被处理的时候才画重叠窗口(回调MDirectScreenAccess::Restart)。
    
    以下是class CDirectScreenAccess的描述:
    An active object used to start direct screen access.
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. Note that some interaction with the window server is needed in order to prevent the application from drawing over other application's data.
    The object's (private) RunL() function is called by the window server in order to abort direct screen access. This might occur when another window needs to be displayed in front or when the window with direct screen access is moved. The active object's priority is RDirectScreenAccess::EPriorityVeryHigh so that direct screen access will be aborted as quickly as possible.
 
示例代码

头文件

#include <W32STD.H> // ws32.lib
    
class CMyDsaAppContainer: public CCoeControl,
        public MDirectScreenAccess
    {
// new functions ++
    ...
// other functions
public:
    virtual ~CMyDsaAppContainer();
    void Repaint(const TRect & aRect);
    
protected:
    void ConstructL(const CCoeControl* aParent, const TRect& aRect);

private
:
    void PaintUsingDsa(CBitmapContext & aGc, const TRect & aRect);
// new functions --

// from MDirectScreenAccess ++
public:
    
    
/** This function is called by the window server when direct screen access must
     * stop (for example because a dialogue is moved in front of the area where direct
     * screen access is taking place).
     * In response to this, direct screen access must stop immediately. In simple cases,
     * this will involve cancelling the active object that is driving the drawing to the
     * screen.
     * No attempt to call a Window Server API function can be made from
     * AbortNow(), because then a temporary deadlock will occur. This is because WSERV
     * is waiting to receive the client's acknowledgment that it has aborted, and so will
     * not be able to service the call. As soon as the restriction no longer applies,
     * the function Restart() will be called.
     *
     * @param aReason The reason why direct screen access was terminated.
     */

    void AbortNow(RDirectScreenAccess::TTerminationReasons aReason);
    
    
/** This function is called by the window server as soon as direct screen access
     * can resume.
     * This function should call CDirectScreenAccess::StartL() within a trap harness.
     * If this leaves, e.g. through lack of memory, direct screen access cannot be
     * restarted. StartL() re-calculates the clipping region, so that if direct screen
     * access was aborted because another window appeared in front of it, that window
     * will not be overwritten when direct screen access resumes.
     * In this function, you can resume calls to Window Server Client Side API functions.
     *
     * @param aReason Provides the reason why direct screen access was terminated.
     */

    void Restart(RDirectScreenAccess::TTerminationReasons aReason);
// from MDirectScreenAccess --
    
    ...
// other functions
    
private:
    CDirectScreenAccess * iDsa;
    ... // other member data
    };


源文件

CMyDsaAppContainer::~CMyDsaAppContainer()
    {
    ...
    iDsa->Cancel();
    delete iDsa;
    ...
    }

void CMyDsaAppContainer::Repaint(const TRect & /*aRect*/)
    {
    TRect rect = Rect();
    PaintUsingDsa(*iDsa->Gc(), rect); // drawing with DSA
    iDsa->ScreenDevice()->Update();
    }

void CMyDsaAppContainer::ConstructL(const CCoeControl* aParent,
        const TRect& aRect)
    {
    ...
    iDsa = CDirectScreenAccess::NewL(CEikonEnv::Static()->WsSession(),
            *(CEikonEnv::Static()->ScreenDevice()), Window(), *this);
    iDsa->StartL();
    ...
    }

void CMyDsaAppContainer::PaintUsingDsa(CBitmapContext & aGc,
        const TRect & aRect)
    {
    // draw with aGc
    aGc.DrawLine(...);
    ...
    }

void CMyDsaAppContainer::AbortNow(
        RDirectScreenAccess::TTerminationReasons aReason)
    {
    ...
    iDsa->Cancel();
    ...
    }

void CMyDsaAppContainer::Restart(
        RDirectScreenAccess::TTerminationReasons aReason)
    {
    TRect rect = Rect();
    ...
    iDsa->StartL();
    Repaint(rect);
    ...
    }

参考资料:
1) Implementing Direct Screen Access.pdf
 

//


2.双缓冲

2.1介绍
当图片直接绘制到屏幕时,产生动画时。屏幕可能会有抖动闪屏。
常用的一个方法是将图片绘制在off-screen缓冲上,然后在绘制完成后拷贝它的内容到屏幕上。

在绘图量上,双缓冲并不比直接画到屏幕上小,但就显示接口的瓶颈来说,双缓冲比直接调用系统设备绘图要小。使用双缓冲的一个主要目的就是为了减小显示的瓶颈问题,从而达到图像不闪烁。如果频繁使用多缓冲,而不加以组织和限制的话,那么图像还是要闪烁的,而瓶颈问题还是存在的。绘图相对于CPU来说还是很慢的,所以要使用双缓冲。

直接往屏幕上画图的速度是很慢的。这个问题在制作动画的时候尤其明显,因为我们可能要在一秒中之内刷新屏幕几十次。如果按每秒二十五屏的速度来刷新的话,就意味着我们要在一秒中之内画一千多个小方块!
而Double Buffering的办法可以解决这个问题。这是因为:
1、写内存远比写屏(IO)快多了
2、无论所画图形有多复杂,我们只需要做一次IO操作

Symbian中的图形设备:

图形设备描述
CGraphicsDevice图形设备的基类
CBitmapDevice位图化图形设备的基类
CFbsDevice使用字体位图服务器的图形设备基类
CPrinterDevice具有打印功能的设备基类
CWsScreenDevice使用窗口服务器的屏幕设备
CFbsBitmapDevice使用字体位图服务器的设备具体实现
CFbsScreenDevice使用直接屏幕访问(DSA),而不通过窗口服务器


Symbian中的绘图上下文的关系:


其中CWindowsGc用于屏幕绘制,CFbsBitGc则用于内存绘制。

只要能配合显示设备的速度,抵消显示瓶颈问题,就不会引起屏幕闪烁,双缓冲只是一个比较好的技术办法而已。当在运动物体动态模糊处理上,可能使用的就不是双缓冲了,而是多缓冲并存了,不过目前Symbian游戏中好像还没有用到这个技术,也许是这个技术对CPU要求过高的原因。


2.2实现方式

#include <W32STD.H> // ws32.lib
    
// protected
void CMyOffScreenPainter::ConstructL()
    {
    iOffScreenBmp = new (ELeave) CWsBitmap(CEikonEnv::Static()->WsSession());
    
    CWsScreenDevice* screenDev = CEikonEnv::Static()->ScreenDevice();
    
    User::LeaveIfError(iOffScreenBmp->Create(screenDev->SizeInPixels(),
            screenDev->DisplayMode()));
    
    iOffScreenDev = CFbsBitmapDevice::NewL(iOffScreenBmp);
    
    
// iOffScreenGc is an instance of CFbsBitGc
    User::LeaveIfError(iOffScreenDev->CreateContext(iOffScreenGc));
    ...
// other code
    }

void CMyOffScreenPainter::SizeChanged()
    {
    TRect rect = Rect();
    User::LeaveIfError(iOffScreenBmp->Resize( rect.Size() ));
    User::LeaveIfError(iOffScreenDev->Resize( rect.Size() ));
    iOffScreenGc->Resized();
    ...
// other code
    }

void CMyOffScreenPainter::DrawWithOffScreenBmp()
    {
    iOffScreenGc->DrawArc(...);
    ...
// some code about drawing
    }

void CMyOffScreenPainter::Draw(const TRect & /*aRect*/)
    {
    CWindowGc & gc = SystemGc();
    
    
// draw the offscreen bmp to srcreen
    gc.BitBlt( Position(), iOffScreenBmp);
    ...
// other code
    }

CMyOffScreenPainter::~CMyOffScreenPainter()
    {
    delete iOffScreenGc; iOffScreenGc = NULL;
    delete iOffScreenDev; iOffScreenDev = NULL;
    delete iOffScreenBmp; iOffScreenBmp = NULL;
    ...
// other code
    }

这篇关于DSA(直接写屏)和双缓冲的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/686662

相关文章

[项目][CMP][直接向堆申请页为单位的大块内存]详细讲解

目录 1.系统调用 1.系统调用 Windows和Linux下如何直接向堆申请页为单位的大块内存: VirtualAllocbrk和mmap // 直接去堆上按页申请空间static inline void *SystemAlloc(size_t kpage){#ifdef _WIN32void *ptr = VirtualAlloc(0, kpage << 13,

PageOfficeCtrl支持直接打开服务器磁盘文件

一般来说,PageOfficeCtrl控件的WebOpen方法的第一个参数是待打开文档的URL,此URL可以是相对于当前页面的相对URL,也可以是相对于整个网站根的相对URL,还可以是http开头的完整URL,但是这个URL必须是当前网站的URL,不能跨域。 现在为了更加方便开发者编程,WebOpen支持打开服务器磁盘文件。也就是说,第一个参数可以写成服务器文件的绝对磁盘路径。例如: P

最直接显示 ubuntu 版本号的命令

有时候去看ubuntu版本号,去网上查,很多文章都列出一堆命令,复制命令运行一下,都是打印一些不相关的信息,我只是想看ubuntu版本号而已,能否直接列出版本号就可以了。 有,下面这条命令就是直接的打印出ubuntu版本号, 没有多余信息 lsb_release -a

【UVA】10651-Pebble Solitaire(直接递归或者记忆化)

不知道这个题UVA的数据是怎么的,用2个方法交了,第一次直接递归,第二次记忆化剪枝,时间竟然一样!? 直接郁闷了,简单的二进制表示状态和二进制运算。 14145176 10651 Pebble Solitaire Accepted C++ 0.009 2014-09-04 09:18:21 #include<cstdio>#include<algorithm>#inclu

Anthropic 创始人 Dario Amodei 谈:关于护城河与风险,AI 大很难直接替代人

护城河的迷思   近期,Anthropic创始人Dario Amodei与投资人Erik Torenberg进行了一场引人关注的对话。他们探讨了AI的护城河与潜在风险。话说,护城河就像酒水的保质期,过了时间就得小心别翻车。Amodei提到,AI虽有强大的潜力,但短期内难以完全替代人类的智慧。这可让很多人松了一口气,毕竟机器发热总比人心复杂,听着都觉得不舒服。 聪明与控制的博弈   Dar

GitHub:代码是程序员沟通最直接的手段

如果不是 Andreessen horowitz 的投资,估计 GitHub 很难被福布斯、CNN、纽约时报等传统媒体注意到。普通大众之前不了解这个工具,是因为它距离记者的世界太远了——GitHub 是一个程序员所使用的托管项目的服务。 但在一些程序员眼里,它不仅是托管项目的地方,还是“开源”项目的大本营,而且是提高程序员“技术水平”和“技术品味”的地方,更是一个程序员社交的地方。

Base64编码 及 在HTML中用Base编码直接显示图片或嵌入其他文件类型

1.为什么要用到BASE64编码的图片信息      Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一。Base64 主要不是加密,它主要的用途是把一些二进制数转成普通字符用于网络传输。由于一些二进制字符在传输协议中属于控制字符,不能直接传送需要转换一下。最常见的用途是作为电子邮件或WebService附件的传输编码.  2.base64编码定义    目前的internet

工作集、granule、缓冲区、缓冲池概念及关系?

工作集、granule、缓冲区、缓冲池概念及关系? granule:为了让内存在db_chache_size和shared_pool_size之间高效的移动,oracle在9i重构SGA,使用固定大小的内存块即为granule。这个参数就是为什么当你分配给shared pool值的时候,为什么有时候比你分配的值要大一点,但是granule的整数倍。 缓冲区:内存存放数据的地方,类似于数

Java虚拟机--直接内存

文章引用: 深入理解Java虚拟机 直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域.但是这部分内存也被频繁的使用,而且也可能导致OutOfMemoryError异常出现. 在JDK1.4中新加入NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式

图形API学习工程(8):使用顶点索引缓冲

工程GIT地址:https://gitee.com/yaksue/yaksue-graphics 目标 在《图形API学习工程(5):图形管线&顶点缓冲》中,实现了渲染出一个三角形。 他有三个顶点。但是考虑图形变得复杂些的情况,就假如是一个四边形吧,那就需要分解为两个三角形来渲染了,而每个三角形需要三个顶点,也就是说,共需要6个顶点数据。 然而,如上图所示,实际上顶点是有重复的:0和3重复