前腐后继 发表于 2006-12-8 19:55:00

[转帖]关于DX多窗口编程的一篇翻译

<strong>&nbsp;<br/><br/></strong>Introduction <p></p><p>In&nbsp;DirectX&nbsp;8,&nbsp;support&nbsp;for&nbsp;rendering&nbsp;to&nbsp;multiple&nbsp;windows&nbsp;is&nbsp;provided&nbsp;through&nbsp;the&nbsp;creation&nbsp;of&nbsp;additional&nbsp;swap&nbsp;chains.&nbsp;&nbsp;However,&nbsp;there&nbsp;are&nbsp;currently&nbsp;no&nbsp;examples&nbsp;of&nbsp;this&nbsp;in&nbsp;the&nbsp;SDK,&nbsp;and&nbsp;the&nbsp;documentation&nbsp;is&nbsp;a&nbsp;bit&nbsp;vague.&nbsp;&nbsp;This&nbsp;article&nbsp;is&nbsp;provided&nbsp;to&nbsp;fill&nbsp;the&nbsp;gaps,&nbsp;and&nbsp;will&nbsp;explain&nbsp;the&nbsp;steps&nbsp;you&nbsp;need&nbsp;to&nbsp;take&nbsp;to&nbsp;write&nbsp;an&nbsp;application&nbsp;that&nbsp;will&nbsp;render&nbsp;multiple&nbsp;views&nbsp;in&nbsp;separate&nbsp;windows.</p><p>在DX8中,对多窗口的支持是通过创建更多的Swap&nbsp;Chains来提供的。SDK中没有相关的例子而且文档也只是泛泛而谈。这篇文章就是为了解决这个问题,它将向您展示应当如何一步步地实现在多个分离窗口中渲染多个视图。<br/>&nbsp;<br/>Step&nbsp;1&nbsp;-&nbsp;Setting&nbsp;Up&nbsp;The&nbsp;Parent&nbsp;Frame</p><p>第一步:设置父框架窗口</p><p>In&nbsp;an&nbsp;application&nbsp;with&nbsp;multiple&nbsp;views,&nbsp;we&nbsp;start&nbsp;with&nbsp;a&nbsp;top&nbsp;level&nbsp;frame&nbsp;that&nbsp;will&nbsp;contain&nbsp;child&nbsp;windows&nbsp;in&nbsp;its&nbsp;client&nbsp;area&nbsp;to&nbsp;display&nbsp;various&nbsp;views.&nbsp;&nbsp;Once&nbsp;the&nbsp;parent&nbsp;frame&nbsp;parent&nbsp;frame&nbsp;has&nbsp;been&nbsp;created,&nbsp;we&nbsp;create&nbsp;our&nbsp;Direct3D&nbsp;device&nbsp;interface,&nbsp;specifying&nbsp;windowed&nbsp;mode&nbsp;and&nbsp;setting&nbsp;the&nbsp;top&nbsp;level&nbsp;window&nbsp;handle&nbsp;as&nbsp;the&nbsp;focus&nbsp;window:</p><p>在多视图的应用程序中,我们需要从最高层次的框架——这个框架将包含所有在他用户区之内的子视图窗口——开始我们的旅程。当父框架创建的时候,我们需要创建Direct3D&nbsp;Device接口,为其指定使用窗口模式,而且设置这最高层次的窗口句柄作为“焦点窗口”的句柄:</p><p>g_pD3D=Direct3DCreate8(D3D_SDK_VERSION);</p><p>if&nbsp;(!g_pD3D)&nbsp;return&nbsp;-1;</p><p>D3DPRESENT_PARAMETERS&nbsp;d3dpp;&nbsp;</p><p>ZeroMemory(&nbsp;&amp;d3dpp,&nbsp;sizeof(d3dpp)&nbsp;);</p><p>d3dpp.Windowed&nbsp;=&nbsp;TRUE;</p><p>d3dpp.SwapEffect&nbsp;=&nbsp;D3DSWAPEFFECT_COPY;</p><p>//&nbsp;Use&nbsp;the&nbsp;current&nbsp;display&nbsp;mode.&nbsp;使用当前的显示模式</p><p>D3DDISPLAYMODE&nbsp;mode;</p><p>if(FAILED(g_pD3D-&gt;GetAdapterDisplayMode(D3DADAPTER_DEFAULT&nbsp;,&nbsp;&amp;mode)))&nbsp;{</p><p>&nbsp;&nbsp;&nbsp;&nbsp;SAFE_RELEASE(g_pD3D);</p><p>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;-1;</p><p>}</p><p>d3dpp.BackBufferFormat&nbsp;=&nbsp;mode.Format;</p><p>d3dpp.BackBufferWidth&nbsp;=&nbsp;mode.Width;</p><p>d3dpp.BackBufferHeight&nbsp;=&nbsp;mode.Height;</p><p>d3dpp.EnableAutoDepthStencil=TRUE;</p><p>d3dpp.AutoDepthStencilFormat&nbsp;=&nbsp;D3DFMT_D16;</p><p>//&nbsp;m_hWnd&nbsp;is&nbsp;handle&nbsp;to&nbsp;top&nbsp;level&nbsp;window&nbsp;&nbsp;&nbsp;&nbsp;m_hWnd是最高层窗口的句柄</p><p>if(&nbsp;FAILED(&nbsp;g_pD3D-&gt;CreateDevice(&nbsp;D3DADAPTER_DEFAULT,&nbsp;D3DDEVTYPE_HAL,&nbsp;m_hWnd,&nbsp;D3DCREATE_SOFTWARE_VERTEXPROCESSING,</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;d3dpp,&nbsp;&amp;g_pd3dDevice)&nbsp;)&nbsp;)&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;SAFE_RELEASE(g_pD3D);</p><p>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;-1;</p><p>Note&nbsp;that&nbsp;for&nbsp;simplicity&nbsp;the&nbsp;above&nbsp;code&nbsp;does&nbsp;not&nbsp;test&nbsp;depth&nbsp;format,&nbsp;instead&nbsp;choosing&nbsp;a&nbsp;fixed&nbsp;format.&nbsp;&nbsp;Your&nbsp;application&nbsp;should&nbsp;determine&nbsp;a&nbsp;compatible&nbsp;depth&nbsp;format&nbsp;for&nbsp;the&nbsp;format&nbsp;of&nbsp;the&nbsp;rendering&nbsp;target.</p><p>注意上面代码处于简单考虑并没有去测试深度缓存的格式(?depth&nbsp;format),而只是选择了一个确定的格式(D3DFMT_D16)。您的程序应该为需要渲染的Render&nbsp;Target选择一个可接受的深度缓存格式。</p><p>The&nbsp;device&nbsp;has&nbsp;a&nbsp;frame&nbsp;buffer,&nbsp;which&nbsp;the&nbsp;child&nbsp;views&nbsp;will&nbsp;be&nbsp;rendered&nbsp;into,&nbsp;as&nbsp;well&nbsp;as&nbsp;a&nbsp;depth&nbsp;buffer&nbsp;which&nbsp;will&nbsp;be&nbsp;shared&nbsp;among&nbsp;the&nbsp;views.&nbsp;&nbsp;The&nbsp;frame&nbsp;buffer&nbsp;and&nbsp;depth&nbsp;buffer&nbsp;are&nbsp;sized&nbsp;to&nbsp;the&nbsp;full&nbsp;screen&nbsp;resolution,&nbsp;to&nbsp;allow&nbsp;for&nbsp;the&nbsp;fact&nbsp;that&nbsp;the&nbsp;window&nbsp;may&nbsp;later&nbsp;be&nbsp;resized.&nbsp;&nbsp;Otherwise,&nbsp;window&nbsp;size&nbsp;changes&nbsp;would&nbsp;require&nbsp;resetting&nbsp;the&nbsp;device&nbsp;and&nbsp;re-creating&nbsp;the&nbsp;swap&nbsp;chains.</p><p>Device都需要有帧缓存,这样子视图才能进行渲染,同时,深度缓冲也应当被不同的视图进行共享。帧缓存和深度缓存都被设置为全屏幕大小,以考虑到可能窗口会被改变大小的情况。如果不的话,窗口改变大小的时候,就需要Reset&nbsp;Device和重新创建Swap&nbsp;Chain。</p><p>Step&nbsp;2&nbsp;-&nbsp;Setting&nbsp;Up&nbsp;View&nbsp;Windows&nbsp;</p><p>第二步:设置子视图窗口</p><p>Now&nbsp;we&nbsp;are&nbsp;ready&nbsp;to&nbsp;create&nbsp;our&nbsp;view&nbsp;windows,&nbsp;and&nbsp;associate&nbsp;them&nbsp;with&nbsp;swap&nbsp;chains&nbsp;that&nbsp;can&nbsp;be&nbsp;rendered&nbsp;to&nbsp;the&nbsp;device.&nbsp;&nbsp;Once&nbsp;the&nbsp;windows&nbsp;have&nbsp;been&nbsp;created,&nbsp;the&nbsp;following&nbsp;code&nbsp;generates&nbsp;a&nbsp;swap&nbsp;chain&nbsp;for&nbsp;the&nbsp;child&nbsp;window:</p><p>现在我们可以准备创建我们的子窗口也就是视图窗口,并把它们与交换链关联以使得他们可以被渲染到Device上。当窗口创建后,下面的代码将为子窗口创建一个交换链:</p><p>D3DPRESENT_PARAMETERS&nbsp;d3dpp;&nbsp;</p><p>ZeroMemory(&nbsp;&amp;d3dpp,&nbsp;sizeof(d3dpp)&nbsp;);</p><p>d3dpp.Windowed&nbsp;=&nbsp;TRUE;</p><p>d3dpp.SwapEffect&nbsp;=&nbsp;D3DSWAPEFFECT_COPY;</p><p>//&nbsp;Use&nbsp;the&nbsp;current&nbsp;display&nbsp;mode.&nbsp;&nbsp;使用当前的显示模式</p><p>D3DDISPLAYMODE&nbsp;mode;</p><p>g_pD3D-&gt;GetAdapterDisplayMode(D3DADAPTER_DEFAULT&nbsp;,&nbsp;&amp;mode);</p><p>d3dpp.BackBufferFormat&nbsp;=&nbsp;mode.Format;</p><p>//&nbsp;m_hWnd&nbsp;contains&nbsp;child&nbsp;window&nbsp;handle&nbsp;&nbsp;m_hWnd储存子窗口的句柄</p><p>d3dpp.hDeviceWindow=m_hWnd;</p><p>//&nbsp;m_pSwapChain&nbsp;is&nbsp;IDirect3DSwapChain&nbsp;*&nbsp;&nbsp;&nbsp;m_pSwapChain是一个IDirect3DSwapChain*对象</p><p>g_pd3dDevice-&gt;CreateAdditionalSwapChain(&amp;d3dpp,&nbsp;&amp;m_pSwapChain);</p><p>After&nbsp;executing&nbsp;this&nbsp;code,&nbsp;the&nbsp;m_pSwapChain&nbsp;variable&nbsp;will&nbsp;contain&nbsp;a&nbsp;pointer&nbsp;to&nbsp;an&nbsp;IDirect3DSwapChain&nbsp;interface,&nbsp;which&nbsp;contains&nbsp;a&nbsp;frame&nbsp;buffer&nbsp;corresponding&nbsp;to&nbsp;the&nbsp;client&nbsp;area&nbsp;of&nbsp;the&nbsp;child&nbsp;window.&nbsp;&nbsp;This&nbsp;process&nbsp;is&nbsp;performed&nbsp;for&nbsp;each&nbsp;view&nbsp;window,&nbsp;so&nbsp;that&nbsp;that&nbsp;there&nbsp;is&nbsp;a&nbsp;swap&nbsp;chain&nbsp;for&nbsp;each&nbsp;view&nbsp;window.</p><p>经过这些代码之后,m_pSwapChain变量就储存了IDirect3DSwapChain接口的指针,这个接口将储存子窗口视图区所对应的帧缓冲。</p><p>Step&nbsp;3&nbsp;-&nbsp;Rendering&nbsp;a&nbsp;View</p><p>第三步:渲染视图</p><p>Prior&nbsp;to&nbsp;rendering&nbsp;each&nbsp;view,&nbsp;we&nbsp;must&nbsp;direct&nbsp;the&nbsp;device&nbsp;to&nbsp;render&nbsp;to&nbsp;the&nbsp;appropriate&nbsp;frame&nbsp;buffer,&nbsp;using&nbsp;the&nbsp;SetRenderTarget()&nbsp;method.&nbsp;&nbsp;We&nbsp;pass&nbsp;the&nbsp;back&nbsp;buffer&nbsp;from&nbsp;the&nbsp;window's&nbsp;swap&nbsp;chain,&nbsp;while&nbsp;using&nbsp;the&nbsp;depth&nbsp;buffer&nbsp;that&nbsp;was&nbsp;originally&nbsp;created&nbsp;with&nbsp;the&nbsp;device:</p><p>在渲染每个视图窗口之前,我们必须使得Device来渲染对应的帧缓冲,这我们就需要用到SetRenderTarget方法。我们向其中传入子窗口SwapChain交换链的后备缓冲BackBuffer,以及使用最开始跟着Device一起创建的深度缓冲。</p><p>LPDIRECT3DSURFACE8&nbsp;pBack=NULL,pStencil=NULL;</p><p>m_pSwapChain-&gt;GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&amp;pBack);</p><p>g_pd3dDevice-&gt;GetDepthStencilSurface(&amp;pStencil);</p><p>g_pd3dDevice-&gt;SetRenderTarget(pBack,pStencil);</p><p>pBack-&gt;Release();</p><p>pStencil-&gt;Release();</p><p>Note&nbsp;that&nbsp;we&nbsp;release&nbsp;the&nbsp;stencil&nbsp;and&nbsp;backbuffer&nbsp;pointers&nbsp;after&nbsp;we&nbsp;use&nbsp;them,&nbsp;because&nbsp;the&nbsp;GetBackBuffer()&nbsp;and&nbsp;GetDepthStencilSurface()&nbsp;functions&nbsp;call&nbsp;AddRef()&nbsp;on&nbsp;these&nbsp;interfaces&nbsp;to&nbsp;increment&nbsp;their&nbsp;reference&nbsp;counters.&nbsp;&nbsp;Failing&nbsp;to&nbsp;release&nbsp;them&nbsp;would&nbsp;lead&nbsp;to&nbsp;a&nbsp;memory&nbsp;leak.</p><p>注意我们必须Release掉Stencil和BackBuffer的指针,因为GetBackBuffer和GetDepthStencilSurface这两个函数都会调用COM的AddRef方法,来增加相应COM接口的引用计数,因此如果不删除它们,将会导致内存泄露。</p><p>We&nbsp;are&nbsp;now&nbsp;ready&nbsp;to&nbsp;render&nbsp;the&nbsp;view.&nbsp;&nbsp;Rendering&nbsp;is&nbsp;performed&nbsp;within&nbsp;a&nbsp;scene&nbsp;in&nbsp;the&nbsp;normal&nbsp;manner,&nbsp;except&nbsp;that&nbsp;we&nbsp;call&nbsp;Present()&nbsp;on&nbsp;the&nbsp;swap&nbsp;chain&nbsp;interface&nbsp;rather&nbsp;than&nbsp;the&nbsp;device&nbsp;interface:</p><p>我们现在已经做好准备渲染视图窗口了。渲染的方法看起来和我们平常用的方法差不多,只是有一点:我们现在需要调用Swap&nbsp;Chain的接口,而不是Device的接口。</p><p>g_pd3dDevice-&gt;Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,0x00000000,1.0,0);</p><p>if&nbsp;(SUCCEEDED(g_pd3dDevice-&gt;BeginScene()))&nbsp;{</p><p>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;rendering&nbsp;code&nbsp;goes&nbsp;here&nbsp;&nbsp;渲染代码写在这里</p><p>&nbsp;&nbsp;&nbsp;&nbsp;g_pd3dDevice-&gt;EndScene();</p><p>}</p><p>m_pSwapChain-&gt;Present(NULL,NULL,NULL,NULL);</p><p><br/>Step&nbsp;4&nbsp;-&nbsp;Handling&nbsp;Resize&nbsp;of&nbsp;Child&nbsp;Views</p><p>第四步,子窗口的Resize问题</p><p>DirectX&nbsp;will&nbsp;automatically&nbsp;deal&nbsp;with&nbsp;changes&nbsp;in&nbsp;the&nbsp;child&nbsp;view&nbsp;by&nbsp;using&nbsp;a&nbsp;stretch&nbsp;blit&nbsp;to&nbsp;present&nbsp;the&nbsp;swap&nbsp;chain&nbsp;if&nbsp;the&nbsp;dimensions&nbsp;have&nbsp;client&nbsp;area&nbsp;is&nbsp;not&nbsp;the&nbsp;same&nbsp;size&nbsp;as&nbsp;the&nbsp;swap&nbsp;chain's&nbsp;frame&nbsp;buffer.&nbsp;&nbsp;However,&nbsp;this&nbsp;may&nbsp;not&nbsp;be&nbsp;desirable,&nbsp;as&nbsp;it&nbsp;will&nbsp;cause&nbsp;aliasing&nbsp;if&nbsp;the&nbsp;client&nbsp;area&nbsp;is&nbsp;increased&nbsp;in&nbsp;size.</p><p>如果窗口的视图区大小和SwapChain的大小不一,那么DirectX将通过Stretch&nbsp;Blit来自动处理图像的伸缩变化。尽管这可能并不令人期待,因为这在视图区变大的时候将导致图像的模糊。</p><p>To&nbsp;prevent&nbsp;this,&nbsp;you&nbsp;can&nbsp;write&nbsp;a&nbsp;handler&nbsp;for&nbsp;the&nbsp;WM_SIZE&nbsp;message&nbsp;of&nbsp;the&nbsp;child&nbsp;window.&nbsp;&nbsp;The&nbsp;handler&nbsp;should&nbsp;release&nbsp;the&nbsp;existing&nbsp;swap&nbsp;chain,&nbsp;and&nbsp;create&nbsp;a&nbsp;new&nbsp;swap&nbsp;chain&nbsp;using&nbsp;the&nbsp;code&nbsp;from&nbsp;Step&nbsp;2.</p><p>如果要解决这个问题,您需要为子窗口的WM_SIZE消息写一段处理代码,这段代码Release已经存在的Swap&nbsp;Chain交换链,并且使用第二步中的代码创建一个崭新的Swap&nbsp;Chain交换链。</p>
页: [1]
查看完整版本: [转帖]关于DX多窗口编程的一篇翻译