D3D9 drawing sprites appears to be slow

-1

My application needs to draw many sprites. But somehow this appears to be slow (30 airplanes which are fighting each other, means + ~30 animated rockets and explosions).

How can I fix this so it animates smoothly? Thanks for help in advance!

Don't know how to approach the problem.

struct d3dsprite_s {
    std::wstring wszFile; //File name
    LPD3DXSPRITE pSprite; //Pointer to the sprite object
    LPDIRECT3DTEXTURE9 pTexture; //Pointer to the sprite texture
    int iFrameCount; //Total amount of frames
    int iFramesPerLine; //Amount of frames per line
    int iFrameWidth; //Single frame width
    int iFrameHeight; //Single frame height
};

std::vector<d3dsprite_s> m_vSprites;

HD3DSPRITE LoadSprite(const std::wstring& wszTexture, int iFrameCount, int iFrameWidth, int iFrameHeight, int iFramesPerLine, const bool bForceCustomSize = false)
    {
        //Load sprite

        if (!wszTexture.length())
            return GFX_INVALID_SPRITE_ID;

        d3dsprite_s sSpriteData;

        //Create sprite
        if (FAILED(D3DXCreateSprite(this->m_pDevice, &sSpriteData.pSprite)))
            return GFX_INVALID_SPRITE_ID;

        //Load texture from file
        if (FAILED(D3DXCreateTextureFromFileEx(this->m_pDevice, wszTexture.c_str(), (bForceCustomSize) ? iFrameWidth : D3DX_DEFAULT_NONPOW2, (bForceCustomSize) ? iFrameHeight : D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT, 0, D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0xFF000000, nullptr, nullptr, &sSpriteData.pTexture))) {
            sSpriteData.pSprite->Release();
            return GFX_INVALID_SPRITE_ID;
        }

        //Save further data
        sSpriteData.wszFile = wszTexture;
        sSpriteData.iFrameCount = iFrameCount;
        sSpriteData.iFramesPerLine = iFramesPerLine;
        sSpriteData.iFrameWidth = iFrameWidth;
        sSpriteData.iFrameHeight = iFrameHeight;

        //Add to list
        this->m_vSprites.push_back(sSpriteData);

        //Return handle
        return sSpriteData.pSprite;
    }

bool DrawSprite(const HD3DSPRITE hSprite, int x, int y, int iFrame, float fRotation, int rotx, int roty, float fScale1, float fScale2, const bool bUseCustomColorMask, byte r, byte g, byte b, byte a)
    {
        //Add sprite to drawing list

        if (!hSprite)
            return false;

        //Find sprite
        size_t uiSprite = this->FindSprite(hSprite);
        if (uiSprite == GFX_INVALID_LIST_ID)
            return false;

        if ((!this->m_vSprites[uiSprite].pTexture) || (!this->m_vSprites[uiSprite].pSprite))
            return false;

        //Calculate horizontal line ID
        int iFrameLineId = (this->m_vSprites[uiSprite].iFramesPerLine > 0) ? iFrame / this->m_vSprites[uiSprite].iFramesPerLine : 0;
        //Calculate vertical frame ID
        int iFrameVerticalId = (this->m_vSprites[uiSprite].iFramesPerLine > 0) ? iFrame % this->m_vSprites[uiSprite].iFramesPerLine : 0;

        //Calculate x position of frame
        int iFrameXPos = iFrameVerticalId * this->m_vSprites[uiSprite].iFrameWidth;
        //Calculate y position of frame
        int iFrameYPos = iFrameLineId * this->m_vSprites[uiSprite].iFrameHeight;

        //Setup rectangle info
        RECT sRect;
        sRect.top = iFrameYPos;
        sRect.left = iFrameXPos;
        sRect.bottom = iFrameYPos + this->m_vSprites[uiSprite].iFrameHeight;
        sRect.right = iFrameXPos + this->m_vSprites[uiSprite].iFrameWidth;

        //Begin drawing sprite
        if (FAILED(this->m_vSprites[uiSprite].pSprite->Begin(D3DXSPRITE_ALPHABLEND)))
            return false;

        //Setup vector with position
        D3DXVECTOR2 vPosition = { (float)x, (float)y };

        //Setup vector with sprite center
        D3DXVECTOR2 vRotCenter;
        if ((rotx != -1) && (roty != -1))
            vRotCenter = { (float)(rotx), (float)(roty) };
        else
            vRotCenter = { (float)(this->m_vSprites[uiSprite].iFrameWidth / 2) , (float)(this->m_vSprites[uiSprite].iFrameHeight / 2) };

        D3DXMATRIX vNewMatrix;

        //Setup scale vector
        D3DXVECTOR2 vScale(fScale1, fScale2);

        //Calculate transformation matrix
        if (!D3DXMatrixTransformation2D(&vNewMatrix, ((fScale1 != 0.0f) && (fScale2 != 0.0f)) ? &vRotCenter : nullptr, 0.0f, ((fScale1 != 0.0f) && (fScale2 != 0.0f)) ? &vScale : nullptr, (D3DXVECTOR2*)&vRotCenter, fRotation, &vPosition))
            return false;

        //Apply transformation
        if (FAILED(this->m_vSprites[uiSprite].pSprite->SetTransform(&vNewMatrix)))
            return false;

        //Add sprite to batch list
        if (FAILED(this->m_vSprites[uiSprite].pSprite->Draw(this->m_vSprites[uiSprite].pTexture, &sRect, nullptr, nullptr, (bUseCustomColorMask) ? D3DCOLOR_RGBA(r, g, b, a) : 0xFFFFFFFF)))
            return false;

        //End drawing sprite
        return SUCCEEDED(this->m_vSprites[uiSprite].pSprite->End());
    }

const size_t FindSprite(const HD3DSPRITE hSprite)
    {
        //Get list ID of sprite handle if exists

        for (size_t i = 0; i < this->m_vSprites.size(); i++) {
            if (this->m_vSprites[i].pSprite == hSprite)
                return i;
        }

        return GFX_INVALID_LIST_ID;
    }

Expected: Fast drawing of sprites Actual: Slow drawing of sprites

c++
directx
sprite
directx-9
asked on Stack Overflow Jan 18, 2019 by dcdr

1 Answer

0

The primary problem is that you are using a distinct ID3DX11Sprite instance for each sprite, at least based on the code above. This eliminates any chance for the sprite mechanism to batch up renders, and you are therefore wasting maximum time rendering. It has to capture state, update a VB/IB, set a VB/IB and all rendering state, draw 4 points, then restore state on every sprite. That's going to be slow.

You should have one instance of ID3DX11Sprite for all your drawing, and draw as many sprites as you can between one call to Begin and one call to End.

Direct3D 9 is legacy. ID3DX11Sprite is part of D3DX9 which is deprecated, and only available in the end-of-life DirectX SDK. See Microsoft Docs.

You are much better off using Direct3D 11 or Direct3D 12 for any modern application. SpriteBatch in DirectX Tool Kit provides the functionality you need to draw sprites efficiently, along with texture loaders, etc. See Living without D3DX.

answered on Stack Overflow Jan 19, 2019 by Chuck Walbourn

User contributions licensed under CC BY-SA 3.0