i was using a dc render target normally but i saw that I am getting a little bit of flickering, so i added a bitmap render target as to run the drawing operations on then draw on the dc acting as a double buffer my high memory usage (almost 60MB) is due to
pDCRenderTarget->CreateCompatibleRenderTarget(&pBitmapRenderTarget);
the huge memory consumption is on when I am resizing the window. (note first time posting an issue since i almost always find the question already asked)
My main.cpp:
#include <windows.h>
#include <d2d1.h>
#pragma comment(lib, "d2d1")
// Global variables
ID2D1Factory* pFactory = nullptr;
ID2D1DCRenderTarget* pDCRenderTarget = nullptr;
ID2D1BitmapRenderTarget* pBitmapRenderTarget = nullptr;
ID2D1Factory* GetFactory() {
if (pFactory == nullptr) {
// Create the Direct2D factory
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pFactory);
}
return pFactory;
}
// Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_PAINT:
{
// Bind the DC render target to the HDC
RECT rc;
GetClientRect(hwnd, &rc);
HDC hdc = GetDC(hwnd);
pDCRenderTarget->BindDC(hdc, &rc);
// Create a compatible bitmap render target
pDCRenderTarget->CreateCompatibleRenderTarget(&pBitmapRenderTarget);
// Begin drawing on the bitmap render target
ID2D1Bitmap* pBitmap = nullptr;
pBitmapRenderTarget->BeginDraw();
// Clear the background
pBitmapRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
// Draw a rectangle
ID2D1SolidColorBrush* pBrush = nullptr;
pBitmapRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Blue), &pBrush);
pBitmapRenderTarget->FillRectangle(D2D1::RectF(50, 50, 200, 200), pBrush);
// End drawing
pBitmapRenderTarget->EndDraw();
// Get the bitmap from the render target
pBitmapRenderTarget->GetBitmap(&pBitmap);
// Draw the bitmap to the DC render target
pDCRenderTarget->BeginDraw();
pDCRenderTarget->DrawBitmap(pBitmap, D2D1::RectF(0, 0, rc.right, rc.bottom)); // Destination rectangle
pDCRenderTarget->EndDraw();
// Release resources
pBrush->Release();
pBitmap->Release();
pBitmapRenderTarget->Release();
pBitmapRenderTarget = nullptr;
break;
}
case WM_DESTROY:
PostQuitMessage(0);
pDCRenderTarget->Release();
pDCRenderTarget = nullptr;
if (pFactory) pFactory->Release();
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
// Main Entry Point
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// Register the window class
const wchar_t CLASS_NAME[] = L"Direct2DDoubleBufferingWindow";
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window
HWND hwnd = CreateWindowEx(
0, // Optional window styles
CLASS_NAME, // Window class
L"Direct2D Double Buffering", // Window title
WS_OVERLAPPEDWINDOW, // Window style
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
nullptr, nullptr, hInstance, nullptr);
if (!hwnd)
return -1;
// Create a DC render target
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE));
GetFactory()->CreateDCRenderTarget(&rtProps, &pDCRenderTarget);
ShowWindow(hwnd, nCmdShow);
// Run the message loop
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
The first thing to do is process WM_PAINT
like instructed in the official documentation and in this Direct2D tutorial Drawing with Direct2D, ie: you need to tell Windows you have handled the WM_PAINT
message.
So you code should instead be like this:
case WM_PAINT:
{
// ==> I will handle WM_PAINT
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// Bind the DC render target to the HDC
RECT rc;
GetClientRect(hwnd, &rc);
pDCRenderTarget->BindDC(hdc, &rc);
// Create a compatible bitmap render target
pDCRenderTarget->CreateCompatibleRenderTarget(&pBitmapRenderTarget);
...
pBitmapRenderTarget->Release();
pBitmapRenderTarget = nullptr;
// ==> now, I have handled WM_PAINT
EndPaint(hwnd, &ps);
break;
If you don't do this, Windows will continue sending WM_PAINT
messages continuously and this will eat your machine's resource (not really the WM_PAINT
itself, but your code in there).
Then, depending on your goals, you also maybe keep the pBitmapRenderTarget
instead of creating it each time you handle WM_PAINT
, it really depends what you plan to do when you "paint" in the general sense (real time updates, seldom changes, etc.)
Note with Direct2D (and Direct3D, etc.) another way to do this is to just do BeginPaint
+EndPaint
once in WM_PAINT
and do the rendering elsewhere, for example: https://gamedev.stackexchange.com/questions/12901/wm-paint-and-direct3d