[SOLVED] D3D11 and Direct2D

Started by
13 comments, last by DieterVW 14 years, 5 months ago
Seems to be working quite well.

Here's the code:
#include <windows.h>#include <D3D11.h>#include <D2D1.h>#include <DXErr.h>#include <DXGI.h>// Libs#pragma comment (lib, "D3D11.lib")#pragma comment (lib, "D2D1.lib")#pragma comment (lib, "DXErr.lib")#pragma comment (lib, "DXGI.lib")LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);// Mainint WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {	// Register windowclass	WNDCLASSEX wc;	ZeroMemory(&wc, sizeof(wc));	wc.cbSize = sizeof(wc);	wc.lpszClassName = TEXT("MyClass");	wc.hInstance = hInstance;	wc.lpfnWndProc = WndProc;	wc.hCursor = LoadCursor(NULL, IDC_ARROW);	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);	RegisterClassEx(&wc);		// Create window	HWND hWnd = CreateWindow(		wc.lpszClassName,		TEXT("D3D11 with Direct2D"),		WS_OVERLAPPEDWINDOW,		CW_USEDEFAULT,		CW_USEDEFAULT,		CW_USEDEFAULT,		CW_USEDEFAULT,		NULL,		NULL,		hInstance,		NULL	);		// Create DXGI factory to enumerate adapters	IDXGIFactory1 *pDXGIFactory;		HRESULT hResult = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&pDXGIFactory);	if(FAILED(hResult)) {		MessageBox(NULL, DXGetErrorDescription(hResult), TEXT("CreateDXGIFactory1"), MB_OK);		return 0;	}		// Use the first adapter	IDXGIAdapter1 *pAdapter;		hResult = pDXGIFactory->EnumAdapters1(0, &pAdapter);	if(FAILED(hResult)) {		MessageBox(NULL, DXGetErrorDescription(hResult), TEXT("EnumAdapters1"), MB_OK);		return 0;	}		pDXGIFactory->Release();		// Create D3D11 device and swapchain	DXGI_SWAP_CHAIN_DESC scd;	ID3D11Device *pDevice11;	IDXGISwapChain *pSwapChain;	ID3D11DeviceContext *pImmediateContext;		ZeroMemory(&scd, sizeof(scd));	scd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;	scd.SampleDesc.Count = 1;	scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;	scd.BufferCount = 1;	scd.OutputWindow = hWnd;	scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;	scd.Windowed = TRUE;		hResult = D3D11CreateDeviceAndSwapChain(		pAdapter,		D3D_DRIVER_TYPE_UNKNOWN,		NULL,		D3D11_CREATE_DEVICE_DEBUG |			D3D11_CREATE_DEVICE_BGRA_SUPPORT |			D3D11_CREATE_DEVICE_SINGLETHREADED,		NULL,		0,		D3D11_SDK_VERSION,		&scd,		&pSwapChain,		&pDevice11,		NULL,		&pImmediateContext	);	if(FAILED(hResult)) {		MessageBox(NULL, DXGetErrorDescription(hResult), TEXT("D3D11CreateDeviceAndSwapChain"), MB_OK);		return 0;	}		// Get the D3D11 back-buffer	ID3D11Texture2D *pBackBuffer11;	D3D11_TEXTURE2D_DESC backBufferDesc;		hResult = pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pBackBuffer11));	if(FAILED(hResult)) {		MessageBox(NULL, DXGetErrorDescription(hResult), TEXT("pSwapChain->GetBuffer for ID3D11Texture2D"), MB_OK);		return 0;	}		pBackBuffer11->GetDesc(&backBufferDesc);		// Create the texture to draw D2D content to	D3D11_TEXTURE2D_DESC textureDesc;	ID3D11Texture2D *pTexture;		ZeroMemory(&textureDesc, sizeof(textureDesc));	textureDesc.Width = backBufferDesc.Width;	textureDesc.Height = backBufferDesc.Height;	textureDesc.MipLevels = 1;	textureDesc.ArraySize = 1;	textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;	textureDesc.SampleDesc.Count = 1;	textureDesc.Usage = D3D11_USAGE_DEFAULT;	textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;	textureDesc.MiscFlags = D3D11_RESOURCE_MISC_GDI_COMPATIBLE;		hResult = pDevice11->CreateTexture2D(&textureDesc, NULL, &pTexture);	if(FAILED(hResult)) {		MessageBox(NULL, DXGetErrorDescription(hResult), TEXT("pDevice11->CreateTexture2D"), MB_OK);		return 0;	}		// Get DXGI surface from texture	IDXGISurface1 *pSurface;		hResult = pTexture->QueryInterface(__uuidof(IDXGISurface1), (void**)&pSurface);	if(FAILED(hResult)) {		MessageBox(NULL, DXGetErrorDescription(hResult), TEXT("pTexture->QueryInterface for IDXGISurface1"), MB_OK);		return 0;	}		// Create D2D factory	ID2D1Factory *pD2DFactory;		hResult = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), (void**)&pD2DFactory);	if(FAILED(hResult)) {		MessageBox(NULL, DXGetErrorDescription(hResult), TEXT("D2D1CreateFactory"), MB_OK);		return 0;	}		// Create D2D DC render target	D2D1_RENDER_TARGET_PROPERTIES renderTargetProperties;	ID2D1DCRenderTarget *pD2DRenderTarget;		ZeroMemory(&renderTargetProperties, sizeof(renderTargetProperties));	renderTargetProperties.type = D2D1_RENDER_TARGET_TYPE_HARDWARE;	renderTargetProperties.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED);		hResult = pD2DFactory->CreateDCRenderTarget(&renderTargetProperties, &pD2DRenderTarget);	if(FAILED(hResult)) {		MessageBox(NULL, DXGetErrorDescription(hResult), TEXT("pD2DFactory->CreateDCRenderTarget"), MB_OK);		return 0;	}		pD2DFactory->Release();		// Create a solid color brush to draw something with	ID2D1SolidColorBrush *pBrush;		hResult = pD2DRenderTarget->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 0.0f, 1.0f), &pBrush);	if(FAILED(hResult)) {		MessageBox(NULL, DXGetErrorDescription(hResult), TEXT("pD2DRenderTarget->CreateSolidColorBrush"), MB_OK);		return 0;	}		// Main loop	float angle = 0.0f;		ShowWindow(hWnd, nCmdShow);	while(true) {		MSG msg;		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0) {			if(msg.message == WM_QUIT)				break;			else {				TranslateMessage(&msg);				DispatchMessage(&msg);			}		}		else {			// Get surface DC for the texture			HDC hDC;			hResult = pSurface->GetDC(TRUE, &hDC);			if(FAILED(hResult)) {			}			else {				// The rect of the texture to draw to with D2D				RECT rect;				SetRect(&rect, 0, 0, backBufferDesc.Width, backBufferDesc.Height);								// Bind the DC render target to the HDC				hResult = pD2DRenderTarget->BindDC(hDC, &rect);				if(FAILED(hResult)) {				}				else {					// Draw D2D content					angle += 360.0f / 200.0f;										pD2DRenderTarget->BeginDraw();										pD2DRenderTarget->Clear(D2D1::ColorF(0.0f, 0.7f, 0.0f, 1.0f));										D2D1_SIZE_F size = pD2DRenderTarget->GetSize();										pD2DRenderTarget->SetTransform(D2D1::Matrix3x2F::Rotation(angle, D2D1::Point2F(size.width/2.0f, size.height/2.0f)));										pBrush->SetColor(D2D1::ColorF(1.0f, 1.0f, 0.0f, 1.0f));					pD2DRenderTarget->DrawEllipse(						D2D1::Ellipse(D2D1::Point2F(size.width/2.0f, size.height/2.0f), size.height/3.0f, size.height/3.0f),						pBrush,						32.0f,						NULL					);										pBrush->SetColor(D2D1::ColorF(1.0f, 0.0f, 0.0f, 1.0f));					pD2DRenderTarget->DrawEllipse(						D2D1::Ellipse(D2D1::Point2F(size.width/4.0f, size.height/2.0f), size.height/4.0f, size.height/4.0f),						pBrush,						32.0f,						NULL					);										pBrush->SetColor(D2D1::ColorF(0.0f, 0.0f, 1.0f, 1.0f));					pD2DRenderTarget->DrawEllipse(						D2D1::Ellipse(D2D1::Point2F(size.width*3.0f/4.0f, size.height/2.0f), size.height/4.0f, size.height/4.0f),						pBrush,						32.0f,						NULL					);										pD2DRenderTarget->EndDraw();				}								// Release the DC specifying the dirty rect				pSurface->ReleaseDC(&rect);			}						// Copy the content from the texture to the back-buffer			pImmediateContext->CopyResource(pBackBuffer11, pTexture);									// Present to screen			pSwapChain->Present(1, 0);		}	}		// Release	pSwapChain->SetFullscreenState(FALSE, NULL);		pImmediateContext->ClearState();		pBrush->Release();	pD2DRenderTarget->Release();		pSurface->Release();	pTexture->Release();	pBackBuffer11->Release();		pImmediateContext->Flush();		pImmediateContext->Release();	pSwapChain->Release();	pDevice11->Release();		UnregisterClass(wc.lpszClassName, hInstance);		return 0;}// Window procedureLRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {	switch(msg) {		// Window destroyed.		case WM_DESTROY:			PostQuitMessage(0);		return 0;	}		return DefWindowProc(hWnd, msg, wParam, lParam);}


