Moving beyond Win32 GDI into the world of DirectX ...
While the GDI HDC magically appears with WM_PAINT, creating the
ID2D1DeviceContext requires navigating a nasty web of prerequisites
and is not a simple thing.
Creating the ID2D1DeviceContext
The ID2D1DeviceContext is created from an
ID2D1Device, which is created from an ID2D1Factory,
which can be created from thin air. There are (at least) two routes to
the DeviceContext: Building up from a DXGI adapter, or building down
from a Window. Building down is faster and easier, but past experience
has shown there are some limitations that are not obvious until much
later. Building up is more complicated but provides better access,
especially important if my goal is to run a shader on the GPU.
All these can be spontaneously lost if the system goes to sleep or
the window is dragged from one monitor to another, so it is best to
assume they will need to be recreated on every repaint. I split the
paint operation into two parts: DrawCreate() and DrawUpdate().
DrawCreate() will recreate anything that does not already exist.
Here is the nasty web of prerequisites:
I need one or more ID2D1Bitmap1 that I can target from ID2D1DeviceContext
ID2D1Bitmap1 is derived from IDXGISurface
IDXGISurface is derived from IDXGISwapChain1
IDXGISwapChain1 is created from IDXGIFactory, ID3D11Device, and HWND.
IDXGIFactory is extracted from IDXGIAdapter
IDXGIAdapter is extracted from IDXGIDevice
ID2D1DeviceContext is created from ID2D1Device
ID2D1Device is created from ID2D1Factory and IDXGIDevice
ID2D1Factory is created by D2D1CreateFactory()
IDXGIDevice is derived from ID3D11Device
ID3D11Device is created by D3D11CreateDevice()
So, working backward, the order of creation evolves to:
D3D11CreateDevice() creates an ID3D11Device and ID3D11DeviceContext.
Retrieve the base IDXGIDevice device using ID3D11Device::QueryInterface()
Retrieve IDXGIAdapter using IDXGIDevice::GetAdapter()
Retrieve IDXGIFactory using IDXGIAdapter::GetParent()
Create IDXGISwapChain1 using IDXGIFactory::CreateSwapChainForHwnd()
Retrieve the IDXGISurface using IDXGISwapChain1::GetBuffer()
D2D1CreateFactory() returns an ID2D1Factory1.
pD2Factory->CreateDevice() to create a Direct2D device from the IDXGIDevice.
pD2Device->CreateDeviceContext() to create ID2D1DeviceContext pdcDraw. This
is the workhorse that will do most of the drawing operations.
Use pdcDraw->CreateBitmapFromDxgiSurface(IDXGISurface) to create the draw targets.
There are two types of target bitmaps: The final, write-only destination bitmap,
and intermediate compositing bitmaps that can be drawn to the final bitmap.
These are the resources that need to be kept for the lifetime of the
window:
PxlShader DirectX resources:
class PxlShader {
...
private:
...
D3D_FEATURE_LEVEL DxFeatures; // Supported feature set
IDXGIFactory2 *pDXGIFactory; // Creates SwapChain
IDXGISwapChain1 *pSwapChain; // Final display and pDXGISurface
IDXGISurface1 *pDXGISurface; // Used to create the bitmaps
IDXGIDevice *pDXGIDevice; // Base display device
ID2D1Factory1 *pD2Factory; // Creates Direct2D stuff
ID2D1Device *pD2Device; // Spawns ID2D1DeviceContext
ID2D1DeviceContext *pdcDraw; // The workhorse, used to draw everything.
ID2D1Bitmap1 *pbmImg; // Final offscreen image (write-only)
};
DrawCreate() creates the top-level DirectX/Direct2D resources:
PxlShader::DrawCreateD2Factory(void):
int PxlShader::DrawCreateD2Factory(void) {
int Err= ERR_OK;
HRESULT WinErr;
REFIID guid= __uuidof(ID2D1Factory1);
D2D1_FACTORY_OPTIONS options;
options.debugLevel= D2D1_DEBUG_LEVEL_INFORMATION;
// DEBUG_LEVEL will trigger exceptions if EndDraw() fails or the factory is released with
// outstanding (unreleased) objects.
if(!SUCCEEDED(WinErr= D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,guid,&options,(void**)&pD2Factory))) {
Err= Error(ERR_DIRECTX,"PxlShader:DrawCreateD2Factory: Unable to create D2D factory. [%X]",WinErr);
}
return(Err);
}
Creating the offscreen bitmaps. Note there are two types of bitmaps, CreateBitmapBase()
creates a bitmap from the SwapChain buffer. Anything drawn onto this bitmap will be visible
when the SwapChain buffer is presented. The base bitmap is write-only, there is no way to
copy from it or extract the pixels. CreateBitmapComposite() creates a bitmap that can be
used for compositing, it can be drawn onto other bitmaps (including the base bitmap) and
the pixels can be extracted to CPU-readable memory. Both bitmaps can be used as a target,
so a single ID2D1DeviceContext can be used to draw to any of the bitmaps by using
ID2D1DeviceContext::SetTarget() to switch between them.
CreateBitmap():
// Create a targetable bitmap from a DXGI surface.
// This serves as the final rendering destination bitmap.
int PxlShader::CreateBitmapBase(ID2D1DeviceContext *pdcDst, D2D1_SIZE_U szBitmap, ID2D1Bitmap1 *&pbmBase) {
int Err= ERR_OK;
HRESULT WinErr;
IDXGISurface *pSurface= 0;
D2D1_BITMAP_PROPERTIES1 bmProp;
Zero(bmProp);
bmProp.bitmapOptions= D2D1_BITMAP_OPTIONS_TARGET|D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
bmProp.pixelFormat= PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_IGNORE);
pdcDst->GetDpi(&bmProp.dpiX,&bmProp.dpiY);
if(!SUCCEEDED(WinErr= pdcDst->CreateBitmapFromDxgiSurface(pDXGISurface,bmProp,&pbmBase))) {
Err= Warn(ERR_DIRECTX,"PxlShader:CreateBitmapBase: Unable to create %ux%u bitmap.",szBitmap.width,szBitmap.height);
} else {
pdcDst->SetTarget(pbmBase);
}
return(Err);
}
// Create a targetable bitmap that can be used for compositing.
int PxlShader::CreateBitmapComposite(ID2D1DeviceContext *pdcDst, D2D1_SIZE_U szBitmap, ID2D1Bitmap1 *&pbmComposite) {
int Err= ERR_OK;
HRESULT WinErr;
D2D1_BITMAP_PROPERTIES1 bmProp;
Zero(bmProp);
bmProp.bitmapOptions= D2D1_BITMAP_OPTIONS_TARGET;
bmProp.pixelFormat= PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_PREMULTIPLIED);
pdcDst->GetDpi(&bmProp.dpiX,&bmProp.dpiY);
if(!SUCCEEDED(WinErr= pdcDst->CreateBitmap(szBitmap,0,0,bmProp,&pbmComposite))) {
Err= Warn(ERR_DIRECTX,"PxlShader:CreateBitmapComposite: Unable to create %ux%u bitmap.",szBitmap.width,szBitmap.height);
} else {
pdcDst->SetTarget(pbmComposite);
}
return(Err);
}
Comments are moderated. Anonymous comments are not visible
to others until moderated. Comments are owned by the author but may be
removed or reused (but not modified) by this site at any time without notice.