Jump to content
  • Advertisement
MarcusAseth

2D DWrite text rendering is not rendering properly

Recommended Posts

Posted (edited)

Hi guys, I'm having a problem rendering with DWrite, and I don't understand why, can you help me figure it out?

As you can see in the image below, if you look carefully you'll notice that the top of R8 is cut (missing 1 row of pixels), the bottom of R11 is cut again, the 4 in R14 is rendered weird compared to the 4 in R4 and so on, if you look closely you'll spot more yourself.

I can't figure out why 😕

Under the image I'll also leave the code, in case I'm doing something wrong like with type conversion or stuff. Any help is much appreciated ^_^

renderProblem.PNG.38ef18d5f4d8b34ac9d6bf519443b8a6.PNG

#include "GBAEmulator_PCH.h"
#include "Disassembler.h"
#include "GBAEmulator.h"


Disassembler::Disassembler(LONG width, LONG height, HINSTANCE hInstance, GBAEmulator* emuInstance) :
	D2DWindowBase(width, height, hInstance, emuInstance),
	m_background(0.156f, 0.087f, 0.16f, 1.f),
	m_textFormat{ nullptr }
{
	//Init Window
	std::string className = "Disassembler";
	std::string windowName = "Disassembler";

	WNDCLASSEX clientClass{};
	clientClass.cbSize = sizeof(WNDCLASSEX);
	clientClass.style = CS_HREDRAW | CS_VREDRAW;
	clientClass.lpfnWndProc = GBAEmulator::DisassemblerWinProc;
	clientClass.hInstance = m_hInstance;
	//clientClass.hIcon =; TODO: Add Icon
	clientClass.hCursor = LoadCursor(m_hInstance, IDC_ARROW);
	clientClass.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
	clientClass.lpszClassName = className.c_str();
	//clientClass.hIconSm =; TODO: Add Icon
	DWORD windowStyle = WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX | WS_TABSTOP | WS_SYSMENU;

	m_isValid = InitWindow(windowName, clientClass, windowStyle, false);

	//Init DWrite
	if (m_isValid)
		m_isValid = InitDWrite();

	std::vector<std::wstring> tempEntries{
		L"PC:  ", L"R0:  ", L"R1:  ", L"R2:  ", L"R3:  ", L"R4:  ",
		L"R5:  ", L"R6:  ", L"R7:  ", L"R8:  ", L"R9:  ", L"R10: ",
		L"R11: ", L"R12: ", L"R13: ", L"R14: ", L"R15: ", L"R16: "
	};

	std::wstring value = L"-UNDEFINED-";

	FLOAT left{}, top{}, right{ 300.f }, bottom{ 50.f };
	for (auto& s : tempEntries)
	{
		m_entries.emplace_back(TextEntry{ s, value, D2D1_RECT_F{ left, top, right, bottom} });
		top += 30.f;
		bottom += 30.f;
	}
}

bool Disassembler::InitDWrite()
{
	//Set Text Format
	HRESULT hr;
	hr = m_DWriteFactory->CreateTextFormat(
		L"consolas",
		NULL,
		DWRITE_FONT_WEIGHT_NORMAL,
		DWRITE_FONT_STYLE_NORMAL,
		DWRITE_FONT_STRETCH_NORMAL,
		22.f,
		L"en-US",
		&m_textFormat
	);
	if (FAILED(hr))
	{
		MessageBox(NULL, "Failed to create TextFormat", "Error", MB_OK);
		return false;
	}

	//Set Colors
	m_renderTarget->CreateSolidColorBrush(
		D2D1::ColorF(D2D1::ColorF::SkyBlue),
		&m_fillBrush1
	);
	m_renderTarget->CreateSolidColorBrush(
		D2D1::ColorF(D2D1::ColorF::Crimson),
		&m_fillBrush2
	);
	return true;
}

Disassembler::~Disassembler()
{
	DestroyWindow(m_hwnd);
	if (m_textFormat) m_textFormat->Release();
	if (m_fillBrush1) m_fillBrush1->Release();
	if (m_fillBrush2) m_fillBrush2->Release();
}

void Disassembler::Updade(float deltaTime)
{

}

void Disassembler::Draw()
{
	m_renderTarget->BeginDraw();
	m_renderTarget->Clear(m_background);

	for (auto& entry : m_entries)
	{
		DrawEntryWithShadow(entry);
	}

	m_renderTarget->EndDraw();
}

void Disassembler::DrawEntryWithShadow(const TextEntry& entry)
{
	//shadow offset
	D2D1_RECT_F shadowPos = entry.position;
	shadowPos.top += 1.05f;
	shadowPos.left -= 0.95f;

	//draw text
	DrawEntry(entry.text, shadowPos, m_fillBrush2);
	DrawEntry(entry.text, entry.position, m_fillBrush1);

	D2D1_RECT_F valuePos = entry.position;
	FLOAT valueOffset = 50.f;
	valuePos.left   += valueOffset;
	valuePos.right  += valueOffset;
	shadowPos.left  += valueOffset;
	shadowPos.right += valueOffset;
	
	//draw value
	DrawEntry(entry.value, shadowPos, m_fillBrush2);
	DrawEntry(entry.value, valuePos, m_fillBrush1);
}

