我希望我能了解你们想要做什么——在两个不同的显示器上提供不同的内容,同时使用一张图形卡片(图形调整器),该卡片将输出映射为这些显示器。为此,你们需要一台设备(用于单个图形卡片/适应器),并列出用户机器中有多少输出。
所以,总而言之,这意味着一个装置,两个输出,两个窗口,因此,两个互换链。
下面是我的小实验的一个快速结果:
"https://i.sstatic.net/oLele.jpg" alt="结果"/ >
A little introduction
在DirectX10+中,这属于DXGI(DirectX图形基础设施)(DirectX图形基础设施),它管理着与DirectX 10+开发有关的普通低水平物流,而正如你可能知道的那样,DXGI(DirectX 图形基础设施)抛弃了列举地物组的旧要求和类似要求,要求每个DX10+有能力的卡片分享API定义的所有特征。唯一不同的是卡片的范围和能力(换句话说,不良性能优于应用程序的崩溃和燃烧) 。这过去都在DirX 9范围内,但微软公司的人决定把它拉出来,称为DXGI。现在,我们可以使用DXGI的功能来设置我们的多监视环境。
我需要多重 ID3D10Render TagetView(s) 吗?
是的, 您确实需要多个化成目标视图, 计数取决于您拥有的显示器数量( 如交换链和窗口) 。 但是, 为了避免您使用扭曲的单词, 请尽可能简单地写出来, 并在需要时提供补充信息 :
- 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()
虽然这在字数上是丰富的, 但有些代码的价值要多得多。 设计这个代码是为了让您粗略地了解一个简单的多监测器应用程序的实施过程。 因此, 假设是只有一个适配器( 比较大胆的语句) 和多个输出 - 没有故障保险。 我将让您来做有趣的部分 。 “ 强大的” 第二个问题在楼下... 坚固 。 < / strong >
Do note there no memory management without. 我们假设所有的东西在不需要用于插图时都会被神奇地清理干净。 成为良好的记忆公民 。 em>
< 强 > 获取适配器 强 >
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();
}
< 强 > 获取我们的适配器 < /强 > 的输出
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 );
}
}
现在,我必须假设,你至少知道Win32 API的考虑, 创建窗口班, 与系统注册, 创建窗口等等... 因此,我将不限定它的创建, 只阐述它与多个窗口的关系。 另外,我在这里只考虑全屏案例, 但以窗口模式创建它不仅仅是可能的, 而且相当微不足道 。
< 强 > 为我们输出 强 > 创建实际窗口 < /强 >
由于我们假设只有一个适配器,我们只考虑与该特定适配器相联系的所列举的产出。 最好将所有窗口数据组织成简洁的小型结构,但为了这个答案的目的,我们只是把它们推入一个简单的支架中,然后放入另一个 std::矢量天体,用它们来说,我是指对各自窗口(HWND)及其大小(尽管就我们的情况而言,它保持不变)进行控制。
但是,我们仍然必须解决一个事实,即我们有一个互换链,一个让目标视图,每个窗口一个深度/高度视图。那么,为什么不在描述我们每个窗口的小支架中喂食所有这些东西呢?这有道理,对吧?
struct WindowDataContainer
{
//Direct3D 10 stuff per window data
IDXGISwapChain* swapChain;
ID3D10RenderTargetView* renderTargetView;
ID3D10DepthStencilView* depthStencilView;
// window goodies
HWND hWnd;
int width;
int height;
}
不错,其实不是,但是...
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 );
}
}
太可爱了。因为我们只有一台适配器,因此只有一台设备可以随身携带,所以,我们照常创建它。就我而言,它只是一个全球界面指示器,可以到处访问。我们不会在这里获取年度代码,所以为什么没有呢?
< 强 > 创建互换链、视图和深度/ 高级 2D 纹理 强 >
现在,我们友好的互换链... 你可能会习惯于通过援引“裸装”函数 D3D10Create DeviceandSwapchain (...)
, (...) ,但你知道,我们已经制造了我们的设备。 我们只想要一个。 还有多个互换链。 嗯,这是一个泡菜。 幸运的是, 我们的DXGIFactory界面在其生产线上设置了互换链条, 我们可以免费接收补充朗姆酒。 然后, 在互换链条上, 为每个窗口创建 :
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 );
}
}
我们现在拥有我们所需要的一切。你需要做的就是定义一个功能, 在所有窗口上循环, 并适当绘制不同的东西。
我应如何和在哪里使用 OMSetRender 目标(...)?
在上述功能中,所有窗口都循环并使用适当的设定目标(我们每个窗口数据容器的价值):
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
}
}
当然, 不要忘了运行所有互换链和每个窗口的缓冲。 这里的代码只是为了这个答案的目的, 它需要多做点工作, 检查错误( 错误安全) 和考虑才能让它按照你喜欢的方式运作, 换句话说, 它应该给你一个简化的概览, 而不是一个生产解决方案 。
祝你好运和好运!