Search code examples
c++direct2d

Direct2D bitmap RAM usage optimization


Currently I wrote some code that loads original image with WIC, stores it in variable as ID2D1Bitmap* and then creates another resized one, via compatible render target and scale effect (I can provide example, if needed), so, for every basic image I have two bitmaps.

However, bitmaps use a lot of ram — loading just 10 2mb images costs more than 100mb of ram. I don’t understand why it uses ram at all, if it should be in GPU memory, as I understand.

So, I asking here the solution to reduce that ram usage.

I read about atlas method, but it seems to be hard to develop. Maybe there are another tricks?

Example of code

#include <Windows.h>

HDC hdcDevice = GetDC(NULL);
int xw = GetDeviceCaps(hdcDevice, HORZRES);
int yw = GetDeviceCaps(hdcDevice, VERTRES);

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);

#include <d2d1_1.h>
#include <dwrite.h>
#include <wincodec.h>

#pragma comment(lib, "d2d1")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dwrite.lib")

using namespace std;

template<class Interface>
inline void SafeRelease(
    Interface** ppInterfaceToRelease)
{
    if (*ppInterfaceToRelease != NULL)
    {
        (*ppInterfaceToRelease)->Release();
        (*ppInterfaceToRelease) = NULL;
    }

}

ID2D1Bitmap* bitmap;

ID2D1Factory* factory;
IWICImagingFactory* d2dWICFactory;
IWICFormatConverter* d2dConverter;
IDWriteFactory* writeFactory;
IWICFormatConverter* d2dConverter2 = nullptr;
ID2D1BitmapRenderTarget* back = nullptr;
ID2D1DeviceContext* tar = nullptr;
ID2D1DeviceContext* target = nullptr;

ID2D1Effect* scale = nullptr;

class UIElement
{
public:
    ID2D1Bitmap* imgOrig;
    ID2D1Bitmap* img;
    D2D1_SIZE_F si;
    ID2D1DeviceContext* tar;

    float x;
    float y;
    float width;
    float height;

    UIElement(ID2D1DeviceContext *tar, float x, float y, float width, float height)
    {
        this->tar = tar;
        this->x = x; this->y = y; this->width = width; this->height = height;
        this->img = nullptr;

        scale->SetValue(D2D1_SCALE_PROP_INTERPOLATION_MODE, D2D1_INTERPOLATION_MODE::D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC);
    }

    void setBackgroundImage(const wchar_t* path)
    {
        IWICBitmapDecoder* d2dDecoder;
        IWICBitmapFrameDecode* d2dBmpSrc;

        IWICFormatConverter* d2dConverter2 = nullptr;
        d2dWICFactory->CreateFormatConverter(&d2dConverter2);

        d2dWICFactory->CreateDecoderFromFilename(path, NULL, GENERIC_READ,
            WICDecodeMetadataCacheOnLoad, &d2dDecoder);
        if (d2dDecoder)
        {
            d2dDecoder->GetFrame(0, &d2dBmpSrc);
            if (d2dBmpSrc && d2dConverter2)
            {
                d2dConverter2->Initialize(d2dBmpSrc, GUID_WICPixelFormat32bppPBGRA,
                    WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut);

                tar->CreateBitmapFromWicBitmap(d2dConverter2, NULL, &imgOrig);

                if (imgOrig)
                {
                    si = imgOrig->GetSize();

                    RescaleImage();
                }
            }
        }

        SafeRelease(&d2dConverter2);
        SafeRelease(&d2dDecoder);
        SafeRelease(&d2dBmpSrc);
    }

    inline void RescaleImage(ID2D1Bitmap* cache = nullptr)
    {
        SafeRelease(&img);
        tar->CreateBitmap(D2D1::SizeU(width, height), 0, 0, D2D1::BitmapProperties(
            D2D1::PixelFormat(DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE::D2D1_ALPHA_MODE_PREMULTIPLIED)
        ), &img);

        if (cache != nullptr)
            scale->SetInput(0, cache);
        else if (this->imgOrig)
            scale->SetInput(0, this->imgOrig);

        scale->SetValue(D2D1_SCALE_PROP_SCALE, D2D1::Vector2F(this->width / si.width, this->height / si.height));

        ID2D1BitmapRenderTarget* rnd = nullptr;
        tar->CreateCompatibleRenderTarget(D2D1::SizeF(width, height), &rnd);
        ID2D1DeviceContext* rndc = nullptr;
        rnd->QueryInterface(&rndc);

        rndc->BeginDraw();
        rndc->DrawImage(scale);
        rndc->EndDraw();

        img->CopyFromRenderTarget(0, rndc, 0);

        SafeRelease(&rndc);
        SafeRelease(&rnd);
    }