EDIT: I noticed that at least for simple test-cases it's faster to use software-mode for the D2D render-target with this method. I assume it's because it copies back-and-forth between GPU and system memory otherwise..
It's significantly slower than using the shared texture method, when drawing large areas.

[Edited by - Erik Rufelt on November 9, 2009 12:58:40 PM]
Advertisement
Tack så mycket för hjälpen! ;) (Thanks!)

Odd thing though, that last sample you posted - while working well, it doesn't actually clear properly. I might have a case of bad drivers?



Edit: Same result on the shared resource example.

[Edited by - DEVLiN on November 9, 2009 12:26:43 PM]
Np =) That's strange.. I would update everything before trouble-shooting at least. There's a Clear in there so I don't know what it could be.
Must be a driver issue. It seems it was a problem with "dirty" areas, only clearing the areas where something happened. It went away if I drew a fullscreen filled rectangle (even with alpha 0).

Might not prove to be a problem in the long run though - have to give it a try in a real application.

Thanks for your help yet again. :)
Using two devices IS the story for D3D11 and D2D interop. Any flickering is very likely due to a sync problem between two devices. Using the GetDC will mean you take a hit since the resource is unbound from the pipeline and transferred to the CPU for CPU side rendering. The hit might be less than using D2D though.

As a possible way around this issue, it would be possible to make a texture cache of your fonts, and then use DWrite for all layout/spacing information. The only new code to write would be whatever is necessary to dynamically emit quads for each character to the screen. DWrite will provide all screen alignment, spacing, and other layout details needed to place your text onscreen efficiently. In this way, the texture font cache is the only thing shared between devices, leaving you to render solely in D3D11.

This topic is closed to new replies.

Advertisement