dev.nlited.com

>>

DirectX

<<<< prev
next >>>>

2022-12-16 18:54:56 chip Page 2470 📢 PUBLIC

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:

So, working backward, the order of creation evolves to:

  1. D3D11CreateDevice() creates an ID3D11Device and ID3D11DeviceContext.
  2. Retrieve the base IDXGIDevice device using ID3D11Device::QueryInterface()
  3. Retrieve IDXGIAdapter using IDXGIDevice::GetAdapter()
  4. Retrieve IDXGIFactory using IDXGIAdapter::GetParent()
  5. Create IDXGISwapChain1 using IDXGIFactory::CreateSwapChainForHwnd()
  6. Retrieve the IDXGISurface using IDXGISwapChain1::GetBuffer()
  7. D2D1CreateFactory() returns an ID2D1Factory1.
  8. pD2Factory->CreateDevice() to create a Direct2D device from the IDXGIDevice.
  9. pD2Device->CreateDeviceContext() to create ID2D1DeviceContext pdcDraw. This is the workhorse that will do most of the drawing operations.
  10. 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::DrawCreate(void): int PxlShader::DrawCreate(void) { int Err= ERR_OK; HRESULT WinErr; D2D1_SIZE_U szWnd= { (UINT32)RWID(rWnd), (UINT32)RHGT(rWnd) }; D2D1_DEVICE_CONTEXT_OPTIONS DCOptions= D2D1_DEVICE_CONTEXT_OPTIONS_NONE; if(!pDXGIDevice && IsErr(DrawCreateDX())) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create DXGI device."); } else if(!pSwapChain && IsErr(Err= DrawCreateSwapChain())) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create SwapChain."); } else if(!pDXGISurface && !SUCCEEDED(WinErr= pSwapChain->GetBuffer(0,IID_PPV_ARGS(&pDXGISurface)))) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to retrieve DXGI surface. [%X]",WinErr); } else if(!pD2Factory && IsErr(Err= DrawCreateD2Factory())) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create Direct2D factory."); } else if(!pD2Device && !SUCCEEDED(WinErr= pD2Factory->CreateDevice(pDXGIDevice,&pD2Device))) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create Direct2D device."); } else if(!pdcDraw && !SUCCEEDED(WinErr= pD2Device->CreateDeviceContext(DCOptions,&pdcDraw))) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create Draw context."); } else if(!pbmImg && IsErr(Err= CreateBitmapBase(pdcDraw,szWnd,pbmImg))) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreate: Unable to create base bitmap."); } return(Err); }

DrawCreateDX() creates the intermediate D3D11 and DXGI resources:

PxlShader::DrawCreateDX(void): int PxlShader::DrawCreateDX(void) { int Err= ERR_OK; HRESULT WinErr; UINT Flags= D3D11_CREATE_DEVICE_BGRA_SUPPORT; static const D3D_FEATURE_LEVEL Levels[]= { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 }; D3D_DRIVER_TYPE Type= D3D_DRIVER_TYPE_HARDWARE; UINT ctLevels= ARRAYSIZE(Levels); UINT Version= D3D11_SDK_VERSION; ID3D11Device *pD3D11Device= 0; ID3D11DeviceContext *pD3D11DC= 0; IDXGIAdapter *pDXGIAdapter= 0; if(!SUCCEEDED(WinErr= D3D11CreateDevice(0,Type,0,Flags,Levels,ctLevels,Version,&pD3D11Device,&DxFeatures,&pD3D11DC))) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateDX: D3D11CreateDevice() failed."); } else if(!SUCCEEDED(WinErr= pD3D11Device->QueryInterface(&pDXGIDevice))) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateDX: Unable to retrieve DXGI device. [%X]",WinErr); } else if(!SUCCEEDED(WinErr= pDXGIDevice->GetAdapter(&pDXGIAdapter))) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateDX: Unable to retrieve DXGI adapter. [%X]",WinErr); } else if(!SUCCEEDED(WinErr= pDXGIAdapter->GetParent(IID_PPV_ARGS(&pDXGIFactory)))) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateDX: Unable to retrieve DXGI factory. [%X]",WinErr); } SafeRelease(pDXGIAdapter); SafeRelease(pD3D11Device); SafeRelease(pD3D11DC); return(Err); }

Creating the SwapChain:

PxlShader::DrawCreateSwapChain(void): int PxlShader::DrawCreateSwapChain(void) { int Err= ERR_OK; HRESULT WinErr; DXGI_SWAP_CHAIN_DESC1 SwapDesc; Zero(SwapDesc); SwapDesc.Format= DXGI_FORMAT_B8G8R8A8_UNORM; SwapDesc.SampleDesc.Count= 1; SwapDesc.BufferUsage= DXGI_USAGE_RENDER_TARGET_OUTPUT; SwapDesc.BufferCount= 2; SwapDesc.Scaling= DXGI_SCALING_STRETCH; SwapDesc.SwapEffect= DXGI_SWAP_EFFECT_DISCARD; if(!SUCCEEDED(WinErr= pDXGIFactory->CreateSwapChainForHwnd(pDXGIDevice,hWnd,&SwapDesc,0,0,&pSwapChain))) { Err= Warn(ERR_DIRECTX,"PxlShader:DrawCreateSwapChain: Unable to create SwapChain. [%X]",WinErr); } return(Err); }

Creating the Direct2D factory:

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); }

Release all the resources:

ReleaseEverything(): void PxlShader::ReleaseEverything(void) { SafeRelease(pbmImg); SafeRelease(pdcDraw); SafeRelease(pD2Device); SafeRelease(pD2Factory); SafeRelease(pDXGIDevice); SafeRelease(pDXGISurface); SafeRelease(pSwapChain); SafeRelease(pDXGIFactory); }

Moderator: close comments Comments are closed.

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.

HTML
  1. Moderator: [] approve delete HTML



WebV7 (C)2018 nlited | Rendered by tikope in 140.167ms | 18.222.110.231