Hello!
I'm working in my own DirectX11.1 engine. Text rendering used to be done via Direct2D and DirectWrite. As rendering text that way is pretty slow I buffered results to small canvases and only updated them when the strings changed.
To improve performance I now render glyphs to an atlas (using the same old D2D and DirectWrite approach), then use a IDWriteTextLayout layout, call its Draw function with a custom renderer (IDWriteTextRenderer), which iterates over the DWRITE_GLYPH_RUN and adds instances to an instanced glyph quad renderer.
This works fine and is much faster than the previous attempt. However, the rendering breaks weirdly on some strings. I first noticed this in my player debug panel:
It seems to break after the small f but only sometimes. If I replace the f with some other character it works fine:
I checked the glyphAdvances in the DrawGlyphRun callback. They are all over the place (they differ from what they should be on a regular string) after those troublesome fs. Then I realized the last character of a messed up string is missing. Turns out the glyphRun->glyphCount is indeed 1 lower than it should be.
I used GetClusterMetrics to analyze the IDWriteTextLayout:
hr = pDWriteFactory_->CreateTextLayout(wszText_, cTextLength_, text->font->textFormat, text->width, text->height, &text->pDWriteTextLayout);
if (FAILED(hr)) ...
DWRITE_CLUSTER_METRICS clusterMetrics[64];
UINT32 actualClusterCount = 0;
text->pDWriteTextLayout->GetClusterMetrics(clusterMetrics, 64, &actualClusterCount);
kuOutPrintf("\n pDWriteTextLayout(%S): cTextLength_(%d) actualClusterCount(%d)", wszText_, (int)cTextLength_, (int)actualClusterCount);
if (ID == 117 || ID == 118) breakpoint
This showed me that
pDWriteTextLayout(tlick: state(0) progress(0.0)): cTextLength_(29) actualClusterCount(29)
pDWriteTextLayout(flick: state(0) progress(0.0)): cTextLength_(29) actualClusterCount(28)
the actualClusterCount is lower here already and that the issue is not in the rendering itself. Looking at the clusterMetrics showed me that the length member of the first clusterMetrics entry on the faulty “flick” string has a length of 2, instead of 1 like all the others. The string I then provided as a fixed literal (forgot what it's called in case that's wrong) L"flick: state(0) progress(0.0)" to prevent string formatting issues and checked the string's codepoints, just in case (102 (f), 108 (l) and so on as it should be).
I made sure that the settings for my IDWriteTextFormat text->font->textFormat are identical on both calls by checking all members (get functions from the interface). I replaced my font Josefin Sans with Arial and guess what, the issue was gone. I thought the font might be the issue in the end then. So I downloaded Noto Sans but the same problem appeared, identical misplacement (and length 2 in the clusterMetrics).
Using the IDWriteTextAnalyzer seems a bit difficult, I attempted that for a bit earlier but I thought I could see something there.
Does anyone have an idea what I could do? The issue is in CreateTextLayout (see code above) in my opinion. Is it a DirectWrite bug?
Thank you.
EDIT: Also, I adjusted text->width and text→height to make sure it's not a size based formatting issue.