    inline void Render()
    {
        D2D1_RECT_F rect = D2D1::RectF(this->x, this->y, this->x + this->width, this->y + this->height);

        if(img)
        this->tar->DrawBitmap(img, rect);
    }
}*elements[100] = { NULL };


inline void Render()
{
    tar->BeginDraw();
    tar->Clear(D2D1::ColorF(1,1,1,1));

    target->BeginDraw();
    target->Clear(D2D1::ColorF(1,0,1,1));

    for (int i = 0; i < 100 && elements[i]; i++)
        elements[i]->Render();

    auto hr = target->EndDraw();

    tar->DrawImage(bitmap);

    hr = tar->EndDraw();


}


int WINAPI WinMain(HINSTANCE hin, HINSTANCE, LPSTR, int)
{
    ReleaseDC(NULL, hdcDevice);

    WNDCLASS c = { NULL };
    c.lpszClassName = L"asd";
    c.lpfnWndProc = WndProc;
    c.hInstance = hin;
    c.style = CS_VREDRAW | CS_HREDRAW;
    c.hCursor = LoadCursor(NULL, IDC_ARROW);
    c.hbrBackground = CreateSolidBrush(RGB(255, 255, 255));
    RegisterClass(&c);

    int cx = 500, cy = 500;
    int x = xw / 2 - cx / 2, y = yw / 2 - cy / 2;

    HWND hwnd = CreateWindowEx(NULL, L"asd", L"asd", WS_POPUP | WS_VISIBLE, x, y, cx, cy, NULL, NULL, hin, 0);

    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
    CoInitialize(NULL);


    D2D1CreateFactory(D2D1_FACTORY_TYPE::D2D1_FACTORY_TYPE_MULTI_THREADED, &factory);
    CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
        __uuidof(IWICImagingFactory), (void**)(&d2dWICFactory));
    d2dWICFactory->CreateFormatConverter(&d2dConverter);
    DWriteCreateFactory(
        DWRITE_FACTORY_TYPE_SHARED,
        __uuidof(writeFactory),
        reinterpret_cast<IUnknown**>(&writeFactory)
    );
    d2dWICFactory->CreateFormatConverter(&d2dConverter2);

    D2D1_SIZE_U size = D2D1::SizeU(cx, cy);
    ID2D1HwndRenderTarget* a = nullptr;
    factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)), D2D1::HwndRenderTargetProperties(hwnd, size), &a);//);
    
    a->QueryInterface(&tar);
    
    tar->CreateCompatibleRenderTarget(&back);
    back->QueryInterface(&target);
    back->GetBitmap(&bitmap);

    target->CreateEffect(CLSID_D2D1Scale, &scale);
    scale->SetValue(D2D1_SCALE_PROP_INTERPOLATION_MODE, D2D1_INTERPOLATION_MODE::D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC);

    MSG msg;

    for (int i = 0; i < 10; i++)
    {
        elements[i] = new UIElement(target, 50*i, 10, 50, 50);
        elements[i]->setBackgroundImage(L"bitmap.bmp");
    }


    while (GetMessage(&msg, NULL, 0, 0))
    {
        Render();
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    CoUninitialize();

    return 0;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
    switch (message)
    {
    case WM_LBUTTONDOWN:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, message, wp, lp);
    }
    return NULL;
}

*I want Windows 7 support.


Solution

  • As Simon Mourier pointed in comments, it seems that in my code WIC bitmap's memory, allocated at RAM is not being free, even despite on realizing all wic's com objects.

    So, I tried to get bitmap from wic not to final direct2d bitmap I need but for temporary direct2d bitmap, then create empty final direct2d bitmap, copy there temp bitmap and release temp bitmap.

    This worked, now with 10 2mb png images (with their resized duplicates) my program uses few megabytes of RAM, and earlier about 120mb.

    // WIC loading stuff
    
    ID2D1Bitmap *temp = nullptr;
    ID2D1Bitmap *imgOrig = nullptr;
    
    target->CreateBitmapFromWicBitmap(d2dConverter, NULL, &temp);
    
    if (temp)
    {
        D2D1_SIZE_F size = temp->GetSize();
    
        target->CreateBitmap(D2D1::SizeU(size.width, size.height), 0, 0, D2D1::BitmapProperties(
                            D2D1::PixelFormat(DXGI_FORMAT::DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE::D2D1_ALPHA_MODE_PREMULTIPLIED)
                        ), &imgOrig);
    
        imgOrig->CopyFromBitmap(0, temp, 0);
        SafeRelease(&temp);
    }
    
    // Release WIC