Jump to content
  • Advertisement
Sign in to follow this  
Devnull

Dynamic Textures (for radar, etc)

This topic is 4900 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

In my game currently, I have a HUD and one of the elements is a radar that shows the player what's around them. The way I do the radar is using a dynamic texture. First, I load a texture from disk and copy the contents into memory. Then every frame, I use this buffer to overwrite the texture, then I add dots for various objects. Finally, I "blit" it onto a textured quad to render it (using a sprite to actually draw it). It works as it should (though I potentially would have problems with cards where the "pitch" of the D3DLOCKED_RECT doesn't match the actual texture width. One problem at a time =) ). However, unsurprisingly, memcpy'ing the buffer to the texture is very, very slow. This one operation is basically taking up approx 25% of my frame calculation time! The size of the original bitmap doesn't seem to matter much, which IS a surprise (I've tried it with a 128x128 and a 64x64 texture and they both take about the same amount of time to memcpy), but the monitor resolution DOES matter (a lot!). Neither of these anomalies make any sense to me, but the facts are the facts. Anyway, I'm fairly sure that there's a better way to do this. So, I'm asking the gurus here if they have a better way that will accomplish the same thing. Here's the code I'm currently using to do all this:
// original input data read from config file:
//
//<DXResource type="Texture" name="HUD1.png" file="HUD1.png" />
//<DXResource type="Texture" name="HUD2.png" file="HUD2.png"
//            pool="default" usage="dynamic" mipLevels="1" />
//
//<HUD type="HUD" name="HUD" typeFlags="eTypeDecoration">
//        <Element type="background" texture="HUD1.png" srcRect="0,0,384,128" destRect="0,-178,384,-50" />
//        <Element type="radar" texture="HUD2.png" srcRect="0,0,64,64" destRect="-328,0,-200,128" radarRadius="256.0" />
//</HUD>

HRESULT
HUD::onFrameWait()
{
    PROFILE_Sample( HUD_onFrameWait );
    if( m_bUpdated )
    {
        return S_OK;
    }
    Vector2 v2Scale;
    Vector2 v2Pos;
    Scene* pScene = SceneManager::getCurrentScene();
    failWarn( pScene, NULL, _T( "No Current Scene!" ) );
    std::set<GameObject*> lpPlayers;
    lpPlayers = pScene->getGameObjectsOfType( GameObject::eTypePlayer );
    Player* pPlayer = (Player*)( *(lpPlayers.begin()) );
    RECT rcRealBackgroundRect = m_lrcDestRects[ HUD::eBackground ];
    RECT rcDestRect;
    Rect2D rcTempRect;
    Vector2 v2BaseScale;
    const D3DSURFACE_DESC* pBackBufferSurfaceDesc = DXUTGetBackBufferSurfaceDesc();
    HRESULT hr;

    for( int i = HUD::eHUDObjectsBegin; i < HUD::eHUDObjectsEnd; ++i )
    {
        if( m_lhTextureHandles[ i ] != HUD::eBackground )
        {
            // copy the original dest rect
            rcDestRect = m_lrcDestRects[ i ];
            if( rcDestRect.left < 0 )
            {
                rcDestRect.left = pBackBufferSurfaceDesc->Width + rcDestRect.left;
            }
            if( rcDestRect.right < 0 )
            {
                rcDestRect.right = pBackBufferSurfaceDesc->Width + rcDestRect.right;
            }
            if( rcDestRect.top < 0 )
            {
                rcDestRect.top = pBackBufferSurfaceDesc->Height + rcDestRect.top;
            }
            if( rcDestRect.bottom < 0 )
            {
                rcDestRect.bottom = pBackBufferSurfaceDesc->Height + rcDestRect.bottom;
            }

            // offset it by base rect
            if( i != HUD::eBackground )
            {
                // determine base scaling
                v2Scale.x = fast_cast<float32,LONG>( rcDestRect.right - rcDestRect.left )
                            / fast_cast<float32,LONG>( m_lrcSrcRects[ i ].right - m_lrcSrcRects[ i ].left );
                v2Scale.y = fast_cast<float32,LONG>( rcDestRect.bottom - rcDestRect.top ) 
                            / fast_cast<float32,LONG>( m_lrcSrcRects[ i ].bottom - m_lrcSrcRects[ i ].top );
                rcTempRect.x0 = fast_cast<float32,LONG>( rcDestRect.left );
                rcTempRect.x1 = fast_cast<float32,LONG>( rcDestRect.right );
                rcTempRect.y0 = fast_cast<float32,LONG>( rcDestRect.top );
                rcTempRect.y1 = fast_cast<float32,LONG>( rcDestRect.bottom );

                rcTempRect.x0 *= v2BaseScale.x;
                rcTempRect.x1 *= v2BaseScale.x;
                rcTempRect.y0 *= v2BaseScale.y;
                rcTempRect.y1 *= v2BaseScale.y;

                rcTempRect.x0 += ( fast_cast<float32,LONG>( rcRealBackgroundRect.left ) );
                rcTempRect.x1 += ( fast_cast<float32,LONG>( rcRealBackgroundRect.left ) );
                rcTempRect.y0 += ( fast_cast<float32,LONG>( rcRealBackgroundRect.top ) );
                rcTempRect.y1 += ( fast_cast<float32,LONG>( rcRealBackgroundRect.top ) );

                rcDestRect.left = realToInt32( rcTempRect.x0 );
                rcDestRect.top = realToInt32( rcTempRect.y0 );
                rcDestRect.right = realToInt32( rcTempRect.x1 );
                rcDestRect.bottom = realToInt32( rcTempRect.y1 );

                v2Scale *= v2BaseScale;
            }
            else
            {
                // set base rect for future elements
                rcRealBackgroundRect = rcDestRect;

                // determine base scaling
                v2Scale.x = fast_cast<float32,int32>( rcDestRect.right - rcDestRect.left ) 
                            / fast_cast<float32,int32>( m_lrcSrcRects[ i ].right - m_lrcSrcRects[ i ].left );
                v2Scale.y = fast_cast<float32,int32>( rcDestRect.bottom - rcDestRect.top ) 
                            / fast_cast<float32,int32>( m_lrcSrcRects[ i ].bottom - m_lrcSrcRects[ i ].top );
                v2BaseScale = v2Scale;
            }

            // determine final scaling/position
            switch( i )
            {
                //
                // snip
                //
            case HUD::eRadar:
                {
                    PROFILE_Sample( HUD_radar );
                    std::set<GameObject*> lpTargets;
                    lpTargets = pScene->getGameObjectsOfType( GameObject::eTypeCharacter );
                    std::set<GameObject*>::iterator kIter;
                    Rect3D rc3SearchRect = pPlayer->getSceneObject()->getWorldBoundingBox();
                    SceneObject* pSceneObject = NULL;
                    Vector3 v3Position = pPlayer->getSceneObject()->getPosition();

                    D3DLOCKED_RECT kLockedRect;
                    Texture* pTextureTemp = (Texture*)TheGameManager.
                                                getDXResourceManager()->getDXResource( m_lhTextureHandles[ i ] );
                    LPDIRECT3DTEXTURE9 pTexture = pTextureTemp->getTexture();
                    int x,y;
                    uint32 unColor = 0xFFFF0000;
                    static int32 nRadarWidth = m_lrcSrcRects[ i ].right - m_lrcSrcRects[ i ].left;
                    static int32 nRadarHeight = m_lrcSrcRects[ i ].bottom - m_lrcSrcRects[ i ].top;
                    static int32 nSizePixels = sizeof( m_lunRadarBitmapPixels ) * nRadarWidth * nRadarHeight;
                    static float32 fScaleX = m_fRadarRadius / fast_cast<float32,int32>( nRadarWidth / 2 );
                    static float32 fScaleY = m_fRadarRadius / fast_cast<float32,int32>( nRadarHeight / 2 );

                    ::ZeroMemory( &kLockedRect, sizeof( D3DLOCKED_RECT ) );
                    if( !m_bIsRadarInitialized )
                    {
                        FULL_ARRAY_DELETE( m_lunRadarBitmapPixels );
                        V_RETURN( pTexture->LockRect( 0, &kLockedRect, NULL, D3DLOCK_READONLY ) );
                        uint32* pSrc = (uint32*)kLockedRect.pBits;

                        // TODO: allocate for size + extra "pitch" for older vid cards
                        m_lunRadarBitmapPixels = new uint32[ nSizePixels ];
                        memcpy( m_lunRadarBitmapPixels, pSrc, nSizePixels );
                        pTexture->UnlockRect( 0 );

                        m_bIsRadarInitialized = true;
                        ::ZeroMemory( &kLockedRect, sizeof( D3DLOCKED_RECT ) );
                    }
                    PROFILE_Begin( HUD_copyRadar );
                    V_RETURN( pTexture->LockRect( 0, &kLockedRect, NULL, D3DLOCK_DISCARD ) );
                    uint32* pDest = (uint32*)kLockedRect.pBits;

                    // copy the base map
                    memcpy( pDest, m_lunRadarBitmapPixels, nSizePixels );
                    PROFILE_End( HUD_copyRadar );

                    // find all viewable objects in radius
                    PROFILE_Begin( HUD_findTargets );
                    rc3SearchRect.expandX( m_fRadarRadius );
                    rc3SearchRect.expandY( m_fRadarRadius );
                    rc3SearchRect.expandZ( m_fRadarRadius );
                    pSceneObject = pScene->getQuadTree()->buildSearchResults( rc3SearchRect );
                    PROFILE_End( HUD_findTargets );
                    while( pSceneObject )
                    {
                        Vector3 v3ObjectPosition = pSceneObject->getPosition();
                        if(    pSceneObject->getFlags().testBit( SceneObject::e_kDoNotShowObjectStats ) 
                            || pSceneObject->getFlags().testBit( SceneObject::e_kDoNotDraw ) 
                            || v3Position.distanceSquared( v3ObjectPosition ) > Square( m_fRadarRadius ) )
                        {
                            pSceneObject = pSceneObject->getForwardSearchLink();
                            continue;
                        }
                        switch( pSceneObject->getUserDefinedType() )
                        {
                        case SCENEOBJECT_TYPE_PLAYER:
                            unColor = 0xFFFFFFFF;
                            break;
                        case SCENEOBJECT_TYPE_ENEMY_BALL:
                            unColor = 0xFFFF0000;
                            break;
                        case SCENEOBJECT_TYPE_POWERUPITEM:
                            unColor = 0xFF00FF00;
                            break;
                        case SCENEOBJECT_TYPE_BOID:
                            unColor = 0xFF0000FF;
                            break;
                        default:
                            pSceneObject = pSceneObject->getForwardSearchLink();
                            continue;
                        }
                        x = realToInt32(   ( ( v3ObjectPosition.x - v3Position.x ) / fScaleX )
                                         + ( fast_cast<float32,int32>( nRadarWidth ) * 0.5f ) );
                        y = nRadarHeight - realToInt32( ( ( v3ObjectPosition.z - v3Position.z ) / fScaleY ) 
                                                        + ( fast_cast<float32,int32>( nRadarHeight ) * 0.5f ) );
                        pDest[ y * nRadarWidth + x ] = unColor;
                        pSceneObject = pSceneObject->getForwardSearchLink();
                    }

                    pTexture->UnlockRect( 0 );

                    v2Pos.x = fast_cast<float32,int32>( rcDestRect.left ) / v2Scale.x;
                    v2Pos.y = fast_cast<float32,int32>( rcDestRect.top ) / v2Scale.y;
                }
                break;
                //
                // snip
                //
            }
            
            TheGameManager.getDisplayManager()->addTexturedQuad( m_lhTextureHandles[ i ],
                                                                 m_lrcSrcRects[ i ],
                                                                 v2Pos,
                                                                 v2Scale,
                                                                 m_lunColors[ i ] );
        }
    }
    m_bUpdated = true;

    return S_OK;
}



[Edited by - Devnull on March 16, 2005 3:44:31 PM]

Share this post


Link to post
Share on other sites
Advertisement
The problem is, of course, the locking, writing and unlocking of the texture. It means firstly that there is no way the texture can be held on the graphics card and so there will be an update of the texture each time. Secondly when you lock you cause a stall in the video card - this problem can be lessened somewhat via usage hints flags e.g. discard. Anyway perhaps the solution is to not write to the texture but overlay textures i.e. draw the dots over the radar and hence avoid ever writing to a texture.

Share this post


Link to post
Share on other sites
Hmmm, actually, yeah, that seems much more efficient.

*headdesk*

So, now all I have to do is figure out how to efficiently render points in 2D space. =) Maybe a point sprite? I'll research it.

Anyway, thanks for your help. I really am doing this the hard way, it seems =)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!