D2D + D3D11 help me to make them work together

Started by
19 comments, last by BlackJoker 11 years, 1 month ago

Hello to all!


I have a problem with connecting D2D to D3D11. I read a lot of forum articles on that theme, but this interop a little bit hard to understand and more harder to use on practice. I tried to implement it as was saying in this article http://www.braynzarsoft.net/index.php?p=D3D11FONT#still but problem in that in the sample all the code is in 1 file and there is no OOP at all, and I make my engine from the rasterteck framework.

I think during implementation of interop D2D and D3D11 I missed something, but I can`t find the problem because program work without errors, but just no text on the screen.



I uploaded my project here, so if you wish to help me, you can look into code and say where i have a mistake because I am completely confused with this problem.

Could someone build up this project and find out why text is not rendered?

I am trying to find a solution already a week, but unsuccessfully.


P.S. I chose to implement D2D + D3D11 because the way to cut texture into pieces seems no so good way to make text output for me, besides it hard to make localization in that way.



Please, help me.

Advertisement

I don't have a good resource available to explain this, but I just thought I'd let you know that Microsoft released a platform update on 2/26/2013 that makes a lot of the things in that article unnecessary. It's now possible to use D3D11 and D2D together directly, without having to create a D3D10.1 device and worry about synchronizing the resources.

If you've installed the update, you should be able to just create a D3D11 texture, get an IDXGISurface1 pointer from it, and then pass that to CreateDxgiSurfaceRenderTarget to create a D2D render target.

I didn`t hear about this update. Is it for DirectX11 or for DirectX 11.1? Where I can read about it more detail?

You can use Direct2D 1.1 since it is fully available for Windows 7 too. http://msdn.microsoft.com/en-us/library/windows/desktop/jj863687.aspx

"Recursion is the first step towards madness." - "Skegg?ld, Skálm?ld, Skildir ro Klofnir!"
Direct3D 12 quick reference: https://github.com/alessiot89/D3D12QuickRef/

Actually, if you are on Windows 8 it was possible before the update. The update (http://support.microsoft.com/kb/2670838) was only for allowing it to work on Windows 7 as well.

Here is a sample for D3D-D2D interop on Windows 8. I didn't get a chance to look at it, but it might be useful.

http://code.msdn.microsoft.com/windowsapps/Direct2D-Direct3D-Interop-ee641e46

Also, here's a quick example I threw together, using D2D to draw text to the backbuffer. It should also be possible to create a texture in D3D11, and then write to it with D2D. (I think you would use QueryInterface to get the IDXGISurface1 pointer from the texture, and then pass that to CreateDxgiSurfaceRenderTarget, but I'm not sure)


#include <Windows.h>
#include <d3d11.h>
#include <d2d1_1.h>
#include <dwrite_1.h>

#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dwrite.lib")

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (msg == WM_CLOSE)
	{
		PostQuitMessage(0);
	}
	return DefWindowProc(hWnd, msg, wParam, lParam);
}

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	// create the window

	WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = sizeof(LONG_PTR);
	wcex.hInstance = hInstance;
	wcex.hbrBackground = NULL;
	wcex.lpszMenuName  = NULL;
	wcex.hCursor  = LoadCursor(NULL, IDI_APPLICATION);
	wcex.lpszClassName = "Vector";
	ATOM a = RegisterClassEx(&wcex);

	RECT r = {0, 0, 800, 600};
	AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false);

	HWND hWnd = CreateWindow(
		(LPCSTR)a,
		"Vector",
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		r.right - r.left,
		r.bottom - r.top,
		NULL,
		NULL,
		hInstance,
		NULL);

	// create the D3D device and swap chain

	D3D_FEATURE_LEVEL level = D3D_FEATURE_LEVEL_11_0;

	DXGI_SWAP_CHAIN_DESC swapChainDesc = { };
	swapChainDesc.BufferCount = 1;
	swapChainDesc.BufferDesc.Width = 800;
	swapChainDesc.BufferDesc.Height = 600;
	swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
	swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	swapChainDesc.OutputWindow = hWnd;
	swapChainDesc.SampleDesc.Count = 1;
	swapChainDesc.SampleDesc.Quality = 0;
	swapChainDesc.Windowed = true;

	IDXGISwapChain *swapChain;
	ID3D11Device *device;
	ID3D11DeviceContext *context;

	HRESULT hr = D3D11CreateDeviceAndSwapChain(
		NULL,
		D3D_DRIVER_TYPE_HARDWARE,
		NULL,
		D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_BGRA_SUPPORT,
		&level,
		1,
		D3D11_SDK_VERSION,
		&swapChainDesc,
		&swapChain,
		&device,
		NULL,
		&context);

	// set up the D3D render target view to the back buffer

	ID3D11Texture2D *backBuffer;
	ID3D11RenderTargetView *backBufferView;
	swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer));
	device->CreateRenderTargetView(backBuffer, NULL, &backBufferView);
	context->OMSetRenderTargets(1, &backBufferView, NULL);

	// create the D2D factory

	ID2D1Factory *factory;
	D2D1_FACTORY_OPTIONS options;
	options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
	D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, options, &factory);

	// set up the D2D render target using the back buffer

	IDXGISurface *dxgiBackbuffer;
	swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackbuffer));
	ID2D1RenderTarget *d2dRenderTarget;
	D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
		D2D1_RENDER_TARGET_TYPE_DEFAULT,
		D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
	factory->CreateDxgiSurfaceRenderTarget(dxgiBackbuffer, props, &d2dRenderTarget);
	dxgiBackbuffer->Release();

	// create the DWrite factory

	IDWriteFactory1 *writeFactory;
	DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(writeFactory), (IUnknown**)(&writeFactory));

	// create the DRwite text format

	IDWriteTextFormat *textFormat;
	writeFactory->CreateTextFormat(
		L"Arial",
		NULL,
		DWRITE_FONT_WEIGHT_NORMAL,
		DWRITE_FONT_STYLE_NORMAL,
		DWRITE_FONT_STRETCH_NORMAL,
		50,
		L"",
		&textFormat);

	// create a brush

	ID2D1SolidColorBrush *whiteBrush;
	d2dRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &whiteBrush);

	// draw the text
	
	d2dRenderTarget->BeginDraw();
	const WCHAR *text = L"Hello World";
	d2dRenderTarget->DrawTextA(text, wcslen(text), textFormat, D2D1::RectF(0, 0, 800, 600), whiteBrush);
	d2dRenderTarget->EndDraw();

	ShowWindow(hWnd, true);

	MSG msg = {0};
	while (WM_QUIT != msg.message)
	{
		if (PeekMessage( &msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
		else
		{
			swapChain->Present(0, 0);
		}
	}
}

I didn`t know that D2D1_1 I can use with D3D11 on Win8. Thanks for that.

hm, it seems that i cannot build that MS sample because my VS 2012 says that this project is incompatible with my version of VS. I have Ultimate version.

Does someone know how to build that project?

And another one thing - now I must use Windows 8 SDK to use new D2D, but here is no D3DX.h, so I must find new analogs for old functions such as D3DXMatrixPerspectiveFovLH and D3DXCreateTextureFromFile

Does someone now which functions I can replace these?

By the way. I have tested you code and it work great after few small corrections regarding CreateWindow function. Thanks a lot for this, but I still don`t know whixh functions to use instead of D3DX functions. Could you please help me with that question?

And another one question: how to get last position of drawn symbol in D2D? For example I want to draw text after previous, but for thi I need to now where the rivious text ended on the screen. Could I get this information somehow?

And another one thing - now I must use Windows 8 SDK to use new D2D, but here is no D3DX.h, so I must find new analogs for old functions such as D3DXMatrixPerspectiveFovLH and D3DXCreateTextureFromFile

Does someone now which functions I can replace these?

By the way. I have tested you code and it work great after few small corrections regarding CreateWindow function. Thanks a lot for this, but I still don`t know whixh functions to use instead of D3DX functions. Could you please help me with that question?

Since D3DX is retired you (must) can use DirectX Tool kit, DirectX Tex and DirectXMath respectively for graphics, texture and math function ad structures.

http://directxtk.codeplex.com/

http://directxtex.codeplex.com/

http://msdn.microsoft.com/en-us/library/windows/desktop/ee418730.aspx

If you still want to use D3DX you must install the June 2010 DXSDK and manually configure the vs project: http://msdn.microsoft.com/en-us/library/windows/desktop/ee663275.aspx

"Recursion is the first step towards madness." - "Skegg?ld, Skálm?ld, Skildir ro Klofnir!"
Direct3D 12 quick reference: https://github.com/alessiot89/D3D12QuickRef/

As far as rendering the text to a texture, you use QueryInterface<T> as mentioned above to get an IDXGISurface1 pointer, and then pass that to ID2D1Factory::CreateDxgiSurfaceRenderTarget. Then you can just use that render target like the render target in my example above.

Example of render target creation:


// create the D3D11 texture

D3D11_TEXTURE2D_DESC texDesc = {};
texDesc.ArraySize = 1;
texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.Width = 512;
texDesc.Height = 512;
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texDesc.SampleDesc.Count = 1;
texDesc.MipLevels = 1;
ID3D11Texture2D *texture;
device->CreateTexture2D(&texDesc, NULL, &texture);

// create D2D render target using texture

ID2D1RenderTarget *renderTarget;
IDXGISurface1* dxgiSurface;
texture->QueryInterface<IDXGISurface1>(&dxgiSurface);
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
	D2D1_RENDER_TARGET_TYPE_DEFAULT,
	D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
factory->CreateDxgiSurfaceRenderTarget(dxgiSurface, &props, &renderTarget);
dxgiSurface->Release();

For determining where the previous text ended on the screen...I think IDWriteFactory::CreateTextLayout and IDWriteTextLayout::GetMetrics might help. It looks like it can take care of wrapping text for you as well.

This topic is closed to new replies.

Advertisement