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
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 ofD3DX9
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.
User contributions licensed under CC BY-SA 3.0