void Disassembler::DrawEntry(const std::wstring& text, const D2D1_RECT_F& pos, ID2D1SolidColorBrush* brush)
{
	m_renderTarget->DrawTextA(
		text.c_str(),
		static_cast<UINT>(text.size()),
		m_textFormat,
		pos,
		brush,
		D2D1_DRAW_TEXT_OPTIONS_NONE
	);
}

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites
Advertisement
Posted (edited)

UPDATE:

The solution boils down to: DO NOT USE ID2D1Factory::CreateHwndRenderTarget

Now my D2D initialization is fairly more complicated, the one in the code below.

Is it me or initializing D2D is a goddamned mess?!

you create a d3dDevice, use it to query the dxgiDevice, use that to create a d2ddevice, use that to create a d2dcontext, go back to the dxgiDevice and use that to get the adapter, use that to get the factory, use that to create the swap chain, use that to access the backbuffer, go back to the d2d context and use it to bind your bitmap to the back buffer, and finilly set your bitmap as the render target of the context.

This is a cursed maze created by an insane person at Microsoft.

 

	//Init Direct3D

	HRESULT hr;
	ID3D11Device* d3d_device;
	ID3D11DeviceContext* d3d_context;
	
	UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		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
	};

	hr = D3D11CreateDevice(nullptr,
						   D3D_DRIVER_TYPE_HARDWARE,
						   0,
						   creationFlags,
						   featureLevels,
						   _countof(featureLevels),
						   D3D11_SDK_VERSION,
						   &d3d_device,
						   NULL,
						   &d3d_context
	);
	if (FAILED(hr))
	{
		MessageBox(NULL, "Failed to initialize D3D", "Error", MB_OK);
		return false;
	}


	//Init Direct2D

	IDXGIDevice* dxgiDevice;
	d3d_device->QueryInterface(&dxgiDevice);

	D2D1_CREATION_PROPERTIES D2D1DeviceDesc{};
	D2D1DeviceDesc.threadingMode = D2D1_THREADING_MODE_SINGLE_THREADED;
	D2D1DeviceDesc.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;

	hr = D2D1CreateDevice(dxgiDevice, D2D1DeviceDesc, &m_device);
	if (FAILED(hr))
	{
		MessageBox(NULL, "Failed to create D2D1 Device", "Error", MB_OK);
		return false;
	}

	hr = m_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &m_context);
	if (FAILED(hr))
	{
		MessageBox(NULL, "Failed to create D2D1 Context", "Error", MB_OK);
		return false;
	}

	//Create Swap Chain
	IDXGIAdapter* dxgiAdapter = nullptr;
	hr = dxgiDevice->GetAdapter(&dxgiAdapter);
	if (FAILED(hr))
	{
		MessageBox(NULL, "Failed to get Adapter", "Error", MB_OK);
		return false;
	}

	IDXGIFactory2* dxgiFactory = nullptr;
	hr = dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory));
	if (FAILED(hr))
	{
		MessageBox(NULL, "Failed to get dxgiFactory", "Error", MB_OK);
		return false;
	}

	//swap chain desc
	DXGI_SWAP_CHAIN_DESC1 swapChainDesc{};
	swapChainDesc.Width = m_winWidth;
	swapChainDesc.Height = m_winHeight;
	swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
	swapChainDesc.Scaling = DXGI_SCALING_NONE;
	swapChainDesc.SampleDesc.Count = 1;
	swapChainDesc.SampleDesc.Quality = 0;
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
	swapChainDesc.BufferCount = 2;
	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;

	dxgiFactory->CreateSwapChainForHwnd(d3d_device,	m_hwnd,	&swapChainDesc,	NULL, NULL, &m_swapChain);
	if (FAILED(hr))
	{
		MessageBox(NULL, "Failed to create Swap Chain", "Error", MB_OK);
		return false;
	}

	//Create Back Buffer
	D2D1_BITMAP_PROPERTIES1 backBufferDesc{};
	backBufferDesc.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
	backBufferDesc.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
	backBufferDesc.dpiX = dpi;
	backBufferDesc.dpiY = dpi;
	backBufferDesc.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;

	IDXGISurface* dxgiBackBuffer;
	m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer));

	hr = m_context->CreateBitmapFromDxgiSurface(dxgiBackBuffer,	&backBufferDesc, &m_backBuffer);
	if (FAILED(hr))
	{
		MessageBox(NULL, "Failed to create back buffer", "Error", MB_OK);
		return false;
	}
	
	m_context->SetTarget(m_backBuffer);

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!