Thursday, February 3, 2022

[SOLVED] RenderTarget->GetSize not working

Issue

To learn myself Direct2D i'm following this example from the MSDN.

I have however one issue. The call D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize(); allways returns a size of 0,0 and in the debugger causes an exception on the DrawLine call. If I leave out the GetSize() call and fill in the D2D1_SIZE_F structure with valid values it works.

The relevant code for initializing the render target is:

    RECT rc;
    GetClientRect(m_hwnd, &rc);

    D2D1_SIZE_U size = D2D1::SizeU(
        rc.right - rc.left,
        rc.bottom - rc.top
        );

    // Create a Direct2D render target.
    hr = m_pDirect2dFactory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(),
        D2D1::HwndRenderTargetProperties(m_hwnd, size),
        &m_pRenderTarget
        );

I have verified with the debugger that valid values are past in size.

The part of the drawing code where GetSize is called:

    m_pRenderTarget->BeginDraw();

    m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());

    m_pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));
    D2D1_SIZE_F rtSize = m_pRenderTarget->GetSize();
    // Draw a grid background.
    int width = static_cast<int>(rtSize.width);
    int height = static_cast<int>(rtSize.height);

    for (int x = 0; x < width; x += 10)
    {
        m_pRenderTarget->DrawLine(
            D2D1::Point2F(static_cast<FLOAT>(x), 0.0f),
            D2D1::Point2F(static_cast<FLOAT>(x), rtSize.height),
            m_pLightSlateGrayBrush,
            0.5f
            );
    }

So my question is why does GetSize() return 0,0 and causes an AV later on?

BTW: I'm using: Windows 7 Ultimate 64-bits Code::Blocks IDE TDM-GCC-64 gcc compiler v4.8.1 I'm compiling in Unicode mode #define UNICODE Problem occurs regardles if I compile to 32-bits or 64-bits (yes I made a few minor adjustments for 64-bits mode to make sure I had a valid pointer to the application object in WndProc)


Solution

Why does GetSize() return 0,0 and causes an AV later on?

Because the call to GetSize generated by GCC/MinGW-W64 doesn't match the calling convention of the implementation in d2d1.dll. The return type D2D_SIZE_F of GetSize is a struct. According to Microsoft Docs there are two ways to return a struct from a function:

User-defined types can be returned by value from global functions and static member functions. To return a user-defined type by value in RAX, it must have a length of 1, 2, 4, 8, 16, 32, or 64 bits. It must also have no user-defined constructor, destructor, or copy assignment operator. It can have no private or protected non-static data members, and no non-static data members of reference type. It can't have base classes or virtual functions. And, it can only have data members that also meet these requirements. (This definition is essentially the same as a C++03 POD type. Because the definition has changed in the C++11 standard, we don't recommend using std::is_pod for this test.) Otherwise, the caller must allocate memory for the return value and pass a pointer to it as the first argument.

When GCC/MinGW-W64 compiles the sample code from the article, the caller only sets up one argument (in rcx) for the call to GetSize, and expects the value to be returned in rax:

# AT&T syntax (destination operand last)
mov 0x10(%rbx),%rcx    # rcx <- pointer to IRenderContext
mov (%rcx),%rax        # rax <- pointer to virtual function table
callq *0x1a8(%rax)     # virtual function call (expect result in rax)

In the code generated by Visual Studio, the caller sets up rdx to point to a location on the stack before calling GetSize:

# Intel syntax (destination operand first)
mov rax,qword ptr [rsp+168h]     # rax <- pointer to IRenderContext
mov rax,qword ptr [rax]          # rax <- pointer to virtual function table
lea rdx,[rsp+68h]                # rdx <- address of return value (hidden argument)
mov rcx,qword ptr [rsp+168h]     # rcx <- this pointer (hidden argument)
call qword ptr [rax+1A8h]        # virtual function call (expect result at [rdx])

On GCC/MinGW-W64 the value in rdx is not a valid address, so when the implementation of GetSize attempts to store the return value in memory there, an access violation occurs.

D2D_SIZE_F is a 64-bit POD struct (just a struct of two floats), so it seems to me that GCC is correct to return it in the rax register. I don't know what it is that makes Visual Studio use return-by-pointer, nor, I'm afraid, how to make GCC do the same for compatibility.



Answered By - Buster
Answer Checked By - Mildred Charles (WPSolving Admin)