This tutorial is part of a Collection: Let's Start Something Cool :)
rate up
1
rate down
1326
views
bookmark
07 - Let's Hello World!

Let's our window scream Hello World!

480 downloads
Hello threre, At the begginging I would like to point that there was a little bug. When there is no config file the window may open on strange parameters. to fix that we need to implement a small amount of code to check if it is the same as windows resolution and corrent window start position: int X{}, Y{}; if (wmc.Width != displayMode.dmPelsWidth) X = (GetSystemMetrics(SM_CXSCREEN) / 2) - (wmc.Width / 2); if (wmc.Height != displayMode.dmPelsHeight) Y = (GetSystemMetrics(SM_CYSCREEN) / 2) - (wmc.Height / 2); MainWnd = CreateWindowEx(NULL, classstitle.c_str(), windowstitle.c_str(), WS_OVERLAPPEDWINDOW, X, Y, wmc.Width, wmc.Height, nullptr, nullptr, hInst, nullptr); and that's it :) Now we can start our tutorial on Direct2D. Since Dx12 don't have its Direct12 Interface we must use Direct2D11 instead. At the beggining there'll be a struct struct Control { bool Border{ false }; Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> BorderBrush{ nullptr }; Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> pTextBrush{ nullptr }; Microsoft::WRL::ComPtr<IDWriteTextFormat> pTextFormat{ nullptr }; std::wstring text{ L"Hello World!" }; }; it will describe our first basic control, at later stage it will contain much more data for various of controls, but as of now it will contain data on: does it have a border, and what color text color text data such font name, size, parameters and what text it contain. at this point it is static since we don't have any possibility to modify it yet. When we look at the D2DGraphics: class D2DGraphics { public: D2DGraphics(); ~D2DGraphics(); private: Microsoft::WRL::ComPtr<ID3D11DeviceContext> pD3D11DeviceContext{ nullptr }; Microsoft::WRL::ComPtr<ID3D11On12Device> pD3D11On12Device{ nullptr }; Microsoft::WRL::ComPtr<ID2D1DeviceContext2> pD2DDeviceContext{ nullptr }; Microsoft::WRL::ComPtr<ID2D1Bitmap1> pD2DRenderTargets[3]{ nullptr }; Microsoft::WRL::ComPtr<ID3D11Resource> pWrappedBackBuffers[3]{ nullptr }; Microsoft::WRL::ComPtr<ID3D11Device> d3d11Device{ nullptr }; Microsoft::WRL::ComPtr<IDWriteFactory> pDWriteFactory{ nullptr }; Microsoft::WRL::ComPtr<ID2D1Device2> pD2DDevice{ nullptr }; Microsoft::WRL::ComPtr<ID2D1Factory3> pD2DFactory{ nullptr }; Microsoft::WRL::ComPtr<IWICImagingFactory> m_pWICFactory{ nullptr }; D2D1_FACTORY_OPTIONS d2dFactoryOptions{}; Control Static{}; public: bool InitD2D(ID3D12Device *pDevice, Microsoft::WRL::ComPtr<ID3D12CommandQueue> pCommandQueue, Microsoft::WRL::ComPtr<ID3D12Resource> Res[]); void RenderD2D(int FrameIndex); }; that we have here information about creating both Direct2D and DirectWrite interfaces, we won't separate it for a while since we won't work that much on DirectWrite now. ID2DBitmap1 and IWICImagingFactory will be used to create a bitmat on whole screen which will let us draw our future User Interface. Let's Init our 2D render frames: bool D2DGraphics::InitD2D(ID3D12Device *pDevice, ComPtr<ID3D12CommandQueue> pCommandQueue, ComPtr<ID3D12Resource> pRenderTargets[]) { UINT d3d11DeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; ErrorHR(D3D11On12CreateDevice(pDevice, d3d11DeviceFlags, nullptr, 0, reinterpret_cast<IUnknown**>(pCommandQueue.GetAddressOf()), 1, 0, &d3d11Device, &pD3D11DeviceContext, nullptr)); ErrorHR(d3d11Device.As(&pD3D11On12Device)); D2D1_DEVICE_CONTEXT_OPTIONS deviceOptions = D2D1_DEVICE_CONTEXT_OPTIONS_NONE; ErrorHR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory3), &d2dFactoryOptions, &pD2DFactory)); ComPtr<IDXGIDevice> dxgiDevice; ErrorHR(pD3D11On12Device.As(&dxgiDevice)); ErrorHR(pD2DFactory->CreateDevice(dxgiDevice.Get(), &pD2DDevice)); ErrorHR(pD2DDevice->CreateDeviceContext(deviceOptions, &pD2DDeviceContext)); ErrorHR(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &pDWriteFactory)); ErrorHR(CoInitialize(nullptr)); ErrorHR(CoCreateInstance(CLSID_WICImagingFactory1, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pWICFactory))); float dpiX{}; float dpiY{}; pD2DFactory->GetDesktopDpi(&dpiX, &dpiY); D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), dpiX, dpiY ); ErrorHR(pD2DDeviceContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &Static.pTextBrush)); ErrorHR(pD2DDeviceContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &Static.BorderBrush)); ErrorHR(pDWriteFactory->CreateTextFormat(L"Verdana", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 14, L"pl-PL", &Static.pTextFormat)); ErrorHR(Static.pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); ErrorHR(Static.pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); for (UINT n = 0; n < 3; n++) { D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET }; ErrorHR(pD3D11On12Device->CreateWrappedResource( pRenderTargets[n].Get(), &d3d11Flags, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT, IID_PPV_ARGS(&pWrappedBackBuffers[n]) )); ComPtr<IDXGISurface> surface; ErrorHR(pWrappedBackBuffers[n].As(&surface)); ErrorHR(pD2DDeviceContext->CreateBitmapFromDxgiSurface(surface.Get(), &bitmapProperties, &pD2DRenderTargets[n])); } return true; } D3D11On12CreateDevice takes our window context from D12Device and put it to d3d11Device pD3D11DeviceContext. D2D1CreateFactory create our starting point to Direct2D. D2D1_FACTORY_TYPE_SINGLE_THREADED means we will be working on one thread. This will not be modificated value by us for some time, since we will not use multithreading. Creating needed D2D devices: pD2DFactory->CreateDevice(dxgiDevice.Get(), &pD2DDevice)); pD2DDevice->CreateDeviceContext(deviceOptions, &pD2DDeviceContext)); and DirectWrite context: DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &pDWriteFactory); than creating a instance and properties for our "screen". ErrorHR(pD2DDeviceContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &Static.pTextBrush)); ErrorHR(pD2DDeviceContext->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &Static.BorderBrush)); ErrorHR(pDWriteFactory->CreateTextFormat(L"Verdana", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 14, L"pl-PL", &Static.pTextFormat)); ErrorHR(Static.pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); ErrorHR(Static.pTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); Here we are going to put our static control needed parameters such as color, font and its size, aligment. and let's create resource for our 3 frames and create final bitmap. so the last thing is to render our bitmap with our text. void D2DGraphics::RenderD2D(int FrameIndex) { pD3D11On12Device->AcquireWrappedResources(pWrappedBackBuffers[FrameIndex].GetAddressOf(), 1); pD2DDeviceContext->SetTarget(pD2DRenderTargets[FrameIndex].Get()); pD2DDeviceContext->BeginDraw(); pD2DDeviceContext->SetTransform(D2D1::Matrix3x2F::Identity()); D2D1_RECT_F Wnd{}; Wnd = D2D1::RectF(static_cast<FLOAT>(100), static_cast<FLOAT>(100), static_cast<FLOAT>(250), static_cast<FLOAT>(30)); if (Static.Border) { pD2DDeviceContext->DrawRectangle(&Wnd, Static.BorderBrush.Get(), 2, NULL); } pD2DDeviceContext->DrawTextW(Static.text.c_str(), wcslen(Static.text.c_str()), Static.pTextFormat.Get(), &Wnd, Static.pTextBrush.Get()); ErrorHR(pD2DDeviceContext->EndDraw()); pD3D11On12Device->ReleaseWrappedResources(pWrappedBackBuffers[FrameIndex].GetAddressOf(), 1); pD3D11DeviceContext->Flush(); } When we acquire our resource we Begin our drawing and setting a matrix. Then we create a rectangle and if border is true than draw that border and text on the screen. In this post it is putted here in render function but in next one we will be putting it into separate function that allows us to render multiple of them. So if we have the init and render our 2D board so let's make that happen: in D12Graphics class we put: std::unique_ptr<D2DGraphics> GUI; We init after closing of our empty CommandList: GUI = std::make_unique <D2DGraphics>(); GUI->InitD2D(pDevice, pCommandQueue, pRenderTargets); and Render it right after ExecuteCommandList GUI->RenderD2D(FrameIndex); and when you run you should see a Wello world text on the screen :) Next time we'll create a functions on which we'll display many such texts and setting some of the parameters on creating tchem which will be an introduction to our User Interface. Note that in attached file #include is setted up for S: drive so if you don't want it just modify tchem for your purposes. Regards and see you next time when we draw multiple of static controls and create our own messaging system for our UI :)