2017-11-25 19:52:44 chip
Page 2065
📢 PUBLIC
Nov 25 2017
D3Text code. The details are described here: Direct2D in Direct3D
☠
WARNING!
This code may have a bug that causes ~D3Text() to kick out a
_com_error exception when the D3Text object is destroyed. It seems to
work fine otherwise. I am currently investigating...
It may be a problem with the VMware graphics driver or some other
local thing. I can run the release build natively (outside the VM)
without any apparent problems. I need to try running it natively on a
system with Visual Studio installed.
TEXT Example Usage :
void Game::RenderText(void) {
D3Text text(d3dDevice.Get(),d3dContext.Get(),swapChain.Get());
// Print frame status text.
text.SetColor(D2D1::ColorF(D2D1::ColorF::Black));
text.Write(L"Frame %06llu %ufps (%6.2f,%6.2f,%6.2f) %3.1f %3.1f"
,frameCt,frameRate
,viewPt.x,viewPt.y,viewPt.z
,XMConvertToDegrees(viewVec.y),XMConvertToDegrees(viewVec.z)
);
// Draw frame rate graph.
text.Rectangle(D2D1::Point2F(10.0f,40.0f),D2D1::Point2F(210.0f,60.0f));
D2D1_POINT_2F pt0,pt1;
pt0= { 10.0f,60.0f-std::min((INT16)120,vecFrameRate[0])*(20.0f/120.0f) };
text.SetColor(D2D1::ColorF(D2D1::ColorF::Yellow));
for(UINT n1=1;n1<vecFrameRate.size();n1++) {
pt1= { 10.0f+n1,60.0f-std::min((INT16)120,vecFrameRate[n1])*(20.0f/120.0f) };
text.Line(pt0,pt1);
pt0= pt1;
}
}
TEXT D3Text.h :
#define D3TEXT_OBJ_SIZE 616
class D3Text {
public:
D3Text(void);
D3Text(ID3D11Device *pDevice, ID3D11DeviceContext *pContext, IDXGISwapChain1 *pSwap);
~D3Text(void);
HRESULT Create(ID3D11Device *pDevice, ID3D11DeviceContext *pContext);
void Begin(IDXGISwapChain1 *pSwap);
void End(void);
void Reset(void);
void SetColor(D2D1::ColorF &clr);
UINT Write(const WCHAR *Fmt,...);
void Line(D2D1_POINT_2F &pt0, D2D1_POINT_2F &pt1);
void Rectangle(D2D1_POINT_2F &pt0, D2D1_POINT_2F &pt1);
private:
class nD3Text *pObj;
BYTE Obj[D3TEXT_OBJ_SIZE];
};
TEXT D3Text.cpp :
/*************************************************************************/
/** D3Text.cpp: Use Direct2D and DirectWrite in a Direct3D environment. **/
/** (C)2017 nlited systems inc. **/
/*************************************************************************/
#include "pch.h"
#include <new>
#include <stdio.h>
#include <windows.h>
#include <d2d1_2.h>
#include <d2d1_1helper.h>
#include <dwrite_1.h>
#include <wrl/client.h>
#include "Game.h"
#pragma message(__FILE__": Optimizer disabled.")
#pragma optimize("",off)
#pragma comment(lib,"d2d1.lib")
#pragma comment(lib,"dwrite.lib")
using namespace D2D1;
#define Zero(O) memset(&O,0,sizeof(O))
#define STRSIZE(S) (sizeof(S)/sizeof(S[0]) - 1)
#define CHECK_OBJ_SIZE(name,size) \
template<int N> struct name##_CheckSizeT { short operator()() { return((N+0x7FFF)-size); } }; \
static void name##_CheckSize(void) { name##_CheckSizeT<sizeof(name)>()(); }
#define SafeRelease(pObj) { if(pObj) pObj->Release(); pObj= 0; }
/*************************************************************************/
/** nD3Text: Internal class. **/
/*************************************************************************/
#define TEXT_MAX 200
class nD3Text {
public:
nD3Text(D3Text *pD3Text);
~nD3Text(void);
HRESULT Create(void);
HRESULT Create(ID3D11Device *pDevice, ID3D11DeviceContext *pContext);
void Reset(void);
HRESULT Begin(IDXGISwapChain1 *pSwap);
HRESULT End(void);
void SetColor(ColorF &clr);
UINT Write(const WCHAR *Fmt, va_list ArgList);
void Line(D2D1_POINT_2F &pt0, D2D1_POINT_2F &pt1);
void Rectangle(D2D1_POINT_2F &pt0,D2D1_POINT_2F &pt1);
private:
D3Text *pText; //Parent D3Text interface object.
bool isCreated; //Was everything created successfully?
bool isBegun; //Was Begin() called?
WCHAR Text[TEXT_MAX]; //Final output text.
UINT ChrCt; //Valid characters in Text[]
WCHAR FontName[40]; //Font face
DWRITE_FONT_WEIGHT FontWgt; //Font weight DWRITE_FONT_WEIGHT_REGULAR
DWRITE_FONT_STYLE FontStyle; //Font style DWRITE_FONT_STYLE_NORMAL
float FontHgt; //Font height (points)
ID2D1SolidColorBrush *pbrText; //Text color
IDWriteTextFormat *pFormat; //DWrite text formatter
ID3D11Device *p3dDevice; //Direct3D 11.1 device
ID3D11DeviceContext *p3dContext; //Direct3D 11.1 device context
IDXGIDevice *pdxDevice; //Common DirectX device
IDXGISurface *pdxSurface; //Common DirectX surface, derived from the 3D swap chain.
ID2D1Device1 *p2dDevice; //Direct2D device
ID2D1DeviceContext1 *p2dContext; //Direct2D device context
ID2D1Bitmap1 *p2dBitmap; //Direct2D target bitmap
D2D1_SIZE_F szLayout; //Size (pixels) of p2dBitmap
D2D1_RECT_F rLayout; //Layout rectangle
};
// If this generates a compiler warning, DBTEXT_OBJ_SIZE needs to be updated.
CHECK_OBJ_SIZE(nD3Text,D3TEXT_OBJ_SIZE);
/*************************************************************************/
/** Public interface **/
/*************************************************************************/
D3Text::D3Text(void) {
Zero(Obj);
pObj= new(Obj) nD3Text(this);
}
D3Text::D3Text(ID3D11Device *pDevice, ID3D11DeviceContext *pContext, IDXGISwapChain1 *pSwap) {
Zero(Obj);
if(pObj= new(Obj) nD3Text(this)) {
if(SUCCEEDED(pObj->Create(pDevice,pContext)))
pObj->Begin(pSwap);
}
}
D3Text::~D3Text(void) {
if(pObj) {
pObj->~nD3Text();
pObj= 0;
}
}
HRESULT D3Text::Create(ID3D11Device *pDevice, ID3D11DeviceContext *pContext) {
return(pObj ? pObj->Create(pDevice,pContext):ERROR_INVALID_HANDLE);
}
void D3Text::Begin(IDXGISwapChain1 *pSwap) {
if(pObj)
pObj->Begin(pSwap);
}
void D3Text::End(void) {
if(pObj)
pObj->End();
}
void D3Text::Reset(void) {
if(pObj)
pObj->Reset();
}
void D3Text::SetColor(ColorF &clr) {
if(pObj)
pObj->SetColor(clr);
}
UINT D3Text::Write(const WCHAR *Fmt, ...) {
va_list ArgList;
va_start(ArgList,Fmt);
UINT ChrCt= pObj ? pObj->Write(Fmt,ArgList):0;
va_end(ArgList);
return(ChrCt);
}
void D3Text::Line(D2D1_POINT_2F &pt0, D2D1_POINT_2F &pt1) {
if(pObj)
pObj->Line(pt0,pt1);
}
void D3Text::Rectangle(D2D1_POINT_2F &pt0,D2D1_POINT_2F &pt1) {
if(pObj)
pObj->Rectangle(pt0,pt1);
}
/*************************************************************************/
/** nText **/
/*************************************************************************/
static ComPtr<ID2D1Factory2> gD2DFactory;
static ComPtr<IDWriteFactory1> gDWriteFactory;
nD3Text::nD3Text(D3Text *pD3Text) {
pText= pD3Text;
wcsncpy(FontName,L"Arial",STRSIZE(FontName));
FontWgt= DWRITE_FONT_WEIGHT_BOLD;
FontStyle= DWRITE_FONT_STYLE_NORMAL;
FontHgt= 18.0f;
}
nD3Text::~nD3Text(void) {
if(isBegun)
End();
isCreated= false;
SafeRelease(pbrText);
SafeRelease(p2dBitmap);
SafeRelease(pdxSurface);
SafeRelease(p2dDevice);
SafeRelease(pdxDevice);
SafeRelease(p3dContext);
SafeRelease(p3dDevice);
SafeRelease(pFormat);
SafeRelease(p2dContext); //This triggers _com_error exception (debug and/or vmware only?)
}
HRESULT nD3Text::Create(void) {
HRESULT Err= 0;
D2D1_FACTORY_OPTIONS d2dOptions;
Zero(d2dOptions);
d2dOptions.debugLevel= D2D1_DEBUG_LEVEL_INFORMATION;
auto uuidD2D= __uuidof(ID2D1Factory2);
auto uuidDWrite= __uuidof(IDWriteFactory1);
auto pDWF= reinterpret_cast<IUnknown**>(gDWriteFactory.ReleaseAndGetAddressOf());
if(!gD2DFactory.Get() && FAILED(Err= D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,uuidD2D,&d2dOptions,(void**)gD2DFactory.ReleaseAndGetAddressOf()))) {
//Failed to create Direct2D factory.
} else if(!gDWriteFactory.Get() && FAILED(Err= DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,uuidDWrite,pDWF))) {
//Failed to create DirectWrite factory.
} else if(FAILED(Err= gDWriteFactory->CreateTextFormat(FontName,0,FontWgt,FontStyle,DWRITE_FONT_STRETCH_NORMAL,FontHgt,L"en-us",&pFormat))) {
//Failed to create the font.
} else {
pFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
pFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
}
return(Err);
}
HRESULT nD3Text::Create(ID3D11Device *pDevice, ID3D11DeviceContext *pContext) {
HRESULT Err= 0;
// I need to re-query the DirectX 11.1 interface in case the caller is using an older one.
if(FAILED(Create())) {
//Failed to create factories.
} else if(FAILED(Err= pDevice->QueryInterface(__uuidof(ID3D11Device1),(void**)&p3dDevice))) {
//Device is invalid or not DX11.1
} else if(FAILED(Err= pContext->QueryInterface(__uuidof(ID3D11DeviceContext1),(void**)&p3dContext))) {
//Context is invalid or not DX11.1
} else if(FAILED(Err= pDevice->QueryInterface(__uuidof(IDXGIDevice1),(void**)&pdxDevice))) {
//Device does not support IDXGI
} else if(FAILED(Err= gD2DFactory->CreateDevice(pdxDevice,&p2dDevice))) {
//Failed to create D2D Device from DXGI.
//Err= Error(Err,L"D3Text:Create: Failed to create D2D device from DXGI device.");
} else if(FAILED(Err= p2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE,&p2dContext))) {
//Failed to create D2D device context.
} else {
p2dContext->CreateSolidColorBrush(ColorF(ColorF::Black),&pbrText);
isCreated= true;
}
SafeRelease(p3dDevice);
SafeRelease(p3dContext);
SafeRelease(pdxDevice);
SafeRelease(p2dDevice);
return(Err);
}
HRESULT nD3Text::Begin(IDXGISwapChain1 *pSwap) {
HRESULT Err= 0;
ChrCt= 0;
if(!isCreated)
return(ERROR_NOT_READY);
if(FAILED(Err= pSwap->GetBuffer(0,__uuidof(pdxSurface),(void**)&pdxSurface))) {
//Failed to create DXGI surface
} else {
auto pPixelFmt= PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_IGNORE);
auto pProp= BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET|D2D1_BITMAP_OPTIONS_CANNOT_DRAW,pPixelFmt);
if(FAILED(Err= p2dContext->CreateBitmapFromDxgiSurface(pdxSurface,pProp,&p2dBitmap))) {
//Failed to create D2D bitmap.
} else {
p2dContext->SetTarget(p2dBitmap);
FLOAT dpiX,dpiY;
szLayout= p2dBitmap->GetSize();
rLayout={ 0,0,szLayout.width,szLayout.height };
gD2DFactory->GetDesktopDpi(&dpiX,&dpiY);
p2dContext->SetDpi(dpiX,dpiY);
p2dContext->BeginDraw();
isBegun= true;
}
}
return(Err);
}
HRESULT nD3Text::End(void) {
HRESULT Err= 0;
if(isBegun) {
p2dContext->EndDraw();
p2dContext->SetTarget(0);
SafeRelease(p2dBitmap);
SafeRelease(pdxSurface);
}
isBegun= false;
return(Err);
}
void nD3Text::Reset(void) {
//TODO: Release everything.
isBegun= false;
}
void nD3Text::SetColor(ColorF &clr) {
if(isCreated) {
SafeRelease(pbrText);
p2dContext->CreateSolidColorBrush(clr,&pbrText);
}
}
UINT nD3Text::Write(const WCHAR *Fmt, va_list ArgList) {
int ct= 0;
if(!isBegun)
return(0);
ct+= _vsnwprintf(Text+ChrCt,STRSIZE(Text)-ChrCt,Fmt,ArgList);
if(ct<0)
ct= STRSIZE(Text)-ChrCt;
ChrCt+= ct;
if(ChrCt)
p2dContext->DrawTextW(Text,ChrCt,pFormat,rLayout,pbrText);
return(ChrCt);
}
void nD3Text::Line(D2D1_POINT_2F &pt0, D2D1_POINT_2F &pt1) {
if(isBegun)
p2dContext->DrawLine(pt0,pt1,pbrText);
}
void nD3Text::Rectangle(D2D1_POINT_2F &pt0,D2D1_POINT_2F &pt1) {
if(isBegun) {
D2D1_RECT_F rect= { pt0.x,pt0.y,pt1.x,pt1.y };
p2dContext->DrawRectangle(rect,pbrText);
}
}
//EOF: TEXT.CPP
WebV7 (C)2018 nlited | Rendered by tikope in 31.258ms | 3.133.129.8