D3DXFont remembering former aspect ratio.

D619d95cddb1edb227f51ef539d15cdc
0
Nautilus 103 May 02, 2012 at 17:51

Greetings.
I’m having a weird problem I can’t seem to find the source of. This has been pausing me for a while now. It’s a mildly complex situation. Read carefully.

First I give you a basic description of the ‘general’ picture:
I have a DummyD3D app I wrote for test purpose. DummyD3D starts and creates an empty overlapped window. Then waits for user input. User input can be either Return or Shift+Return.

On Return:

  • IDirect3D9 is instanced.
  • IDirect3DDevice9 is instanced.
  • ID3DXFont is created and used to display some ‘service text’.

On Shift+Return:

  • ID3DXFont is released.
  • IDirect3DDevice9 is released.
  • IDirect3D9 is released.

The user can cycle Return -> Shift+Return -> Return -> Shift+Return … at will.

The problem I’m having:
The Font object is somewhat reminiscent of its former properties, and will retain the aspect ratio it had assumed on the very first time the user pressed Return. No matter how much I Shift+Return, then resize the window, then hit Return again. The new font object will possess the same aspect ratio of the previous one… as if the Font/Device were never released. I can create a different (facename) Font object at every new attempt, but the aspect ratio shall always be the same.

So, to recap:

  1. I set the window Height to match half the height of the screen.
  2. I hit Return to start rendering.
  3. D3D created. Device created. Font created with a preset height of 50 pixels. Rendering starts.
  4. The font is displayed and its height is measured in 50 pixels <– correct
  5. I hit Shift+Return.
  6. Rendering stops. Font->Release(). Device->Release(). D3D->Release().
  7. I maximize the window, so its Height is now roughly twice as much.
  8. I hit Return again.
  9. D3D created. Device created. Font created with a preset height of 50 pixels. Rendering starts.
  10. The font is displayed and its height is measured in \~100 pixels <– wrong!

Now the missing details, an in-depth description of the complete picture:
DummyD3D only renders an empty scene as fast as it can. DummyD3D has no notion of fonts, service text, windows’ size or what not. The Font object and the ‘service text’ displayed on the RenderTarget are coming from an external app I have injected -at runtime- into DummyD3D. This app (which I’m still writing) will intercept the calls to the IDirect3D9 and IDirect3DDevice9 interfaces by wrapping them into a wrapper class. The caller (DummyD3D) will be returned a pointer of the Dx interfaces wrapped in my class. This I do both for IDirect3D9 and IDirect3DDevice9 interfaces.

I’m afraid there’s little code to show. For the most part I just forward the calls made by DummyD3D to the underlying Dx interfaces. Anyway, the following illustrates what I do:

(NOTE : For simplicity’s sake, IInterface is a placeholder name for both IDirect3D9 and IDirect3DDevice9 interfaces)

// We have the COM interface IInterface which inherits from IUnknown.
interface __declspec(novtable) IInterface : public IUnknown
{
    // All pure virtual methods.
    // No Ctor nor Dtor exist.
    //  .
    //  .
    //  .
};

And we have my wrapper class, to wrap IInterface:

class Wrap : public IInterface
{
    private:
        IInterface* p;

    public:
        Wrap (const IInterface* ptr) : p (ptr) {}
        virtual ~Wrap () {}

        // Implement all pure virtual methods from IInterface.
        //  .
        //  .
        //  .
};

When I detect that an IInterface is instanced, I intercept the pointer and wrap it in my class.
Then I return my wrapper class to the caller. Like this:

IInterface* pII = /* ... */

if (pII)
{
    // Wrap* g_pWrap <-- defined globally.
    g_pWrap = new Wrap (pII);
    if (g_pWrap)
    {
        pII = static_cast (g_pWrap);
    }
}

return pII;

My wrapper logs all activity so I see what’s being called and in which order. The wrapped interfaces appear to be functioning properly at all times. Yet I have that Font aspect ratio problem I told above. I thought that the bug hides in the Release() calls.

But here’s the implementation of Wrap::Release()

ULONG Wrap::Release (void)
{
    // With p = IInterface*
    return this->p->Release ();
}

\^\^ Which couldn’t be any simpler…

… but I have also used this, just to make sure:

ULONG Wrap::Release (void)
{
    // With p = IInterface*
    ULONG Ret;
    while (Ret = this->p->Release ()) {};

    return Ret;
}

\^\^ Which confirmed that Release() is to be called only once, as ‘Ret’ is correctly set to 0 on the first call (meaning: no outstanding references).

[__edit__]
Before you think that I’m not releasing the Font interface when I should, let me clarify. The Device::Release() code (when IInterface is IDirect3DDevice9) looks like this:

ULONG Wrap::Release (void)
{
    if (...->pServiceFont)
    {
        ...->pServiceFont->Release ();
        ...->pServiceFont = NULL;
    }

    // With p = IDirect3DDevice9*
    return this->p->Release ();
}

[/__edit__]

But when I Release() everything and start anew (that is, when -in DummyD3D- the user hits Shift+Return, followed by Return), the Font interface is reminiscent of its former aspect ratio. It makes no sense.

Here is how I handle the calls to Device::Reset and Device::TestCooperativeLevel. As you can expect I do a little extra just to manage the Font object myself.

HRESULT Wrap::TestCooperativeLevel (void)
{
    // With p = IDirect3DDevice9*
    const HRESULT hRes = this->p->TestCooperativeLevel ();
    if (hRes == D3DERR_DEVICELOST)
    {
        ...->pServiceFont->OnLostDevice ();
    }
    return hRes;
}

HRESULT Wrap::Reset (/* params */)
{
    ...->pServiceFont->OnLostDevice ();

    // With p = IDirect3DDevice9*
    const HRESULT hRes = this->p->Reset (/* params */);
    if (SUCCEEDED (hRes))
    {
        ...->pServiceFont->OnResetDevice ();
    }
    return hRes;
}

When you create the IDirect3DDevice9 interface, DirectX starts a new thread.
I have checked. When the device is released, this thread terminates readily. Always.

My guess is that the device isn’t being fully released. The font object is merely stretched on the RenderTarget, bound to the aspect ratio imposed by the device. It’s the device (or a part of it) that’s somehow remainnig in memory. Just how? What am I missing?

1 Reply

Please log in or register to post a reply.

D619d95cddb1edb227f51ef539d15cdc
0
Nautilus 103 May 02, 2012 at 21:36

There’s no problem with the Font nor with the Device (I knew my code was fine. I told it was making no sense!)
It’s the presentation parameters. DummyD3D was re-using the same structure for every call to CreateDevice(). The first time around, the backbuffer Width and Height were set to 0 by me. After the 1st call to CreateDevice() they were silently modified by DirectX. And that’s how the next device would remember its former Width and Height (which I never explicited myself) and from there impose the old aspect ratio on the service text font. And I spent so many days to chase a problem that wasn’t a problem…

It’s official, I’m getting old.
*jokes-suit ON* :ph34r: