I just bumped into an obscure issue, while implementing a prototype for a native Win32 UI using Direct2D/DirectWrite for high performance text rendering. Things went fine and looked promising: Resizing the application window was devoid of any lag, and the entire text rendering was as solid as one could expect.
That is, until I decided to rename the binary to "ride.exe". At that point, the OS opted to force my application into "glitch mode". Resizing suddenly introduced a very noticeable lag, combined with gaps when sizing up, that would only eventually fill again. Plus, text output got all jumpy, with the perceived font size varying. Which is odd considering that I only ever create the font resource at application startup.
All those artifacts instantly go away, if I rename the binary to anything else, and instantly return, when I choose to call it "ride.exe" again. No recompilation or linking involved, it's literally just a rename operation.
I'm lost at this point, though it looks a lot like the OS is using some heuristic that involves the image name to decide, how to execute the code. So, what's going on here?
This is a complete repro to illustrate the issue:
#include <Windows.h>
#include <d2d1.h>
#include <d2d1_1helper.h>
#include <dwrite.h>
#pragma comment(lib, "D2d1.lib")
#pragma comment(lib, "Dwrite.lib")
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void create_graphics_resources(HWND const hwnd) noexcept;
void discard_graphics_resources() noexcept;
void resize_render_target(HWND const hwnd) noexcept;
void create_dw_resources() noexcept;
void discard_dw_resources() noexcept;
ID2D1Factory1* pd2d_factory { nullptr };
ID2D1HwndRenderTarget* prender_target { nullptr };
ID2D1SolidColorBrush* ptext_color_brush { nullptr };
IDWriteFactory* pdw_factory { nullptr };
IDWriteTextFormat* pdw_text_format { nullptr };
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int nCmdShow)
{
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
WNDCLASSEXW wcex {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszClassName = L"d2d_mcve";
RegisterClassExW(&wcex);
auto const hWnd { CreateWindowExW(0, L"d2d_mcve", L"D2D MCVE", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT,
0, nullptr, nullptr, hInstance, nullptr) };
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg {};
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
CoUninitialize();
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: {
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, D2D1_FACTORY_OPTIONS {}, &pd2d_factory);
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(pdw_factory),
reinterpret_cast<IUnknown**>(&pdw_factory));
}
break;
case WM_DESTROY:
discard_dw_resources();
discard_graphics_resources();
pdw_factory->Release();
pd2d_factory->Release();
PostQuitMessage(0);
break;
case WM_SIZE:
resize_render_target(hWnd);
InvalidateRect(hWnd, nullptr, FALSE);
break;
case WM_PAINT: {
PAINTSTRUCT ps {};
BeginPaint(hWnd, &ps);
create_graphics_resources(hWnd);
prender_target->BeginDraw();
prender_target->Clear(D2D1::ColorF(D2D1::ColorF::Black));
create_dw_resources();
auto const target_size { prender_target->GetSize() };
prender_target->SetTransform(D2D1::Matrix3x2F::Identity());
auto const& placeholder_text { L"Lorem ipsum dolor sit amet, consectetur adipiscing elit." };
prender_target->DrawTextW(placeholder_text, ARRAYSIZE(placeholder_text) - 1, pdw_text_format,
D2D1::RectF(0.0f, 0.0f, target_size.width, target_size.height),
ptext_color_brush);
auto const hr { prender_target->EndDraw() };
if (hr == D2DERR_RECREATE_TARGET)
{
discard_graphics_resources();
InvalidateRect(hWnd, nullptr, FALSE);
}
EndPaint(hWnd, &ps);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
void create_graphics_resources(HWND const hwnd) noexcept
{
if (prender_target == nullptr)
{
RECT rc {};
GetClientRect(hwnd, &rc);
auto const size { D2D1::SizeU(static_cast<UINT32>(rc.right), static_cast<UINT32>(rc.bottom)) };
pd2d_factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hwnd, size), &prender_target);
prender_target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White, 1.0f), &ptext_color_brush);
}
}
void discard_graphics_resources() noexcept
{
if (ptext_color_brush != nullptr)
{
ptext_color_brush->Release();
ptext_color_brush = nullptr;
}
if (prender_target != nullptr)
{
prender_target->Release();
prender_target = nullptr;
}
}
void resize_render_target(HWND const hwnd) noexcept
{
if (prender_target != nullptr)
{
RECT rc {};
GetClientRect(hwnd, &rc);
auto const size { D2D1::SizeU(static_cast<UINT32>(rc.right), static_cast<UINT32>(rc.bottom)) };
prender_target->Resize(size);
}
}
void create_dw_resources() noexcept
{
if (pdw_text_format == nullptr)
{
pdw_factory->CreateTextFormat(L"Courier New", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 48.0f, L"en-US", &pdw_text_format);
}
}
void discard_dw_resources() noexcept
{
if (pdw_text_format != nullptr)
{
pdw_text_format->Release();
pdw_text_format = nullptr;
}
}
- Save the source code as "main.cpp".
- Compile the code using the following command line:
cl.exe /O2 /EHsc /D "NDEBUG" /D "UNICODE" /MD main.cpp user32.lib ole32.lib
- Create a directory named "choosing_a_trident_for_a_buttplug_is_not_prudent" anywhere in the filesystem and copy "main.exe" into that directory.
- Launch "main.exe" and observe the behavior of the application when resizing the main window.
- Rename the copy of "main.exe" under "choosing_a_trident_for_a_buttplug_is_not_prudent" to "ride.exe", launch "ride.exe" and observe the behavior when resizing the main window.
The last step exhibits all sorts of visual glitches on my system (Windows 10 19041.330). The application instantly returns to normal operation when either renaming the executable image to anything but "ride.exe", or renaming every parent directory to no longer contain the phrase "ride". Casing doesn't make a difference.