屏幕双缓冲和DSA

⑴图形设置和图形上下文

       应用程序绘制图形时,必须要使用图形设备和图形上下文。

       图形设备是绘制操作的对象(如屏幕、打印机等),图形设备上下文提供了一种设备的抽象机制从而完全屏蔽了具体的图形设备,应用程序在使用这些图形设备时只需要和这些抽象的图形设备类交互即可,而不必考虑其具体的设备。

       图形上下文是绘画平台及绘画所需要工具的集合体,它还包括平台的尺寸、方向、颜色和所有能实现绘画想象力的附件。

       下面为Symbian中使用的图形设备及其功能,它们是实现绘画的基础。

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

      这些设备类的继承关系如下: 

 

   CWindowsGc用于屏幕绘制,CFbsBitGc则用于内存绘制!

⑵双缓冲

      应用程序在屏幕上的描画一般是使用CWsScreenDevice图形设备来完成,与CWindowGc图形上下文相关联。CONE提供了一个CWindowGc实例作为描画控件的标准图形上下文。它被CCoeEnv创建并且可以使用CCoeControls::SystemGc()方法访问。 CWindowGc的描画方法在客户端窗口服务器缓冲区上进行缓冲。

      

void CExampleControl::Draw( const TRect& /*aRect*/ ) const
{
 // Get the system graphics context
 CWindowGc& gc = SystemGc();
 // Set drawing settings
 gc.SetBrushStyle( CGraphicsContext::ESolidBrush );
 gc.SetBrushColor( KRgbRed );
 // Draw
 gc.DrawLine( TPoint(10,10), TPoint(30,10) );

        DrawNow()强制控件立即重画自己,而DrawDeferred()导致一个重画事件使用低优先级操作!

        如果一个游戏的图形由多个需要被经常更新的运动对象组成,窗口服务器的客户端缓冲可能被充满并且可能会在所有对象都更新的时候溢出,用户可能会发现屏幕出现闪烁。如果一个视图仍然在更新的时候,可能会出现闪烁或者其他不希望的效果。这些问题的解决方案是双缓冲,图形先被画在一个屏外位图上,然后被画到屏幕上作为一个单一窗口服务器操作。尤其是对于那种在一秒钟内重画几次屏幕的游戏,使用屏外位图可以改善它们的性能。

 

  一个屏外位图可以使用位图化的图形上下文和图形设备类来创建:CFbsBitGc和CFbsBitmapDevice。它们使用其他的上下文和设备类来创建和使用。为了获得额外的性能,位图自己就应该是一个CWsBitmap位图。在屏外位图更新之后,它可以使用正常的窗口服务器的描画方法画在窗口中。

  当一个应用程序在一个窗口画位图时,它转化为和窗口相同的显示模式。这是一个很消耗时间的操作,实质上可能降低描画的速度。因此把位图用于动画的游戏应该在动画开始之前就完成转化。转化可以通过使用一个屏外位图来执行,如下面的示例方法演示:

CFbsBitmap* CExampleControl::LoadAndConvertBitmapL( Const TDesC& aFileName, TInt aBitmapId )
{
 // Load the bitmap
 CFbsBitmap* originalBitmap = new ( ELeave ) CFbsBitmap();
 CleanupStack::PushL( originalBitmap );
 User::LeaveIfError( originalBitmap->Load( aFileName, aBitmapId, EFalse ) );


 // Create a new bitmap, graphics device and context
 CFbsBitmap* newBitmap = new ( ELeave ) CFbsBitmap();
 CleanupStack::PushL( newBitmap );
 newBitmap->Create( originalBitmap->SizeInPixels(), Window()->DisplayMode() );
 CFbsBitmapDevice* graphicsDevice = CFbsBitmapDevice::NewL(newBitmap);
 CleanupStack::PushL( graphicsDevice );
 CFbsBitGc* graphicsContext;
 User::LeaveIfError( graphicsDevice->CreateContext( graphicsContext ) );
 TPoint zero(0,0);

 // Blit the loaded bitmap to the new bitmap
 graphicsContext->BitBlt( zero, originalBitmap );
 CleanupStack::Pop(3);
 delete graphicsContext;
 delete graphicsDevice ;
 delete originalBitmap;
 return newBitmap;

  示例方法使用一个文件名和位图ID作为参数,并且从一个MBM文件中装载相应的位图。如果一个游戏有许多位图应该转化,那么应该在游戏或者等级的初始化阶段转化,因此用户就不会看到这个操作了。

⑵直接屏幕访问(DSA)

  使用窗口服务器在屏幕上描画需要一个上下文转换,这会减慢描画速度。为了绕过窗口服务器省去繁琐的上下文转换,可以直接访问屏幕。这被称作直接屏幕访问。

        在Symbian OS中有两种方法来直接在屏幕上描画:

  CFbsScreenDevice是一个可以被发送到屏幕驱动程序SCDV.DLL的图形设备。在创建一个CFbsBitGc图形上下文之后,它能像任何其他的图形设备一样使用。然而,可以直接在屏幕上描画,而不需要使用窗口服务器。

        直接在屏幕上描画的另一种方法是从系统中查询屏幕内存地址,这可以使用UserSrv类来实现:

TPckgBuf<TScreenInfoV01> infoPckg;
TScreenInfoV01& screenInfo = infoPckg();
UserSvr::ScreenInfo(infoPckg);
TUint16* screenMemory = screenInfo.iScreenAddress + 16;
  屏幕内存有一个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 

  这两种直接描画方法的一个共同的问题是窗口服务器不了解描画,因此它不能通知应用程序是否出现另一个窗口或者窗口组。 即使当应用程序失去焦点的时候得到一个事件,它们也不能停止直接描画,因为直接描画实在太快了,并且屏幕内容有可能被弄乱。 这可能发生在玩游戏的时候,突然有电话打进来的情况下。

 
        新近的GT 6.1版本提供了一个应用编程接口用于直接描画,将能解决前面提到的问题

        这个应用编程接口由两个类组成:一个MDirectScreenAccess类,提供用于应用程序的回调方法,还有一个CDirectScreenAccess类处理与窗口服务器的通讯。 下面的代码说明CDirectScreenAccess实例是如何构造的,以及直接描画支持是如何激活的。

iDrawer = CDirectScreenAccess::NewL(iEikonEnv->WsSession(), *iEikonEnv->ScreenDevice(), Window(), *this);
iEikonEnv->WsSession().Flush();
iDrawer->StartL();
iDrawer->ScreenDevice()->SetAutoUpdate(ETrue); 

  CDirectScreenAccess的NewL方法获得一个窗口服务器会话CONE的图形设备应用程序窗口一个到MDirectedScreenAccess导出类的指针作为参数。 在CDirectScreenAccess::StartL被调用来激活直接描画支持之前,客户端窗口服务器缓冲应该溢出。 为了能自动更新屏幕,屏幕设备的SetAutoUpdate方法需要使用ETrue参数,否则gc draw指令不会在模拟器中立即显示,而是在下一次冲刷(Flush)视窗服务器时才显示。 当直接描画支持激活的时候,CDirectScreenAccess产生一个CFbsBitGc图形上下文,可以被应用程序用来在屏幕上绘画。

iDrawer->Gc()->BitBlt( TPoint(0,0), iBitmap ); 

  当另一个窗口出现在应用程序窗口上时,CDirectScreenAccess从窗口服务器取得一个事件来中断描画。 CDirectScreenAccess然后调用MDirectScreenAccess派生类的AbortNow方法,这个方法必须被应用程序重载以便中断描画。 为了防止屏幕被弄乱,窗口服务器直到中断描画事件被处理的时候才画重叠窗口。

 
<think> 我们正在讨论的是在Linux系统中使用双网卡(两个以太网控制器)注册DSA(分布式交换机架构)交换芯片设备。DSA允许将多个物理网口组织成一个交换机,并通过一个或多个CPU端口连接到主机系统。 用户之前的问题是关于注册两台DSA交换芯片,现在用户想了解双网卡设备(即主机有两个独立的网络接口)如何用于DSA配置。 在DSA架构中,通常有一个或多个CPU端口,这些端口连接到主机的以太网控制器(网卡)。当主机有两个网卡时,这两个网卡可以被配置为两个独立的DSA交换机的CPU端口,或者用于同一个DSA交换机的多个CPU端口(例如,用于负载均衡或冗余)。 以下是双网卡设备注册DSA的几种可能配置方法: 1. **两个独立的DSA交换机**:每个网卡作为一个DSA交换机的CPU端口。 - 这种情况下,每个交换机都有自己的DSA节点,分别连接到对应的网卡。 - 设备树配置示例(简化): ```dts /* 第一个DSA交换机连接到第一个网卡 */ switch0: switch@0 { compatible = "vendor,switch0"; reg = <0>; ports { port@0 { reg = <0>; label = "cpu"; ethernet = <&eth0>; // 连接到第一个网卡 }; /* 其他端口... */ }; }; /* 第二个DSA交换机连接到第二个网卡 */ switch1: switch@1 { compatible = "vendor,switch1"; reg = <1>; ports { port@0 { reg = <0>; label = "cpu"; ethernet = <&eth1>; // 连接到第二个网卡 }; /* 其他端口... */ }; }; ``` 2. **同一个DSA交换机使用两个CPU端口**:某些高端交换机芯片支持多个上行端口(CPU端口)。这样,两个网卡可以连接到同一个交换机的两个不同的CPU端口,实现负载均衡或冗余。 - 设备树配置示例(简化): ```dts switch0: switch@0 { compatible = "vendor,switch0"; reg = <0>; ports { port@0 { reg = <0>; label = "cpu"; ethernet = <&eth0>; // 第一个网卡 // 可以指定为同一个交换机的一个CPU端口 }; port@1 { reg = <1>; label = "cpu"; ethernet = <&eth1>; // 第二个网卡 // 同一个交换机的第二个CPU端口 }; /* 其他端口... */ }; }; ``` 注意:并不是所有的交换机驱动都支持多个CPU端口,需要查阅具体交换芯片的数据手册驱动支持情况。 3. **级联的DSA交换机**:两个网卡分别作为两个交换机的CPU端口,然后这两个交换机通过级联端口连接。 - 设备树配置示例(简化): ```dts /* 第一个交换机连接到第一个网卡 */ switch0: switch@0 { compatible = "vendor,switch0"; reg = <0>; ports { port@0 { reg = <0>; label = "cpu"; ethernet = <&eth0>; // 第一个网卡 }; /* 级联端口 */ port@5 { reg = <5>; label = "dsa"; link = <&switch1>; // 指向第二个交换机 }; }; }; /* 第二个交换机连接到第二个网卡 */ switch1: switch@1 { compatible = "vendor,switch1"; reg = <1>; ports { port@0 { reg = <0>; label = "cpu"; ethernet = <&eth1>; // 第二个网卡 }; /* 级联端口(可选,如果级联是双向的) */ }; }; ``` 这种配置下,两个交换机通过级联端口连接,而主机则通过两个网卡分别连接到两个交换机。这种拓扑结构可以增加端口数量,但需要注意避免环路(通常通过STP协议)。 ### 相关技术要点 1. **DSA驱动加载**: 需要加载DSA核心模块具体的交换机驱动模块。例如: ```bash modprobe dsa_core modprobe mv88e6xxx # 例如Marvell的交换机驱动 ``` 2. **网络接口命名**: - 每个交换机的用户端口(非CPU端口)会被命名为`swpX`(如`swp0s0`, `swp0s1`等)。 - 两个CPU端口分别对应两个网卡,通常命名为`eth0``eth1`(或者根据系统命名规则)。在DSA配置中,这两个网卡不再作为普通网卡使用,而是作为交换机的上行口。 3. **双网卡的使用**: - 在独立交换机配置中,两个网卡分别管理各自的交换机。 - 在多个CPU端口的配置中,两个网卡共同管理同一个交换机,可以实现链路聚合(需要交换机支持)或者作为冗余备份(如使用LACP)。 4. **配置网络**: 配置DSA交换机后,主机上通常不需要直接配置`eth0``eth1`(因为数据包会通过DSA框架从用户端口进出),但需要配置用户端口(如`swp0s0`)的IP地址等。如果需要将两个网卡用于交换机间的通信,可能需要配置桥接或者路由。 ### 验证方法 1. 检查生成的网络接口: ```bash ip link show ``` 应该看到`eth0`, `eth1`(状态为UP),以及一系列`swpX`接口。 2. 查看DSA交换机的状态: ```bash cat /sys/class/net/<dsa-port>/dsa/switch_id ``` 3. 使用`dsa-tool`(如果有的话)查看拓扑。 ### 注意事项 - 设备树中的`ethernet`属性需要指向正确的以太网控制器节点(例如`&eth0`, `&eth1`)。 - 确保内核配置了对应的DSA驱动以及双网卡驱动。 - 级联时需要正确连接硬件,并在设备树中通过`link`属性指定。 关于双网卡注册DSA的更多详细信息,可以参考Linux内核文档:`Documentation/networking/dsa/dsa.rst`。 如果用户需要具体的设备树配置示例(根据具体的硬件),需要知道使用的交换芯片型号主控的SoC。 §§ 相关问题 §§ 1. DSA架构中多个CPU端口的负载均衡如何配置? 2. 如何为DSA交换机配置VLAN以隔离不同的端口? 3. 在级联的DSA交换机中如何防止网络环路? 4. 双网卡在DSA架构下能否实现链路聚合(LACP)? </think> ### 双网卡设备注册 DSA 的方法 在 Linux 系统中为配备**双网卡的主机注册 DSA(分布式交换架构)** 时,核心是通过设备树配置将两个物理网卡绑定为 DSA 交换机的 CPU 端口。以下是关键步骤技术实现: #### 一、设备树配置(核心) ```dts /* 网卡1定义 */ &mac0 { compatible = "vendor,eth-mac"; phy-mode = "sgmii"; dsa,member = <0 0>; // 关联到第一个交换机 }; /* 网卡2定义 */ &mac1 { compatible = "vendor,eth-mac"; phy-mode = "sgmii"; dsa,member = <1 0>; // 关联到第二个交换机 }; /* 第一个DSA交换机 */ switch0: switch@0 { compatible = "marvell,mv88e6190"; // 示例芯片 reg = <0>; ports { port@0 { reg = <0>; label = "cpu"; ethernet = <&mac0>; // 绑定网卡1 }; port@1 { reg = <1>; label = "lan1"; phy-handle = <&phy1>; }; }; }; /* 第二个DSA交换机 */ switch1: switch@1 { compatible = "marvell,mv88e6190"; reg = <1>; ports { port@0 { reg = <0>; label = "cpu"; ethernet = <&mac1>; // 绑定网卡2 }; port@2 { reg = <2>; label = "dsa-link"; link = <&switch0 1>; // 级联到第一个交换机的端口1 }; }; }; ``` #### 二、关键配置说明 1. **双网卡绑定机制** - 通过 `ethernet = <&macX>` 将物理网卡声明为交换机的 CPU 端口 - `dsa,member` 属性标记网卡所属的交换机实例(如 `<1 0>` 表示交换机1的端口0) - 每个交换机独立管理,通过级联端口(`link`属性)连接[^3] 2. **网络数据结构** DSA 框架通过 `net_device` 结构体抽象交换端口,双网卡对应两个独立的 `net_device` 实例: ```c struct net_device { struct dsa_port *dsa_ptr; // 指向DSA端口数据 const struct net_device_ops *netdev_ops; // 操作集 }; ``` 内核为每个交换机端口生成 `swpX` 虚拟接口[^2] 3. **驱动加载流程** ```bash # 加载DSA核心模块 sudo modprobe dsa_core # 加载网卡驱动(例如Intel双网卡) sudo modprobe igb # 加载交换芯片驱动 sudo modprobe mv88e6xxx ``` #### 三、双网卡拓扑示例 ```mermaid graph LR HOST[主机系统] MAC1[网卡1 eth0] --> SW1[交换机1] MAC2[网卡2 eth1] --> SW2[交换机2] SW1 -->|级联端口| SW2 SW1 --> LAN1[设备1] SW2 --> LAN2[设备2] ``` #### 四、验证命令 ```bash # 查看网络接口(应显示eth0, eth1, swp0s0, swp1s0等) ip link show # 检查DSA拓扑 echo "switch0" > /sys/class/net/eth0/dsa/switch_id cat /sys/class/net/eth0/dsa/ports # 查看交换芯片状态 ethtool -m swp0s0 ``` #### 五、应用场景 1. **冗余备份**:双网卡提供链路冗余(需配合LACP协议) 2. **带宽聚合**:合并两个10GbE网卡实现20Gbps上行[^1] 3. **网络分段**:通过VLAN隔离不同交换机的流量 > **技术文档参考**: > - Linux内核文档:`Documentation/networking/dsa/dsa.rst` > - Marvell交换芯片手册:`mv88e6xxx.yaml`(设备树绑定)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值