问题描述
我是DirectX 10编程的新手,我一直在努力用我有限的技能(虽然我有一个强大的背景与OpenGL)。
我试图显示2不同纹理的四边形,每个显示器1。要这样做,我明白我需要一个D3D10设备,多(2)交换链和单个VertexBuffer
虽然我想我可以创建所有的那些,我还是很不确定如何处理所有的。我需要多个ID3D10RenderTargetView吗?如何和在哪里使用OMSetRenderTargets(...)?
除了MSDN,这些概念的文档或解释是相当有限的,所以任何帮助将是非常欢迎。这是一些代码我有:
这是渲染代码
现在,我们的友好交换链...你可能习惯于通过调用naked函数来创建它们 D3D10CreateDeviceAndSwapChain (...),但正如你所知,我们已经制作了我们的设备。我们只想要一个。和多个互换链。好吧,这是一个泡菜。幸运的是,我们的DXGIFactory接口在其生产线上有交换链,我们可以免费获得补充的朗姆酒桶。然后,为每个窗口创建一个:
void CreateSwapChainsAndViews()
{
(int i = 0; i {
WindowDataContainer * window = windowsArray.at(i);
//获取dxgi设备
IDXGIDevice * DXGIDevice = NULL;
device-> QueryInterface(IID_IDXGIDevice,(void **)& DXGIDevice); // COM的东西,希望你熟悉
//创建一个交换链
DXGI_SWAP_CHAIN_DESC swapChainDesc;
//填入
HRESULT hr = factory-> CreateSwapChain(DXGIDevice,& swapChainDesc,& p_Window-> swapChain);
DXGIDevice-> Release();
DXGIDevice = NULL;
//获取backbuffer
ID3D10Texture2D * backBuffer = NULL;
hr = window-> swapChain-> GetBuffer(0,IID_ID3D10Texture2D,(void **)& backBuffer);
//获取backbuffer desc
D3D10_TEXTURE2D_DESC backBufferDesc;
backBuffer-> GetDesc(& backBufferDesc);
//创建渲染目标视图
D3D10_RENDER_TARGET_VIEW_DESC RTVDesc;
//填入
device-> CreateRenderTargetView(backBuffer,& RTVDesc,& window-> renderTargetView);
backBuffer-> Release();
backBuffer = NULL;
//创建深度模板纹理
ID3D10Texture2D * depthStencil = NULL;
D3D10_TEXTURE2D_DESC descDepth;
//填入
device-> CreateTexture2D(& descDepth,NULL,& depthStencil);
//创建深度模板视图
D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
//填入
device-> CreateDepthStencilView(depthStencil,& descDSV,& window-> depthStencilView);
}
}
我们需要的一切。所有你需要做的是定义一个函数,它迭代所有窗口,并适当地绘制不同的东西。
在刚刚提到的函数中,它遍历所有窗口并使用适当的渲染目标-window data container):
void MultiRender()
{
//清除所有
for(int i = 0; i {
WindowDataContainer * window = windowsArray.at(i);
//有第二个问题的答案:
device-> OMSetRenderTargets(1,& window-> renderTargetView,window-> depthStencilView);
//不要忘记调整视口,全屏不重要...
D3D10_VIEWPORT Viewport;
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
Viewport.Width = window-> width;
Viewport.Height = window-> height;
Viewport.MinDepth = 0.0f;
Viewport.MaxDepth = 1.0f;
device-> RSSetViewports(1,& Viewport);
// TO DO:AMAZING STUFF PER WINDOW
}
}
b $ b当然,不要忘记每个窗口基础上运行所有的交换链和交换缓冲区。这里的代码只是为了这个答案的目的,它需要一些更多的工作,错误检查(故障)和沉思得到它的工作方式,你喜欢它 - 换句话说 - 它应该给你一个简化的概述,而不是生产解决方案。
祝你好运和快乐的编程! Sheesh,这是巨大的。
I am fairly new to DirectX 10 programming, and I have been trying to do the following with my limited skills (though I have a strong background with OpenGL)
I am trying to display 2 different textured Quads, 1 per monitor. To do so, I understood that I need a single D3D10 Device, multiple (2) swap chains, and single VertexBuffer
While I think I'm able to create all of those, I'm still pretty unsure how to handle all of them. Do I need multiple ID3D10RenderTargetView(s) ? How and where should I Use OMSetRenderTargets(...) ?
Other than MSDN, documentation or explaination of those concepts are rather limited, so any help would be very welcome. Here is some code I have :
Here's the rendering code
for(int i = 0; i < screenNumber; i++){ //clear scene pD3DDevice->ClearRenderTargetView( pRenderTargetView, D3DXCOLOR(0,1,0,0) ); //fill vertex buffer with vertices UINT numVertices = 4; vertex* v = NULL; //lock vertex buffer for CPU use pVertexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**) &v ); v[0] = vertex( D3DXVECTOR3(-1,-1,0), D3DXVECTOR4(1,0,0,1), D3DXVECTOR2(0.0f, 1.0f) ); v[1] = vertex( D3DXVECTOR3(-1,1,0), D3DXVECTOR4(0,1,0,1), D3DXVECTOR2(0.0f, 0.0f) ); v[2] = vertex( D3DXVECTOR3(1,-1,0), D3DXVECTOR4(0,0,1,1), D3DXVECTOR2(1.0f, 1.0f) ); v[3] = vertex( D3DXVECTOR3(1,1,0), D3DXVECTOR4(1,1,0,1), D3DXVECTOR2(1.0f, 0.0f) ); pVertexBuffer->Unmap(); // Set primitive topology pD3DDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP ); //set texture pTextureSR->SetResource( textureSRV[textureIndex] ); //get technique desc D3D10_TECHNIQUE_DESC techDesc; pBasicTechnique->GetDesc( &techDesc ); // This is where you actually use the shader code for( UINT p = 0; p < techDesc.Passes; ++p ) { //apply technique pBasicTechnique->GetPassByIndex( p )->Apply( 0 ); //draw pD3DDevice->Draw( numVertices, 0 ); } //flip buffers pSwapChain[i]->Present(0,0); }And here's the code for creating rendering targets, which I am not sure is good
for(int i = 0; i < screenNumber; ++i){ //try to get the back buffer ID3D10Texture2D* pBackBuffer; if ( FAILED( pSwapChain[1]->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*) &pBackBuffer) ) ) return fatalError("Could not get back buffer"); //try to create render target view if ( FAILED( pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRenderTargetView) ) ) return fatalError("Could not create render target view"); pBackBuffer->Release(); pD3DDevice->OMSetRenderTargets(1, &pRenderTargetView, NULL); } return true;}
解决方案I hope I got the gist of what you wish to do - render different content on two different monitors while using a single graphics card (graphics adapter) which maps its output to those monitors. For that, you're going to need one device (for the single graphics card/adapter) and enumerate just how many outputs there are at the user's machine.
So, in total - that means one device, two outputs, two windows and therefore - two swap chains.
Here's a quick result of my little experiment:
A little introduction
With DirectX 10+, this falls into the DXGI (DirectX Graphics Infrastructure) which manages the common low-level logistics involved with DirectX 10+ development which, as you probably know, dumped the old requirement of enumerating feature sets and the like - requiring every DX10+ capable card to share in on all of the features defined by the API. The only thing that varies is the extent and capability of the card (in other words, lousy performance is preferable to the app crashing and burning). This was all within DirectX 9 in the past, but people at Microsoft decided to pull it out and call it DXGI. Now, we can use DXGI functionality to set up our multi monitor environment.
Yes, you do need multiple render target views, count depends (like the swap chains and windows) on the number of monitors you have. But, to save you from spewing words, let's write it out as simple as possible and additional information where it's needed:
- Enumerate all adapters available on the system.
- For each adapter, enumerate all outputs available (and active) and create a device to accompany it.
- With the enumerated data stored in a suitable structure (think arrays which can quickly relinquish size information), use it to create n windows, swap chains, render target views, depth/stencil textures and their respective views where n is equal to the number of outputs.
- With everything created, for each window you are rendering into, you can define special routines which will use the available geometry (and other) data to output your results - which resolves to what each monitor gets in fullscreen (don't forget to adjust the viewport for every window accordingly).
- Present your data by iterating over every swap chain which is linked to its respective window and swap buffers with Present()
Now, while this is rich in word count, some code is worth a lot more. This is designed to give you a coarse idea of what goes into implementing a simple multimonitor application. So, assumptions are that there is only one adapter ( a rather bold statement nowadays ) and multiple outputs - and no failsafes. I'll leave the fun part to you. Answer to the second question is downstairs...
Do note there's no memory management involved. We assume everything magically gets cleaned up when it is not needed for illustration purposes. Be a good memory citizen.
Getting the adapter
IDXGIAdapter* adapter = NULL; void GetAdapter() // applicable for multiple ones with little effort { // remember, we assume there's only one adapter (example purposes) for( int i = 0; DXGI_ERROR_NOT_FOUND != factory->EnumAdapters( i, &adapter ); ++i ) { // get the description of the adapter, assuming no failure DXGI_ADAPTER_DESC adapterDesc; HRESULT hr = adapter->GetDesc( &adapterDesc ); // Getting the outputs active on our adapter EnumOutputsOnAdapter(); }
Acquiring the outputs on our adapter
std::vector<IDXGIOutput*> outputArray; // contains outputs per adapter void EnumOutputsOnAdapter() { IDXGIOutput* output = NULL; for(int i = 0; DXGI_ERROR_NOT_FOUND != adapter->EnumOutputs(i, &output); ++i) { // get the description DXGI_OUTPUT_DESC outputDesc; HRESULT hr = output->GetDesc( &outputDesc ); outputArray.push_back( output ); } }
Now, I must assume that you're at least aware of the Win32 API considerations, creating window classes, registering with the system, creating windows, etc... Therefore, I will not qualify its creation, only elaborate how it pertains to multiple windows. Also, I will only consider the fullscreen case here, but creating it in windowed mode is more than possible and rather trivial.
Creating the actual windows for our outputs
Since we assume existence of just one adapter, we only consider the enumerated outputs linked to that particular adapter. It would be preferable to organize all window data in neat little structures, but for the purposes of this answer, we'll just shove them into a simple struct and then into yet another std::vector object, and by them I mean handles to respective windows (HWND) and their size (although for our case it's constant).
But still, we have to address the fact that we have one swap chain, one render target view, one depth/stencil view per window. So, why not feed all of that in that little struct which describes each of our windows? Makes sense, right?
struct WindowDataContainer { //Direct3D 10 stuff per window data IDXGISwapChain* swapChain; ID3D10RenderTargetView* renderTargetView; ID3D10DepthStencilView* depthStencilView; // window goodies HWND hWnd; int width; int height; }
Nice. Well, not really. But still... Moving on! Now to create the windows for outputs:
std::vector<WindowDataContainer*> windowsArray; void CreateWindowsForOutputs() { for( int i = 0; i < outputArray.size(); ++i ) { IDXGIOutput* output = outputArray.at(i); DXGI_OUTPUT_DESC outputDesc; p_Output->GetDesc( &outputDesc ); int x = outputDesc.DesktopCoordinates.left; int y = outputDesc.DesktopCoordinates.top; int width = outputDesc.DesktopCoordinates.right - x; int height = outputDesc.DesktopCoordinates.bottom - y; // Don't forget to clean this up. And all D3D COM objects. WindowDataContainer* window = new WindowDataContainer; window->hWnd = CreateWindow( windowClassName, windowName, WS_POPUP, x, y, width, height, NULL, 0, instance, NULL ); // show the window ShowWindow( window->hWnd, SW_SHOWDEFAULT ); // set width and height window->width = width; window->height = height; // shove it in the std::vector windowsArray.push_back( window ); //if first window, associate it with DXGI so it can jump in // when there is something of interest in the message queue // think fullscreen mode switches etc. MSDN for more info. if(i == 0) factory->MakeWindowAssociation( window->hWnd, 0 ); } }
Cute, now that's done. Since we only have one adapter and therefore only one device to accompany it, create it as usual. In my case, it's simply a global interface pointer which can be accessed all over the place. We are not going for code of the year here, so why the hell not, eh?
Creating the swap chains, views and the depth/stencil 2D texture
Now, our friendly swap chains... You might be used to actually creating them by invoking the "naked" function D3D10CreateDeviceAndSwapChain(...), but as you know, we've already made our device. We only want one. And multiple swap chains. Well, that's a pickle. Luckily, our DXGIFactory interface has swap chains on its production line which we can receive for free with complementary kegs of rum. Onto the swap chains then, create for every window one:
void CreateSwapChainsAndViews() { for( int i = 0; i < windowsArray.size(); i++ ) { WindowDataContainer* window = windowsArray.at(i); // get the dxgi device IDXGIDevice* DXGIDevice = NULL; device->QueryInterface( IID_IDXGIDevice, ( void** )&DXGIDevice ); // COM stuff, hopefully you are familiar // create a swap chain DXGI_SWAP_CHAIN_DESC swapChainDesc; // fill it in HRESULT hr = factory->CreateSwapChain( DXGIDevice, &swapChainDesc, &p_Window->swapChain ); DXGIDevice->Release(); DXGIDevice = NULL; // get the backbuffer ID3D10Texture2D* backBuffer = NULL; hr = window->swapChain->GetBuffer( 0, IID_ID3D10Texture2D, ( void** )&backBuffer ); // get the backbuffer desc D3D10_TEXTURE2D_DESC backBufferDesc; backBuffer->GetDesc( &backBufferDesc ); // create the render target view D3D10_RENDER_TARGET_VIEW_DESC RTVDesc; // fill it in device->CreateRenderTargetView( backBuffer, &RTVDesc, &window->renderTargetView ); backBuffer->Release(); backBuffer = NULL; // Create depth stencil texture ID3D10Texture2D* depthStencil = NULL; D3D10_TEXTURE2D_DESC descDepth; // fill it in device->CreateTexture2D( &descDepth, NULL, &depthStencil ); // Create the depth stencil view D3D10_DEPTH_STENCIL_VIEW_DESC descDSV; // fill it in device->CreateDepthStencilView( depthStencil, &descDSV, &window->depthStencilView ); } }
We now have everything we need. All that you need to do is define a function which iterates over all windows and draws different stuff appropriately.
In the just mentioned function which iterates over all windows and uses the appropriate render target (courtesy of our per-window data container):
void MultiRender( ) { // Clear them all for( int i = 0; i < windowsArray.size(); i++ ) { WindowDataContainer* window = windowsArray.at(i); // There is the answer to your second question: device->OMSetRenderTargets( 1, &window->renderTargetView, window->depthStencilView ); // Don't forget to adjust the viewport, in fullscreen it's not important... D3D10_VIEWPORT Viewport; Viewport.TopLeftX = 0; Viewport.TopLeftY = 0; Viewport.Width = window->width; Viewport.Height = window->height; Viewport.MinDepth = 0.0f; Viewport.MaxDepth = 1.0f; device->RSSetViewports( 1, &Viewport ); // TO DO: AMAZING STUFF PER WINDOW } }
Of course, don't forget to run through all the swap chains and swap buffers per window basis. The code here is just for the purposes of this answer, it requires a bit more work, error checking (failsafes) and contemplation to get it working just the way you like it - in other words - it should give you a simplified overview, not a production solution.
Good luck and happy coding! Sheesh, this is huge.
这篇关于显示每个监视器不同的图像directX 10的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!